skeleton/stepX/main.go
の TODO コメントを参考にして実装してくださいtestdata/sample.go
に実装したプログラムをtoolexecフラグで渡して動作を確認しますgo run skeleton/stepX/main.go
go build -toolexec="$PWD/skeleton/stepX/main" testdata/sample.go
元のツールをそのまま実行する toolexec プログラムが動作することを確認する
-toolexec
フラグを使うと、Go のビルドツール(compile, link など)の実行に任意のプログラムを適用することができます。例えばDataDog/orchestrionやalibaba/loongsuite-go-agentではビルド前に自動計装のためのコードを差し込むために使用しています。
最初のステップでは、単に元のツールを呼び出すだけの透過的なラッパーを作ります。
go build -toolexec="mytool" main.go
↓
mytool /path/to/compile [compile の引数...]
↓
mytool /path/to/link [link の引数...]
toolexec プログラムが受け取る引数:
os.Args[0]
: toolexec プログラム自身のパスos.Args[1]
: 実行するツール(compile, link など)のパスos.Args[2:]
: ツールに渡す引数skeleton/step1/main.go
を開いて、TODO コメントの箇所を実装してください。
必要な実装:
exec.Command
で元のツールを実行するコマンドを作成# Step 1 のプログラムをビルド
cd skeleton/step1
go build -o mytoolexec main.go
# toolexec として使用
go build -toolexec="$PWD/mytoolexec" ../../testdata/sample.go
# 正常にビルドできれば成功!
./sample
期待される出力:
=== Sample Program ===
Hello, Gopher Welcome to Go Conference 2025!
ビルドで実行されるツール(compile、link など)を可視化する
Go のビルドは複数のツールが連携して動作します:
toolexec を使用してgo build
で実際にどのようなツールが内部で動いているか観察してみましょう。
os.Args[1]
: 実行されるツールのパス(例: /usr/local/go/pkg/tool/darwin_amd64/compile
)TOOLEXEC_IMPORTPATH
: 環境変数。現在ビルド中のパッケージ名skeleton/step2/main.go
を開いて、以下を実装してください:
// 1. ツール名を取得(パスから最後の部分だけ)
toolName := filepath.Base(toolPath)
// 2. ビルド対象のパッケージ名を環境変数から取得
pkg := os.Getenv("TOOLEXEC_IMPORTPATH")
if pkg == "" {
pkg = "(unknown)"
}
// 3. 何が起きているか表示
fmt.Fprintf(os.Stderr, "[TOOLEXEC] Running %s for package %s\n", toolName, pkg)
# Step 2 のプログラムをビルド
cd skeleton/step2
go build -o mytoolexec main.go
# toolexec として使用(-a で全パッケージを再ビルド)
go build -a -toolexec="$PWD/mytoolexec" ../../testdata/sample.go 2>&1 | grep TOOLEXEC
期待される出力例:
# internal/unsafeheader
[TOOLEXEC] Running compile for package internal/unsafeheader
# internal/msan
[TOOLEXEC] Running compile for package internal/msan
このままだとビルドキャッシュが残っているため、別プログラムをビルドする際にキャッシュヒットすると他のプログラムでもプリントされてしまいます。
一度下記のコマンドでキャッシュを削除することをお勧めします
go clean -cache
実行結果を見ると:
まずは、表示したい Gopher を準備しましょう。 思いつかない方は下記のGopherを使用してください。
// メインの Gopher
const gopher = `
D;;:;;:;;:;;:;:;:;;:;:;:;:;;:;;:;;:;;:;z
$;:;:;::;::;:;;:;;:;;:;;:;;::;;::;;:;;;I
.I;;:;;:;;:;;::;;:;:;:;;:;:;;:;:;:;::;;:I
,<;;::;:;;::;;:;;;;;;;;:;::;;:;;:;;;:;;;I
,(;;;:;::;:;;::;;j=1J71<;;;:;:;;::;:;:;:I
J;;:;;;:;;::;;;;:r ] .>;;;:;:;:;;:;:;;;r
z;;::;:;;:;;:;;j=<?75?7~?I;;:;;:;;;:;:;<]
(<;;;;;;:;;;;;;?+~(J-J-_(3;;;;;;::;;:;;+\
,(;:;:;j/7!''??1+?MMMMM1+?7771+<;;;:;;:j
.P;;;;J!.. 4;<<iJ .4<;;:;;2
.3;J<;;j\(M#Q D;<2.MM5. 1:;;;j73,
$;jMN<;?|,WH3 $;:t.MM# ,(;;jP;;?|
4<;T9TJ;?. .J;;;?& .t;;jM@:;+%
(1++++Y+;?C+...J7<;;;:;;?i.. ..J>;jv<;;;j=
.71+<;;;;;;;:;;;;;;;;;;<+J= ?77!
'_?771+++++++++?77!
`
cd skeleton/step3
go build -o mytoolexec main.go
go build -toolexec="$PWD/mytoolexec" ../../testdata/sample.go -o sample
出力例:
=== Go Build with Gopher ===
D;;:;;:;;:;;:;:;:;;:;:;:;:;;:;;:;;:;;:;z
$;:;:;::;::;:;;:;;:;;:;;:;;::;;::;;:;;;I
.I;;:;;:;;:;;::;;:;:;:;;:;:;;:;:;:;::;;:I
,<;;::;:;;::;;:;;;;;;;;:;::;;:;;:;;;:;;;I
,(;;;:;::;:;;::;;j=1J71<;;;:;:;;::;:;:;:I
J;;:;;;:;;::;;;;:r ] .>;;;:;:;:;;:;:;;;r
z;;::;:;;:;;:;;j=<?75?7~?I;;:;;:;;;:;:;<]
(<;;;;;;:;;;;;;?+~(J-J-_(3;;;;;;::;;:;;+\
,(;:;:;j/7!''??1+?MMMMM1+?7771+<;;;:;;:j
.P;;;;J!.. 4;<<iJ .4<;;:;;2
.3;J<;;j\(M#Q D;<2.MM5. 1:;;;j73,
$;jMN<;?|,WH3 $;:t.MM# ,(;;jP;;?|
4<;T9TJ;?. .J;;;?& .t;;jM@:;+%
(1++++Y+;?C+...J7<;;;:;;?i.. ..J>;jv<;;;j=
.71+<;;;;;;;:;;;;;;;;;;<+J= ?77!
'_?771+++++++++?77!
コンパイル中 ██████████████████████████████ ✅
リンク中 ██████████████████████████████ ✅
🎉 ビルド完了!
学んだ技術は以下のような場面で活用できます: