[インデックス 19303] ファイルの概要
このコミットは、Goのcmd/go
ツールにおけるgo test
コマンドの依存関係解決に関する問題を修正します。具体的には、testmain
(テスト実行のために生成される特別なmain
パッケージ)がregexp
パッケージをその依存関係として正しく認識しないために発生していたビルド時の問題を解決します。特にgo test -a
フラグを使用した場合に、regexp
パッケージが適切にリビルドされないという問題に対処しています。
コミット
commit 52961b902fa89e89eff318e907e7f331bcf09736
Author: Robert Hencke <robert.hencke@gmail.com>
Date: Fri May 9 12:19:00 2014 -0400
cmd/go: mark regexp as dependency of testmain
Fixes #6844.
LGTM=rsc
R=golang-codereviews, rsc
CC=golang-codereviews
https://golang.org/cl/97840043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/52961b902fa89e89eff318e907e7f331bcf09736
元コミット内容
cmd/go: mark regexp as dependency of testmain
Fixes #6844.
LGTM=rsc
R=golang-codereviews, rsc
CC=golang-codereviews
https://golang.org/cl/97840043
変更の背景
このコミットは、GoのIssue 6844を修正するために行われました。元のIssueの詳細は現在参照できませんが、コミットメッセージとコードの変更内容から、go test -a
コマンドがregexp
パッケージをtestmain
の依存関係として正しく扱わないという問題があったと推測されます。
go test
コマンドは、テストを実行するために、テスト対象のパッケージとテストコードをリンクした特別な実行可能ファイル(testmain
)を生成します。このtestmain
は、テストコードが依存するすべてのパッケージを適切に含める必要があります。しかし、何らかの理由でregexp
パッケージがtestmain
の依存関係ツリーから漏れてしまうことがあり、特にgo test -a
(すべてのパッケージを強制的にリビルドするフラグ)を使用した場合に、regexp
パッケージが最新の状態にリビルドされず、テストの実行に問題が生じる可能性がありました。
この問題は、testmain
の依存関係解決ロジックが、テストファイルが間接的に依存するパッケージ(この場合はregexp
)を完全に追跡できていなかったことに起因すると考えられます。結果として、go test -a
が期待通りに動作せず、開発者が古いバージョンのregexp
パッケージにリンクされたテストを実行してしまう可能性がありました。
前提知識の解説
go test
コマンド
go test
はGo言語の標準的なテスト実行ツールです。指定されたパッケージ内のテスト関数(Test
、Benchmark
、Example
で始まる関数)を検出し、実行します。go test
は、テスト対象のパッケージとテストコードをコンパイルし、それらをリンクして単一の実行可能ファイルを作成します。
testmain
パッケージ
go test
がテストを実行する際、内部的にtestmain
と呼ばれる特別なmain
パッケージを生成します。このtestmain
は、テスト対象のパッケージ、テストファイル自体、およびそれらの依存関係をすべて含み、テストランナーとして機能します。testmain
は、テストのセットアップ、実行、結果の報告といった一連の処理を管理します。
Goのビルドプロセスと依存関係管理
Goのビルドシステムは、ソースコードから実行可能ファイルを生成する際に、パッケージ間の依存関係を自動的に解決します。あるパッケージが別のパッケージをimport
している場合、ビルドシステムはその依存関係を認識し、必要なすべてのパッケージをコンパイルしてリンクします。go test
もこのビルドシステムを利用しており、testmain
のビルド時にすべての必要な依存関係が解決される必要があります。
go test -a
フラグ
go test -a
は、go test
コマンドのオプションの一つで、「すべてのパッケージを強制的にリビルドする」ことを意味します。通常、Goのビルドシステムは、ソースファイルが変更されていない限り、すでにコンパイルされたパッケージを再利用することでビルド時間を短縮します。しかし、-a
フラグを使用すると、キャッシュされたビルド結果を無視し、すべての依存関係を含むすべてのパッケージを最初から再コンパイルします。これは、ビルドキャッシュに起因する可能性のある問題をデバッグする際や、クリーンなビルドを保証したい場合に役立ちます。
regexp
パッケージ
regexp
はGoの標準ライブラリに含まれるパッケージで、正規表現の操作を提供します。このコミットでは、regexp
パッケージ自体が問題なのではなく、testmain
がその依存関係を正しく追跡できていなかったことが問題でした。
技術的詳細
go test
コマンドは、テスト対象のパッケージのテストファイル(_test.go
で終わるファイル)を解析し、それらのファイルがインポートしているパッケージを特定します。これらのインポートされたパッケージは、testmain
実行可能ファイルの依存関係となります。
このコミット以前のgo test
の動作では、testmain
の依存関係を解決する際に、テストファイルが直接インポートするパッケージは考慮されていましたが、それらのパッケージがさらにインポートする間接的な依存関係(推移的依存関係)の一部が適切に追跡されていなかった可能性があります。特に、regexp
パッケージのように、多くのGoプログラムで広く利用される基本的なパッケージが、特定の条件下でtestmain
の依存関係リストから漏れてしまうことがありました。
go test -a
フラグがこの問題を顕在化させたのは、このフラグがすべてのパッケージの強制的なリビルドをトリガーするためです。もしregexp
がtestmain
の依存関係として認識されていなければ、go test -a
が実行されてもregexp
はリビルドされず、古いバージョンのregexp
がtestmain
にリンクされてしまう可能性がありました。これは、テストの実行結果に予期せぬ影響を与える可能性があります。
このコミットの修正は、testmain
の依存関係解決ロジックを強化し、テストファイルがインポートするすべてのパッケージ(直接的および間接的)がtestmain
のビルド時に確実に考慮されるようにすることで、この問題を解決しています。これにより、go test -a
が期待通りに動作し、すべての依存関係が常に最新の状態でビルドされることが保証されます。
コアとなるコードの変更箇所
このコミットでは、主に以下の3つのファイルが変更されています。
src/cmd/go/test.bash
:go test
コマンドのテストスクリプト。Issue 6844を再現し、修正を検証するための新しいテストケースが追加されました。src/cmd/go/test.go
:go test
コマンドのGoソースコード。testmain
の依存関係解決ロジックが修正されました。src/cmd/go/testdata/dep_test.go
: 新しく追加されたテストデータファイル。regexp
パッケージへの依存関係をシミュレートし、Issue 6844のテストケースとして機能します。
src/cmd/go/test.bash
の変更点
--- a/src/cmd/go/test.bash
+++ b/src/cmd/go/test.bash
@@ -708,12 +708,28 @@ if ./testgo test notest >/dev/null 2>&1; then
fi
unset GOPATH
+<<<<<<< local
+TEST 'Issue 6844: cmd/go: go test -a foo does not rebuild regexp'
+if ! ./testgo test -x -a -c testdata/dep_test.go 2>deplist; then
+\techo "go test -x -a -c testdata/dep_test.go failed"
+\tok=false
+elif ! grep -q regexp deplist; then
+\techo "go test -x -a -c testdata/dep_test.go did not rebuild regexp"
+=======\n TEST list template can use context function
if ! ./testgo list -f "GOARCH: {{context.GOARCH}}"; then
\techo unable to use context in list template
+>>>>>>> other
\tok=false
fi
+<<<<<<< local
+rm -f deplist
+rm -f deps.test
+=======\n+>>>>>>> other
+
+<<<<<<< local
+=======\n TEST build -i installs dependencies
d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)\n export GOPATH=$d
@@ -748,6 +764,7 @@ fi
rm -rf $d
unset GOPATH
+>>>>>>> other
# clean up
if $started; then stop; fi
rm -rf testdata/bin testdata/bin1
このdiffは、マージコンフリクトの残骸を含んでいますが、重要なのはTEST 'Issue 6844: cmd/go: go test -a foo does not rebuild regexp'
で始まる新しいテストブロックです。
このテストは、./testgo test -x -a -c testdata/dep_test.go
を実行し、その出力(-x
フラグによる実行コマンドのトレース)にregexp
が含まれているかどうかをgrep
で確認しています。regexp
が含まれていれば、go test -a
がregexp
をリビルド対象として認識したことを意味し、修正が機能していることを示します。
src/cmd/go/test.go
の変更点
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -418,6 +418,7 @@ func runTest(cmd *Command, args []string) {
var coverFiles []string
coverFiles = append(coverFiles, p.GoFiles...)
coverFiles = append(coverFiles, p.CgoFiles...)
+ coverFiles = append(coverFiles, p.TestGoFiles...)
p.coverVars = declareCoverVars(p.ImportPath, coverFiles...)
}
}
@@ -676,7 +677,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\n stk.push("testmain")
for dep := range testMainDeps {
if ptest.ImportPath != dep {
- p1 := loadImport("testing", "", &stk, nil)
+ p1 := loadImport(dep, "", &stk, nil)
if p1.Error != nil {
return nil, nil, nil, p1.Error
}
このファイルには2つの重要な変更があります。
-
runTest
関数内の変更:coverFiles = append(coverFiles, p.TestGoFiles...)
これは、カバレッジ分析の対象となるファイルリストに、テストファイル自体(p.TestGoFiles
)を追加しています。これは直接的な依存関係解決の修正とは異なりますが、テスト関連のファイル処理の網羅性を高めるための改善です。 -
builder.test
関数内の変更:- p1 := loadImport("testing", "", &stk, nil)
+ p1 := loadImport(dep, "", &stk, nil)
これがこのコミットの核心的な修正です。以前は、testmain
の依存関係として常に"testing"
パッケージをロードしようとしていました。しかし、testMainDeps
セットにはtestmain
が依存する可能性のあるすべてのパッケージが含まれるべきです。この変更により、testMainDeps
から取得した実際の依存パッケージ名dep
を使用してloadImport
が呼び出されるようになります。これにより、testmain
がtesting
だけでなく、regexp
のような他の推移的依存関係も正しくロードし、ビルドプロセスに含めることが保証されます。
src/cmd/go/testdata/dep_test.go
の変更点
--- /dev/null
+++ b/src/cmd/go/testdata/dep_test.go
@@ -0,0 +1,7 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package deps
+
+import _ "testing"
これは新しく追加されたテストファイルです。import _ "testing"
という行は、testing
パッケージをインポートしていますが、そのパッケージ内の関数を直接使用しないことを示しています(ブランクインポート)。testing
パッケージは、Goのテストフレームワークの基盤であり、通常はregexp
パッケージに依存しています。このテストファイルが存在することで、testmain
がtesting
パッケージを介してregexp
パッケージに間接的に依存するシナリオが作成され、以前のバグが再現されるようになります。このファイルは、test.bash
スクリプトで実行されるテストケースの一部として使用されます。
コアとなるコードの解説
このコミットの主要な修正は、src/cmd/go/test.go
内のbuilder.test
関数にあります。
builder.test
関数は、go test
コマンドがテスト実行可能ファイル(testmain
)をビルドする際のロジックを管理しています。この関数内で、testMainDeps
というセットが、testmain
が依存するすべてのパッケージを追跡するために使用されます。
修正前のコードでは、testMainDeps
をイテレートする際に、loadImport("testing", "", &stk, nil)
という固定の呼び出しがありました。これは、testmain
が常に"testing"
パッケージに依存しているという前提に基づいていました。しかし、testMainDeps
には"testing"
以外のパッケージも含まれる可能性があり、特にテストファイルが間接的に依存するパッケージ(例: regexp
)が含まれるべきでした。
新しいコードp1 := loadImport(dep, "", &stk, nil)
では、testMainDeps
から取得した実際の依存パッケージ名dep
を使用してloadImport
が呼び出されます。これにより、testmain
のビルド時に、testMainDeps
に含まれるすべてのパッケージが適切にロードされ、依存関係として考慮されるようになります。
この変更により、regexp
パッケージがtestmain
の推移的依存関係として正しく認識され、go test -a
が実行された際に、regexp
パッケージも必要に応じてリビルドされることが保証されます。結果として、go test
のビルドプロセスがより堅牢になり、依存関係の欠落による潜在的な問題が解消されます。
src/cmd/go/test.bash
に追加されたテストケースは、この修正が正しく機能することを確認するためのものです。testdata/dep_test.go
という新しいテストファイルは、testing
パッケージをインポートすることで、regexp
への間接的な依存関係を作成します。test.bash
スクリプトは、go test -x -a -c
を実行し、ビルドトレースにregexp
が含まれていることを確認することで、regexp
が正しくリビルドされたことを検証します。
関連リンク
参考にした情報源リンク
- Go言語の公式ドキュメント(
go test
コマンド、パッケージ管理、ビルドプロセスに関する一般的な情報) - Go言語のソースコード(
src/cmd/go/test.go
の関連部分) - (注: コミットメッセージに記載されているGo CLリンク
https://golang.org/cl/97840043
は現在利用できませんでした。)