Goツールチェーンのビルドステージ
Plan 9で最初からGoをビルドするでも少し見たが、Go 1.10でcontent-aware build cacheが導入されて、ビルドログの出力がステージを表現するように変更された。
% ./make.rcBuilding Go cmd/dist using /usr/glenda/go1.22. (go1.22.12 plan9/amd64)Building Go toolchain1 using /usr/glenda/go1.22.Building Go bootstrap cmd/go (go_bootstrap) using Go toolchain1.Building Go toolchain2 using go_bootstrap and Go toolchain1.Building Go toolchain3 using go_bootstrap and Go toolchain2.Building packages and commands for plan9/amd64.
Packaging archives for plan9/amd64.各ビルドステージの意味
Section titled “各ビルドステージの意味”- Perfectly Reproducible, Verified Go Toolchains
- Go 1.5 Bootstrap Plan
- go/src/cmd/build.go:cmdbootstrap
Building Go cmd/dist
Section titled “Building Go cmd/dist”GOROOT_BOOTSTRAP によって参照される以前のツールチェーンで cmd/dist をビルドする。dist コマンドはGoツールチェーンのビルドやテストを総括するためのコマンド群で、ビルドプロセスが完了すれば削除される。
GOROOT_BOOTSTRAP に利用可能な最小バージョンはGoツールチェーンをブートストラップ可能な最小バージョンの条件に書いた。
Building Go toolchain1
Section titled “Building Go toolchain1”GOROOT_BOOTSTRAP によって参照される以前のツールチェーンと go コマンドで、ターゲットバージョンのツールチェーンをビルドする。ツールチェーンが具体的に何を意味するのかはGoツールチェーンの定義に書いたが、dist/build.goのコメントによれば cmd 以下のアセンブラ、リンカ等をツールチェーンと扱うらしい。
// To recap, so far we have built the new toolchain// (cmd/asm, cmd/cgo, cmd/compile, cmd/link, cmd/preprofile)// using the Go bootstrap toolchain and go command.toolchain1 をビルドするとき、GOROOT_BOOTSTRAP によって参照されるコンパイラはターゲットよりも以前のバージョンなので、ターゲットをビルドしたときに生成されるコードや最適化の程度は、以前のバージョンにどれを使うかで結果が変わる。極端な例ではバイナリのフォーマットやABIが異なる場合もあるかもしれない。
toolchain1 = mk(new toolchain, go1.17 toolchain, go1.17 cmd/go)Building Go bootstrap cmd/go
Section titled “Building Go bootstrap cmd/go”toolchain1 と cmd/dist でターゲットバージョンの go コマンド(go_bootstrap)をビルドする1。おそらくだが、go_bootstrap は toolchain1 でビルドしたライブラリを静的リンクする。なので、フォーマットや最適化状況などは GOROOT_BOOTSTRAP に依存するものの、バグがなければ動作としてはターゲットと同一になる。
go_bootstrap = mk(new cmd/go, toolchain1, cmd/dist)コメントによると、toolchain1 は cmd/go を使っていてビルドIDが無いので再ビルドが必要とあった。
Building Go toolchain2
Section titled “Building Go toolchain2”toolchain1 と go_bootstrap でターゲットのソースコードを改めてビルドする。この時点で、toolchain1 とビルドターゲットが同じバージョンで揃うので、toolchain1 をビルドしたGoのバージョンが何であれ同じバイナリが作られる。このときソースコードの解釈は toolchain1 と全く同じになるが、新しいバージョンでビルドするため生成されるバイナリは異なる。
toolchain2 = mk(new toolchain, toolchain1, go_bootstrap)Building Go toolchain3
Section titled “Building Go toolchain3”toolchain2 と go_bootstrap でターゲットのソースコードを改めてビルドする。
toolchain3 = mk(new toolchain, toolchain2, go_bootstrap)Building packages and commands
Section titled “Building packages and commands”a
Go 1.25のソースコードをGo 1.22でビルドした場合、
| ステージ | コンパイラ | ツールチェーン | ソースコード | 出力フォーマット |
|---|---|---|---|---|
| cmd/dist | Go 1.22 | Go 1.22 | Go 1.22と解釈 | Go 1.22相当 |
| toolchain1 | Go 1.22 | Go 1.22 | Go 1.22と解釈 | Go 1.22相当 |
| go_bootstrap | cmd/dist | toolchain1 | Go 1.25と解釈 | Go 1.25相当 |
| toolchain2 | go_bootstrap | toolchain1 | Go 1.25と解釈 | Go 1.25相当 |
| toolchain3 | go_bootstrap | toolchain2 | Go 1.25と解釈 | Go 1.25相当 |
toolchain1 はコンパイラもツールチェーンも古いものが使われる。toolchain2 の時点では、go_bootstrap と toolchain2 が出力するバイナリは新しいものになっているのだが、toolchain2 のビルドに使われた toolchain1 が古いバージョンでビルドされている。なので最後に全て新しいバージョンでビルドした toolchain3 がある。
Footnotes
Section titled “Footnotes”-
ここでGoコンパイラはどれが使われるんだろう、cmd/dist がそれ? ↩