Go1.13のGOPATHとgoコマンドの挙動の関係を図で整理した @Tomofiles

目次

はじめに

こんにちは、Tomofilesです。

Go1.13でGo Modulesがデフォルトで利用可能になってから、半年以上経ちました。
Go Modulesのモジュール対応モードで開発するときに、私は以下がイメージできなくて困ることがありました。

  • PATHやGOPATHに設定する内容
  • 依存するモジュールがどこにインストールされるのか
  • go build、go installしたときの成果物の生成

この記事では、モジュール対応モードでGoのコードを書いて、ビルド、実行するときの、環境のイメージと各成果物の生成について、図で表現して整理しています。

Goを学習されている方の、理解の助けになれれば、幸いです。

環境

  • OS:Ubuntu Desktop 18.04.4 LTS
  • Go:1.13.7
  • パッケージ管理:Go Modules

​モジュール対応モードでの環境イメージ​

Goのモジュール対応モードでの環境の全体像をイメージ図にしてみると、下図のようになります。

go env figure 1

ディレクトリーツリーについて

それぞれのディレクトリーツリーの概要は、以下の通りです。
ディレクトリーツリーに付けている名前は、公式の名称ではなく私が便宜的に付けた名前です。

Goインストールディレクトリー

Go公式のインストール手順に従うと、Goのバイナリは​/usr/local​配下にインストールされます。

​Go - Getting Started​

手順では、​/usr/local/go/bin​にPATHを通して、bin配下のgoコマンドを使えるようにしています。
上図の3列あるうち、一番左のツリーがGoのインストールディレクトリーを示しています。

GOROOTはGoが自動的に認識しているので、特に設定不要です。

GOPATHディレクトリー

さて、ググると見つかるgoの環境構築手順では、GOPATHも環境変数に設定しているのをよく見ます。

公式の手順では、GOPATHの説明はコードの書き方でちょろっと書かれているだけで、Go1.13現在は環境変数への設定は必須ではないようですね。

​Go - How to Write Go Code​

特に設定しない場合、$HOME/goがGOPATHだと認識されるようです。
ただ、まぁ慣習というか、明示的に設定しておいても良いかもしれません。
(ネット上のどの記事もそんな感じの説明ですね)

上図の真ん中のツリーが、GOPATHで指定した$HOME/goを示しています。
このGOPATH配下のbinも、PATHを通しておきます。

ワークスペースディレクトリー

最後に、ワークスペースディレクトリーです。
ワークスペースディレクトリーは、何かアプリケーションを開発しようとしたときに作成するディレクトリーです。

ワークスペースディレクトリーは任意の場所に作成して問題ありません。
上図の一番右のツリーがワークスペースディレクトリーを示しており、例として$HOME/workspaceというディレクトリーで用意しています。

環境変数への設定

私のPC環境では、Ubuntuデスクトップを使用しているので、環境変数の変更は$HOME/.profileもしくは$HOME/.bash_profileを更新します。
$HOME/.bash_profileの方だと、GUIのアイコン起動のターミナルでは読み込まれないみたいなので、私は$HOME/.profileを更新しました。

# .profile
# 〜省略〜

export GOPATH="$HOME/go"
export PATH="$PATH:$GOPATH/bin"
export PATH="$PATH:/usr/local/go/bin"

こんな感じに、ファイルの末尾に追加します。

Visual Studio Codeでの開発

ここから、上記で構築した環境を前提に、Visual Studio Code(vs code)で開発を始めた場合を想定して、Goの挙動を追っていきます。

開発支援ライブラリ

vs code上でGoの環境構築をする際に、いくつか開発支援ライブラリ(Go tools)をインストールすることになると思います。
下図は、Go toolsが一つもインストールされていない状態で、ワークスペースを開いたときのスクリーンショットです。

Screenshot 2020 04 24

かなり見づらいですが、画面右下にAnalysis Tools Missingと表示されています。
カーソルを合わせると、Not all Go tools are available on the GOPATHとツールチップが表示されます。

クリックすると、インストールダイアログが表示されるので、ここからインストールできます。
インストールが成功すると、Goという表示に変わります。

今回は説明しないですが、開発支援としてはLanguage Serverも導入すると快適なので、goplsをインストールしておきましょう。

さて、ここでインストールされたGo toolsたちは、どこに格納されたでしょうか?

さきほどインストールする前のツールチップにGOPATHというワードが出ているのでお気づきかと思いますが、GOPATHディレクトリーに格納されています。

go env figure 3

GOPATHディレクトリーにインストールされたGo toolsは、適宜、配下のbinやpkgに資材が格納されます。

$ ls -1 $GOPATH/bin
go-outline
gocode
gocode-gomod
godef
goimports
golint
gopkgs
gopls

vs codeで利用するGo toolsがGOPATHディレクトリー配下にインストールされたので、以後、別のワークスペースを作成して開発する際も、Go toolsは有効な状態となります。

依存モジュール

続いて、実際に開発を始めた際の、自分でコーディングしたソースコードが依存している、サードパーティのモジュールについてです。

その前に、この記事でサンプルとして使用するプロジェクトを先に紹介します。
ワークスペースの構成と、各種プログラムを以下に示します。

$ tree
.
├── cmd
│   ├── stdir_first
│   │   └── main.go
│   └── stdir_second
│       └── main.go
├── go.mod
├── go.sum
└── pkg
    └── stdir
        ├── first
        │   └── first.go
        └── second
            └── second.go

7 directories, 6 files
// cmd/stdir_first/main.go
package main

import (
	"fmt"
	"stdir/pkg/stdir/first"
)

func main() {
	fmt.Println(first.Message())
}
// cmd/stdir_second/main.go
package main

import (
	"fmt"
	"stdir/pkg/stdir/second"
)

func main() {
	fmt.Println(second.Message())
}
// pkg/stdir/first/first.go
package first

import "github.com/go-shadow/moment"

func Message() string {
	return "First Message " + moment.New().Format("YYYY/MM/DD HH:mm:ss")
}
// pkg/stdir/second/second.go
package second

import "github.com/go-shadow/moment"

func Message() string {
	return "Second Message " + moment.New().Format("YYYY/MM/DD HH:mm:ss")
}
// go.mod
module stdir

go 1.13

require (
	github.com/go-shadow/moment v0.0.0-20140422073900-e837f27dad94
	github.com/smartystreets/goconvey v1.6.4 // indirect
)

ただ、メッセージと日付が表示されるプログラムです。
cmdpkgは、Goの標準的なディレクトリー構成に則っています。

GitHub - Standard Go Project Layout

サードパーティモジュールとして、日付ライブラリを使用しています。
もちろんパッケージ標準のtimeパッケージで機能は十分なのですが、今回は外部依存の例として使用しています。

さて、モジュール対応モードなのでgo.modが生成されていますが、vs codeでは、この状態ですでに依存モジュールが自動でインストールされます。
(Language Serverによるものか、vs codeによるものかは、ちょっと調べてません)

依存モジュールのインストール先がどこかというと、もちろん、GOPATHディレクトリーですね。
依存モジュールはワークスペースの中には、格納されません。

go env figure 4

ワークスペースの中からの依存モジュールへの参照(青い矢印)は、$GOPATHを参照して場所を特定します。

依存モジュールがGOPATHディレクトリー配下に格納されるので、異なるワークスペースでインストールされた依存モジュールを共有します。
JavaのMavenのような、リモートリポジトリーと、ローカルリポジトリーの考え方と似ていますね。

ビルド・インストール

開発したプロジェクトは、次に、ビルドして実行ファイルを生成すると思います。
Goでは、ビルド、インストールは、それぞれ、以下のように実行します。

ビルド

ビルドは以下のコマンドで実行できます。ワークスペースのルートで実行します。

$ go build ./...

ビルドコマンドは、基本的に最終成果物の実行ファイルは生成しません。
コンパイルを行い、生成された中間成果物をローカルビルドキャッシュというキャッシュに保存します。

よくネット上の記事で-oオプションを付けて、ビルドコマンドで実行ファイルを生成しているのを見ますが、インストールコマンドが別に用意されているので、ビルドはあくまでコンパイルだけを行う目的で使ったほうが、シンプルだと個人的に思います。

クロスコンパイルの場合、話が別らしいですが、通常のコンパイルなら問題ないと思います
Just one side of me - go build vs go install

ビルド時にキャッシュを生成しているので、何か高速化・効率化とかを図っているんですかね。

go env figure 5

コマンド引数の./...は、ワークスペース配下のサブディレクトリーを再帰的にスキャンして、main関数を探してビルドするという意味です。
今回のサンプルプロジェクトでビルドすると、cmd配下の2つのmain.goがエントリーポイントとして認識されて、ビルドが行われます。

インストール

インストールは以下のコマンドで実行できます。ワークスペースのルートで実行します。

$ go install ./...

インストールコマンドは、最終成果物の実行ファイルを生成して、インストールを行います。
ビルドも同時に行われるので、このコマンドを一発叩くだけでも問題ありません。

生成された実行ファイルは、$GOPATH/binに格納されます。
ワークスペース配下ではないんですよね。これが、最初はよく理解できず、不思議に感じていました。

go env figure 6

図からは省略してしまいましたが、ビルド時に生成したローカルキャッシュも、インストール時に使用しているので、ビルド時間が短縮されます

今回のサンプルプロジェクトでインストールすると、$GOPATH/binに以下のように実行ファイルが生成されます。

$ ls -1 $GOPATH/bin
go-outline
gocode
gocode-gomod
godef
goimports
golint
gopkgs
gopls
stdir_first
stdir_second

Go toolsのモジュールと並んで、ビルドされた実行ファイルが格納されています。
上図の赤い点線の枠は、PATHが通ったディレクトリーを意味しており、このディレクトリーに展開されると、絶対パス・相対パスによる指定をせずとも、実行できるようになります。

$ stdir_first
First Message 2020/04/24 23:14:56
$ stdir_second
Second Message 2020/04/24 23:15:04

これが、つまるところ、インストールされた ということになります。
どうしても、実行ファイルを生成することに意識が向かいがちですが、改めてGoの環境を鳥瞰してみると、然るべき場所に配置し、実行可能な状態にするところまで、Goは考えられているということですね。

今回は紹介しないですが、Dockerのgolangイメージでビルド、実行するときも、考え方は同じです。
そのため、任意の場所でビルド・インストールすると、$GOPATH/binとして定義してある/go/binに実行ファイルが展開されます。

終わりに

個人的にGoを使い始めてから1年ちょっと経過してるので、ここらで改めて整理しておきたいということで、この記事を書きました。

図でGoの環境周りを説明されている記事が、意外とネット上になくて、目で見て動きが追えるような資料があればいいなと思っていたところだったので、このテックブログ開設の記念すべき1記事目に書いてみました。

Goを学習している誰かの、理解の助けになれば良いなと思います。

何か、間違い、疑問、コメント等がありましたが、お手数ですがTwitterまで連絡ください。

それでは、今回はこの辺で。

  • この記事をはてなブックマークに追加
Tomofiles

Tomofiles

Drone Software Developer.

Read More