[インデックス 18189] ファイルの概要
このコミットは、Go言語のツールチェイン、特にcmd/go
コマンドにおけるテスト実行時の問題修正に関するものです。具体的には、go test
コマンドがgccgo
コンパイラを使用する際に、テスト対象のパッケージ名が標準ライブラリのパッケージ名と衝突する場合に発生するオブジェクトファイル配置の問題を解決します。
コミット
commit 7178c05d05f2372c0c2b027c16b5e904d4259f6e
Author: Michael Hudson-Doyle <michael.hudson@linaro.org>
Date: Tue Jan 7 23:53:16 2014 -0500
cmd/go: test: do not put object files where later steps will find them
When recompiling a package whose basename is the name of a standard
package for testing with gccgo, a .o file with the basename of the
package being tested was being placed in the _test/ directory where the
compilation of the test binary then found it when looking for the
standard library package.
This change puts the object files in a separate directory.
Fixes #6793
R=golang-codereviews, dave, gobot, rsc, iant
CC=golang-codereviews
https://golang.org/cl/27650045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/7178c05d05f2372c0c2b027c16b5e904d4259f6e
元コミット内容
cmd/go: test: do not put object files where later steps will find them
このコミットは、cmd/go
ツールにおけるテスト実行時の挙動を修正するものです。特に、gccgo
を使用してテストを行う際に、テスト対象のパッケージのベース名が標準パッケージの名前と一致する場合に問題が発生していました。具体的には、テスト対象パッケージのオブジェクトファイル(.o
ファイル)が_test/
ディレクトリに配置され、その後のテストバイナリのコンパイル時に、標準ライブラリのパッケージを探しているコンパイラが誤ってこのオブジェクトファイルを見つけてしまうという問題です。この変更は、オブジェクトファイルを別の専用ディレクトリに配置することで、この問題を解決します。
変更の背景
Go言語のビルドシステムは、パッケージのコンパイル時に生成される中間ファイル(オブジェクトファイルなど)を特定のディレクトリに配置します。go test
コマンドは、テストを実行するために、テスト対象のパッケージとそのテストコードをコンパイルし、テストバイナリを生成します。このプロセスにおいて、一時的なディレクトリ(通常は_test/
のような名前)が使用されます。
問題は、gccgo
コンパイラを使用している場合に顕在化しました。gccgo
は、GoのコードをGCCのバックエンドを使用してコンパイルする代替コンパイラです。特定のシナリオ、特にテスト対象のパッケージのベース名(例: fmt
、net/http
など)がGoの標準ライブラリのパッケージ名と偶然一致する場合に、コンパイル時の名前解決の衝突が発生していました。
具体的には、go test
がテスト対象パッケージをコンパイルして生成したオブジェクトファイル(例: fmt.o
)が、テストバイナリをリンクする際に_test/
ディレクトリに存在していました。gccgo
は、標準ライブラリのfmt
パッケージを探す際に、このfmt.o
ファイルを誤って見つけてしまい、本来の標準ライブラリのfmt
パッケージではなく、テスト対象パッケージのオブジェクトファイルを使用してしまい、結果としてビルドエラーや予期せぬ動作を引き起こしていました。
このコミットは、このオブジェクトファイルの配置に関する競合を解消し、テストプロセスの堅牢性を向上させることを目的としています。
前提知識の解説
このコミットを理解するためには、以下のGo言語のビルドとテストに関する基本的な知識が必要です。
-
Goのパッケージとビルドプロセス:
- Goのコードは「パッケージ」として組織されます。各パッケージは、通常、ディレクトリに対応し、そのディレクトリ内のソースファイルがパッケージを構成します。
go build
コマンドは、Goのソースコードをコンパイルして実行可能ファイルやライブラリを生成します。この際、中間ファイルとしてオブジェクトファイル(.o
ファイル)が生成されます。- Goのビルドシステムは、依存関係を解決し、必要なパッケージを再帰的にコンパイルします。
-
go test
コマンド:go test
は、Goのパッケージのテストを実行するためのコマンドです。- テストを実行する際、
go test
はテスト対象のパッケージと、_test.go
で終わるテストファイルをコンパイルし、単一のテストバイナリを生成します。 - このテストバイナリは、テスト対象のパッケージのコードとテストコードの両方を含みます。
- テストのコンパイルと実行のために、一時的な作業ディレクトリが作成されます。このディレクトリは通常、パッケージのソースディレクトリ内に
_test/
のような名前で作成されます。
-
gccgo
コンパイラ:- Go言語には、公式のコンパイラである
gc
(Go Compiler)と、GCCのフロントエンドとして実装されたgccgo
の2つの主要なコンパイラがあります。 gc
はGo言語で書かれており、Goの標準的なビルドツールチェーンの一部です。gccgo
はC/C++コンパイラであるGCCのフレームワークを利用しており、異なる最適化やプラットフォームサポートを提供することがあります。- この問題は、特に
gccgo
のビルドプロセスにおけるオブジェクトファイルの検索パスと名前解決の挙動に関連していました。
- Go言語には、公式のコンパイラである
-
オブジェクトファイルとリンク:
- コンパイラはソースコードをオブジェクトファイルに変換します。オブジェクトファイルには、コンパイルされた機械語コードと、他のオブジェクトファイルやライブラリへの参照(シンボル)が含まれます。
- リンカは、複数のオブジェクトファイルとライブラリを結合して、最終的な実行可能ファイルや共有ライブラリを生成します。この際、リンカは必要なシンボルを解決するために、指定されたパスからオブジェクトファイルやライブラリを検索します。
このコミットは、go test
がgccgo
を使用する際に、テスト対象パッケージのオブジェクトファイルが、リンカが標準ライブラリのオブジェクトファイルを検索するパスと衝突するという、特定のビルド環境におけるエッジケースを扱っています。
技術的詳細
この問題の核心は、go test
がテスト対象パッケージをコンパイルする際に生成するオブジェクトファイル(.o
)の配置場所と、gccgo
が標準ライブラリのオブジェクトファイルを検索するメカニズムの間の相互作用にありました。
通常、go test
はテスト実行のために一時的なディレクトリ(例: _test/
)を作成し、その中にコンパイルされたオブジェクトファイルを配置します。例えば、my_package
というパッケージをテストする場合、my_package.o
のようなオブジェクトファイルが_test/
ディレクトリに生成されます。
問題が発生したのは、テスト対象のパッケージのベース名が、Goの標準ライブラリのパッケージ名と偶然一致した場合です。例えば、ユーザーがfmt
という名前のカスタムパッケージを作成し、それをテストしようとしたとします。go test
は_test/fmt.o
を生成します。
gccgo
は、テストバイナリをリンクする際に、依存する標準ライブラリ(例: fmt
パッケージ)のオブジェクトファイルを検索します。この検索パスには、一時的な_test/
ディレクトリが含まれることがありました。その結果、gccgo
は標準ライブラリのfmt
パッケージの代わりに、ユーザーが作成したカスタムfmt
パッケージの_test/fmt.o
を誤って見つけてしまい、リンクエラーや不正な動作を引き起こしていました。これは、コンパイラが「最も近い」または「最初に発見した」同名のオブジェクトファイルを優先してしまう挙動に起因します。
このコミットの解決策はシンプルかつ効果的です。オブジェクトファイルの配置ディレクトリを、テストバイナリのコンパイル時に標準ライブラリの検索パスと衝突しないように変更します。具体的には、オブジェクトファイルを_test/
ディレクトリのサブディレクトリ(例: _test/_obj_test/
や_test/_obj_xtest/
)に移動させることで、名前の衝突を回避します。これにより、gccgo
は正しく標準ライブラリのオブジェクトファイルを見つけることができ、テストプロセスが正常に完了するようになります。
この変更は、Goのビルドシステムが、異なるコンパイラや複雑なパッケージ構造の下でも堅牢に動作するための、細かながらも重要な改善点と言えます。
コアとなるコードの変更箇所
変更はsrc/cmd/go/test.go
ファイルに集中しています。
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -711,7 +711,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
if ptest != p {
a := b.action(modeBuild, modeBuild, ptest)
- a.objdir = testDir + string(filepath.Separator)
+ a.objdir = testDir + string(filepath.Separator) + "_obj_test" + string(filepath.Separator)
a.objpkg = ptestObj
a.target = ptestObj
a.link = false
@@ -719,7 +719,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,
if pxtest != nil {
a := b.action(modeBuild, modeBuild, pxtest)
- a.objdir = testDir + string(filepath.Separator)
+ a.objdir = testDir + string(filepath.Separator) + "_obj_xtest" + string(filepath.Separator)
a.objpkg = buildToolchain.pkgpath(testDir, pxtest)
a.target = a.objpkg
}
コアとなるコードの解説
このコミットの変更は、builder
構造体のtest
メソッド内で行われています。このメソッドは、go test
コマンドがテストバイナリをビルドする際のロジックをカプセル化しています。
変更の対象となっているのは、a.objdir
というフィールドです。objdir
は、コンパイルされたオブジェクトファイルが配置されるディレクトリパスを指定します。
元のコードでは、ptest
(内部テストパッケージ)とpxtest
(外部テストパッケージ)の両方について、objdir
が単にtestDir + string(filepath.Separator)
と設定されていました。これは、テスト用の一時ディレクトリ(例: _test/
)のルートにオブジェクトファイルを直接配置することを意味します。
変更後のコードでは、objdir
に新しいサブディレクトリが追加されています。
-
ptest
(内部テストパッケージ)の場合:a.objdir = testDir + string(filepath.Separator) + "_obj_test" + string(filepath.Separator)
これにより、内部テストパッケージのオブジェクトファイルは、_test/_obj_test/
のようなパスに配置されるようになります。 -
pxtest
(外部テストパッケージ)の場合:a.objdir = testDir + string(filepath.Separator) + "_obj_xtest" + string(filepath.Separator)
同様に、外部テストパッケージのオブジェクトファイルは、_test/_obj_xtest/
のようなパスに配置されるようになります。
この変更により、テスト対象パッケージのオブジェクトファイルが、_test/
ディレクトリのルートではなく、その下の専用サブディレクトリに隔離されます。これにより、gccgo
が標準ライブラリのパッケージを検索する際に、_test/
ディレクトリのルートにある同名のオブジェクトファイルを誤って見つけてしまうという問題が解消されます。リンカは、標準ライブラリのオブジェクトファイルを正しく見つけ、テストバイナリのコンパイルとリンクが正常に行われるようになります。
filepath.Separator
は、オペレーティングシステムに応じたパス区切り文字(Windowsでは\
、Unix系では/
)を挿入するために使用されています。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/doc/
go test
コマンドのドキュメント: https://golang.org/cmd/go/#hdr-Test_packagesgccgo
に関する情報: https://golang.org/doc/install/gccgo
参考にした情報源リンク
- コミットメッセージ自体
- Go言語のソースコード(
src/cmd/go/test.go
) - Go言語のビルドシステムと
go test
の一般的な知識 gccgo
の動作に関する一般的な知識
(注: コミットメッセージに記載されているFixes #6793
のGitHub Issueは、現在の検索では直接見つけることができませんでした。これは、非常に古いIssueであるか、またはIssueトラッカーの移行などにより参照が変更された可能性があります。)