[インデックス 12331] ファイルの概要
このコミットは、Go言語のコマンドラインツール cmd/go
におけるテストのインポート依存関係のバグを修正するものです。具体的には、src/cmd/go/build.go
、src/cmd/go/pkg.go
、src/cmd/go/test.go
の3つのファイルが変更されています。合計で20行が追加され、10行が削除されています。
コミット
- コミットハッシュ:
d08a8848bb0833cfe0dcf6f0fcc3e9f0c1b05e10
- Author: Russ Cox rsc@golang.org
- Date: Fri Mar 2 11:27:36 2012 -0500
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d08a8848bb0833cfe0dcf6f0fcc3e9f0c1b05e10
元コミット内容
cmd/go: fix test import dependency bug
Fixes a problem Rob is having with goprotobuf.
Cannot add a test because the same case is more broken
when using ./ imports. That still needs to be fixed,
and is one aspect of issue 3169.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5725043
変更の背景
このコミットは、Go言語のビルドツール cmd/go
におけるテストのインポート依存関係に関する既存のバグを修正するために行われました。コミットメッセージによると、この問題は特にRob Pikeが goprotobuf
を使用している際に直面したものであり、テストを追加しようとすると、./
(カレントディレクトリ相対) インポートを使用した場合にさらに問題が悪化するという状況でした。
このバグは、Goのテストフレームワークがパッケージの依存関係を正しく解決できないことに起因していると考えられます。特に、テストコードがテスト対象のパッケージ自体をインポートする場合や、テスト専用の外部テストパッケージ(_test
サフィックスを持つパッケージ)が関連する依存関係を持つ場合に問題が発生しやすかったようです。
コミットメッセージには「That still needs to be fixed, and is one aspect of issue 3169」とあり、この修正が issue 3169
の一部であり、完全な解決ではないことが示唆されています。issue 3169
は、Goのビルドシステムにおけるより広範なインポートパスの解決に関する問題、特に相対パスや特殊なテストパッケージの扱いに関するものだった可能性があります。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびcmd/go
ツールの概念を理解しておく必要があります。
cmd/go
ツール: Go言語の公式ビルドツールであり、パッケージのビルド、テスト、インストール、依存関係の管理など、Go開発における主要な操作を行います。- Goパッケージ: Goのコードはパッケージにまとめられます。各パッケージは独自のインポートパスを持ち、他のパッケージからインポートされることで再利用されます。
- インポートパス: Goのパッケージを一意に識別するための文字列です。標準ライブラリのパッケージ(例:
fmt
,net/http
)や、外部モジュールのパッケージ(例:github.com/user/repo/pkg
)などがあります。 - テストパッケージ: Goでは、テストコードは通常、テスト対象のパッケージと同じディレクトリに配置され、
_test.go
というサフィックスを持つファイルに記述されます。- 内部テスト: テスト対象のパッケージと同じパッケージ名で定義され、パッケージの内部要素(非エクスポートされた関数や変数)にアクセスできます。
- 外部テスト: テスト対象のパッケージとは異なるパッケージ名(通常は
packagename_test
)で定義され、パッケージのエクスポートされた要素のみにアクセスできます。これは、パッケージが外部からどのように見えるかをテストするのに役立ちます。
go test
コマンド: Goのテストを実行するためのコマンドです。テストファイルをコンパイルし、テストバイナリを実行します。このプロセス中に、テストパッケージとその依存関係がビルドされます。build.Package
構造体:go/build
パッケージで定義されている構造体で、Goのパッケージに関するメタデータ(ファイルリスト、インポートパス、依存関係など)を保持します。cmd/go
ツール内部でパッケージのビルド情報を管理するために使用されます。Package
構造体 (cmd/go内部):cmd/go
ツールが内部的に使用するパッケージ表現で、build.Package
をラップし、ビルドプロセスにおける追加の状態(例:Stale
フラグ、依存関係グラフ)を管理します。computeStale
関数:cmd/go
ツール内部で使用される関数で、パッケージが「stale」(古くなっている、再ビルドが必要)であるかどうかを計算します。これは、依存関係グラフを辿り、変更されたファイルや依存パッケージに基づいて再ビルドの必要性を判断するために重要です。ビルドの効率化のために、不要な再ビルドを避ける役割があります。./
インポート: カレントディレクトリからの相対パスでパッケージをインポートする方法です。これは通常、モジュールモードが導入される前の古いGoのワークスペースで使われていましたが、問題を引き起こす可能性があり、推奨されません。
技術的詳細
このコミットの技術的詳細は、cmd/go
ツールがテストパッケージの依存関係と「stale」状態をどのように管理しているか、そしてその管理における既存の欠陥をどのように修正したかに焦点を当てています。
主な問題は、go test
コマンドがテスト対象のパッケージ、その内部テスト、および外部テストパッケージをビルドする際に、依存関係の解決と Stale
フラグの計算が正しく行われていなかった点にあります。
-
computeStale
関数の引数変更:- 変更前:
func computeStale(pkgs []*Package)
(スライスを受け取る) - 変更後:
func computeStale(pkgs ...*Package)
(可変長引数を受け取る) この変更により、computeStale
関数は単一のパッケージまたは複数のパッケージをより柔軟に受け取れるようになりました。これは、goFilesPackage
関数やpackagesAndErrors
関数からの呼び出しで、単一のpkg
オブジェクトを直接渡せるようにするために行われました。
- 変更前:
-
packageList
関数における依存関係のトラバース:- 変更前:
for _, p1 := range p.deps
- 変更後:
for _, p1 := range p.imports
packageList
関数は、パッケージの依存関係グラフをトラバースして、ビルド順序を決定したり、関連するパッケージのリストを収集したりするために使用されます。以前はp.deps
(おそらくビルド時の依存関係)を見ていましたが、p.imports
(ソースコードで宣言されたインポート)を見るように変更されました。これにより、テストパッケージが実際にインポートしているパッケージに基づいて依存関係がより正確に解決されるようになります。
- 変更前:
-
テストパッケージの
Stale
フラグ設定とcomputeStale
の呼び出し:src/cmd/go/test.go
における最も重要な変更は、テスト実行時に生成される一時的なテストパッケージ(ptest
,pxtest
,pmain
)のStale
フラグの扱いと、それらに対するcomputeStale
の呼び出しです。p.XTestImports
の自己インポート回避:for _, path := range p.XTestImports
ループ内で、if path == p.ImportPath { continue }
という行が追加されました。これは、外部テストパッケージがテスト対象のパッケージ自体をインポートしようとした場合に、無限ループや不正確な依存関係の解決を防ぐためのガードです。- 生成されるテストパッケージの
Stale
フラグ設定:ptest
(内部テストパッケージ)、pxtest
(外部テストパッケージ)、pmain
(テスト実行バイナリのメインパッケージ) の各生成時に、ptest.Stale = true
、pxtest.Stale = true
、pmain.Stale = true
が明示的に設定されるようになりました。これにより、これらの生成されたパッケージは常に「stale」であるとマークされ、go test
が実行されるたびに再ビルドされることが保証されます。これは、テストバイナリが常に最新のソースコードに基づいてビルドされるべきであるため、非常に重要です。 - 生成されるテストパッケージに対する
computeStale
の呼び出し:ptest
、pxtest
、pmain
の各パッケージが設定された後、それぞれに対してcomputeStale(ptest)
、computeStale(pxtest)
、computeStale(pmain)
が呼び出されています。これにより、これらの生成されたテストパッケージとその依存関係ツリー全体に対して、Stale
フラグが正しく伝播・計算されるようになります。以前は、これらの生成されたパッケージに対するStale
計算が不十分だったため、テストの依存関係が正しく解決されず、Rob Pikeが直面したような問題が発生していたと考えられます。 pmain
のImportPath
設定:pmain
パッケージにImportPath: "testmain"
が追加されました。これは、生成されるテスト実行バイナリのメインパッケージに一意のインポートパスを与えることで、ビルドシステム内での識別と管理を容易にするためと考えられます。
これらの変更により、cmd/go
はテストパッケージの依存関係をより正確に追跡し、テストバイナリが常に最新の状態であることを保証できるようになりました。特に、Stale
フラグの適切な設定とcomputeStale
の呼び出しは、テストのビルドプロセスにおけるキャッシュの無効化と正確な再ビルドを保証するために不可欠です。
コアとなるコードの変更箇所
src/cmd/go/build.go
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -363,7 +363,7 @@ func goFilesPackage(gofiles []string) *Package {
pkg.Target = ""
pkg.Stale = true
- computeStale([]*Package{pkg})
+ computeStale(pkg)
return pkg
}
src/cmd/go/pkg.go
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -376,7 +376,7 @@ func packageList(roots []*Package) []*Package {
return
}
seen[p] = true
- for _, p1 := range p.deps {
+ for _, p1 := range p.imports {
walk(p1)
}
all = append(all, p)
@@ -389,7 +389,7 @@ func packageList(roots []*Package) []*Package {
// computeStale computes the Stale flag in the package dag that starts
// at the named pkgs (command-line arguments).
-func computeStale(pkgs []*Package) {
+func computeStale(pkgs ...*Package) {
topRoot := map[string]bool{}
for _, p := range pkgs {
topRoot[p.Root] = true
@@ -579,7 +579,7 @@ func packagesAndErrors(args []string) []*Package {
pkgs = append(pkgs, loadPackage(arg, &stk))
}
- computeStale(pkgs)
+ computeStale(pkgs...)
return pkgs
}
src/cmd/go/test.go
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -396,6 +396,9 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\
imports = append(imports, p1)
}
for _, path := range p.XTestImports {
+ if path == p.ImportPath {
+ continue
+ }
p1 := loadImport(path, p.Dir, &stk, p.build.XTestImportPos[path])
if p1.Error != nil {
return nil, nil, nil, p1.Error
@@ -447,6 +450,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\
ptest.imports = append(append([]*Package{}, p.imports...), imports...)
ptest.pkgdir = testDir
ptest.fake = true
+ ptest.Stale = true
ptest.build = new(build.Package)
*ptest.build = *p.build
m := map[string][]token.Position{}
@@ -457,6 +461,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\
m[k] = append(m[k], v...)
}
ptest.build.ImportPos = m
+ computeStale(ptest)
a := b.action(modeBuild, modeBuild, ptest)
a.objdir = testDir + string(filepath.Separator)
a.objpkg = ptestObj
@@ -480,7 +485,9 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\
imports: append(ximports, ptest),
pkgdir: testDir,
fake: true,
+ Stale: true,
}
+ computeStale(pxtest)
a := b.action(modeBuild, modeBuild, pxtest)
a.objdir = testDir + string(filepath.Separator)
a.objpkg = buildToolchain.pkgpath(testDir, pxtest)
@@ -489,12 +496,14 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\
// Action for building pkg.test.
pmain = &Package{
- Name: "main",
- Dir: testDir,
- GoFiles: []string{"_testmain.go"},
- imports: []*Package{ptest},
- build: &build.Package{},
- fake: true,
+ Name: "main",
+ Dir: testDir,
+ GoFiles: []string{"_testmain.go"},
+ ImportPath: "testmain",
+ imports: []*Package{ptest},
+ build: &build.Package{},
+ fake: true,
+ Stale: true,
}
if pxtest != nil {
pmain.imports = append(pmain.imports, pxtest)
@@ -511,6 +520,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\
return nil, nil, nil, pregexp.Error
}
pmain.imports = append(pmain.imports, ptesting, pregexp)
+ computeStale(pmain)
a := b.action(modeBuild, modeBuild, pmain)
a.objdir = testDir + string(filepath.Separator)
コアとなるコードの解説
src/cmd/go/build.go
の変更
goFilesPackage
関数内のcomputeStale
の呼び出しがcomputeStale([]*Package{pkg})
からcomputeStale(pkg)
に変更されました。これは、computeStale
のシグネチャがスライスではなく可変長引数を受け取るように変更されたことに対応しています。これにより、単一のパッケージを渡す際に冗長なスライス作成が不要になります。
src/cmd/go/pkg.go
の変更
packageList
関数内の依存関係のトラバースロジックがp.deps
からp.imports
を参照するように変更されました。p.imports
はソースコードで明示的にインポートされているパッケージのリストであり、p.deps
はビルド時に解決された依存関係のリストである可能性があります。この変更により、パッケージの依存関係グラフの構築が、よりソースコードの記述に忠実な形で行われるようになります。テストパッケージの依存関係を正確に追跡するために重要です。computeStale
関数のシグネチャがfunc computeStale(pkgs []*Package)
からfunc computeStale(pkgs ...*Package)
に変更されました。これにより、この関数は任意の数の*Package
引数を受け取れるようになり、呼び出し元での柔軟性が向上します。packagesAndErrors
関数内のcomputeStale
の呼び出しがcomputeStale(pkgs)
からcomputeStale(pkgs...)
に変更されました。これは、computeStale
のシグネチャ変更に対応するもので、スライスを可変長引数として展開して渡しています。
src/cmd/go/test.go
の変更
このファイルは、go test
コマンドの動作を定義する上で最も重要な変更が含まれています。
-
外部テストの自己インポート回避:
for _, path := range p.XTestImports
ループ内に、if path == p.ImportPath { continue }
という条件が追加されました。これは、外部テストパッケージ(XTestImports
にリストされる)が、テスト対象のパッケージ自体をインポートしようとする場合に、そのインポートをスキップするためのものです。これにより、循環依存や不正確な依存関係の解決を防ぎ、特に.
や./
を使った相対インポートで発生しがちな問題を緩和します。 -
生成されるテストパッケージの
Stale
フラグ設定とcomputeStale
の呼び出し:go test
は、テストを実行するためにいくつかの一時的なPackage
オブジェクトを内部的に生成します。これらはptest
(内部テストパッケージ)、pxtest
(外部テストパッケージ)、pmain
(テスト実行バイナリのメインパッケージ) です。- これらの生成されたパッケージに対して、明示的に
Stale = true
が設定されるようになりました。これは、テストが実行されるたびにこれらのパッケージが常に再ビルドされるべきであることを保証します。テストバイナリは常に最新のソースコードとテストコードを反映している必要があります。 - それぞれのパッケージが設定された後、
computeStale(ptest)
、computeStale(pxtest)
、computeStale(pmain)
が呼び出されています。これにより、これらの生成されたパッケージだけでなく、それらが依存するすべてのパッケージに対してもStale
フラグが適切に計算され、必要に応じて再ビルドがトリガーされるようになります。これにより、テストの依存関係がより正確に解決され、Rob Pikeが直面したような「テストインポート依存関係バグ」が修正されます。
- これらの生成されたパッケージに対して、明示的に
-
pmain
パッケージへのImportPath
の追加:pmain
パッケージの定義にImportPath: "testmain"
が追加されました。これは、生成されるテスト実行バイナリのメインパッケージに一意のインポートパスを与えることで、ビルドシステム内での識別と管理を容易にするためと考えられます。
これらの変更は、Goのビルドシステムがテストパッケージの複雑な依存関係をより堅牢に処理できるようにするためのものであり、特に go test
コマンドの信頼性と正確性を向上させることを目的としています。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/d08a8848bb0833cfe0dcf6f0fcc3e9f0c1b05e10
- Gerrit Change-Id: https://golang.org/cl/5725043
- Go Issue 3169 (関連する可能性のある問題): https://github.com/golang/go/issues/3169
参考にした情報源リンク
- Go言語の公式ドキュメント (パッケージ、テスト、
go
コマンドに関する情報) - Go言語のソースコード (
src/cmd/go
ディレクトリ内の関連ファイル) - Go Issue Tracker (特に
issue 3169
の議論) goprotobuf
プロジェクトに関する一般的な情報 (Rob Pikeが直面した問題の文脈理解のため)- Goのビルドシステムと依存関係解決に関する一般的な知識