タラバガニー設計局stalins.clubNOTE/notes/go-toolexec-build-cache

-toolexecでソースを書き換えるならGoのbuild cache identityを考える

Goの-toolexecはビルドツール呼び出しを横取りする でsourceを変換できても、変換内容がGoのbuild cacheへ正しく反映されなければ、古いinstrumented objectやplain objectを再利用してしまう危険がある。

そのため -toolexec wrapperでは「sourceを書き換えること」だけでなく、Go commandから見たtool identityをどう変化させるかが重要になる。

Go commandはwrapper越しのtool identityを問い合わせる

Goのbuild systemはcompiler等のtoolが変わったとき、古いpackage archiveを再利用しないようtool IDをbuild actionへ含める。

-toolexec がある場合、Go commandは本物のcompilerだけを見ても不十分である。wrapperがcompilerの挙動を変更しているかもしれないからである。

そのためGo commandは概念的に、

<toolexec wrapper> <compiler> -V=full

を実行し、wrapper越しに得られるtool version/build IDを使う。

Goのsourceにも、「wrapperが何をするかわからないので、実際にwrapper経由でtoolへ -V=full を問い合わせる」という理由が明記されている。

単純なpass-through wrapper

Go自身の toolexec.txt testにある最小wrapperは、-V=full を検出した場合には余計なoutputを出さず、そのままunderlying toolへ渡す。

これはwrapperが実質的に何も変換しないためである。

しかしinstrumentation toolは事情が違う。ruleやtool versionによって生成されるmachine codeが変わるので、それらをcache keyへ反映できるtool identityが必要になる。

OpenTelemetry otelcの扱い

OpenTelemetryの otelc は、instrumented buildとplain buildのartifactがbuild cache上で分離されるよう、Goがhashするtool identityを変化させる設計になっている。

これにより、

go build ./...

と

otelc go build ./...

を切り替えるたびに go clean -cache する必要はない。

compile-time instrumentation toolにとって、これは単なるperformance optimizationではなくcorrectnessの一部である。

cache identityへ入れるべきもの

source transformationの結果を変え得るものは、原則としてcache identityへ反映される必要がある。

例えば、

  • instrumentation toolのversion
  • instrumentation ruleのversion
  • enable/disableされるinstrumentation set
  • code generation algorithmの変更
  • source transformationへ影響する設定

などである。

逆にOTLP endpointのようなruntime-only configurationまでcompile cacheへ混ぜる必要はない。

GOFLAGS で有効化する場合ほど重要

Goの-toolexecを実際のビルドへ差し込む方法 のように GOFLAGS で -toolexec を暗黙注入すると、同じcheckoutでplain buildとinstrumented buildを行き来しやすい。

このときcache separationが正しくなければ「commandはinstrumentedなのにpackageの一部だけ過去のplain cacheを使う」といった最悪の状態を作れる。

したがってtoolを設計する場合は、単にcompiler invocationをwrapできた時点で完成と考えず、

  1. -V=full probeへの応答
  2. configuration変更時のidentity
  3. plain/instrumented buildの分離
  4. Go version変更時の挙動

までをtestする必要がある。

cgoなどは境界条件になる

-toolexec はすべての外部compile処理を一様にwrapするわけではない。2026年にもGo本体で、cgo compiler version probeと -toolexec の関係を修正する変更が入っている。

つまりtoolchain内部のどのprocessがwrapperを通るかは、source rewriting toolにとって実装詳細ではなく互換性条件である。

compile、asm、linkだけを見る小さなtoolなら比較的単純だが、cgoや独自compilerまで扱う場合はGo releaseごとの検証が必要になる。

関連

出典

▸ ノート一覧に戻る