[インデックス 17647] ファイルの概要
このコミットは、Go言語のコマンドラインツール cmd/go における出力の挙動を修正するものです。具体的には、-x フラグ(実行されるコマンドを表示するデバッグフラグ)または -work フラグ(ビルド時に作成される一時作業ディレクトリを残すフラグ)が指定された際に表示される WORK=/tmp/... という一時ディレクトリのパスが、標準出力 (stdout) ではなく標準エラー出力 (stderr) に出力されるように変更します。これにより、他のデバッグ出力との一貫性が保たれ、スクリプトなどでの出力解析が容易になります。
コミット
commit ba10318607fc131a0c53aded88fd3da681b294c9
Author: Rob Pike <r@golang.org>
Date: Thu Sep 19 11:19:11 2013 +1000
cmd/go: write the WORK=/tmp/... line to stderr
Unlike the other output from the -x flag, it was going to stdout.
Fixes #6362.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/13746044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ba10318607fc131a0c53aded88fd3da681b294c9
元コミット内容
cmd/go: write the WORK=/tmp/... line to stderr
Unlike the other output from the -x flag, it was going to stdout.
Fixes #6362.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/13746044
変更の背景
この変更の背景には、go コマンドのデバッグ出力の一貫性の問題がありました。go build -x のように -x フラグを使用してビルドプロセスを詳細に表示する場合、通常、実行されるコマンドやその他のデバッグ情報は標準エラー出力 (stderr) に出力されます。しかし、一時作業ディレクトリのパスを示す WORK=/tmp/... という行だけが標準出力 (stdout) に出力されていました。
この不一致は、go コマンドの出力をスクリプトで解析する際に問題を引き起こす可能性がありました。例えば、stdout からプログラムの通常の出力を取得し、stderr からデバッグ情報を取得しようとする場合、WORK の行が stdout に混入することで、意図しない結果を招くことがありました。
コミットメッセージには Fixes #6362 とありますが、Go言語の公式リポジトリでこの番号のIssueを直接検索しても見つかりませんでした。しかし、コミットメッセージの内容から、このIssueは go コマンドの出力ストリームの不整合に関するバグ報告であったと推測できます。開発者はこの不整合を修正し、すべてのデバッグ関連の出力が stderr に統一されるようにしました。
前提知識の解説
cmd/go
cmd/go は、Go言語のビルドシステムとパッケージ管理を司る公式のコマンドラインツールです。go build, go run, go test, go get など、Go開発者が日常的に使用する多くのコマンドを提供します。
-x フラグ
go コマンドの多くのサブコマンド(例: go build, go install)で利用できるデバッグフラグです。このフラグを指定すると、go コマンドが内部で実行するすべての外部コマンド(コンパイラ、リンカなど)が標準エラー出力に表示されます。これにより、ビルドプロセスがどのように進行しているかを詳細に確認できます。
-work フラグ
go コマンドのサブコマンド(例: go build, go install)で利用できるフラグです。通常、go コマンドはビルドプロセス中に一時的な作業ディレクトリを作成し、ビルドが完了するとそのディレクトリを削除します。-work フラグを指定すると、この一時作業ディレクトリが削除されずに残されます。これにより、ビルド中に生成された中間ファイルなどを検査することができます。このフラグを使用すると、一時作業ディレクトリのパスが WORK=/tmp/... の形式で出力されます。
標準出力 (stdout) と標準エラー出力 (stderr)
Unix系システムにおけるプロセスは、通常3つの標準I/Oストリームを持っています。
- 標準入力 (stdin): プロセスへの入力。
- 標準出力 (stdout): プロセスが生成する通常の出力。
- 標準エラー出力 (stderr): プロセスが生成するエラーメッセージや診断情報。
これらのストリームは、シェルでリダイレクトすることで、ファイルに保存したり、他のコマンドの入力としてパイプしたりすることができます。例えば、command > output.txt は stdout を output.txt にリダイレクトし、command 2> error.txt は stderr を error.txt にリダイレクトします。デバッグ情報や診断メッセージは、通常のプログラム出力と区別するために stderr に出力されるのが一般的です。
fmt.Printf と fmt.Fprintf
Go言語の fmt パッケージは、フォーマットされたI/Oを提供します。
fmt.Printf(format string, a ...interface{}) (n int, err error): フォーマットされた文字列を標準出力 (stdout) に出力します。fmt.Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error): フォーマットされた文字列を指定されたio.Writerインターフェースに書き込みます。os.Stderrはio.Writerインターフェースを実装しており、標準エラー出力への書き込みに使用できます。
技術的詳細
このコミットの技術的な詳細は、go コマンドが一時作業ディレクトリのパス (WORK=/tmp/...) を出力する際の出力ストリームの選択にあります。
Goのビルドプロセスでは、コンパイルやリンクなどの作業を行うための一時ディレクトリが作成されます。このディレクトリのパスは、デバッグやトラブルシューティングの際に有用な情報となります。go build -x や go build -work のようにデバッグ関連のフラグが指定された場合、この WORK ディレクトリのパスがユーザーに表示されます。
従来の挙動では、この WORK=/tmp/... の行は fmt.Printf を使用して標準出力 (stdout) に書き込まれていました。しかし、go コマンドの他のデバッグ出力(例えば、-x フラグによって表示される実行コマンドのログ)はすべて標準エラー出力 (stderr) に書き込まれるように設計されていました。
この不整合は、以下のような問題を引き起こす可能性がありました。
- スクリプトでの出力解析の複雑化: シェルスクリプトなどで
goコマンドの出力を解析する場合、通常のプログラム出力とデバッグ情報を分離して処理することがよくあります。stdoutにはプログラムの最終的な成果物や主要な情報が、stderrには診断メッセージやエラーが流れるのが一般的です。WORKの行がstdoutに混入すると、stdoutから純粋なプログラム出力を抽出することが難しくなります。 - ユーザーの混乱: ユーザーが
stderrを監視してデバッグ情報を確認している場合、WORKの行がstdoutに出力されることで、情報を見落とす可能性がありました。
このコミットは、fmt.Printf を fmt.Fprintf(os.Stderr, ...) に変更することで、WORK の行も他のデバッグ出力と同様に標準エラー出力にリダイレクトします。これにより、go コマンドのデバッグ出力全体の一貫性が保たれ、スクリプトでの処理やユーザーによる情報の把握がより直感的かつ容易になります。これは、Goツールの堅牢性と使いやすさを向上させるための細かながらも重要な改善です。
コアとなるコードの変更箇所
変更は src/cmd/go/build.go ファイルの1箇所のみです。
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -435,7 +435,7 @@ func (b *builder) init() {
tfatalf("%s", err)
}
if buildX || buildWork {
- fmt.Printf("WORK=%s\\n", b.work)
+ fmt.Fprintf(os.Stderr, "WORK=%s\\n", b.work)
}
if !buildWork {
atexit(func() { os.RemoveAll(b.work) })
コアとなるコードの解説
変更されたコードは src/cmd/go/build.go ファイル内の builder.init() メソッドの一部です。このメソッドは、go コマンドがビルドプロセスを開始する際に初期化処理を行う場所です。
func (b *builder) init() {
// ... (前略) ...
// buildX または buildWork フラグが設定されている場合
if buildX || buildWork {
// 以前のコード: 標準出力に WORK ディレクトリのパスを出力
// fmt.Printf("WORK=%s\\n", b.work)
// 変更後のコード: 標準エラー出力に WORK ディレクトリのパスを出力
fmt.Fprintf(os.Stderr, "WORK=%s\\n", b.work)
}
// ... (後略) ...
}
buildXおよびbuildWorkは、それぞれ-xフラグと-workフラグがコマンドラインで指定されたかどうかを示すブール変数です。b.workは、ビルドプロセスで使用される一時作業ディレクトリのパスを保持するbuilder構造体のフィールドです。
この if buildX || buildWork ブロックは、ユーザーがデバッグ目的で一時作業ディレクトリのパスを知りたい場合に実行されます。
変更の核心は、fmt.Printf から fmt.Fprintf(os.Stderr, ...) への置き換えです。
fmt.Printf("WORK=%s\\n", b.work): これは、フォーマットされた文字列をGoプログラムの標準出力 (stdout) に書き込む関数呼び出しです。fmt.Fprintf(os.Stderr, "WORK=%s\\n", b.work): これは、フォーマットされた文字列をos.Stderrというio.Writerに書き込む関数呼び出しです。os.StderrはGo言語の標準ライブラリで提供される、標準エラー出力ストリームを表すグローバル変数です。
この変更により、WORK=/tmp/... の行は、他のデバッグ情報と同様に標準エラー出力に統一して出力されるようになり、go コマンドの出力の一貫性が向上しました。
関連リンク
- Go言語の公式GitHubリポジトリ: https://github.com/golang/go
- このコミットのGo CL (Change List) ページ: https://golang.org/cl/13746044
参考にした情報源リンク
- Go言語の
fmtパッケージドキュメント: https://pkg.go.dev/fmt - Go言語の
osパッケージドキュメント (特にos.Stderr): https://pkg.go.dev/os - Unixの標準I/Oストリームに関する一般的な情報 (stdout, stderr): https://ja.wikipedia.org/wiki/%E6%A8%99%E6%BA%96%E3%82%B9%E3%83%88%E3%83%AA%E3%83%BC%E3%83%A0
go buildコマンドのドキュメント (-xおよび-workフラグについて):go help buildまたは https://pkg.go.dev/cmd/go#hdr-Build_commands (オンラインドキュメント)