概要
Go Toolchainはモジュールで使うバージョン管理をgoディレクティブだけの頃よりも正確に制御できます。
しかしビルドバージョンは以下の要素の組み合わせから決定されるため、ちゃんと理解していないと期待しない挙動になることがあります。
- GOTOOLCHAIN環境変数
- ローカルバージョン
- go.modのgoディレクティブ
- go.modのtoolchainディレクティブ
今回は簡単に検証できる環境を紹介して、どうなるのか確認してみます。
環境
- Go v1.21以上
前提知識
goディレクティブとは
go.modにおけるこれです。
module example.com/myapp go 1.21 // これ
役割
- モジュールで使用するGoの最小バージョンを指定
toolchainディレクティブとは
go.modにおけるこれです。
module example.com/myapp go 1.21 toolchain go1.21.2 // これ
役割
- モジュールがビルドまたは実行される際に使用されるツールチェインのバージョンを制御
- モジュールが期待するバージョンを明示的に指定
検証環境
こちらに用意した環境のファイルを用意します。
. ├── go.mod └── main.go
main.goの中身はこうです。ビルドされたバージョンを表示するようにします。
package main import ( "fmt" "runtime/debug" ) func main() { info, ok := debug.ReadBuildInfo() if ok && info != nil { fmt.Println("Go Version:", info.GoVersion) } else { fmt.Println("Unable to determine Go version") } }
使い方
基本的な例
例えば次のような設定の場合は
| 要素 | 設定値 |
|---|---|
| GOTOOLCHAIN環境変数 | 未設定(=local) |
| ローカルバージョン | v1.21.3 |
| goディレクティブ | 1.21 |
| toolchainディレクティブ | go1.22.1 |
このように実行します。
$ docker run --rm -v "$(pwd)":/app -w /app golang:1.21.3 go run main.go Go Version: go1.21.3
結果はローカルバージョンが優先されてv1.21.3が使われました。
ローカルバージョンを変更する
Docker imageのバージョンを指定します。
| 要素 | 設定値 |
|---|---|
| GOTOOLCHAIN環境変数 | 未設定(=local) |
| ローカルバージョン | v1.22.9 |
| goディレクティブ | 1.21 |
| toolchainディレクティブ | go1.21.1 |
$ docker run --rm -v "$(pwd)":/app -w /app golang:1.22.9 go run main.go Go Version: go1.22.9
結果はローカルバージョンが優先されてv1.22.9になります。
GOTOOLCHAIN環境変数にautoを指定する
GOTOOLCHAIN環境変数は未設定の場合localになっています。
$ docker run --rm -v "$(pwd)":/app -w /app golang:1.21 go env GOTOOLCHAIN local
なので今回はautoに指定します。
| 要素 | 設定値 |
|---|---|
| GOTOOLCHAIN環境変数 | auto |
| ローカルバージョン | v1.21.13 |
| goディレクティブ | 1.22 |
| toolchainディレクティブ | go1.22.1 |
環境変数を渡します。
$ docker run --rm -v "$(pwd)":/app -w /app -e GOTOOLCHAIN=auto golang:1.21 go run main.go go: downloading go1.22.1 (linux/arm64) Go Version: go1.22.1
結果はtoolchainディレクティブが優先されてv1.22.1になります。
GOTOOLCHAIN環境変数にgo1.21.3を指定する
| 要素 | 設定値 |
|---|---|
| GOTOOLCHAIN環境変数 | go1.21.3 |
| ローカルバージョン | v1.22.9 |
| goディレクティブ | 1.21 |
| toolchainディレクティブ | go1.21.5 |
環境変数を渡します。
$ docker run --rm -v "$(pwd)":/app -w /app -e GOTOOLCHAIN=go1.21.3 golang:1.22.9 go run main.go go: downloading go1.21.3 (linux/arm64) Go Version: go1.21.3
結果はGOTOOLCHAINが優先されてv1.21.3になります。
toolchainディレクティブをなしにする
| 要素 | 設定値 |
|---|---|
| GOTOOLCHAIN環境変数 | 未設定(=local) |
| ローカルバージョン | v1.21.3 |
| goディレクティブ | 1.21 |
| toolchainディレクティブ | - |
go.modをいじります。
module github.com/jun06t/go-sample/toolchain go 1.21 //toolchain go1.22.1
$ docker run --rm -v "$(pwd)":/app -w /app golang:1.21.3 go run main.go Go Version: go1.21.3
結果はローカルバージョンが優先されてv1.21.3になります。
依存ライブラリを考慮する
次の依存ライブラリも考慮します。
package main import ( "fmt" "runtime/debug" library "github.com/jun06t/go-sample/toolchain-library" ) func main() { info, ok := debug.ReadBuildInfo() if ok && info != nil { fmt.Println("Go Version:", info.GoVersion) } else { fmt.Println("Unable to determine Go version") } fmt.Println(library.Echo()) }
として、
toolchain-libraryのgo.modは
module github.com/jun06t/go-sample/toolchain-library go 1.24.1
としています。
自分のプロジェクトが古い状態のまま
自分のプロジェクトが古い状態(v1.21.3)のままとします。
| 要素 | 設定値 |
|---|---|
| GOTOOLCHAIN環境変数 | 未設定(=local) |
| ローカルバージョン | v1.21.3 |
| goディレクティブ | 1.21 |
| toolchainディレクティブ | go1.22.1 |
| 依存するライブラリのgoディレクティブ | go1.24.1 |
この場合次のようにエラーになります。
$ docker run --rm -v "$(pwd)":/app -w /app golang:1.21.3 go run main.go go: go.mod requires go >= 1.24.1 (running go 1.21.3; GOTOOLCHAIN=local)
GOTOOLCHAIN環境変数にautoを指定する
GOTOOLCHAIN=autoにすると、別の go mod tidy をするようエラーになります。
| 要素 | 設定値 |
|---|---|
| GOTOOLCHAIN環境変数 | auto |
| ローカルバージョン | v1.21.3 |
| goディレクティブ | 1.21 |
| toolchainディレクティブ | go1.22.1 |
| 依存するライブラリのgoディレクティブ | go1.24.1 |
$ docker run --rm -v "$(pwd)":/app -w /app -e GOTOOLCHAIN=auto golang:1.21 go run main.go go: downloading go1.24.1 (linux/arm64) go: downloading github.com/jun06t/go-sample/toolchain-library v0.0.0-20250321045900-9500a10f8366 go: updates to go.mod needed; to update it: go mod tidy
go mod tidyをすると自分のプロジェクトもgo1.24.1に上書きされます。
module github.com/jun06t/go-sample/toolchain go 1.24.1 require github.com/jun06t/go-sample/toolchain-library v0.0.0-20250321045900-9500a10f8366
再実行すると通ります。
$ docker run --rm -v "$(pwd)":/app -w /app -e GOTOOLCHAIN=auto golang:1.21 go run main.go go: downloading go1.24.1 (linux/arm64) go: downloading github.com/jun06t/go-sample/toolchain-library v0.0.0-20250321045900-9500a10f8366 Go Version: go1.24.1 Echo
結果まとめ
今回の結果(+いくつか追加)は以下になりました。
| GOTOOLCHAIN | ローカル | goディレクティブ | toolchainディレクティブ | 結果 |
|---|---|---|---|---|
| 未設定(=local) | 1.21.3 | 1.21 | 1.22.1 | 1.21.3 |
| 未設定(=local) | 1.22.9 | 1.21 | 1.22.1 | 1.22.9 |
| 未設定(=local) | 1.22.9 | 1.23 | 1.23.3 | requiresエラー |
| auto | 1.21.3 | 1.21.1 | 1.22.1 | 1.22.1 |
| go1.21.3 | 1.22.3 | 1.21.1 | 1.21.5 | 1.21.3 |
| go1.21.3+auto | 1.22.3 | 1.21.1 | 1.21.5 | 1.21.5 |
| 未設定(=local) | 1.21.3 | 1.21 | - | 1.21.3 |
| path | 1.22.3 | 1.21 | 1.21.5 | 1.22.3 |
| path | 1.22.3 | 1.21 | 1.23.3 | cannot findエラー |
こんな感じで簡単に検証ができるので、挙動を確認してみたい方は一度このやり方をやってみてください。