[インデックス 15074] ファイルの概要
このコミットは、Go言語のビルドツールである cmd/dist
における改善です。具体的には、runtime
パッケージ内のC言語ソースファイル(proc.c
など)をコンパイルする際に、コンパイラ(6c
)が出力する「acid output」と呼ばれる詳細な中間出力を一時ファイルにリダイレクトするように変更しています。これにより、コンパイルエラーが発生した場合に、エラーメッセージが大量の「acid output」に埋もれて見つけにくくなる問題を解消し、エラーの視認性を向上させています。
コミット
commit 7fc64a2a1da272361fc78453c7ffea17def3bcb0
Author: Russ Cox <rsc@golang.org>
Date: Thu Jan 31 22:02:20 2013 -0800
cmd/dist: redirect acid output to file to separate from errors
If runtime's proc.c does not compile, cmd/dist used to show
the compile errors in a sea of acid output, making them impossible
to find. Change the command invocation to write the acid output
to a file, so that the errors are the only thing shown on failure.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/7221082
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/7fc64a2a1da272361fc78453c7ffea17def3bcb0
元コミット内容
cmd/dist: redirect acid output to file to separate from errors
runtime
の proc.c
がコンパイルされない場合、cmd/dist
はコンパイルエラーを大量の acid output の中に表示し、見つけることを不可能にしていました。このコマンド呼び出しを変更し、acid output をファイルに書き出すようにすることで、エラーが発生した際にエラーのみが表示されるようにしました。
変更の背景
Go言語の初期のビルドシステムでは、cmd/dist
というツールがGoのツールチェイン全体をビルドする役割を担っていました。このツールは、Goのランタイム(runtime
)の一部であるC言語で書かれたファイル(例: proc.c
)をコンパイルするために、6c
のような特定のアーキテクチャ向けのCコンパイラを呼び出していました。
問題は、これらのCコンパイラが -a
フラグ("acid output" を生成するフラグ)と共に実行された場合、非常に詳細で冗長な中間出力(「acid output」)を標準出力に吐き出すことでした。もしコンパイル中にエラーが発生すると、この大量の「acid output」の中に実際のエラーメッセージが埋もれてしまい、開発者がエラーの原因を特定することが極めて困難になっていました。
このコミットの目的は、この「エラーメッセージの視認性の低下」という問題を解決し、ビルドプロセスにおけるデバッグ体験を改善することにありました。
前提知識の解説
cmd/dist
: Go言語のソースコードからGoツールチェイン全体(コンパイラ、リンカ、標準ライブラリなど)をビルドするためのコマンドラインツールです。Goの自己ホスト型コンパイラが成熟する以前は、C言語で書かれた部分が多く、それらをコンパイルするためにCコンパイラを呼び出す必要がありました。6c
(および8c
,5c
など): Go言語の初期のツールチェインで使用されていた、Plan 9 CコンパイラをベースにしたCコンパイラ群です。6c
はamd64
(x86-64) アーキテクチャ向け、8c
はarm
、5c
は386
(x86) アーキテクチャ向けでした。これらはGoのランタイムや一部の低レベルな部分をコンパイルするために使われました。proc.c
: Goランタイムの一部を構成するC言語のソースファイルの一つです。プロセス管理やスケジューリングに関する低レベルな処理が含まれています。- "acid output":
6c
などのPlan 9ベースのCコンパイラが-a
フラグを付けて実行された際に生成する、コンパイラの内部状態や中間コード表現に関する詳細な出力のことです。これは主にコンパイラ開発者がデバッグや解析のために使用するもので、通常のコンパイル時には不要な情報であり、非常に冗長です。この出力は、コンパイラがソースコードをどのように解析し、中間表現に変換しているかを示します。 runv
関数:cmd/dist
内部で使用されるヘルパー関数で、外部コマンドを実行するためのものです。引数として、標準入力として使用するBuf
(バッファ) へのポインタ、実行ディレクトリ、終了コードチェックのフラグ、およびコマンド引数のベクトルを受け取ります。Buf
およびVec
:cmd/dist
内部で使われるカスタムのバッファおよびベクトル(動的配列)構造体です。これらは文字列操作やコマンド引数の構築に利用されます。
技術的詳細
このコミットの核心は、6c
コンパイラの呼び出し方法を変更し、その「acid output」を標準出力ではなく一時ファイルにリダイレクトすることです。
変更前は、6c
コマンドは以下のように実行されていました(簡略化)。
6c -D GOOS_goos -D GOARCH_goarch -I workdir -a proc.c
このコマンドは、proc.c
をコンパイルし、その「acid output」を runv
関数の第一引数で指定された Buf
(バッファ) に直接書き込んでいました。このバッファは通常、後続の処理で利用されるか、あるいは単に標準出力に流れていました。エラーが発生した場合、エラーメッセージもこの同じストリームに出力されるため、両者が混在していました。
変更後、6c
コマンドの引数に -n
と -o <output_file>
が追加されました。
6c -D GOOS_goos -D GOARCH_goarch -I workdir -a -n -o workdir/proc.acid proc.c
-n
フラグ: このフラグは、コンパイラが通常生成するオブジェクトファイル(.o
ファイル)の生成を抑制します。この場合、コンパイラの目的はオブジェクトファイルの生成ではなく、「acid output」の生成であるため、オブジェクトファイルは不要です。-o workdir/proc.acid
: このフラグは、コンパイラの出力を指定されたファイル(例:workdir/proc.acid
)にリダイレクトします。これにより、「acid output」は標準出力ではなく、このファイルに書き込まれるようになります。
また、runv
関数の呼び出しも変更されました。
変更前: runv(&in, dir, CheckExit, &argv);
変更後: runv(nil, dir, CheckExit, &argv);
runv
の第一引数が &in
から nil
に変更されたことで、6c
の標準出力は runv
内部のバッファには取り込まれなくなりました。代わりに、-o
フラグによって指定されたファイルに直接書き込まれます。
runv
の実行後、新しく追加された readfile(&in, bpathf(&b, "%s/proc.acid", workdir));
という行によって、proc.acid
ファイルの内容が in
バッファに読み込まれます。これは、後続の処理でこの「acid output」の内容が必要になるためです。
同様の変更が mkzruntimedefs
関数にも適用されています。この関数はランタイム定義ファイルを生成するために複数のCファイルを処理しますが、ここでも 6c
の呼び出しに対して -n
と -o
フラグが追加され、出力が一時ファイルにリダイレクトされ、その後読み戻されるようになっています。
この変更により、コンパイルエラーが発生した場合、6c
コンパイラはエラーメッセージのみを標準エラー出力(または標準出力)に直接出力し、冗長な「acid output」は別のファイルに隔離されるため、エラーメッセージが明確に表示されるようになります。
コアとなるコードの変更箇所
src/cmd/dist/buildruntime.c
ファイルの mkzasm
関数と mkzruntimedefs
関数が変更されています。
mkzasm
関数における変更:
--- a/src/cmd/dist/buildruntime.c
+++ b/src/cmd/dist/buildruntime.c
@@ -205,7 +205,7 @@ mkzasm(char *dir, char *file)\
fatal("unknown $GOOS/$GOARCH in mkzasm");
ok:\
- // Run 6c -D GOOS_goos -D GOARCH_goarch -I workdir -a proc.c
+ // Run 6c -D GOOS_goos -D GOARCH_goarch -I workdir -a -n -o workdir/proc.acid proc.c
// to get acid [sic] output.
vreset(&argv);\
vadd(&argv, bpathf(&b, "%s/%sc", tooldir, gochar));\
@@ -216,8 +216,12 @@ ok:\
vadd(&argv, "-I");\
vadd(&argv, bprintf(&b, "%s", workdir));\
vadd(&argv, "-a");\
+\tvadd(&argv, "-n");\
+\tvadd(&argv, "-o");\
+\tvadd(&argv, bpathf(&b, "%s/proc.acid", workdir));\
vadd(&argv, "proc.c");\
-\trunv(&in, dir, CheckExit, &argv);\
+\trunv(nil, dir, CheckExit, &argv);\
+\treadfile(&in, bpathf(&b, "%s/proc.acid", workdir));\
\
// Convert input like\
//\taggr G
mkzruntimedefs
関数における変更:
--- a/src/cmd/dist/buildruntime.c
+++ b/src/cmd/dist/buildruntime.c
@@ -288,11 +292,12 @@ mkzruntimedefs(char *dir, char *file)\
{\n \tint i, skip;\n \tchar *p;\n-\tBuf in, b, out;\n+\tBuf in, b, b1, out;\n \tVec argv, lines, fields, seen;\n \t\n \tbinit(&in);\n \tbinit(&b);\n+\tbinit(&b1);\
\tbinit(&out);\
\tvinit(&argv);\
\tvinit(&lines);\
@@ -308,7 +313,7 @@ mkzruntimedefs(char *dir, char *file)\
\t);\
\n \t\
-\t// Run 6c -D GOOS_goos -D GOARCH_goarch -I workdir -q\
+\t// Run 6c -D GOOS_goos -D GOARCH_goarch -I workdir -q -n -o workdir/runtimedefs\
\t// on each of the runtimedefs C files.\
\tvadd(&argv, bpathf(&b, "%s/%sc", tooldir, gochar));\
\tvadd(&argv, "-D");\
@@ -318,11 +323,15 @@ mkzruntimedefs(char *dir, char *file)\
\tvadd(&argv, "-I");\
\tvadd(&argv, bprintf(&b, "%s", workdir));\
\tvadd(&argv, "-q");\
+\tvadd(&argv, "-n");\
+\tvadd(&argv, "-o");\
+\tvadd(&argv, bpathf(&b, "%s/runtimedefs", workdir));\
\tvadd(&argv, "");\
\tp = argv.p[argv.len-1];\
\tfor(i=0; i<nelem(runtimedefs); i++) {\
\t\targv.p[argv.len-1] = runtimedefs[i];\
-\t\trunv(&b, dir, CheckExit, &argv);\
+\t\trunv(nil, dir, CheckExit, &argv);\
+\t\treadfile(&b, bpathf(&b1, "%s/runtimedefs", workdir));\
\t\tbwriteb(&in, &b);\
\t}\
\targv.p[argv.len-1] = p;\
@@ -364,6 +373,7 @@ mkzruntimedefs(char *dir, char *file)\
\n \tbfree(&in);\
\tbfree(&b);\
+\tbfree(&b1);\
\tbfree(&out);\
\tvfree(&argv);\
\tvfree(&lines);\
コアとなるコードの解説
-
mkzasm
関数:vadd(&argv, "-n");
vadd(&argv, "-o");
vadd(&argv, bpathf(&b, "%s/proc.acid", workdir));
- これらの行は、
6c
コンパイラを呼び出す際のコマンドライン引数リストargv
に、-n
(オブジェクトファイル生成抑制) と-o <output_file>
(出力をファイルにリダイレクト) のオプションを追加しています。出力ファイル名はworkdir/proc.acid
となります。
- これらの行は、
runv(nil, dir, CheckExit, &argv);
runv
関数の第一引数が&in
からnil
に変更されました。これにより、6c
の標準出力はrunv
が管理するバッファには取り込まれず、-o
で指定されたファイルに直接書き込まれます。
readfile(&in, bpathf(&b, "%s/proc.acid", workdir));
runv
の実行後、新しく生成されたproc.acid
ファイルの内容をin
バッファに読み込み直しています。これは、後続の処理でこの「acid output」の内容が必要となるためです。
-
mkzruntimedefs
関数:Buf in, b, b1, out;
- 新しい
Buf
型の変数b1
が追加されました。これはreadfile
関数で一時的なバッファとして使用されます。
- 新しい
binit(&b1);
- 追加された
b1
の初期化です。
- 追加された
vadd(&argv, "-n");
vadd(&argv, "-o");
vadd(&argv, bpathf(&b, "%s/runtimedefs", workdir));
mkzasm
と同様に、6c
コンパイラの引数に-n
と-o <output_file>
を追加し、出力をworkdir/runtimedefs
にリダイレクトします。
runv(nil, dir, CheckExit, &argv);
- ここでも
runv
の第一引数がnil
に変更され、標準出力への直接書き込みを抑制します。
- ここでも
readfile(&b, bpathf(&b1, "%s/runtimedefs", workdir));
runv
実行後、リダイレクトされたruntimedefs
ファイルの内容をb
バッファに読み込み直しています。
bfree(&b1);
- 追加された
b1
バッファの解放処理です。
- 追加された
これらの変更により、6c
コンパイラからの冗長な「acid output」は一時ファイルに隔離され、コンパイルエラーが発生した際には、エラーメッセージが他の出力に埋もれることなく、開発者にとって明確に表示されるようになりました。
関連リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Go Code Review (Gerrit): https://go-review.googlesource.com/ (このコミットのCL: https://golang.org/cl/7221082)
参考にした情報源リンク
- Go言語のソースコード (特に
src/cmd/dist
ディレクトリ) - Plan 9 C Compiler (6c, 8c, 5c) のドキュメント (Goの初期コンパイラのベース)
- Go言語のコミット履歴と関連する議論
cmd/dist
のrunv
関数やBuf
,Vec
などのユーティリティ関数の実装- Go言語のビルドプロセスに関する一般的な知識
- https://golang.org/cl/7221082 (コミットメッセージに記載されているCLへのリンク)
- https://github.com/golang/go/commit/7fc64a2a1da272361fc78453c7ffea17def3bcb0 (GitHub上のコミットページ)