フューチャー技術ブログ

Go 1.24リリース連載 Go Modulesにおけるツール管理の進化

はじめに

Go1.24 リリース連携の7本目です。

Go Modules におけるツール管理の進化について取り上げます。

アップデートサマリ

  • Go Modules で go.mod に tool ディレクティブが追加になった
    • go get -tooltool ディレクティブに指定されたパッケージを追加できる
    • 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? にまとまっています。

  1. tools.go を利用する方法
  2. 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 とすることが多いでしょう。

tools.go
// +build tools

package tools

import (
_ "golang.org/x/tools/cmd/stringer"
)

go install でバージョンを指定する方法

Go 1.16 からは go install でインストール時にバージョン指定できるようになりました。このリリースにより go.mod の依存関係に影響を与えず、ツールをインストールできるようになりました。このあたりの話は、過去のGo1.16のリリース連携記事(Go 1.16のgo installについて)でも触れています。

余談ですが、私たちのチームでは go install でバージョン指定してインストールするコマンドを Makefile などにまとめて記載することがよくあります。

Makefile
.PHONY: install

install:
go install golang.org/x/tools/cmd/stringer@v0.28.0
go install github.com/sqlc-dev/sqlc/cmd/sqlc@v1.27.0

提案のモチベーション

tools.gogo install を利用したプラクティスは Go の開発者にはよく知られています。ただ Go Modules によるバージョン管理のサポートとしては比較的弱い、Go で書かれたツール周りの開発者体験を向上させたい、というのが #48429Proposal:Adding tool dependencies to go.mod で述べられています。

試してみた

導入された新機能のうち、ツールの追加、バージョンの更新、ツールの実行のそれぞれを試してみます。なお、Goバージョンは go 1.24rc2 です。

空の go.modgithub.com/sqlc-dev/sqlc/cmd/sqlc@v1.27.0 をツールとして追加します。

go.mod
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: downloading github.com/sqlc-dev/sqlc v1.27.0
go: downloading github.com/google/cel-go v0.21.0
go: downloading github.com/jackc/pgx/v5 v5.6.0
go: downloading golang.org/x/sync v0.8.0
go: downloading google.golang.org/grpc v1.65.0
go: downloading google.golang.org/protobuf v1.34.2

・・・(略)

go: added modernc.org/libc v1.55.3
go: added modernc.org/mathutil v1.6.0
go: added modernc.org/memory v1.8.0
go: added modernc.org/sqlite v1.31.1
go: added modernc.org/strutil v1.2.0
go: added modernc.org/token v1.1.0

コマンドを実行すると go.modtool ディレクティブに github.com/sqlc-dev/sqlc/cmd/sqlc が追加されており、バージョンが require で管理されていることがわかります。バージョンは v1.27.0 が入っていますね。

go.mod
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

・・・(略)・・・
modernc.org/sqlite v1.31.1 // indirect
modernc.org/strutil v1.2.0 // indirect
modernc.org/token v1.1.0 // indirect
)

また go get tool でツールのバージョンを最新化(v1.27.0v1.28.0)してみます。

> go get tool 
go: upgraded cel.dev/expr v0.15.0 => v0.18.0
go: upgraded github.com/google/cel-go v0.21.0 => v0.22.1
go: upgraded github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a => v0.0.0-20240606120523-5a60cdf6a761

・・・(略)・・・
go: upgraded google.golang.org/protobuf v1.34.2 => v1.36.3
go: upgraded modernc.org/sqlite v1.31.1 => v1.34.5

go.mod を確認すると、たしかにバージョンが2025/01/23現在の最新である v.1.28.0 に更新されていることがわかります。

go.mod
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
Usage:
sqlc [command]

Available Commands:
compile Statically check SQL for syntax and type errors
completion Generate the autocompletion script for the specified shell
createdb Create an ephemeral database
diff Compare the generated files to the existing files
generate Generate source code from SQL
help Help about any command
init Create an empty sqlc.yaml settings file
push Push the schema, queries, and configuration for this project
verify Verify schema, queries, and configuration for this project
version Print the sqlc version number
vet Vet examines queries

Flags:
-f, --file string specify an alternate config file (default: sqlc.yaml)
-h, --help help for sqlc
--no-remote disable remote execution (default: false)
--remote enable remote execution (default: false)

Use "sqlc [command] --help" for more information about a command.

まとめ

go.modtool ディレクティブの追加により、ツールのバージョン管理がシンプルにできるようになりました。Makefile などによるインストールスクリプトや tools.go のテクニックが不要になる日も近いかもしれません。