[インデックス 14369] ファイルの概要
このコミットは、Go言語のコマンドラインツール cmd/go
における、テスト対象パッケージの選択ロジックの不具合を修正するものです。具体的には、ユーザーが独自のビルドタグを指定した場合や、競合検出(race detection)を有効にした場合に、go test
コマンドが正しくパッケージを選択できない問題を解決します。
コミット
commit 4ef91fc8540ef3c77906be387f9915914a27595d
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Fri Nov 9 14:00:41 2012 +0400
cmd/go: fix selection of packages for testing
Currently it works incorrectly if user specifies own build tags
and with race detection (e.g. runtime/race is not selected,
because it contains only test files with +build race).
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6814107
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4ef91fc8540ef3c77906be387f9915914a27595d
元コミット内容
cmd/go
: テストのためのパッケージ選択を修正。
現在、ユーザーが独自のビルドタグを指定した場合や、競合検出(例えば runtime/race
が選択されないのは、+build race
タグを持つテストファイルしか含まれていないため)を有効にした場合に、正しく動作しない。
変更の背景
Go言語のビルドシステムでは、特定の条件に基づいてソースファイルをコンパイルするかどうかを制御するために「ビルドタグ(build tags)」を使用します。例えば、// +build linux
のようなコメントをファイルの先頭に記述することで、そのファイルがLinux環境でのみコンパイルされるように指定できます。同様に、// +build race
は競合検出が有効な場合にのみコンパイルされるファイルを示します。
このコミットが修正しようとしている問題は、go test
コマンドがテスト対象のパッケージを決定する際に、これらのビルドタグや競合検出の有効化といったユーザーが指定したビルドコンテキストを適切に考慮していなかった点にあります。
具体的には、runtime/race
のようなパッケージは、通常、+build race
タグを持つテスト関連ファイルのみを含んでいます。ユーザーが go test -race
を実行して競合検出を有効にした場合、cmd/go
ツールは runtime/race
パッケージをテスト対象として含めるべきです。しかし、従来のロジックでは、デフォルトのビルドコンテキストでパッケージをインポートしようとしていたため、+build race
タグが考慮されず、結果として runtime/race
パッケージがテスト対象から漏れてしまうという不具合がありました。これは、ユーザーが独自のビルドタグを GOFLAGS
環境変数などで指定した場合にも同様の問題を引き起こしていました。
この不具合は、特定の機能(競合検出など)が有効になっているにもかかわらず、それに関連するテストが実行されないという、テストの網羅性や信頼性に関わる重要な問題でした。
前提知識の解説
Goのビルドシステムとコマンド
go build
: Goのソースコードをコンパイルして実行可能ファイルを生成するコマンドです。go test
: Goのテストを実行するコマンドです。パッケージ内のテストファイル(_test.go
で終わるファイル)をコンパイルし、テスト関数を実行します。cmd/go
:go
コマンド自体を実装しているGoの標準ライブラリの一部です。ユーザーがgo build
やgo test
などのコマンドを実行すると、このcmd/go
パッケージ内のコードが実行され、実際のビルドやテストのロジックが処理されます。
ビルドタグ (Build Tags)
Go言語では、ソースファイルの先頭に特別なコメント行を記述することで、そのファイルが特定のビルド条件を満たす場合にのみコンパイルされるように指定できます。これを「ビルドタグ」と呼びます。
例:
// +build linux,amd64
package main
// このコードはLinuxかつAMD64アーキテクチャの場合にのみコンパイルされる
複数のタグをカンマで区切るとAND条件(すべて満たす)、スペースで区切るとOR条件(いずれかを満たす)になります。ビルドタグは、OS、アーキテクチャ、Goのバージョン、またはカスタムのタグ(例: race
)に基づいて、条件付きコンパイルを可能にします。
競合検出 (Race Detection)
Goは、並行処理におけるデータ競合(複数のGoroutineが同時に同じメモリ位置にアクセスし、少なくとも1つが書き込み操作である場合に発生する未定義の動作)を検出するための組み込みツールを提供しています。go test -race
コマンドを実行すると、Goランタイムがデータ競合の発生を監視し、検出された場合には詳細なレポートを出力します。この機能は、runtime/race
パッケージによって提供されており、このパッケージ内のコードは通常 +build race
ビルドタグが付与されています。
go/build
パッケージとビルドコンテキスト
Goの標準ライブラリには、Goのソースコードを解析し、パッケージの依存関係やビルド情報を取得するための go/build
パッケージが含まれています。
-
build.ImportDir(dir string, mode build.ImportMode)
: この関数は、指定されたディレクトリdir
からGoパッケージをインポートしようとします。しかし、この関数はデフォルトのビルドコンテキストを使用します。つまり、環境変数(GOOS
,GOARCH
など)やコマンドライン引数で指定されたカスタムのビルドタグを考慮しない可能性があります。 -
build.Context
構造体:go/build
パッケージにはContext
という構造体があります。この構造体は、Goのビルドプロセスに関するすべての情報(ターゲットOS、アーキテクチャ、環境変数、そしてアクティブなビルドタグのリストなど)をカプセル化します。 -
buildContext.ImportDir(dir string, mode build.ImportMode)
:build.Context
のメソッドとして提供されるImportDir
は、そのContext
オブジェクトが保持するビルド設定(アクティブなビルドタグを含む)に基づいてパッケージをインポートします。これにより、ユーザーが指定したカスタムビルドタグや、go test -race
のようなコマンドによって暗黙的に有効になるタグ(例:race
)が、パッケージのインポートプロセスで適切に考慮されるようになります。
技術的詳細
このコミットの核心は、cmd/go
ツールがパッケージをスキャンし、テスト対象として選択する際に、適切なビルドコンテキストを使用するように変更した点です。
以前のコードでは、matchPackages
関数内で build.ImportDir(path, 0)
を直接呼び出していました。この build.ImportDir
関数は、go/build
パッケージのグローバルなデフォルトコンテキストを使用します。このデフォルトコンテキストは、ユーザーが go test -tags customtag
のようにコマンドラインで指定したビルドタグや、go test -race
のように競合検出を有効にした際に暗黙的に追加される race
タグを認識しませんでした。
その結果、例えば runtime/race
パッケージのように、そのソースファイルが // +build race
のようなビルドタグに依存している場合、build.ImportDir
はそのファイルを通常のビルドプロセスの一部として認識せず、「Goソースファイルがない」と判断してしまうことがありました。これは、go test
が runtime/race
パッケージをテスト対象として適切に含めることができない原因となっていました。
この修正では、build.ImportDir(path, 0)
の呼び出しを buildContext.ImportDir(path, 0)
に変更しています。ここで buildContext
は、cmd/go
ツールが現在のコマンド実行のために構築した、ユーザーの指定(ビルドタグ、競合検出の有効化など)をすべて反映した build.Context
オブジェクトです。
この変更により、matchPackages
関数がパッケージをインポートする際に、現在のビルド環境とユーザーの意図が正確に反映されるようになりました。例えば、go test -race
が実行された場合、buildContext
には race
タグが含まれるため、buildContext.ImportDir
は +build race
タグを持つファイルを正しく認識し、runtime/race
パッケージがテスト対象として適切に選択されるようになります。これにより、テストの網羅性が向上し、ユーザーが期待する動作が実現されます。
コアとなるコードの変更箇所
変更は src/cmd/go/main.go
ファイルの2箇所です。
--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -430,7 +430,7 @@ func matchPackages(pattern string) []string {
return filepath.SkipDir
}
- _, err = build.ImportDir(path, 0)
+ _, err = buildContext.ImportDir(path, 0)
if err != nil {
return nil
}
@@ -471,7 +471,7 @@ func matchPackages(pattern string) []string {
}
have[name] = true
- _, err = build.ImportDir(path, 0)
+ _, err = buildContext.ImportDir(path, 0)
if err != nil && strings.Contains(err.Error(), "no Go source files") {
return nil
}
コアとなるコードの解説
上記の差分が示すように、src/cmd/go/main.go
内の matchPackages
関数において、build.ImportDir
の呼び出しが buildContext.ImportDir
に置き換えられています。
matchPackages
関数は、go test
コマンドが実行された際に、指定されたパターンに一致するパッケージを検索し、テスト対象として選択する役割を担っています。この関数はファイルシステムを走査し、各ディレクトリがGoパッケージであるかどうかを判断するために ImportDir
を使用します。
変更前:
_, err = build.ImportDir(path, 0)
この行では、go/build
パッケージのトップレベル関数 ImportDir
が呼び出されていました。この関数は、現在の環境のデフォルト設定(GOOS
, GOARCH
など)のみを考慮し、ユーザーがコマンドラインで指定したカスタムビルドタグや、go test -race
のような特定のコマンドによって有効になるタグ(例: race
)を考慮しませんでした。
変更後:
_, err = buildContext.ImportDir(path, 0)
この行では、buildContext
という変数(これは build.Context
型のオブジェクトであり、現在の go
コマンドの実行コンテキスト、つまりユーザーが指定したビルドタグや競合検出の有効化などの情報がすべて含まれている)のメソッドとして ImportDir
が呼び出されています。
この変更により、パッケージのインポート処理が、現在の go
コマンドの実行環境とユーザーの意図を正確に反映するようになりました。例えば、go test -race
が実行された場合、buildContext
オブジェクトには race
ビルドタグがアクティブであるという情報が含まれており、buildContext.ImportDir
は +build race
タグを持つソースファイルを正しく認識し、それらをパッケージの一部として含めることができるようになります。これにより、runtime/race
のような特定のビルドタグに依存するパッケージも、期待通りにテスト対象として選択されるようになりました。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/4ef91fc8540ef3c77906be387f9915914a27595d
- Go Code Review (Gerrit): https://golang.org/cl/6814107
参考にした情報源リンク
- Go Modules: Build constraints and build tags: https://go.dev/doc/go.mod#build-constraints
- Go Race Detector: https://go.dev/doc/articles/race_detector
go/build
package documentation: https://pkg.go.dev/go/build