-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できた時点で完成と考えず、
-V=fullprobeへの応答- configuration変更時のidentity
- plain/instrumented buildの分離
- 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ごとの検証が必要になる。