[インデックス 12380] ファイルの概要
このコミットは、Goコマンドラインツール(cmd/go
)におけるgo test
コマンドの振る舞いを修正するものです。具体的には、go test
がgo build
コマンドで利用可能なビルド関連のフラグ(例: -a
, -n
, -x
, -p
, -gcflags
, -ldflags
, -tags
, -work
)を適切に認識し、適用するように変更されています。
変更されたファイルは以下の通りです。
src/cmd/go/build.go
: ビルドフラグに関するコメントが追加されました。src/cmd/go/test.go
:go test
コマンドの内部ロジックから、ビルドフラグに関する冗長な処理が削除されました。src/cmd/go/testflag.go
:go test
が認識するフラグの定義が拡張され、go build
のフラグと直接連携するように修正されました。
コミット
commit c073a1602a63562c7ee66a2821c4b400b3c3f34e
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date: Mon Mar 5 19:58:04 2012 +0100
cmd/go: honor buildflags in go test.
Fixes #3196.
R=golang-dev, rsc
CC=golang-dev, remy
https://golang.org/cl/5725044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c073a1602a63562c7ee66a2821c4b400b3c3f34e
元コミット内容
cmd/go: honor buildflags in go test.
Fixes #3196.
変更の背景
この変更の背景には、Goのgo test
コマンドが、テストのビルドプロセスを制御するための標準的なビルドフラグ(例: -a
, -n
, -x
, -p
など)を適切に処理していなかったという問題があります。具体的には、GoのIssue #3196で報告された問題に対応しています。
以前のgo test
コマンドは、テストの実行だけでなく、テストバイナリのコンパイルも内部的に行っていました。しかし、このコンパイルプロセスがgo build
コマンドが提供するような詳細なビルド制御フラグを完全に尊重していませんでした。例えば、ユーザーが-x
フラグ(実行されるコマンドを表示する)や-a
フラグ(すべてのパッケージを強制的に再ビルドする)をgo test
に渡しても、それらのフラグがテストバイナリのビルドフェーズに正しく伝播されず、期待通りの動作が得られないという状況でした。
この不整合は、開発者がテストのビルドプロセスをデバッグしたり、特定のビルド設定(例: クロスコンパイルのためのgcflags
やldflags
、条件付きコンパイルのためのtags
)を適用したりする際に、大きな障壁となっていました。このコミットは、go test
コマンドがgo build
コマンドと同様に、これらのビルドフラグを「尊重」し、テストバイナリのビルドに適用されるようにすることで、この問題を解決することを目的としています。これにより、go test
の振る舞いがより予測可能になり、go build
との一貫性が保たれるようになりました。
前提知識の解説
このコミットを理解するためには、以下のGoコマンドラインツールの基本的な知識が必要です。
-
go test
コマンド:- Go言語のテストを実行するための主要なコマンドです。指定されたパッケージ内のテスト関数(
TestXxx
、BenchmarkXxx
、ExampleXxx
)を検出し、テストバイナリをコンパイルして実行します。 - テスト実行に関する様々なフラグ(例:
-v
で詳細出力、-run
で特定のテストを実行、-bench
でベンチマークを実行)を持っています。 - 内部的には、テストバイナリをビルドするためにGoコンパイラとリンカを呼び出します。
- Go言語のテストを実行するための主要なコマンドです。指定されたパッケージ内のテスト関数(
-
go build
コマンド:- Goのソースコードをコンパイルして実行可能ファイルやパッケージを生成するためのコマンドです。
- 多くのビルド制御フラグを提供します。
-a
: 依存関係を含むすべてのパッケージを強制的に再ビルドします。-n
: 実際にはコマンドを実行せず、実行されるコマンドを表示します(ドライラン)。-x
: 実行されるコマンドを表示します。ビルドプロセスをデバッグする際に非常に有用です。-p N
: 並行して実行するビルドコマンドの数を指定します。-work
: ビルド中に作成される一時作業ディレクトリを削除せずに残します。デバッグに役立ちます。-gcflags 'flag list'
: コンパイラ(go tool compile
)に渡すフラグを指定します。-ldflags 'flag list'
: リンカ(go tool link
)に渡すフラグを指定します。-tags 'tag list'
: ビルドタグを指定します。これにより、特定のビルドタグが有効な場合にのみコンパイルされるコードブロック(// +build tag
ディレクティブで指定)を制御できます。
-
cmd/go
パッケージ:- Goの標準ライブラリの一部であり、
go
コマンドラインツール自体の実装が含まれています。 go build
、go test
、``go run`などのサブコマンドのロジックがここに定義されています。- コマンドライン引数のパース、ビルドプロセスのオーケストレーション、テストの実行などがこのパッケージの役割です。
- Goの標準ライブラリの一部であり、
-
ビルドコンテキスト (
go/build
パッケージ):- Goのビルドシステムが、ソースファイルの検索、パッケージの解決、ビルドタグの解釈などを行う際に使用する環境情報を提供します。
build.Context
構造体には、BuildTags
などのフィールドが含まれており、これらがビルドの振る舞いを決定します。
このコミットは、go test
がこれらのgo build
関連のフラグを、cmd/go
パッケージ内でどのようにパースし、内部のビルドコンテキストに適用するかというメカニズムを改善しています。
技術的詳細
このコミットの技術的な核心は、go test
コマンドが独自のフラグ処理ロジックを持っていたために、go build
コマンドとフラグの解釈に不整合が生じていた点を解消することにあります。
以前のgo test
は、一部のビルド関連フラグ(例: -p
, -x
)について、test.go
内で独自の変数(testP
, testX
)を持っていました。これらの変数は、go test
コマンドラインで指定された値を一時的に保持し、その後、go build
の内部変数(buildP
, buildX
)にコピーされていました。しかし、すべてのビルドフラグがこのように同期されていたわけではなく、特にgcflags
やldflags
、tags
といったより複雑なフラグは、go test
のコンテキストで適切に処理されていませんでした。
このコミットでは、この問題を解決するために以下の主要な変更が行われました。
-
testflag.go
におけるフラグ定義の統合:testFlagSpec
構造体の変更: 以前はブーリアンフラグをisBool bool
で識別していましたが、これをboolVar *bool
に変更しました。これにより、go test
のフラグが、go build
の内部で使われているブーリアン変数へのポインタと直接関連付けられるようになりました。これにより、フラグの値がパースされた際に、対応するビルドフラグ変数に直接設定されるようになります。testFlagDefn
配列の拡張:go build
で使われる主要なビルドフラグ(-a
,-n
,-x
,-work
,-gcflags
,-ldflags
,-gccgoflags
,-tags
)がtestFlagDefn
に追加されました。これにより、go test
コマンドがこれらのフラグを認識し、パースできるようになります。特に、-x
フラグは&buildX
に、-work
フラグは&buildWork
に直接バインドされるようになりました。testFlags
関数の改善: コマンドライン引数をパースするtestFlags
関数が、新しいboolVar
フィールドを利用してブーリアンフラグを処理するように変更されました。また、gcflags
,ldflags
,gccgoflags
,tags
といった文字列リストを受け取るフラグについても、それぞれbuildGcflags
,buildLdflags
,buildGccgoflags
,buildContext.BuildTags
に直接値を設定するロジックが追加されました。
-
test.go
からの冗長なロジックの削除:test.go
内で定義されていたtestP
、testX
といったgo test
独自のビルド関連フラグ変数が削除されました。- これらの変数を
buildP
やbuildX
にコピーする冗長なロジックも削除されました。これにより、go test
はビルドフラグの値を直接go build
の内部変数から取得するようになり、コードの重複が解消され、一貫性が向上しました。 cleanTest
関数にif buildWork { return nil }
という条件が追加されました。これは、ユーザーが-work
フラグを指定して一時作業ディレクトリを残すことを意図している場合、テスト実行後もそのディレクトリが削除されないようにするための変更です。
-
build.go
へのコメント追加:addBuildFlags
関数に「NOTE: If you add flags here, also add them to testflag.go.
」というコメントが追加されました。これは、将来的にgo build
に新しいフラグが追加された場合、go test
でもそれらを認識させるためにtestflag.go
も更新する必要があることを開発者に注意喚起するものです。これにより、将来的なフラグの不整合を防ぐためのガイドラインが明示されました。
これらの変更により、go test
コマンドは、テストバイナリのビルドフェーズにおいて、go build
コマンドが提供するすべてのビルドフラグを完全に尊重するようになりました。これにより、ユーザーはテストのビルドプロセスをより細かく制御できるようになり、デバッグや特定のビルド環境への適応が容易になりました。
コアとなるコードの変更箇所
src/cmd/go/build.go
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -100,6 +100,7 @@ var buildContext = build.Default
// addBuildFlags adds the flags common to the build and install commands.
func addBuildFlags(cmd *Command) {
+ // NOTE: If you add flags here, also add them to testflag.go.
cmd.Flag.BoolVar(&buildA, "a", false, "")
cmd.Flag.BoolVar(&buildN, "n", false, "")
cmd.Flag.IntVar(&buildP, "p", buildP, "")
src/cmd/go/test.go
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -192,8 +192,6 @@ See the documentation of the testing package for more information.
var (
testC bool // -c flag
testI bool // -i flag
- testP int // -p flag
- testX bool // -x flag
testV bool // -v flag
testFiles []string // -file flag(s) TODO: not respected
testTimeout string // -timeout flag
@@ -241,11 +239,6 @@ func runTest(cmd *Command, args []string) {
testStreamOutput = len(pkgArgs) == 0 || testBench ||
(len(pkgs) <= 1 && testShowPass)
- buildX = testX
- if testP > 0 {
- buildP = testP
- }
-
var b builder
b.init()
@@ -639,6 +632,9 @@ func (b *builder) runTest(a *action) error {
// cleanTest is the action for cleaning up after a test.
func (b *builder) cleanTest(a *action) error {
+ if buildWork {
+ return nil
+ }
run := a.deps[0]
testDir := filepath.Join(b.work, filepath.FromSlash(run.p.ImportPath+"/_test"))
os.RemoveAll(testDir)
src/cmd/go/testflag.go
--- a/src/cmd/go/testflag.go
+++ b/src/cmd/go/testflag.go
@@ -47,7 +47,7 @@ func testUsage() {
// testFlagSpec defines a flag we know about.
type testFlagSpec struct {
name string
- isBool bool
+ boolVar *bool
passToTest bool // pass to Test
multiOK bool // OK to have multiple instances
present bool // flag has been seen
@@ -56,11 +56,20 @@ type testFlagSpec struct {
// testFlagDefn is the set of flags we process.
var testFlagDefn = []*testFlagSpec{
// local.
- {name: "c", isBool: true},
+ {name: "c", boolVar: &testC},
{name: "file", multiOK: true},
- {name: "i", isBool: true},
+ {name: "i", boolVar: &testI},
+
+ // build flags.
+ {name: "a", boolVar: &buildA},
+ {name: "n", boolVar: &buildN},
{name: "p"},
- {name: "x", isBool: true},
+ {name: "x", boolVar: &buildX},
+ {name: "work", boolVar: &buildWork},
+ {name: "gcflags"},
+ {name: "ldflags"},
+ {name: "gccgoflags"},
+ {name: "tags"},
// passed to 6.out, adding a "test." prefix to the name if necessary: -v becomes -test.v.
{name: "bench", passToTest: true},
@@ -71,9 +80,9 @@ var testFlagDefn = []*testFlagSpec{
{name: "memprofilerate", passToTest: true},
{name: "parallel", passToTest: true},
{name: "run", passToTest: true},
- {name: "short", isBool: true, passToTest: true},
+ {name: "short", boolVar: new(bool), passToTest: true},
{name: "timeout", passToTest: true},
- {name: "v", isBool: true, passToTest: true},
+ {name: "v", boolVar: &testV, passToTest: true},
}
// testFlags processes the command line, grabbing -x and -c, rewriting known flags
@@ -118,16 +127,19 @@ func testFlags(args []string) (packageNames, passToTest []string) {
continue
}
switch f.name {
- case "c":
- setBoolFlag(&testC, value)
- case "i":
- setBoolFlag(&testI, value)
+ // bool flags.
+ case "a", "c", "i", "n", "x", "v", "work":
+ setBoolFlag(f.boolVar, value)
case "p":
- setIntFlag(&testP, value)
- case "x":
- setBoolFlag(&testX, value)
- case "v":
- setBoolFlag(&testV, value)
+ setIntFlag(&buildP, value)
+ case "gcflags":
+ buildGcflags = strings.Fields(value)
+ case "ldflags":
+ buildLdflags = strings.Fields(value)
+ case "gccgoflags":
+ buildGccgoflags = strings.Fields(value)
+ case "tags":
+ buildContext.BuildTags = strings.Fields(value)
case "file":
testFiles = append(testFiles, value)
case "bench":
@@ -172,7 +184,7 @@ func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool)
for _, f = range testFlagDefn {
if name == f.name {
// Booleans are special because they have modes -x, -x=true, -x=false.
- if f.isBool {
+ if f.boolVar != nil {
if equals < 0 { // otherwise, it's been set and will be verified in setBoolFlag
value = "true"
} else {
コアとなるコードの解説
src/cmd/go/build.go
の変更
addBuildFlags
関数にコメントが追加されました。この関数はgo build
やgo install
コマンドに共通のビルドフラグを追加する役割を担っています。追加されたコメント「// NOTE: If you add flags here, also add them to testflag.go.
」は、将来的にgo build
に新しいフラグが追加された場合、go test
コマンドでもそのフラグを認識し、適切に処理するためにtestflag.go
ファイルも更新する必要があることを開発者に明示しています。これは、フラグ定義の一貫性を維持し、将来的な不整合を防ぐための重要なガイドラインです。
src/cmd/go/test.go
の変更
- 冗長なフラグ変数の削除:
testP
(-p
フラグ用)とtestX
(-x
フラグ用)というgo test
独自の変数が削除されました。これらは以前、go test
がビルドフラグを独自に処理し、その後go build
の内部変数に値をコピーするために使われていました。 - フラグ値コピーロジックの削除:
runTest
関数から、buildX = testX
やif testP > 0 { buildP = testP }
といった、go test
独自の変数からgo build
の変数へ値をコピーするロジックが削除されました。これは、testflag.go
の変更により、go test
がビルドフラグを直接go build
の変数にバインドするようになったため、これらのコピーが不要になったためです。これにより、コードの重複が解消され、フラグ処理の単一責任の原則が強化されました。 -work
フラグの尊重:cleanTest
関数にif buildWork { return nil }
という条件が追加されました。cleanTest
はテスト実行後に一時ディレクトリをクリーンアップする役割を担っています。buildWork
は-work
フラグが指定された場合にtrue
となるブーリアン変数です。この変更により、ユーザーが-work
フラグを指定して一時ディレクトリを残すことを意図している場合、go test
はクリーンアップを行わなくなります。これは、デバッグ目的で一時ディレクトリの内容を確認したい場合に非常に有用な機能です。
src/cmd/go/testflag.go
の変更
このファイルは、go test
コマンドが認識するコマンドラインフラグの定義と処理ロジックを管理する中心的な場所です。
-
testFlagSpec
構造体の変更:isBool bool
フィールドがboolVar *bool
に変更されました。以前は、フラグがブーリアン型かどうかを示すフラグでしたが、新しいboolVar *bool
は、そのブーリアンフラグがバインドされる実際のbool
変数へのポインタを保持します。これにより、フラグがパースされた際に、対応する変数に直接値を設定できるようになり、より柔軟で直接的なフラグ処理が可能になりました。
-
testFlagDefn
配列の拡張と修正:testFlagDefn
は、go test
が認識するすべてのフラグの定義を保持する配列です。- 既存フラグの更新:
c
,i
,v
,short
といった既存のブーリアンフラグの定義が、isBool: true
からboolVar: &testC
(または対応する変数へのポインタ)に変更されました。 - ビルドフラグの追加:
go build
コマンドで使われる主要なビルドフラグ(a
,n
,x
,work
,gcflags
,ldflags
,gccgoflags
,tags
)がこの配列に追加されました。- 特に注目すべきは、
{name: "x", boolVar: &buildX}
や{name: "work", boolVar: &buildWork}
のように、これらのフラグがgo build
の内部変数(buildX
,buildWork
など)へのポインタと直接関連付けられている点です。これにより、go test
がこれらのフラグを受け取ると、その値が直接go build
のロジックに伝播されるようになります。
- 特に注目すべきは、
-
testFlags
関数の変更:- この関数は、
go test
に渡されたコマンドライン引数をパースし、既知のフラグを処理する役割を担っています。 - ブーリアンフラグの統合処理: 以前は
c
,i
,x
,v
といったブーリアンフラグごとに個別のcase
文でsetBoolFlag
を呼び出していましたが、新しい変更ではcase "a", "c", "i", "n", "x", "v", "work": setBoolFlag(f.boolVar, value)
のように、boolVar
フィールドを持つすべてのブーリアンフラグをまとめて処理するようになりました。これにより、コードが簡潔になり、新しいブーリアンビルドフラグの追加が容易になります。 - ビルド関連フラグの直接処理:
-p
フラグはsetIntFlag(&buildP, value)
のように、直接go build
のbuildP
変数に設定されるようになりました。gcflags
,ldflags
,gccgoflags
は、それぞれstrings.Fields(value)
を使って値を分割し、buildGcflags
,buildLdflags
,buildGccgoflags
といったgo build
のグローバル変数に設定されるようになりました。tags
フラグは、buildContext.BuildTags = strings.Fields(value)
のように、go/build
パッケージのbuild.Context
のBuildTags
フィールドに直接設定されるようになりました。これは、条件付きコンパイルの振る舞いを制御するために非常に重要です。
- この関数は、
-
testFlag
関数の変更:testFlag
関数内のフラグの型チェックロジックがif f.isBool
からif f.boolVar != nil
に変更されました。これは、testFlagSpec
構造体の変更に合わせたものです。
これらの変更により、go test
コマンドは、go build
コマンドが提供するビルドフラグを完全に認識し、それらをテストバイナリのビルドプロセスに直接適用できるようになりました。これにより、go test
の機能が拡張され、開発者はテストのビルドと実行をより細かく制御できるようになりました。
関連リンク
- Go Issue #3196: cmd/go: honor buildflags in go test
参考にした情報源リンク
- Go CL 5725044: cmd/go: honor buildflags in go test.
- Go Command Documentation: https://pkg.go.dev/cmd/go
- Go
go/build
Package Documentation: https://pkg.go.dev/go/build