[インデックス 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 build
や go install
コマンドは、ビルドプロセス中に発生する可能性のあるエラーメッセージや警告、あるいは通常のビルド進行状況を示すメッセージなど、ツールチェーンからの様々な出力を、標準出力(stdout)と標準エラー出力(stderr)の区別なく、すべて標準出力にまとめていました。
しかし、UNIX/Linux系のシステムにおける標準的な慣習として、プログラムの通常の出力は標準出力に、エラーメッセージや診断情報は標準エラー出力に出力されるべきです。これにより、ユーザーはコマンドの出力をパイプで別のコマンドに渡したり、ファイルにリダイレクトしたりする際に、エラー情報と通常の情報を容易に区別できます。例えば、command > output.txt
とした場合、エラーメッセージは画面に表示されたままになり、output.txt
には通常の出力のみが書き込まれます。
Goツールチェーンの場合、クリーンなビルドが成功した際にはほとんど出力がないため、ビルド中に発生する可能性のあるメッセージ(エラー、警告、デバッグ情報など)は、実質的にすべて「エラーまたは診断情報」と見なすことができます。したがって、これらを標準エラー出力に統一して出力する方が、ユーザーにとってより直感的で、スクリプトなどでの扱いも容易になります。
また、test.bash
スクリプトにおける mktemp
コマンドのテンプレートに関する問題も修正されています。mktemp
は一時ファイルや一時ディレクトリを作成するためのコマンドですが、テンプレート文字列の末尾に少なくとも3つの X
を含める必要があります。このコミット以前の test.bash
では、この要件を満たしていない箇所があり、特にLinux環境でエラーが発生していました。
この変更は、Goツールのユーザーエクスペリエンスとスクリプトとの互換性を向上させるための重要な改善です。
前提知識の解説
このコミットを理解するためには、以下の概念について基本的な知識が必要です。
-
標準ストリーム (Standard Streams):
- UNIX/Linux系のオペレーティングシステムにおいて、プロセスは通常、3つの標準ストリームを持っています。
- 標準入力 (stdin): プロセスがデータを読み込むための入力ストリーム。通常はキーボードに接続されています。
- 標準出力 (stdout): プロセスが通常の出力を書き込むための出力ストリーム。通常は画面(ターミナル)に接続されています。
- 標準エラー出力 (stderr): プロセスがエラーメッセージや診断情報を書き込むための出力ストリーム。通常は画面(ターミナル)に接続されています。
- これらのストリームは、リダイレクト(例:
>
でstdoutをファイルに、2>
でstderrをファイルに)やパイプ(例:|
でstdoutを別のコマンドのstdinに)によって操作できます。
- UNIX/Linux系のオペレーティングシステムにおいて、プロセスは通常、3つの標準ストリームを持っています。
-
Go言語のビルドシステム:
- Go言語は、
go build
コマンドを使用してソースコードをコンパイルし、実行可能ファイルを生成します。 go install
コマンドは、ビルドに加えて、生成された実行可能ファイルを$GOPATH/bin
または$GOBIN
に、パッケージアーカイブを$GOPATH/pkg
にインストールします。- これらのコマンドは内部的にGoコンパイラ (
go tool compile
) やリンカ (go tool link
) などのツールチェーンを呼び出します。
- Go言語は、
-
mktemp
コマンド:mktemp
は、シェルスクリプトなどで一時ファイルや一時ディレクトリを安全に作成するためのコマンドです。- ファイル名の衝突を避けるために、テンプレート文字列(例:
myfile.XXXXXX
)を指定し、X
の部分がランダムな文字列に置き換えられます。 - POSIX標準では、テンプレートの末尾に少なくとも3つの
X
が必要とされています。これは、セキュリティ上の理由(予測可能なファイル名を避けるため)と、異なるシステム間での互換性を確保するためです。
-
Goのテストスクリプト (
test.bash
):- Goプロジェクトのルートディレクトリには、Goのツールやライブラリのテストを実行するためのシェルスクリプトが含まれています。
- これらのスクリプトは、Goのビルドシステムやランタイムの様々な側面を検証するために使用されます。
-
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 hello
がrm -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...)
が呼び出されています。これにより、builder
がb.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の関数シグネチャの厳密性によるもので、機能的な変更ではありません。この特定のケースでは、出力は標準エラー出力ではなく、バッファに書き込まれます。
- この関数内でも同様の変更が行われていますが、これは特定のコンテキスト(Cgoパッケージのビルドスクリプト生成時)で
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: https://github.com/golang/go/issues/4917
- 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システム (変更セットの詳細)
[インデックス 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 build
や go install
コマンドは、ビルドプロセス中に発生する可能性のあるエラーメッセージや警告、あるいは通常のビルド進行状況を示すメッセージなど、ツールチェーンからの様々な出力を、標準出力(stdout)と標準エラー出力(stderr)の区別なく、すべて標準出力にまとめていました。
しかし、UNIX/Linux系のシステムにおける標準的な慣習として、プログラムの通常の出力は標準出力に、エラーメッセージや診断情報は標準エラー出力に出力されるべきです。これにより、ユーザーはコマンドの出力をパイプで別のコマンドに渡したり、ファイルにリダイレクトしたりする際に、エラー情報と通常の情報を容易に区別できます。例えば、command > output.txt
とした場合、エラーメッセージは画面に表示されたままになり、output.txt
には通常の出力のみが書き込まれます。
Goツールチェーンの場合、クリーンなビルドが成功した際にはほとんど出力がないため、ビルド中に発生する可能性のあるメッセージ(エラー、警告、デバッグ情報など)は、実質的にすべて「エラーまたは診断情報」と見なすことができます。したがって、これらを標準エラー出力に統一して出力する方が、ユーザーにとってより直感的で、スクリプトなどでの扱いも容易になります。
また、test.bash
スクリプトにおける mktemp
コマンドのテンプレートに関する問題も修正されています。mktemp
は一時ファイルや一時ディレクトリを作成するためのコマンドですが、テンプレート文字列の末尾に少なくとも3つの X
を含める必要があります。このコミット以前の test.bash
では、この要件を満たしていない箇所があり、特にLinux環境でエラーが発生していました。
この変更は、Goツールのユーザーエクスペリエンスとスクリプトとの互換性を向上させるための重要な改善です。
前提知識の解説
このコミットを理解するためには、以下の概念について基本的な知識が必要です。
-
標準ストリーム (Standard Streams):
- UNIX/Linux系のオペレーティングシステムにおいて、プロセスは通常、3つの標準ストリームを持っています。
- 標準入力 (stdin): プロセスがデータを読み込むための入力ストリーム。通常はキーボードに接続されています。
- 標準出力 (stdout): プロセスが通常の出力を書き込むための出力ストリーム。通常は画面(ターミナル)に接続されています。
- 標準エラー出力 (stderr): プロセスがエラーメッセージや診断情報を書き込むための出力ストリーム。通常は画面(ターミナル)に接続されています。
- これらのストリームは、リダイレクト(例:
>
でstdoutをファイルに、2>
でstderrをファイルに)やパイプ(例:|
でstdoutを別のコマンドのstdinに)によって操作できます。
- UNIX/Linux系のオペレーティングシステムにおいて、プロセスは通常、3つの標準ストリームを持っています。
-
Go言語のビルドシステム:
- Go言語は、
go build
コマンドを使用してソースコードをコンパイルし、実行可能ファイルを生成します。 go install
コマンドは、ビルドに加えて、生成された実行可能ファイルを$GOPATH/bin
または$GOBIN
に、パッケージアーカイブを$GOPATH/pkg
にインストールします。- これらのコマンドは内部的にGoコンパイラ (
go tool compile
) やリンカ (go tool link
) などのツールチェーンを呼び出します。
- Go言語は、
-
mktemp
コマンド:mktemp
は、シェルスクリプトなどで一時ファイルや一時ディレクトリを安全に作成するためのコマンドです。- ファイル名の衝突を避けるために、テンプレート文字列(例:
myfile.XXXXXX
)を指定し、X
の部分がランダムな文字列に置き換えられます。 - POSIX標準では、テンプレートの末尾に少なくとも3つの
X
が必要とされています。これは、セキュリティ上の理由(予測可能なファイル名を避けるため)と、異なるシステム間での互換性を確保するためです。
-
Goのテストスクリプト (
test.bash
):- Goプロジェクトのルートディレクトリには、Goのツールやライブラリのテストを実行するためのシェルスクリプトが含まれています。
- これらのスクリプトは、Goのビルドシステムやランタイムの様々な側面を検証するために使用されます。
-
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 hello
がrm -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...)
が呼び出されています。これにより、builder
がb.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の関数シグネチャの厳密性によるもので、機能的な変更ではありません。この特定のケースでは、出力は標準エラー出力ではなく、バッファに書き込まれます。
- この関数内でも同様の変更が行われていますが、これは特定のコンテキスト(Cgoパッケージのビルドスクリプト生成時)で
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システム (変更セットの詳細)