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

[インデックス 15668] ファイルの概要

このコミットは、Go言語のコマンドラインツール go build および go install の出力ストリームの扱いを変更し、ビルドおよびインストールプロセスからの標準出力と標準エラー出力を適切に分離することを目的としています。また、test.bash スクリプトにおける mktemp コマンドのテンプレートに関するバグも修正しています。

コミット

commit 53f034c7a87c2d802b992bb88814d943c30bf306
Author: Jeff R. Allen <jra@nella.org>
Date:   Mon Mar 11 11:31:14 2013 +1100

    cmd/go: send output of build and install to stderr
    
    "go build" and "go install" were mixing stdout and stderr
    from the toolchain, then putting it all on stdout. With this
    change, it stays mixed, and is sent to stderr. Because
    the toolchain does not create output in a clean compile/install,
    sending all output to stderr makese more sense.
    
    Also fix test.bash because of "mktemp: too few X's
    in template `testgo'" on Linux.
    
    Fixes #4917.
    
    R=golang-dev, rsc, adg
    CC=golang-dev
    https://golang.org/cl/7393073

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/53f034c7a87c2d802b992bb88814d943c30bf306

元コミット内容

cmd/go: build および install コマンドの出力を標準エラー出力に送るように変更。

go build および go install は、ツールチェーンからの標準出力と標準エラー出力を混在させ、それらすべてを標準出力に出力していました。この変更により、出力は混在したまま標準エラー出力に送られるようになります。ツールチェーンがクリーンなコンパイル/インストール時に出力を生成しないため、すべての出力を標準エラー出力に送る方が理にかなっています。

また、Linux上での「mktemp: too few X's in template testgo'」というエラーのため、test.bash` を修正しました。

Issue #4917 を修正します。

変更の背景

このコミットの主な背景は、Goツールチェーン(コンパイラ、リンカなど)からの出力の扱いに関する問題です。従来の go buildgo install コマンドは、ビルドプロセス中に発生する可能性のあるエラーメッセージや警告、あるいは通常のビルド進行状況を示すメッセージなど、ツールチェーンからの様々な出力を、標準出力(stdout)と標準エラー出力(stderr)の区別なく、すべて標準出力にまとめていました。

しかし、UNIX/Linux系のシステムにおける標準的な慣習として、プログラムの通常の出力は標準出力に、エラーメッセージや診断情報は標準エラー出力に出力されるべきです。これにより、ユーザーはコマンドの出力をパイプで別のコマンドに渡したり、ファイルにリダイレクトしたりする際に、エラー情報と通常の情報を容易に区別できます。例えば、command > output.txt とした場合、エラーメッセージは画面に表示されたままになり、output.txt には通常の出力のみが書き込まれます。

Goツールチェーンの場合、クリーンなビルドが成功した際にはほとんど出力がないため、ビルド中に発生する可能性のあるメッセージ(エラー、警告、デバッグ情報など)は、実質的にすべて「エラーまたは診断情報」と見なすことができます。したがって、これらを標準エラー出力に統一して出力する方が、ユーザーにとってより直感的で、スクリプトなどでの扱いも容易になります。

また、test.bash スクリプトにおける mktemp コマンドのテンプレートに関する問題も修正されています。mktemp は一時ファイルや一時ディレクトリを作成するためのコマンドですが、テンプレート文字列の末尾に少なくとも3つの X を含める必要があります。このコミット以前の test.bash では、この要件を満たしていない箇所があり、特にLinux環境でエラーが発生していました。

この変更は、Goツールのユーザーエクスペリエンスとスクリプトとの互換性を向上させるための重要な改善です。

前提知識の解説

このコミットを理解するためには、以下の概念について基本的な知識が必要です。

  1. 標準ストリーム (Standard Streams):

    • UNIX/Linux系のオペレーティングシステムにおいて、プロセスは通常、3つの標準ストリームを持っています。
      • 標準入力 (stdin): プロセスがデータを読み込むための入力ストリーム。通常はキーボードに接続されています。
      • 標準出力 (stdout): プロセスが通常の出力を書き込むための出力ストリーム。通常は画面(ターミナル)に接続されています。
      • 標準エラー出力 (stderr): プロセスがエラーメッセージや診断情報を書き込むための出力ストリーム。通常は画面(ターミナル)に接続されています。
    • これらのストリームは、リダイレクト(例: > でstdoutをファイルに、2> でstderrをファイルに)やパイプ(例: | でstdoutを別のコマンドのstdinに)によって操作できます。
  2. Go言語のビルドシステム:

    • Go言語は、go build コマンドを使用してソースコードをコンパイルし、実行可能ファイルを生成します。
    • go install コマンドは、ビルドに加えて、生成された実行可能ファイルを $GOPATH/bin または $GOBIN に、パッケージアーカイブを $GOPATH/pkg にインストールします。
    • これらのコマンドは内部的にGoコンパイラ (go tool compile) やリンカ (go tool link) などのツールチェーンを呼び出します。
  3. mktemp コマンド:

    • mktemp は、シェルスクリプトなどで一時ファイルや一時ディレクトリを安全に作成するためのコマンドです。
    • ファイル名の衝突を避けるために、テンプレート文字列(例: myfile.XXXXXX)を指定し、X の部分がランダムな文字列に置き換えられます。
    • POSIX標準では、テンプレートの末尾に少なくとも3つの X が必要とされています。これは、セキュリティ上の理由(予測可能なファイル名を避けるため)と、異なるシステム間での互換性を確保するためです。
  4. Goのテストスクリプト (test.bash):

    • Goプロジェクトのルートディレクトリには、Goのツールやライブラリのテストを実行するためのシェルスクリプトが含まれています。
    • これらのスクリプトは、Goのビルドシステムやランタイムの様々な側面を検証するために使用されます。
  5. GoのIssueトラッカー:

    • Goプロジェクトは、バグ報告や機能リクエストを管理するためにIssueトラッカー(GitHub Issuesまたは以前のGo Bug Tracker)を使用しています。
    • コミットメッセージの「Fixes #XXXX」は、そのコミットが特定のIssueを修正したことを示します。

これらの知識があれば、コミットがなぜ行われたのか、そしてそれがシステムにどのような影響を与えるのかをより深く理解できます。

技術的詳細

このコミットは、主にGoコマンドのビルドおよびインストールプロセスにおける出力のハンドリングと、テストスクリプトの堅牢性向上に焦点を当てています。

1. go build および go install の出力リダイレクト

  • 変更前: go build および go install は、内部的に呼び出すGoツールチェーン(コンパイラ、リンカなど)からの標準出力と標準エラー出力を区別せず、すべてを自身の標準出力にまとめていました。これは、builder 構造体の print フィールドが fmt.Print (標準出力に書き込む) に設定されていたためです。
  • 変更後: src/cmd/go/build.go 内の builder 構造体の init メソッドにおいて、b.print フィールドが fmt.Fprint(os.Stderr, a...) を使用するように変更されました。
    • fmt.Fprint は、指定された io.Writer にフォーマットされた文字列を書き込む関数です。
    • os.Stderr は、Go言語で標準エラー出力を表す io.Writer インターフェースを実装したオブジェクトです。
    • これにより、builder が出力するすべてのメッセージ(ツールチェーンからのメッセージを含む)が、Goコマンド自身の標準エラー出力にリダイレクトされるようになります。
  • 例外処理: builder.libgcc メソッド内では、特定の状況下でコマンドラインをキャプチャするために b.print が一時的に bytes.Buffer に書き込むように再定義されています。この変更後も、この特殊なケースでは fmt.Fprint(&buf, a...) が使用され、標準エラー出力へのリダイレクトは行われません。これは、コマンドライン情報を変数に格納する必要があるため、意図的な挙動です。

この変更の技術的な利点は以下の通りです。

  • 出力の分離: エラーメッセージや警告が標準エラー出力に送られることで、スクリプトや自動化ツールがGoコマンドの出力をより正確に解析できるようになります。例えば、go build > /dev/null としても、エラーは画面に表示されるため、ビルドの成否を視覚的に確認しやすくなります。
  • UNIX哲学への準拠: プログラムの通常の出力とエラー/診断情報を異なるストリームに分離するというUNIXの基本的な哲学に準拠します。

2. test.bash スクリプトの修正

  • mktemp テンプレートの修正: src/cmd/go/test.bash 内の複数の箇所で、mktemp -d -t testgo のようなテンプレートが mktemp -d -t testgoXXX に変更されました。
    • これは、mktemp コマンドが一時ファイル/ディレクトリ名を生成する際に、テンプレート文字列の末尾に少なくとも3つの X を要求するというPOSIX標準に準拠するためです。
    • この修正により、特にLinux環境で発生していた「mktemp: too few X's in template testgo'`」というエラーが解消されます。
  • エラーメッセージテストの改善:
    • 以前は testdata/errmsg/*.go のような既存のファイルに対してエラーメッセージのテストを行っていましたが、このコミットでは一時ディレクトリと一時ファイル ($d/err.go) を動的に作成してテストするように変更されました。
    • これにより、テストの独立性が高まり、既存のテストデータファイルに依存しない形でエラーメッセージのフォーマット(ファイル名:行番号)と、出力が標準エラー出力に送られることの両方を検証できるようになりました。
    • ./testgo test $i 2>&1 | cat >err.out || true のような複雑なリダイレクトとパイプの組み合わせが、./testgo run $fn 2>$d/err.out || true のように簡潔になり、テストの意図がより明確になりました。
    • 不要になった testdata/errmsg/x.go, x1_test.go, x_test.go ファイルが削除されました。
  • 一時ファイルのクリーンアップ: rm -f err.out hello.out hellorm -f hello.out hello に変更され、err.out のクリーンアップが不要になりました。これは、エラーメッセージのテストが一時ディレクトリ内で行われるようになったためです。

これらの test.bash の変更は、テストの信頼性と移植性を向上させ、開発者が異なる環境でGoツールをテストする際の潜在的な問題を減少させます。

コアとなるコードの変更箇所

src/cmd/go/build.go

--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -391,7 +391,9 @@ var (
 
 func (b *builder) init() {
 	var err error
-	b.print = fmt.Print
+	b.print = func(a ...interface{}) (int, error) {
+		return fmt.Fprint(os.Stderr, a...)
+	}
 	b.actionCache = make(map[cacheKey]*action)
 	b.mkdirCache = make(map[string]bool)
 
@@ -1632,7 +1634,7 @@ func (b *builder) libgcc(p *Package) (string, error) {
 		// print function to capture the command-line. This
 		// let's us assign it to $LIBGCC and produce a valid
 		// buildscript for cgo packages.
-		b.print = func(a ...interface{}) (n int, err error) {
+		b.print = func(a ...interface{}) (int, error) {
 			return fmt.Fprint(&buf, a...)
 		}
 	}

src/cmd/go/test.bash

--- a/src/cmd/go/test.bash
+++ b/src/cmd/go/test.bash
@@ -11,18 +11,19 @@ ok=true
 unset GOPATH
 unset GOBIN
 
-# Test that error messages have file:line information
-# at beginning of line.
-for i in testdata/errmsg/*.go
-do
-# TODO: |cat should not be necessary here but is.
-./testgo test $i 2>&1 | cat >err.out || true
-if ! grep -q "^$i:" err.out; then
-echo "$i: missing file:line in error message"
-cat err.out
-ok=false
-fi
-done
+# Test that error messages have file:line information at beginning of
+# the line. Also test issue 4917: that the error is on stderr.
+d=$(mktemp -d -t testgoXXX)
+fn=$d/err.go
+echo "package main" > $fn
+echo 'import "bar"' >> $fn
+./testgo run $fn 2>$d/err.out || true
+if ! grep -q "^$fn:" $d/err.out; then
+echo "missing file:line in error message"
+cat $d/err.out
+ok=false
+fi
+rm -r $d
 
 # Test local (./) imports.
 testlocal() {
@@ -51,7 +52,7 @@ testlocal() {
 		ok=false
 	fi
 	
-	rm -f err.out hello.out hello
+	rm -f hello.out hello
 	
 	# Test that go install x.go fails.
 	if ./testgo install "testdata/$local/easy.go" >/dev/null 2>&1; then
@@ -183,7 +184,7 @@ fi
 
 # issue 4186. go get cannot be used to download packages to $GOROOT
 # Test that without GOPATH set, go get should fail
-d=$(mktemp -d -t testgo)\n+d=$(mktemp -d -t testgoXXX)
 mkdir -p $d/src/pkg
 if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then 
 	echo 'go get code.google.com/p/go.codereview/cmd/hgpatch should not succeed with $GOPATH unset'
@@ -191,7 +192,7 @@ if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch
 fi	
 rm -rf $d
 # Test that with GOPATH=$GOROOT, go get should fail
-d=$(mktemp -d -t testgo)\n+d=$(mktemp -d -t testgoXXX)
 mkdir -p $d/src/pkg
 if GOPATH=$d GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then
         echo 'go get code.google.com/p/go.codereview/cmd/hgpatch should not succeed with GOPATH=$GOROOT'
@@ -200,7 +201,7 @@ fi
 rm -rf $d
 
 # issue 3941: args with spaces
-d=$(mktemp -d -t testgo)\n+d=$(mktemp -d -t testgoXXX)
 cat >$d/main.go<<EOF
  package main
  var extern string
@@ -226,7 +227,7 @@ rm -f strings.prof strings.test
 
 # issue 4568. test that symlinks don't screw things up too badly.
 old=$(pwd)
-d=$(mktemp -d -t testgo)\n+d=$(mktemp -d -t testgoXXX)
 mkdir -p $d/src
 (
 	ln -s $d $d/src/dir1
@@ -247,7 +248,7 @@ fi
 rm -rf $d
 
 # issue 4515.
-d=$(mktemp -d -t testgo)\n+d=$(mktemp -d -t testgoXXX)
 mkdir -p $d/src/example/a $d/src/example/b $d/bin
 cat >$d/src/example/a/main.go <<EOF
  package main
@@ -280,7 +281,7 @@ unset GOPATH
 rm -rf $d
 
 # issue 4773. case-insensitive collisions
-d=$(mktemp -d -t testgo)\n+d=$(mktemp -d -t testgoXXX)
 export GOPATH=$d
 mkdir -p $d/src/example/a $d/src/example/b
 cat >$d/src/example/a/a.go <<EOF

削除されたファイル

  • src/cmd/go/testdata/errmsg/x.go
  • src/cmd/go/testdata/errmsg/x1_test.go
  • src/cmd/go/testdata/errmsg/x_test.go

コアとなるコードの解説

src/cmd/go/build.go の変更

このファイルは、go コマンドのビルドロジックを実装しています。

  • func (b *builder) init():
    • b.print = fmt.Print から b.print = func(a ...interface{}) (int, error) { return fmt.Fprint(os.Stderr, a...) } への変更が最も重要です。
    • builder 構造体は、ビルドプロセス中にメッセージを出力するための print フィールドを持っています。このフィールドは関数型 (func(...interface{}) (int, error)) です。
    • 変更前は、Goの標準ライブラリ関数である fmt.Print が直接割り当てられていました。fmt.Print はデフォルトで標準出力に書き込みます。
    • 変更後は、匿名関数が割り当てられ、その匿名関数内で fmt.Fprint(os.Stderr, a...) が呼び出されています。これにより、builderb.print を通じて出力するすべてのメッセージが、標準エラー出力 (os.Stderr) にリダイレクトされるようになります。
  • func (b *builder) libgcc(p *Package) (string, error):
    • この関数内でも同様の変更が行われていますが、これは特定のコンテキスト(Cgoパッケージのビルドスクリプト生成時)で b.print が一時的に bytes.Buffer に書き込むように再定義される部分です。
    • ここでの変更は、b.print の型定義が (n int, err error) から (int, error) に変更されたことによるものです。これはGoの関数シグネチャの厳密性によるもので、機能的な変更ではありません。この特定のケースでは、出力は標準エラー出力ではなく、バッファに書き込まれます。

src/cmd/go/test.bash の変更

このファイルは、Goツールのテストスイートの一部です。

  • エラーメッセージテストのセクション:
    • 以前は testdata/errmsg/*.go のような静的なテストファイルを使用していましたが、新しいコードでは mktemp -d -t testgoXXX を使用して一時ディレクトリを作成し、その中に err.go という一時的なGoソースファイルを作成しています。
    • この err.go ファイルは、意図的にコンパイルエラーを引き起こすような内容(例: import "bar")を含んでいます。
    • ./testgo run $fn 2>$d/err.out || true コマンドは、この一時ファイルを実行しようとし、その標準エラー出力を $d/err.out にリダイレクトします。
    • grep -q "^$fn:" $d/err.out は、エラー出力が期待されるファイル名と行番号のプレフィックス(例: /tmp/testgoXXXXX/err.go:) を含んでいるか、そしてその出力が標準エラー出力に正しくリダイレクトされているかを検証します。
    • テスト終了後、rm -r $d で一時ディレクトリがクリーンアップされます。
  • mktemp テンプレートの修正:
    • スクリプト全体で mktemp -d -t testgo と書かれていた箇所が、すべて mktemp -d -t testgoXXX に変更されています。
    • これは、mktemp コマンドの要件(テンプレートの末尾に少なくとも3つの X)を満たすための修正であり、特にLinux環境での互換性問題を解決します。

削除されたファイル

  • src/cmd/go/testdata/errmsg/x.go, x1_test.go, x_test.go: これらは以前のエラーメッセージテストで使用されていた静的なテストファイルです。テストロジックが一時ファイルを動的に生成するように変更されたため、これらのファイルは不要となり削除されました。

これらの変更により、Goコマンドの出力管理が改善され、テストスイートの堅牢性と移植性が向上しています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (Goコマンド、パッケージ、ビルドシステムに関する情報)
  • UNIX/Linuxの標準ストリームに関するドキュメント (stdout, stderr, リダイレクトなど)
  • mktemp コマンドのmanページまたはドキュメント (テンプレートの要件など)
  • Goのソースコード (特に src/cmd/go/ ディレクトリ内のファイル)
  • GoのIssueトラッカー (Issue #4917の詳細)
  • GoのCode Reviewシステム (変更セットの詳細)

[インデックス 15668] ファイルの概要

このコミットは、Go言語のコマンドラインツール go build および go install の出力ストリームの扱いを変更し、ビルドおよびインストールプロセスからの標準出力と標準エラー出力を適切に分離することを目的としています。また、test.bash スクリプトにおける mktemp コマンドのテンプレートに関するバグも修正しています。

コミット

commit 53f034c7a87c2d802b992bb88814d943c30bf306
Author: Jeff R. Allen <jra@nella.org>
Date:   Mon Mar 11 11:31:14 2013 +1100

    cmd/go: send output of build and install to stderr
    
    "go build" and "go install" were mixing stdout and stderr
    from the toolchain, then putting it all on stdout. With this
    change, it stays mixed, and is sent to stderr. Because
    the toolchain does not create output in a clean compile/install,
    sending all output to stderr makese more sense.
    
    Also fix test.bash because of "mktemp: too few X's
    in template `testgo'" on Linux.
    
    Fixes #4917.
    
    R=golang-dev, rsc, adg
    CC=golang-dev
    https://golang.org/cl/7393073

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/53f034c7a87c2d802b992bb88814d943c30bf306

元コミット内容

cmd/go: build および install コマンドの出力を標準エラー出力に送るように変更。

go build および go install は、ツールチェーンからの標準出力と標準エラー出力を混在させ、それらすべてを標準出力に出力していました。この変更により、出力は混在したまま標準エラー出力に送られるようになります。ツールチェーンがクリーンなコンパイル/インストール時に出力を生成しないため、すべての出力を標準エラー出力に送る方が理にかなっています。

また、Linux上での「mktemp: too few X's in template testgo'」というエラーのため、test.bash` を修正しました。

Issue #4917 を修正します。

変更の背景

このコミットの主な背景は、Goツールチェーン(コンパイラ、リンカなど)からの出力の扱いに関する問題です。従来の go buildgo install コマンドは、ビルドプロセス中に発生する可能性のあるエラーメッセージや警告、あるいは通常のビルド進行状況を示すメッセージなど、ツールチェーンからの様々な出力を、標準出力(stdout)と標準エラー出力(stderr)の区別なく、すべて標準出力にまとめていました。

しかし、UNIX/Linux系のシステムにおける標準的な慣習として、プログラムの通常の出力は標準出力に、エラーメッセージや診断情報は標準エラー出力に出力されるべきです。これにより、ユーザーはコマンドの出力をパイプで別のコマンドに渡したり、ファイルにリダイレクトしたりする際に、エラー情報と通常の情報を容易に区別できます。例えば、command > output.txt とした場合、エラーメッセージは画面に表示されたままになり、output.txt には通常の出力のみが書き込まれます。

Goツールチェーンの場合、クリーンなビルドが成功した際にはほとんど出力がないため、ビルド中に発生する可能性のあるメッセージ(エラー、警告、デバッグ情報など)は、実質的にすべて「エラーまたは診断情報」と見なすことができます。したがって、これらを標準エラー出力に統一して出力する方が、ユーザーにとってより直感的で、スクリプトなどでの扱いも容易になります。

また、test.bash スクリプトにおける mktemp コマンドのテンプレートに関する問題も修正されています。mktemp は一時ファイルや一時ディレクトリを作成するためのコマンドですが、テンプレート文字列の末尾に少なくとも3つの X を含める必要があります。このコミット以前の test.bash では、この要件を満たしていない箇所があり、特にLinux環境でエラーが発生していました。

この変更は、Goツールのユーザーエクスペリエンスとスクリプトとの互換性を向上させるための重要な改善です。

前提知識の解説

このコミットを理解するためには、以下の概念について基本的な知識が必要です。

  1. 標準ストリーム (Standard Streams):

    • UNIX/Linux系のオペレーティングシステムにおいて、プロセスは通常、3つの標準ストリームを持っています。
      • 標準入力 (stdin): プロセスがデータを読み込むための入力ストリーム。通常はキーボードに接続されています。
      • 標準出力 (stdout): プロセスが通常の出力を書き込むための出力ストリーム。通常は画面(ターミナル)に接続されています。
      • 標準エラー出力 (stderr): プロセスがエラーメッセージや診断情報を書き込むための出力ストリーム。通常は画面(ターミナル)に接続されています。
    • これらのストリームは、リダイレクト(例: > でstdoutをファイルに、2> でstderrをファイルに)やパイプ(例: | でstdoutを別のコマンドのstdinに)によって操作できます。
  2. Go言語のビルドシステム:

    • Go言語は、go build コマンドを使用してソースコードをコンパイルし、実行可能ファイルを生成します。
    • go install コマンドは、ビルドに加えて、生成された実行可能ファイルを $GOPATH/bin または $GOBIN に、パッケージアーカイブを $GOPATH/pkg にインストールします。
    • これらのコマンドは内部的にGoコンパイラ (go tool compile) やリンカ (go tool link) などのツールチェーンを呼び出します。
  3. mktemp コマンド:

    • mktemp は、シェルスクリプトなどで一時ファイルや一時ディレクトリを安全に作成するためのコマンドです。
    • ファイル名の衝突を避けるために、テンプレート文字列(例: myfile.XXXXXX)を指定し、X の部分がランダムな文字列に置き換えられます。
    • POSIX標準では、テンプレートの末尾に少なくとも3つの X が必要とされています。これは、セキュリティ上の理由(予測可能なファイル名を避けるため)と、異なるシステム間での互換性を確保するためです。
  4. Goのテストスクリプト (test.bash):

    • Goプロジェクトのルートディレクトリには、Goのツールやライブラリのテストを実行するためのシェルスクリプトが含まれています。
    • これらのスクリプトは、Goのビルドシステムやランタイムの様々な側面を検証するために使用されます。
  5. GoのIssueトラッカー:

    • Goプロジェクトは、バグ報告や機能リクエストを管理するためにIssueトラッカー(GitHub Issuesまたは以前のGo Bug Tracker)を使用しています。
    • コミットメッセージの「Fixes #XXXX」は、そのコミットが特定のIssueを修正したことを示します。

これらの知識があれば、コミットがなぜ行われたのか、そしてそれがシステムにどのような影響を与えるのかをより深く理解できます。

技術的詳細

このコミットは、主にGoコマンドのビルドおよびインストールプロセスにおける出力のハンドリングと、テストスクリプトの堅牢性向上に焦点を当てています。

1. go build および go install の出力リダイレクト

  • 変更前: go build および go install は、内部的に呼び出すGoツールチェーン(コンパイラ、リンカなど)からの標準出力と標準エラー出力を区別せず、すべてを自身の標準出力にまとめていました。これは、builder 構造体の print フィールドが fmt.Print (標準出力に書き込む) に設定されていたためです。
  • 変更後: src/cmd/go/build.go 内の builder 構造体の init メソッドにおいて、b.print フィールドが fmt.Fprint(os.Stderr, a...) を使用するように変更されました。
    • fmt.Fprint は、指定された io.Writer にフォーマットされた文字列を書き込む関数です。
    • os.Stderr は、Go言語で標準エラー出力を表す io.Writer インターフェースを実装したオブジェクトです。
    • これにより、builder が出力するすべてのメッセージ(ツールチェーンからのメッセージを含む)が、Goコマンド自身の標準エラー出力にリダイレクトされるようになります。
  • 例外処理: builder.libgcc メソッド内では、特定の状況下でコマンドラインをキャプチャするために b.print が一時的に bytes.Buffer に書き込むように再定義されています。この変更後も、この特殊なケースでは fmt.Fprint(&buf, a...) が使用され、標準エラー出力へのリダイレクトは行われません。これは、コマンドライン情報を変数に格納する必要があるため、意図的な挙動です。

この変更の技術的な利点は以下の通りです。

  • 出力の分離: エラーメッセージや警告が標準エラー出力に送られることで、スクリプトや自動化ツールがGoコマンドの出力をより正確に解析できるようになります。例えば、go build > /dev/null としても、エラーは画面に表示されるため、ビルドの成否を視覚的に確認しやすくなります。
  • UNIX哲学への準拠: プログラムの通常の出力とエラー/診断情報を異なるストリームに分離するというUNIXの基本的な哲学に準拠します。

2. test.bash スクリプトの修正

  • mktemp テンプレートの修正: src/cmd/go/test.bash 内の複数の箇所で、mktemp -d -t testgo のようなテンプレートが mktemp -d -t testgoXXX に変更されました。
    • これは、mktemp コマンドが一時ファイル/ディレクトリ名を生成する際に、テンプレート文字列の末尾に少なくとも3つの X を要求するというPOSIX標準に準拠するためです。
    • この修正により、特にLinux環境で発生していた「mktemp: too few X's in template testgo'`」というエラーが解消されます。
  • エラーメッセージテストの改善:
    • 以前は testdata/errmsg/*.go のような既存のファイルに対してエラーメッセージのテストを行っていましたが、このコミットでは一時ディレクトリと一時ファイル ($d/err.go) を動的に作成してテストするように変更されました。
    • これにより、テストの独立性が高まり、既存のテストデータファイルに依存しない形でエラーメッセージのフォーマット(ファイル名:行番号)と、出力が標準エラー出力に送られることの両方を検証できるようになりました。
    • ./testgo test $i 2>&1 | cat >err.out || true のような複雑なリダイレクトとパイプの組み合わせが、./testgo run $fn 2>$d/err.out || true のように簡潔になり、テストの意図がより明確になりました。
    • 不要になった testdata/errmsg/x.go, x1_test.go, x_test.go ファイルが削除されました。
  • 一時ファイルのクリーンアップ: rm -f err.out hello.out hellorm -f hello.out hello に変更され、err.out のクリーンアップが不要になりました。これは、エラーメッセージのテストが一時ディレクトリ内で行われるようになったためです。

これらの test.bash の変更は、テストの信頼性と移植性を向上させ、開発者が異なる環境でGoツールをテストする際の潜在的な問題を減少させます。

コアとなるコードの変更箇所

src/cmd/go/build.go

--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -391,7 +391,9 @@ var (
 
 func (b *builder) init() {
 	var err error
-	b.print = fmt.Print
+	b.print = func(a ...interface{}) (int, error) {
+		return fmt.Fprint(os.Stderr, a...)
+	}
 	b.actionCache = make(map[cacheKey]*action)
 	b.mkdirCache = make(map[string]bool)
 
@@ -1632,7 +1634,7 @@ func (b *builder) libgcc(p *Package) (string, error) {
 		// print function to capture the command-line. This
 		// let's us assign it to $LIBGCC and produce a valid
 		// buildscript for cgo packages.
-		b.print = func(a ...interface{}) (n int, err error) {
+		b.print = func(a ...interface{}) (int, error) {
 			return fmt.Fprint(&buf, a...)
 		}
 	}

src/cmd/go/test.bash

--- a/src/cmd/go/test.bash
+++ b/src/cmd/go/test.bash
@@ -11,18 +11,19 @@ ok=true
 unset GOPATH
 unset GOBIN
 
-# Test that error messages have file:line information
-# at beginning of line.
-for i in testdata/errmsg/*.go
-do
-# TODO: |cat should not be necessary here but is.
-./testgo test $i 2>&1 | cat >err.out || true
-if ! grep -q "^$i:" err.out; then
-echo "$i: missing file:line in error message"
-cat err.out
-ok=false
-fi
-done
+# Test that error messages have file:line information at beginning of
+# the line. Also test issue 4917: that the error is on stderr.
+d=$(mktemp -d -t testgoXXX)
+fn=$d/err.go
+echo "package main" > $fn
+echo 'import "bar"' >> $fn
+./testgo run $fn 2>$d/err.out || true
+if ! grep -q "^$fn:" $d/err.out; then
+echo "missing file:line in error message"
+cat $d/err.out
+ok=false
+fi
+rm -r $d
 
 # Test local (./) imports.
 testlocal() {
@@ -51,7 +52,7 @@ testlocal() {
 		ok=false
 	fi
 	
-	rm -f err.out hello.out hello
+	rm -f hello.out hello
 	
 	# Test that go install x.go fails.
 	if ./testgo install "testdata/$local/easy.go" >/dev/null 2>&1; then
@@ -183,7 +184,7 @@ fi
 
 # issue 4186. go get cannot be used to download packages to $GOROOT
 # Test that without GOPATH set, go get should fail
-d=$(mktemp -d -t testgo)\n+d=$(mktemp -d -t testgoXXX)
 mkdir -p $d/src/pkg
 if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then 
 	echo 'go get code.google.com/p/go.codereview/cmd/hgpatch should not succeed with $GOPATH unset'
@@ -191,7 +192,7 @@ if GOPATH= GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch
 fi	
 rm -rf $d
 # Test that with GOPATH=$GOROOT, go get should fail
-d=$(mktemp -d -t testgo)\n+d=$(mktemp -d -t testgoXXX)
 mkdir -p $d/src/pkg
 if GOPATH=$d GOROOT=$d ./testgo get -d code.google.com/p/go.codereview/cmd/hgpatch ; then
         echo 'go get code.google.com/p/go.codereview/cmd/hgpatch should not succeed with GOPATH=$GOROOT'
@@ -200,7 +201,7 @@ fi
 rm -rf $d
 
 # issue 3941: args with spaces
-d=$(mktemp -d -t testgo)\n+d=$(mktemp -d -t testgoXXX)
 cat >$d/main.go<<EOF
  package main
  var extern string
@@ -226,7 +227,7 @@ rm -f strings.prof strings.test
 
 # issue 4568. test that symlinks don't screw things up too badly.
 old=$(pwd)
-d=$(mktemp -d -t testgo)\n+d=$(mktemp -d -t testgoXXX)
 mkdir -p $d/src
 (
 	ln -s $d $d/src/dir1
@@ -247,7 +248,7 @@ fi
 rm -rf $d
 
 # issue 4515.
-d=$(mktemp -d -t testgo)\n+d=$(mktemp -d -t testgoXXX)
 mkdir -p $d/src/example/a $d/src/example/b $d/bin
 cat >$d/src/example/a/main.go <<EOF
  package main
@@ -280,7 +281,7 @@ unset GOPATH
 rm -rf $d
 
 # issue 4773. case-insensitive collisions
-d=$(mktemp -d -t testgo)\n+d=$(mktemp -d -t testgoXXX)
 export GOPATH=$d
 mkdir -p $d/src/example/a $d/src/example/b
 cat >$d/src/example/a/a.go <<EOF

削除されたファイル

  • src/cmd/go/testdata/errmsg/x.go
  • src/cmd/go/testdata/errmsg/x1_test.go
  • src/cmd/go/testdata/errmsg/x_test.go

コアとなるコードの解説

src/cmd/go/build.go の変更

このファイルは、go コマンドのビルドロジックを実装しています。

  • func (b *builder) init():
    • b.print = fmt.Print から b.print = func(a ...interface{}) (int, error) { return fmt.Fprint(os.Stderr, a...) } への変更が最も重要です。
    • builder 構造体は、ビルドプロセス中にメッセージを出力するための print フィールドを持っています。このフィールドは関数型 (func(...interface{}) (int, error)) です。
    • 変更前は、Goの標準ライブラリ関数である fmt.Print が直接割り当てられていました。fmt.Print はデフォルトで標準出力に書き込みます。
    • 変更後は、匿名関数が割り当てられ、その匿名関数内で fmt.Fprint(os.Stderr, a...) が呼び出されています。これにより、builderb.print を通じて出力するすべてのメッセージが、標準エラー出力 (os.Stderr) にリダイレクトされるようになります。
  • func (b *builder) libgcc(p *Package) (string, error):
    • この関数内でも同様の変更が行われていますが、これは特定のコンテキスト(Cgoパッケージのビルドスクリプト生成時)で b.print が一時的に bytes.Buffer に書き込むように再定義される部分です。
    • ここでの変更は、b.print の型定義が (n int, err error) から (int, error) に変更されたことによるものです。これはGoの関数シグネチャの厳密性によるもので、機能的な変更ではありません。この特定のケースでは、出力は標準エラー出力ではなく、バッファに書き込まれます。

src/cmd/go/test.bash の変更

このファイルは、Goツールのテストスイートの一部です。

  • エラーメッセージテストのセクション:
    • 以前は testdata/errmsg/*.go のような静的なテストファイルを使用していましたが、新しいコードでは mktemp -d -t testgoXXX を使用して一時ディレクトリを作成し、その中に err.go という一時的なGoソースファイルを作成しています。
    • この err.go ファイルは、意図的にコンパイルエラーを引き起こすような内容(例: import "bar")を含んでいます。
    • ./testgo run $fn 2>$d/err.out || true コマンドは、この一時ファイルを実行しようとし、その標準エラー出力を $d/err.out にリダイレクトします。
    • grep -q "^$fn:" $d/err.out は、エラー出力が期待されるファイル名と行番号のプレフィックス(例: /tmp/testgoXXXXX/err.go:) を含んでいるか、そしてその出力が標準エラー出力に正しくリダイレクトされているかを検証します。
    • テスト終了後、rm -r $d で一時ディレクトリがクリーンアップされます。
  • mktemp テンプレートの修正:
    • スクリプト全体で mktemp -d -t testgo と書かれていた箇所が、すべて mktemp -d -t testgoXXX に変更されています。
    • これは、mktemp コマンドの要件(テンプレートの末尾に少なくとも3つの X)を満たすための修正であり、特にLinux環境での互換性問題を解決します。

削除されたファイル

  • src/cmd/go/testdata/errmsg/x.go, x1_test.go, x_test.go: これらは以前のエラーメッセージテストで使用されていた静的なテストファイルです。テストロジックが一時ファイルを動的に生成するように変更されたため、これらのファイルは不要となり削除されました。

これらの変更により、Goコマンドの出力管理が改善され、テストスイートの堅牢性と移植性が向上しています。

関連リンク

  • Go Issue #4917: このコミットが修正したIssueですが、GoのIssueトラッカーの移行により、現在のGitHubリポジトリでは直接この番号のIssueを見つけるのが難しい場合があります。当時のIssueは code.google.com/p/go/issues に存在していた可能性があります。
  • Go Code Review 7393073: https://golang.org/cl/7393073 (現在は go.googlesource.com にリダイレクトされます)

参考にした情報源リンク

  • Go言語の公式ドキュメント (Goコマンド、パッケージ、ビルドシステムに関する情報)
  • UNIX/Linuxの標準ストリームに関するドキュメント (stdout, stderr, リダイレクトなど)
  • mktemp コマンドのmanページまたはドキュメント (テンプレートの要件など)
  • Goのソースコード (特に src/cmd/go/ ディレクトリ内のファイル)
  • GoのIssueトラッカー (Issue #4917の詳細)
  • GoのCode Reviewシステム (変更セットの詳細)