go tool と Minimal Version Selection
アルゴリズム
〜 CA.go #16 LT大会 〜
株式会社サイバーエージェント 石上 敬祐
自己紹介
名前
石上 敬祐 / いしがみ けいすけ
技術領域
ソフトウェアエンジニア(バックエンド)
配属部署
株式会社 AJA
執筆記事
● 内定者が ABEMA ワークロードの Redis コストを3割削減した話
● 新卒が挑む、数万 QPS をさばく広告配信サーバのリクエスト制御
● 2 node で HA 構成を実現する Extended Raft Algorithm 〜入社2年目AJA所属〜
LGTM Cat(自作)
結論
結論
go tool は npm の devDependencies のようなものではない。
つまり、go tool で管理される tool はプロジェクト本体の依存関係から分離されている
わけではない。
go tool で管理される tool 本体とその依存パッケージは、従来の tools.go パターンと
同様に、プロジェクト本体の MVS による依存関係 tree の構築・解決に影響を与える。
ただし、これは意図された動作である。
しかし、tool の依存関係をプロジェクト本体の依存関係 tree から分離したい場合もあ
る。
そのような場合には、少し工夫が必要になる。
tools.go を使う場合 go tool を使う場合
//go:build tools
// +build tools
package tools
import (
_ "go.uber.org/mock/mockgen"
)
1. 上記のような tools.go を用意
2. go get を実行
3. go run go.uber.org/mock/mockgen
-version で mockgen を実行可能
1. go get -tool
go.uber.org/mock/mockgen を実行
2. go tool mockgen -version で
mockgen を実行可能
tools.go を使う場合 の go.mod go tool を使う場合の go.mod
module hoge
go 1.24.3
require go.uber.org/mock v0.5.2
require (
golang.org/x/mod v0.18.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/tools v0.22.0 // indirect
)
module hoge
go 1.24.3
tool go.uber.org/mock/mockgen
require (
go.uber.org/mock v0.5.2 // indirect
golang.org/x/mod v0.18.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/tools v0.22.0 // indirect
)
両者で同じようなgo.mod が出来上がっていることがわかる
→ go tool は tools.go を使う時と同様にmain パッケージの依存関係tree に影響を与える
(go mod graph を実行しても確認できる)
MVS (Minimal Version Selection) アルゴリズムの気持ち
画像引用: https://go.dev/ref/mod#minimal-version-selection
If a module appears in the list
multiple times, keep only the
newest version.
モジュールがリストに複数回現れる場
合、最新のバージョンのみを保持す
る。
引用:
https://research.swtch.com/vgo-mvs
go tool (tools.go) の使用と依存関係 tree への影響
main モジュールはバージョン
1.1のモジュールBを使う。
しかしながら、tool A がバー
ジョン1.2のモジュール B を
使うために main モジュール
の build にはモジュール B
が使われる 。
→ ただし、この挙動は意図
されたもの。
main
B 1.1 B 1.2
tool A
tool によるプロジェクト本体への依存関係の影響の良し悪し
tool がプロジェクト本体の依存関係 tree
に入っていてほしい場合
ex) sqlboiler
tool 自体がライブラリでもある場合、依存
関係を分離すると tool と生成コードで
バージョンが食い違い、バグを生む危険
性がある。
tool がプロジェクト本体の依存関係 tree
に入っていてほしくない場合
ex) lefthook
tool 自体がライブラリではない場合、「プ
ロジェクト本体のコードとは直接関係がな
い依存関係」による依存関係 tree へのノ
イズの追加になる。
また、MVS の際の意図しないモジュール
のバージョンアップにつながる。
https://github.com/golang/go/issues/48
429#issuecomment-1551584506
大規模なプロジェクトであるほど、プロジェクト本体と関係
のない tool がプロジェクト本体の依存関係 tree に入り込
んでくることは許容できない。
画像引用: https://github.com/aarondl/sqlboiler 画像引用: https://github.com/evilmartians/lefthook
tool をプロジェクト本体の依存関係に含めない工夫
kubernetes/kubernetes の hack/tools ディレクトリ
解説を省きますが、
-modfile オプションを 使う方
法もあります
どっちの選択をどの場面でするとよさそうかの個人的見解
tool をプロジェクト本体の依存関係に含める tool をプロジェクト本体の依存関係に含めない
tool 自体がライブラリでもある場合 tool 自体がライブラリではない場合
(プロジェクトが小規模な場合) プロジェクトが大規模な場合
結論
go tool は npm の devDependencies のようなものではない。
つまり、go tool で管理される tool はプロジェクト本体の依存関係から分離されている
わけではない。
go tool で管理される tool 本体とその依存パッケージは、従来の tools.go パターンと
同様に、プロジェクト本体の MVS による依存関係 tree の構築・解決に影響を与える。
ただし、これは意図された動作である。
しかし、tool の依存関係をプロジェクト本体の依存関係 tree から分離したい場合もあ
る。
そのような場合には、少し工夫が必要になる。
go tool と Minimal Version Selection
アルゴリズム
〜 CA.go #16 LT大会 〜
株式会社サイバーエージェント 石上 敬祐
想定 Q&A 集
「ただし、これは意図された動作である。」の根拠は?
The go command is not going to start dealing with multiple build graphs in a single command - that
would be a very large amount of implementation complexity, far outweighing the entire benefit of
having tool lines at all.
goコマンドは、単一のコマンドで複数のビルドグラフを扱うことはしません。それは非常に大量の実装の複雑さを
伴い、ツール行を持つことの全体的な利益を大幅に上回ってしまうからです。
https://github.com/golang/go/issues/48429#issuecomment-1599344310
The definition of "all" influences many things. After talking to @bcmills and @matloob, I believe
(and I believe they agree) that modules providing tools and modules providing tool dependencies
need to be included in "all". The reason is that commands like "go mod graph", "go mod verify",
"go mod download", and "go mod vendor" are all keyed off of "all", as is the content of go.sum.
「all」の定義は多くのことに影響を与えます。 @bcmillsと@matloobと話し合った結果、私は(彼らも同意している
と思いますが)、ツールを提供するモジュールとツール依存関係を提供するモジュールは「 all」に含まれる必要が
あると考えています。その理由は、「 go mod graph」、「go mod verify」、「go mod download」、「go mod vendor」
などのコマンドはすべて「 all」をキーとしており、go.sumの内容も同様だからです。
https://github.com/golang/go/issues/48429#issuecomment-1652292960
-modfile オプションを使った例を知りたい
go get -modfile=tools.mod
go tool -modfile=tools.mod go.uber.org/mock/mockgen -version
上記のようにして -modfile オプションを利用可能。

go tool と Minimal Version Selection アルゴリズム

  • 1.
    go tool とMinimal Version Selection アルゴリズム 〜 CA.go #16 LT大会 〜 株式会社サイバーエージェント 石上 敬祐
  • 2.
    自己紹介 名前 石上 敬祐 /いしがみ けいすけ 技術領域 ソフトウェアエンジニア(バックエンド) 配属部署 株式会社 AJA 執筆記事 ● 内定者が ABEMA ワークロードの Redis コストを3割削減した話 ● 新卒が挑む、数万 QPS をさばく広告配信サーバのリクエスト制御 ● 2 node で HA 構成を実現する Extended Raft Algorithm 〜入社2年目AJA所属〜 LGTM Cat(自作)
  • 3.
  • 4.
    結論 go tool はnpm の devDependencies のようなものではない。 つまり、go tool で管理される tool はプロジェクト本体の依存関係から分離されている わけではない。 go tool で管理される tool 本体とその依存パッケージは、従来の tools.go パターンと 同様に、プロジェクト本体の MVS による依存関係 tree の構築・解決に影響を与える。 ただし、これは意図された動作である。 しかし、tool の依存関係をプロジェクト本体の依存関係 tree から分離したい場合もあ る。 そのような場合には、少し工夫が必要になる。
  • 5.
    tools.go を使う場合 gotool を使う場合 //go:build tools // +build tools package tools import ( _ "go.uber.org/mock/mockgen" ) 1. 上記のような tools.go を用意 2. go get を実行 3. go run go.uber.org/mock/mockgen -version で mockgen を実行可能 1. go get -tool go.uber.org/mock/mockgen を実行 2. go tool mockgen -version で mockgen を実行可能
  • 6.
    tools.go を使う場合 のgo.mod go tool を使う場合の go.mod module hoge go 1.24.3 require go.uber.org/mock v0.5.2 require ( golang.org/x/mod v0.18.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/tools v0.22.0 // indirect ) module hoge go 1.24.3 tool go.uber.org/mock/mockgen require ( go.uber.org/mock v0.5.2 // indirect golang.org/x/mod v0.18.0 // indirect golang.org/x/sync v0.7.0 // indirect golang.org/x/tools v0.22.0 // indirect ) 両者で同じようなgo.mod が出来上がっていることがわかる → go tool は tools.go を使う時と同様にmain パッケージの依存関係tree に影響を与える (go mod graph を実行しても確認できる)
  • 7.
    MVS (Minimal VersionSelection) アルゴリズムの気持ち 画像引用: https://go.dev/ref/mod#minimal-version-selection If a module appears in the list multiple times, keep only the newest version. モジュールがリストに複数回現れる場 合、最新のバージョンのみを保持す る。 引用: https://research.swtch.com/vgo-mvs
  • 8.
    go tool (tools.go)の使用と依存関係 tree への影響 main モジュールはバージョン 1.1のモジュールBを使う。 しかしながら、tool A がバー ジョン1.2のモジュール B を 使うために main モジュール の build にはモジュール B が使われる 。 → ただし、この挙動は意図 されたもの。 main B 1.1 B 1.2 tool A
  • 9.
    tool によるプロジェクト本体への依存関係の影響の良し悪し tool がプロジェクト本体の依存関係tree に入っていてほしい場合 ex) sqlboiler tool 自体がライブラリでもある場合、依存 関係を分離すると tool と生成コードで バージョンが食い違い、バグを生む危険 性がある。 tool がプロジェクト本体の依存関係 tree に入っていてほしくない場合 ex) lefthook tool 自体がライブラリではない場合、「プ ロジェクト本体のコードとは直接関係がな い依存関係」による依存関係 tree へのノ イズの追加になる。 また、MVS の際の意図しないモジュール のバージョンアップにつながる。 https://github.com/golang/go/issues/48 429#issuecomment-1551584506 大規模なプロジェクトであるほど、プロジェクト本体と関係 のない tool がプロジェクト本体の依存関係 tree に入り込 んでくることは許容できない。 画像引用: https://github.com/aarondl/sqlboiler 画像引用: https://github.com/evilmartians/lefthook
  • 10.
    tool をプロジェクト本体の依存関係に含めない工夫 kubernetes/kubernetes のhack/tools ディレクトリ 解説を省きますが、 -modfile オプションを 使う方 法もあります
  • 11.
    どっちの選択をどの場面でするとよさそうかの個人的見解 tool をプロジェクト本体の依存関係に含める toolをプロジェクト本体の依存関係に含めない tool 自体がライブラリでもある場合 tool 自体がライブラリではない場合 (プロジェクトが小規模な場合) プロジェクトが大規模な場合
  • 12.
    結論 go tool はnpm の devDependencies のようなものではない。 つまり、go tool で管理される tool はプロジェクト本体の依存関係から分離されている わけではない。 go tool で管理される tool 本体とその依存パッケージは、従来の tools.go パターンと 同様に、プロジェクト本体の MVS による依存関係 tree の構築・解決に影響を与える。 ただし、これは意図された動作である。 しかし、tool の依存関係をプロジェクト本体の依存関係 tree から分離したい場合もあ る。 そのような場合には、少し工夫が必要になる。
  • 13.
    go tool とMinimal Version Selection アルゴリズム 〜 CA.go #16 LT大会 〜 株式会社サイバーエージェント 石上 敬祐
  • 14.
  • 15.
    「ただし、これは意図された動作である。」の根拠は? The go commandis not going to start dealing with multiple build graphs in a single command - that would be a very large amount of implementation complexity, far outweighing the entire benefit of having tool lines at all. goコマンドは、単一のコマンドで複数のビルドグラフを扱うことはしません。それは非常に大量の実装の複雑さを 伴い、ツール行を持つことの全体的な利益を大幅に上回ってしまうからです。 https://github.com/golang/go/issues/48429#issuecomment-1599344310 The definition of "all" influences many things. After talking to @bcmills and @matloob, I believe (and I believe they agree) that modules providing tools and modules providing tool dependencies need to be included in "all". The reason is that commands like "go mod graph", "go mod verify", "go mod download", and "go mod vendor" are all keyed off of "all", as is the content of go.sum. 「all」の定義は多くのことに影響を与えます。 @bcmillsと@matloobと話し合った結果、私は(彼らも同意している と思いますが)、ツールを提供するモジュールとツール依存関係を提供するモジュールは「 all」に含まれる必要が あると考えています。その理由は、「 go mod graph」、「go mod verify」、「go mod download」、「go mod vendor」 などのコマンドはすべて「 all」をキーとしており、go.sumの内容も同様だからです。 https://github.com/golang/go/issues/48429#issuecomment-1652292960
  • 16.
    -modfile オプションを使った例を知りたい go get-modfile=tools.mod go tool -modfile=tools.mod go.uber.org/mock/mockgen -version 上記のようにして -modfile オプションを利用可能。