はじめに
Go1.24 リリース連携の7本目です。
Go Modules におけるツール管理の進化について取り上げます。
アップデートサマリ
- Go Modules で go.mod に
toolディレクティブが追加になったgo get -toolでtoolディレクティブに指定されたパッケージを追加できるgo get toolでツールのアップデートや、go install toolでツールのインストール、go tool hogeでツールの実行ができる、など- インストールした実行ファイルは、Goのビルドキャッシュにキャッシュされる
アップデートのモチベーションなど(#48429)
歴史的経緯
Goでアプリケーションを実装するときに、Goのツールを使って開発することはよくあります。Goのツールの例として、たとえばや fmt.Stringer インターフェースを満たすコードを生成する Stringer や SQL から Go のコードを生成できる sqlc などが挙げられるでしょう。
チーム開発するうえでツールのバージョン管理は重要です。チーム内で同じバージョンのツールを使って開発できることが望ましく、別バージョンを利用していると開発者間で不要な差分や、動作しないソースがリポジトリにコミットされてしまう可能性などが考えられます。
Go1.24の本機能がリリースされる以前は、主に以下の方法でツールのバージョン管理が行われていました。このあたりのノウハウは Go Wiki の How can I track tool dependencies for a module? にまとまっています。
tools.goを利用する方法go installでバージョンを指定する方法
tools.go を利用する方法
go install でツールのバイナリをインストールできましたが、Go 1.15 以前ではインストール時にバージョン指定ができませんでした。インストール時に go.mod にバージョンが追記されますが、どこからも import されていない場合 go mod tidy で依存関係が削除されてしまいます。そこで Build Constraints を利用するテクニックが知られています。ブランクインポートにより import されているが、Build Constraints でコンパイル/ビルドはされないようにしています。Build Constraints を利用する Go のファイル例の記載例は以下のようなものです。ファイル名は慣習として tools.go とすることが多いでしょう。
// +build tools |
go install でバージョンを指定する方法
Go 1.16 からは go install でインストール時にバージョン指定できるようになりました。このリリースにより go.mod の依存関係に影響を与えず、ツールをインストールできるようになりました。このあたりの話は、過去のGo1.16のリリース連携記事(Go 1.16のgo installについて)でも触れています。
余談ですが、私たちのチームでは go install でバージョン指定してインストールするコマンドを Makefile などにまとめて記載することがよくあります。
|
提案のモチベーション
tools.go や go install を利用したプラクティスは Go の開発者にはよく知られています。ただ Go Modules によるバージョン管理のサポートとしては比較的弱い、Go で書かれたツール周りの開発者体験を向上させたい、というのが #48429 や Proposal:Adding tool dependencies to go.mod で述べられています。
試してみた
導入された新機能のうち、ツールの追加、バージョンの更新、ツールの実行のそれぞれを試してみます。なお、Goバージョンは go 1.24rc2 です。
空の go.mod に github.com/sqlc-dev/sqlc/cmd/sqlc@v1.27.0 をツールとして追加します。
module sample go 1.24rc2 |
go get -tool github.com/sqlc-dev/sqlc/cmd/sqlc@v1.27.0 します。
> go get -tool github.com/sqlc-dev/sqlc/cmd/sqlc@v1.27.0 |
コマンドを実行すると go.mod の tool ディレクティブに github.com/sqlc-dev/sqlc/cmd/sqlc が追加されており、バージョンが require で管理されていることがわかります。バージョンは v1.27.0 が入っていますね。
module sample go 1.24rc2 tool github.com/sqlc-dev/sqlc/cmd/sqlc require ( filippo.io/edwards25519 v1.1.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/cubicdaiya/gonp v1.0.4 // indirect github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect ・・・(略)・・・ github.com/sqlc-dev/sqlc v1.27.0 // indirect |
また go get tool でツールのバージョンを最新化(v1.27.0 → v1.28.0)してみます。
> go get tool |
go.mod を確認すると、たしかにバージョンが2025/01/23現在の最新である v.1.28.0 に更新されていることがわかります。
module sample go 1.24rc2 tool github.com/sqlc-dev/sqlc/cmd/sqlc require ( cel.dev/expr v0.18.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/cubicdaiya/gonp v1.0.4 // indirect github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 // indirect ・・・(略)・・・ github.com/sqlc-dev/sqlc v1.28.0 // indirect ・・・(略)・・・ modernc.org/sqlite v1.34.5 // indirect modernc.org/strutil v1.2.0 // indirect modernc.org/token v1.1.0 // indirect ) |
また go install tool で $GOBIN (デフォルトで $GOPATH/bin) にバイナリがインストールされていることも確認できました。これで go.mod で管理しているツールをまとめてインストールできます。
ツールは go tool sqlc として実行できました。なお $GOBIN へのパスが通っていれば、go tool コマンドなしで sqlc としても実行できます。
> go tool sqlc |
まとめ
go.mod の tool ディレクティブの追加により、ツールのバージョン管理がシンプルにできるようになりました。Makefile などによるインストールスクリプトや tools.go のテクニックが不要になる日も近いかもしれません。