[インデックス 12518] ファイルの概要
このコミットは、Go言語のビルドツールであるcmd/go
において、外部テストパッケージ(_test
サフィックスを持つパッケージ)が依存するパッケージが適切に再コンパイルされない問題を修正するものです。また、ビルドプロセスの効率化のため、computeStale
関数の呼び出しを一度にまとめるリファクタリングも含まれています。
コミット
- コミットハッシュ:
2b64e00f164e951f24221c0d4c5b3fb66a604531
- Author: Russ Cox rsc@golang.org
- Date: Thu Mar 8 08:32:38 2012 -0500
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2b64e00f164e951f24221c0d4c5b3fb66a604531
元コミット内容
cmd/go: rebuild external test package dependencies
Was missing recompilation of packages imported only
by external test packages (package foo_test), primarily
because Root was not set, so those packages looked like
they were from a different Go tree, so they were not
recompiled if they already existed.
Also clean things up so that only one call to computeStale
is needed.
Fixes #3238.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5786048
変更の背景
このコミットの主な背景は、Goのビルドシステムにおける外部テストパッケージの依存関係の取り扱いに関するバグ修正です。具体的には、go test
コマンドで外部テストパッケージ(例: package foo_test
)を実行する際、そのテストパッケージがインポートしている他のパッケージが、すでにコンパイル済みであっても、変更があった場合に適切に再コンパイルされないという問題がありました。
この問題の根本原因は、外部テストパッケージのビルド時に、そのパッケージのRoot
フィールドが正しく設定されていなかったことにあります。Root
が設定されていないと、ビルドシステムはそれらのパッケージを現在のGoツリーとは異なる場所にあるものと誤認し、結果として、すでに存在するコンパイル済みバイナリを再利用してしまい、最新の変更が反映されないという事態が発生していました。これは、開発者がコードを変更してもテストが古いバイナリで実行され、期待通りの結果が得られないという、非常に混乱を招く挙動につながります。
また、このコミットは、computeStale
関数の呼び出しが複数箇所で行われていたのを、一度の呼び出しに集約することで、コードの整理と効率化を図ることも目的としています。
前提知識の解説
Goのパッケージとテスト
Go言語では、コードは「パッケージ」という単位で管理されます。各パッケージは、通常、ディレクトリに対応し、そのディレクトリ内の.go
ファイルがパッケージを構成します。
Goのテストは、go test
コマンドによって実行されます。テストファイルは、テスト対象のパッケージと同じディレクトリに配置され、ファイル名の末尾に_test.go
が付きます。テストには大きく分けて二つの種類があります。
-
内部テストパッケージ (Internal Test Packages):
- テスト対象のパッケージと同じパッケージ名(例:
package foo
)を持つテストファイルです。 - テスト対象のパッケージの内部(非エクスポート)の関数や変数にアクセスできます。
- ビルド時には、テスト対象のパッケージとテストコードが一緒にコンパイルされます。
- テスト対象のパッケージと同じパッケージ名(例:
-
外部テストパッケージ (External Test Packages):
- テスト対象のパッケージとは異なるパッケージ名(例:
package foo_test
)を持つテストファイルです。 - テスト対象のパッケージをインポートしてテストを行います。
- テスト対象のパッケージのエクスポートされた(大文字で始まる)関数や変数のみにアクセスできます。
- これにより、ユーザーがパッケージをインポートして利用するのと同じ視点でテストを行うことができます。
- このコミットで問題となっていたのは、この外部テストパッケージの依存関係の再コンパイルです。
- テスト対象のパッケージとは異なるパッケージ名(例:
GoのビルドシステムとRoot
Goのビルドシステムは、ソースコードから実行可能ファイルやライブラリを生成する役割を担っています。このシステムは、パッケージの依存関係を解決し、必要に応じて再コンパイルを行います。
Root
は、Goのビルドシステムにおいて、パッケージがどのGoのワークスペース(GOPATH)またはGoモジュールに属しているかを示す重要な情報です。ビルドシステムは、このRoot
情報を用いて、パッケージのパスを解決し、そのパッケージが「古い」(stale)かどうか、つまり再コンパイルが必要かどうかを判断します。
もしパッケージのRoot
が正しく設定されていない場合、ビルドシステムはそのパッケージを現在のビルドコンテキストとは異なる場所にあるものと誤認し、そのパッケージの依存関係が変更されていても、すでにコンパイル済みのバイナリが存在すれば、それを再利用してしまう可能性があります。これが、外部テストパッケージの依存関係が再コンパイルされない問題の直接的な原因でした。
computeStale
関数
computeStale
関数は、Goのビルドシステム内部で使用される関数で、特定のパッケージが「古い」(stale)状態にあるかどうか、つまり再コンパイルが必要かどうかを計算します。この関数は、ソースファイルのタイムスタンプや依存関係の変更などを考慮して、ビルドの必要性を判断します。
このコミットでは、computeStale
が複数回呼び出されていた箇所を整理し、一度の呼び出しに集約することで、ビルドプロセスの効率化とコードの可読性向上を図っています。
技術的詳細
このコミットは、src/cmd/go/test.go
ファイルに対して行われ、主に以下の二つの技術的な変更を含んでいます。
-
外部テストパッケージ (
pxtest
) へのRoot
の設定:- 以前のコードでは、外部テストパッケージを表す
pxtest
構造体の生成時にRoot
フィールドが設定されていませんでした。 - このコミットでは、
pxtest
の生成時に、元のパッケージp
のRoot
をpxtest.Root
にコピーするように変更されました。 - これにより、ビルドシステムは外部テストパッケージが現在のGoツリーの一部であることを正しく認識し、その依存関係が変更された場合に適切に再コンパイルを行うようになります。
- 以前のコードでは、外部テストパッケージを表す
-
computeStale
呼び出しの集約とビルドアクションの再配置:- 変更前は、
ptest
(内部テストパッケージ)とpxtest
(外部テストパッケージ)のそれぞれに対してcomputeStale
が呼び出され、その直後にそれぞれのビルドアクションが定義されていました。 - このコミットでは、
ptest
とpxtest
に対するcomputeStale
の呼び出しと、それに対応するビルドアクションの定義が、pmain
(テスト実行用のメインパッケージ)に対するcomputeStale
の呼び出しの後に移動されました。 - これにより、
computeStale
の呼び出しが論理的に一箇所に集約され、コードの流れがより明確になりました。また、ビルドアクションの定義も、関連するパッケージの準備が整った後にまとめて行われるようになり、コードの整理に貢献しています。
- 変更前は、
これらの変更により、go test
コマンドが外部テストパッケージの依存関係を正しく処理し、開発者が期待する通りに最新のコード変更がテストに反映されるようになりました。
コアとなるコードの変更箇所
src/cmd/go/test.go
ファイルにおける変更点は以下の通りです。
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -258,6 +258,9 @@ func runTest(cmd *Command, args []string) {
for _, path := range p.TestImports {
deps[path] = true
}
+ for _, path := range p.XTestImports {
+ deps[path] = true
+ }
}
// translate C to runtime/cgo
@@ -454,12 +457,6 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\
m[k] = append(m[k], v...)\n }\n ptest.build.ImportPos = m\n-\t\tcomputeStale(ptest)\n-\t\ta := b.action(modeBuild, modeBuild, ptest)\n-\t\ta.objdir = testDir + string(filepath.Separator)\n-\t\ta.objpkg = ptestObj\n-\t\ta.target = ptestObj\n-\t\ta.link = false
\t} else {\n \t\tptest = p\n \t}\n@@ -470,6 +467,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\
Name: p.Name + "_test",\n ImportPath: p.ImportPath + "_test",\n localPrefix: p.localPrefix,\n+\t\t\tRoot: p.Root,\n \t\t\tDir: p.Dir,\n \t\t\tGoFiles: p.XTestGoFiles,\n \t\t\tImports: p.XTestImports,\n@@ -481,11 +479,6 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\
fake: true,\n Stale: true,\n }\n-\t\tcomputeStale(pxtest)\n-\t\ta := b.action(modeBuild, modeBuild, pxtest)\n-\t\ta.objdir = testDir + string(filepath.Separator)\n-\t\ta.objpkg = buildToolchain.pkgpath(testDir, pxtest)\n-\t\ta.target = a.objpkg
\t}\n \n \t// Action for building pkg.test.\n@@ -494,6 +487,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\
Dir: testDir,\n GoFiles: []string{"_testmain.go"},\n ImportPath: "testmain",\n+\t\tRoot: p.Root,\n imports: []*Package{ptest},\n build: &build.Package{},\n fake: true,\n@@ -516,6 +510,21 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\
pmain.imports = append(pmain.imports, ptesting, pregexp)\n computeStale(pmain)\n \n+\tif ptest != p {\n+\t\ta := b.action(modeBuild, modeBuild, ptest)\n+\t\ta.objdir = testDir + string(filepath.Separator)\n+\t\ta.objpkg = ptestObj\n+\t\ta.target = ptestObj\n+\t\ta.link = false\n+\t}\n+\n+\tif pxtest != nil {\n+\t\ta := b.action(modeBuild, modeBuild, pxtest)\n+\t\ta.objdir = testDir + string(filepath.Separator)\n+\t\ta.objpkg = buildToolchain.pkgpath(testDir, pxtest)\n+\t\ta.target = a.objpkg\n+\t}\n+\n \ta := b.action(modeBuild, modeBuild, pmain)\n \ta.objdir = testDir + string(filepath.Separator)\n \ta.objpkg = filepath.Join(testDir, "main.a")
コアとなるコードの解説
1. p.XTestImports
の追加 (行 258-261)
@@ -258,6 +258,9 @@ func runTest(cmd *Command, args []string) {
for _, path := range p.TestImports {
deps[path] = true
}
+ for _, path := range p.XTestImports {
+ deps[path] = true
+ }
}
runTest
関数内で、テストパッケージが依存するパッケージのリストdeps
を構築する際に、p.XTestImports
(外部テストパッケージがインポートするパッケージ)も追加されるようになりました。これにより、外部テストパッケージの依存関係もビルドシステムによって適切に追跡されるようになります。
2. ptest
および pxtest
のビルドアクションの移動と Root
の設定 (行 454-483, 494-520)
@@ -454,12 +457,6 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\
m[k] = append(m[k], v...)\n }\n ptest.build.ImportPos = m\n-\t\tcomputeStale(ptest)\n-\t\ta := b.action(modeBuild, modeBuild, ptest)\n-\t\ta.objdir = testDir + string(filepath.Separator)\n-\t\ta.objpkg = ptestObj\n-\t\ta.target = ptestObj\n-\t\ta.link = false
\t} else {\n \t\tptest = p\n \t}\n@@ -470,6 +467,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\
Name: p.Name + "_test",\n ImportPath: p.ImportPath + "_test",\n localPrefix: p.localPrefix,\n+\t\t\tRoot: p.Root,\n \t\t\tDir: p.Dir,\n \t\t\tGoFiles: p.XTestGoFiles,\n \t\t\tImports: p.XTestImports,\n@@ -481,11 +479,6 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\
fake: true,\n Stale: true,\n }\n-\t\tcomputeStale(pxtest)\n-\t\ta := b.action(modeBuild, modeBuild, pxtest)\n-\t\ta.objdir = testDir + string(filepath.Separator)\n-\t\ta.objpkg = buildToolchain.pkgpath(testDir, pxtest)\n-\t\ta.target = a.objpkg
\t}\n \n \t// Action for building pkg.test.\n@@ -494,6 +487,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\
Dir: testDir,\n GoFiles: []string{"_testmain.go"},\n ImportPath: "testmain",\n+\t\tRoot: p.Root,\n imports: []*Package{ptest},\n build: &build.Package{},\n fake: true,\n@@ -516,6 +510,21 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\
pmain.imports = append(pmain.imports, ptesting, pregexp)\n computeStale(pmain)\n \n+\tif ptest != p {\n+\t\ta := b.action(modeBuild, modeBuild, ptest)\n+\t\ta.objdir = testDir + string(filepath.Separator)\n+\t\ta.objpkg = ptestObj\n+\t\ta.target = ptestObj\n+\t\ta.link = false\n+\t}\n+\n+\tif pxtest != nil {\n+\t\ta := b.action(modeBuild, modeBuild, pxtest)\n+\t\ta.objdir = testDir + string(filepath.Separator)\n+\t\ta.objpkg = buildToolchain.pkgpath(testDir, pxtest)\n+\t\ta.target = a.objpkg\n+\t}\n+\n \ta := b.action(modeBuild, modeBuild, pmain)\n \ta.objdir = testDir + string(filepath.Separator)\n \ta.objpkg = filepath.Join(testDir, "main.a")
Root: p.Root,
の追加 (行 473, 497):- 外部テストパッケージ
pxtest
とテスト実行用のメインパッケージpmain
の生成時に、元のパッケージp
のRoot
フィールドをコピーするように変更されました。これにより、これらのパッケージが現在のGoツリーの一部として正しく認識され、依存関係の再コンパイルが適切に行われるようになります。
- 外部テストパッケージ
computeStale
呼び出しとビルドアクションの移動:- 以前は
ptest
とpxtest
の定義直後にそれぞれcomputeStale
とビルドアクションの定義がありましたが、これらが削除されました。 - 代わりに、
pmain
に対するcomputeStale(pmain)
の呼び出しの後に、ptest
とpxtest
に対するビルドアクションの定義がまとめて追加されました。 - この変更により、
computeStale
の呼び出しがpmain
の準備ができた後の一箇所に集約され、コードの論理的な流れが改善されました。また、ビルドアクションの定義も一箇所にまとまり、コードの可読性と保守性が向上しています。
- 以前は
これらの変更は、Goのビルドシステムが外部テストパッケージの依存関係をより正確に管理し、開発者が期待する再コンパイルの挙動を実現するために不可欠でした。
関連リンク
- https://github.com/golang/go/commit/2b64e00f164e951f24221c0d4c5b3fb66a604531
- https://golang.org/cl/5786048
- Go Issue #3238: cmd/go: rebuild external test package dependencies
参考にした情報源リンク
- 上記のGitHubコミットページとGo CL (Change List) ページ
- Go言語の公式ドキュメント(パッケージ、テスト、ビルドシステムに関する一般的な情報)
- Go言語のソースコード(
src/cmd/go/test.go
の変更前後の比較) - Go言語のIssueトラッカー(Issue #3238の議論)