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

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

このコミットは、Goコマンドラインツール(cmd/go)におけるgo testコマンドの振る舞いを修正するものです。具体的には、go testgo 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に渡しても、それらのフラグがテストバイナリのビルドフェーズに正しく伝播されず、期待通りの動作が得られないという状況でした。

この不整合は、開発者がテストのビルドプロセスをデバッグしたり、特定のビルド設定(例: クロスコンパイルのためのgcflagsldflags、条件付きコンパイルのためのtags)を適用したりする際に、大きな障壁となっていました。このコミットは、go testコマンドがgo buildコマンドと同様に、これらのビルドフラグを「尊重」し、テストバイナリのビルドに適用されるようにすることで、この問題を解決することを目的としています。これにより、go testの振る舞いがより予測可能になり、go buildとの一貫性が保たれるようになりました。

前提知識の解説

このコミットを理解するためには、以下のGoコマンドラインツールの基本的な知識が必要です。

  1. go testコマンド:

    • Go言語のテストを実行するための主要なコマンドです。指定されたパッケージ内のテスト関数(TestXxxBenchmarkXxxExampleXxx)を検出し、テストバイナリをコンパイルして実行します。
    • テスト実行に関する様々なフラグ(例: -vで詳細出力、-runで特定のテストを実行、-benchでベンチマークを実行)を持っています。
    • 内部的には、テストバイナリをビルドするためにGoコンパイラとリンカを呼び出します。
  2. 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ディレクティブで指定)を制御できます。
  3. cmd/goパッケージ:

    • Goの標準ライブラリの一部であり、goコマンドラインツール自体の実装が含まれています。
    • go buildgo test、``go run`などのサブコマンドのロジックがここに定義されています。
    • コマンドライン引数のパース、ビルドプロセスのオーケストレーション、テストの実行などがこのパッケージの役割です。
  4. ビルドコンテキスト (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)にコピーされていました。しかし、すべてのビルドフラグがこのように同期されていたわけではなく、特にgcflagsldflagstagsといったより複雑なフラグは、go testのコンテキストで適切に処理されていませんでした。

このコミットでは、この問題を解決するために以下の主要な変更が行われました。

  1. 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に直接値を設定するロジックが追加されました。
  2. test.goからの冗長なロジックの削除:

    • test.go内で定義されていたtestPtestXといったgo test独自のビルド関連フラグ変数が削除されました。
    • これらの変数をbuildPbuildXにコピーする冗長なロジックも削除されました。これにより、go testはビルドフラグの値を直接go buildの内部変数から取得するようになり、コードの重複が解消され、一貫性が向上しました。
    • cleanTest関数にif buildWork { return nil }という条件が追加されました。これは、ユーザーが-workフラグを指定して一時作業ディレクトリを残すことを意図している場合、テスト実行後もそのディレクトリが削除されないようにするための変更です。
  3. 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 buildgo 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 = testXif 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 buildbuildP変数に設定されるようになりました。
      • gcflags, ldflags, gccgoflagsは、それぞれstrings.Fields(value)を使って値を分割し、buildGcflags, buildLdflags, buildGccgoflagsといったgo buildのグローバル変数に設定されるようになりました。
      • tagsフラグは、buildContext.BuildTags = strings.Fields(value)のように、go/buildパッケージのbuild.ContextBuildTagsフィールドに直接設定されるようになりました。これは、条件付きコンパイルの振る舞いを制御するために非常に重要です。
  • testFlag関数の変更:

    • testFlag関数内のフラグの型チェックロジックがif f.isBoolからif f.boolVar != nilに変更されました。これは、testFlagSpec構造体の変更に合わせたものです。

これらの変更により、go testコマンドは、go buildコマンドが提供するビルドフラグを完全に認識し、それらをテストバイナリのビルドプロセスに直接適用できるようになりました。これにより、go testの機能が拡張され、開発者はテストのビルドと実行をより細かく制御できるようになりました。

関連リンク

参考にした情報源リンク