はじめに
Debian 11 を使い始めていて、Go を apt でインストールしたのですが、バージョンが 1.15.15 と古いものでした。
% go version go version go1.15.15 linux/amd64
なので新しいバージョンのものをインストールしてみることにしました。 goenv とかを使う手もありますが、特にこだわりもないのでソースコードからインストールしてみました。
環境
環境は以下のようなものです。
% lsb_release -a No LSB modules are available. Distributor ID: Debian Description: Debian GNU/Linux 11 (bullseye) Release: 11 Codename: bullseye
% uname -vos Linux #1 SMP Debian 5.10.136-1 (2022-08-13) GNU/Linux
インストール
ソースを取得して /tmp/
に配置します。
% wget https://go.dev/dl/go1.20.linux-amd64.tar.gz -P /tmp/
ローカルにインストールするので、ホームディレクトリに .local
ディレクトリを作成します。
% mkdir $HOME/.local
取得したソースを ~/.local
に展開します。
% tar -C $HOME/.local -xzf go1.20.linux-amd64.tar.gz
~/.local/go/bin
にバイナリが配置されるので、PATH を通します。
% export PATH=$PATH:$HOME/.local/go/bin
これで go
コマンドが実行可能になりました。
% go version go version go1.20 linux/amd64
Go 関係の環境変数の設定
Go 関係の環境変数がいくつかあります。
go env
コマンドで確認できます。
% go env GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/home/hogehoge/.cache/go-build" GOENV="/home/hogehoge/.config/go/env" GOEXE="" GOEXPERIMENT="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GOMODCACHE="/home/hogehoge/go/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/home/hogehoge/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/home/hogehoge/.local/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/home/hogehoge/.local/go/pkg/tool/linux_amd64" GOVCS="" GOVERSION="go1.20" GCCGO="gccgo" GOAMD64="v1" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="0" GOMOD="/home/hogehoge/workspcace/go-tutorial/hello/go.mod" GOWORK="" CGO_CFLAGS="-O2 -g" CGO_CPPFLAGS="" CGO_CXXFLAGS="-O2 -g" CGO_FFLAGS="-O2 -g" CGO_LDFLAGS="-O2 -g" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -fno-caret-diagnostics -Qunused-arguments -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build308324398=/tmp/go-build -gno-record-gcc-switches"
メジャーなものを見てみます。 まず `GOROOT`は go のインストール先の PATH で、今回のやり方だと以下のようになっています。
% go env GOROOT /home/hogehoge/.local/go
GOPATH
は外部モジュール等が配置されるディレクトリです。
ドキュメントを見ると Workspace だという記述がありますが、詳しくはわかっていませんが、古いバージョンの Go の環境だと $GOPATH/src
配下にモジュールパッケージとなるディレクトリを配置して開発するというような形だったようです。
今は Go Modules がよしなにやってくれるようになり、外部モジュールも $GOPATH/pkg
が使われ、 $GOPATH/src
はなくても良いようなので、あまり意識せずデフォルト値を用いるので良さそうです。
デフォルトでは $HOME/go
です。
% go env GOPATH /home/hogehoge/go
チュートリアルをやってGo Modules を学ぶ
以下のチュートリアルをやって Go Modules の理解を深めてみます。
- Call your code from another module - The Go Programming Language
- Tutorial: Create a Go module - The Go Programming Language
モジュールとパッケージ、バイナリ関連
雰囲気で使ってましたが、Go Modules は Go 1.16 から導入され始めた新しいモジュール管理ツールで、 go.mod
と go.sum
を用いて依存しているモジュールの管理を行う機能のようです。
- New module changes in Go 1.16 - The Go Programming Language
- Go Modules Reference - The Go Programming Language
上記のドキュメントによると、モジュールは配布されるパッケージ群で、バージョン管理リポジトリまたはモジュール プロキシ サーバーから直接ダウンロードできるものを指すようで、パッケージはモジュール内のソースコードのファイル群を指すようです。
元々 go get
でモジュールの管理もバイナリインストールも行われていたようですが、機能が分割され、今のところ Go Modulesの管理のための go get
、ツールなどのバイナリインストールの go install
という使い分けになっているようです。
go get
でダウンロードされたモジュール類はモジュールキャッシュと呼ばれるディレクトリに配置されるようで、デフォルトでは $GOPATH/go/pkg/mod
に配置されます。
コードの中の import 文で読み込まれるモジュールはこのディレクトリ上のファイルを見ているようです。
go install
で取得されたものは $GOPATH/go/bin
に配置され、これらは実行ファイルとして利用できます。
モジュールの作成
Go のインストールは終わりました。このままでも簡単なコードであれば動かすことはできますが、標準パッケージを用いたものしか動かせません。 外部モジュールのパッケージを利用する場合は、モジュールを作成して、外部モジュールのパッケージを読み込めるようにする必要があります。 パッケージ管理ツールはかつてはいろいろあったようですが、今は Go Modules を使うのが一般的なようです。
適当な作業ディレクトリを作って移動します。ここでは workspaces
という名前のディレクトリにしています。
% mkdir workspaces && cd workspaces
workspaces
以下のディレクトリを作成します。
% mkdir greetings && cd greetings
モジュールの初期化を行います。
localhost/greetings
の部分はモジュールパスと呼ばれる、モジュールの名前を表すもので、通常はソースコードが保持されるリポジトリの場所を指定するようです。
ここでは手元で試す分には何でも良いので localhost/greetings
としています。
外部公開する場合はそのソースコードを配置するリポジトリの場所を指定します。GitHub に公開する場合は github.com/aaaa
といった感じで、独自ドメインの場所で公開する場合はそのドメイン名とパスを記載し、その名前を用いてダウンロード可能となるようにしておく必要があるようです。
% go mod init localhost/greetings
go.mod
というファイルが作成されます。
% ls go.mod
初期状態の go.mod
の中身を見るとこんな感じです。
module localhost/greetings go 1.20
一旦 localhost/greetings
のモジュールはこの状態にしておきます。
別モジュールからのコードの呼び出し
別のモジュール localhost/hello
を作って localhost/greetings
のモジュールを呼び出してみます。
一旦作業ディレクトリへ移動します。
% pwd /path/to/workspaces
hello
というディレクトリを作成します。
% mkdir hello
この時点で、以下のようなディレクトリ・ファイル構成になります。
% tree -L 2 ├── greetings │ ├── go.mod │ └── greetings.go └── hello 2 directories, 2 files
hello ディレクトリへ移動します。
% cd hello
モジュールの初期化を行います。
今度は localhost/hello
というモジュールパスにします。
% go mod init localhost/hello
go.mod
はこんな感じになります。
module localhost/hello go 1.20
以下のようなコードを配置します。
greetings.Hello(...)
が greetings
モジュール内の関数です。 import
文で "localhost/greetings"
を指定してあります。
package main import ( "fmt" "localhost/greetings" ) func main() { // Get a greeting message and print it. message := greetings.Hello("Gopher") fmt.Println(message) }
この状態で実行すると greetings
モジュールのパッケージが見つからないというエラーになります。
% go run main.go main.go:6:2: package localhost/greetings is not in GOROOT (/path/to/.local/go/src/localhost/greetings)
localhost/greetings
モジュールを参照できるよう、 go.mod
を編集します。
go mod edit
で編集できるので、 localhost/greetings
のモジュールを hello
モジュールから見た相対パス ../greetings
になるように指定します。
% go mod edit -replace localhost/greetings=../greetings
この状態で go.mod
は以下のようになります。
module localhost/hello go 1.20 replace localhost/greetings => ../greetings
この状態で実行してもまだエラーになります。
% go run main.go main.go:6:2: module localhost/greetings provides package localhost/greetings and is replaced but not required; to add it: go get localhost/greetings
go get
しろと言われますが、go mod tidy
で調整します。
go mod tidy
は、簡単に言うとソースコードで使われているモジュールと go.mod
に記載されているファイルが同じになるよう追加・削除してくれます。
% go mod tidy go: found localhost/greetings in localhost/greetings v0.0.0-00010101000000-000000000000
この状態で go.mod
は以下のようになります。
module localhost/hello go 1.20 replace localhost/greetings => ../greetings require localhost/greetings v0.0.0-00010101000000-000000000000
これで実行できるようになりました。
% go run main.go Hi, Gopher. Welcome!
まとめ
Go 1.20 のインストールと Go Modules 周りを設定しながらドキュメントを眺めて浅く理解するように努めました。 Go Modulesは公式ドキュメントがあるものの、ネットで検索すると他の情報が上位にヒットしてなかなか辿り着けなかったので、改めてドキュメントも確認できてよかったです。
参考
- How to Write Go Code (with GOPATH) - The Go Programming Language
- Go Modules Reference - The Go Programming Language
- Deprecation of 'go get' for installing executables - The Go Programming Language
- cmd/go: 'go install' should install executables in module mode outside a module · Issue #40276 · golang/go · GitHub
- New module changes in Go 1.16 - The Go Programming Language