Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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.txtstdoutoutput.txt にリダイレクトし、command 2> error.txtstderrerror.txt にリダイレクトします。デバッグ情報や診断メッセージは、通常のプログラム出力と区別するために stderr に出力されるのが一般的です。

fmt.Printffmt.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.Stderrio.Writer インターフェースを実装しており、標準エラー出力への書き込みに使用できます。

技術的詳細

このコミットの技術的な詳細は、go コマンドが一時作業ディレクトリのパス (WORK=/tmp/...) を出力する際の出力ストリームの選択にあります。

Goのビルドプロセスでは、コンパイルやリンクなどの作業を行うための一時ディレクトリが作成されます。このディレクトリのパスは、デバッグやトラブルシューティングの際に有用な情報となります。go build -xgo build -work のようにデバッグ関連のフラグが指定された場合、この WORK ディレクトリのパスがユーザーに表示されます。

従来の挙動では、この WORK=/tmp/... の行は fmt.Printf を使用して標準出力 (stdout) に書き込まれていました。しかし、go コマンドの他のデバッグ出力(例えば、-x フラグによって表示される実行コマンドのログ)はすべて標準エラー出力 (stderr) に書き込まれるように設計されていました。

この不整合は、以下のような問題を引き起こす可能性がありました。

  1. スクリプトでの出力解析の複雑化: シェルスクリプトなどで go コマンドの出力を解析する場合、通常のプログラム出力とデバッグ情報を分離して処理することがよくあります。stdout にはプログラムの最終的な成果物や主要な情報が、stderr には診断メッセージやエラーが流れるのが一般的です。WORK の行が stdout に混入すると、stdout から純粋なプログラム出力を抽出することが難しくなります。
  2. ユーザーの混乱: ユーザーが stderr を監視してデバッグ情報を確認している場合、WORK の行が stdout に出力されることで、情報を見落とす可能性がありました。

このコミットは、fmt.Printffmt.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 コマンドの出力の一貫性が向上しました。

関連リンク

参考にした情報源リンク