[インデックス 12333] ファイルの概要
このコミットは、Go言語のコマンドラインツール cmd/go におけるビルドプロセスの一部を修正するものです。具体的には、ビルド時にコンパイラに渡されるインクルードディレクトリのパスが重複して追加されるのを防ぐための変更が行われています。これにより、ビルドの効率性が向上し、潜在的なビルドエラーや警告が回避されます。
コミット
commit 1feecdd633c1943e5c4a0ced3a14788ee00f343c
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date: Fri Mar 2 11:31:13 2012 -0500
cmd/go: avoid repeated include dirs.
Fixes #3171.
R=golang-dev, rsc
CC=golang-dev, remy
https://golang.org/cl/5724045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1feecdd633c1943e5c4a0ced3a14788ee00f343c
元コミット内容
このコミットは、src/cmd/go/build.go ファイルに対して1行の追加と1行の削除を行っています。変更の目的は、go build コマンドがコンパイラに渡すインクルードディレクトリのリストに、同じディレクトリが複数回含まれることを避けることです。これは、Issue #3171 を修正するものです。
変更の背景
Go言語のビルドシステム、特に go build コマンドは、ソースコードをコンパイルし、実行可能ファイルやライブラリを生成する際に、必要なパッケージや依存関係を解決します。この過程で、コンパイラは特定のディレクトリ(インクルードディレクトリ)を検索して、ヘッダーファイルやその他の必要なリソースを見つけます。
このコミットが行われる前は、cmd/go のビルドロジックにおいて、インクルードディレクトリのパスを管理する際に、特定の条件下で同じパスが複数回リストに追加されてしまうバグが存在していました。これは、incMap というマップ(Go言語におけるハッシュマップ)を使用して重複を避ける試みがなされていたにもかかわらず発生していました。
具体的には、incMap[dir] = true という行が、dir 変数が filepath.Join によって変更される可能性のある処理の後に配置されていたため、元の dir の値が incMap に正しく記録されず、結果として重複したパスが inc スライスに追加される可能性がありました。このような重複は、ビルド時間のわずかな増加や、一部のコンパイラやツールチェーンで予期せぬ警告やエラーを引き起こす可能性がありました。
Issue #3171 はこの問題点を指摘しており、このコミットはその修正を目的としています。
前提知識の解説
cmd/go: Go言語の公式ツールチェーンの主要なコマンドラインインターフェースです。go build,go run,go test,go getなど、Go開発者が日常的に使用する多くのサブコマンドを提供します。- インクルードディレクトリ (Include Directories): コンパイラがソースコードをコンパイルする際に、
#include(C/C++) やimport(Go) などのディレクティブで指定された外部ファイル(ヘッダーファイル、パッケージなど)を探すためのディレクトリのリストです。コンパイラはこれらのディレクトリを順番に検索し、必要なファイルを見つけます。 go build: Goのソースコードをコンパイルして実行可能バイナリを生成するコマンドです。このプロセスには、依存関係の解決、コンパイル、リンクが含まれます。filepath.Join: Go言語の標準ライブラリpath/filepathパッケージに含まれる関数で、複数のパス要素を結合して単一のパスを生成します。OS固有のパス区切り文字(Windowsでは\、Unix系では/)を適切に処理します。map(マップ): Go言語におけるキーと値のペアを格納するデータ構造で、他の言語のハッシュマップや辞書に相当します。キーの一意性を保証するために使用され、このコミットではインクルードディレクトリのパスの重複を検出・防止するためにincMapとして利用されています。slice(スライス): Go言語における可変長配列です。このコミットでは、最終的にコンパイラに渡されるインクルードディレクトリのパスを格納するincというスライスが使用されています。gccgoToolchain: Go言語のコンパイラには、公式のgcコンパイラと、GCCをバックエンドとするgccgoコンパイラがあります。このコードは、どちらのツールチェーンが使用されているかに応じて、インクルードパスの構造を調整しています。
技術的詳細
src/cmd/go/build.go 内の builder.includeArgs 関数は、ビルドプロセス中にコンパイラに渡すインクルードディレクトリの引数を生成する役割を担っています。この関数は、all という *action のスライスを受け取り、それぞれの action に関連付けられたパッケージディレクトリ (a1.pkgdir) を処理します。
この関数は、incMap という map[string]bool 型のマップを使用して、既に追加されたディレクトリのパスを追跡し、重複を防ぐことを意図していました。また、inc という []string 型のスライスに、最終的なインクルードパスの引数を flag と dir のペアとして追加します。
問題のコードブロックは以下の通りでした(変更前):
if dir := a1.pkgdir; dir == a1.p.build.PkgRoot && !incMap[dir] {
if _, ok := buildToolchain.(gccgoToolchain); ok {
dir = filepath.Join(dir, "gccgo")
} else {
dir = filepath.Join(dir, goos+"_"+goarch)
}
incMap[dir] = true // ここが問題の箇所
inc = append(inc, flag, dir)
}
このコードでは、incMap[dir] = true の行が、dir 変数が filepath.Join によって変更された後に実行されていました。
例えば、a1.pkgdir が /path/to/pkg であったとします。
if dir := a1.pkgdir; ...でdirは/path/to/pkgになります。!incMap[dir]のチェックが行われます。もし/path/to/pkgがまだincMapになければ、条件は真となり、ブロックに入ります。dir = filepath.Join(dir, "gccgo")のような行で、dirの値が/path/to/pkg/gccgoに変更されます。- 問題点:
incMap[dir] = trueが実行されるとき、dirは既に/path/to/pkg/gccgoになっています。したがって、incMapには/path/to/pkg/gccgoが追加されますが、元の/path/to/pkgは追跡されません。 - もし別の
actionで、同じa1.pkgdir(/path/to/pkg) が処理され、かつfilepath.Joinの結果が異なる場合(例えば、goos+"_"+goarchのパスが異なる場合)、またはa1.pkgdirがincMapに追加される前に別のパスとして処理された場合、元のa1.pkgdirが重複して処理される可能性がありました。
このバグは、incMap が a1.pkgdir の元の値を正しく追跡できていなかったために発生しました。
コアとなるコードの変更箇所
変更は src/cmd/go/build.go ファイルの builder.includeArgs 関数内で行われました。
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -790,12 +790,12 @@ func (b *builder) includeArgs(flag string, all []*action) []string {
// Finally, look in the installed package directories for each action.
for _, a1 := range all {
if dir := a1.pkgdir; dir == a1.p.build.PkgRoot && !incMap[dir] {
- if _, ok := buildToolchain.(gccgoToolchain); ok {
- dir = filepath.Join(dir, "gccgo")
- } else {
- dir = filepath.Join(dir, goos+"_"+goarch)
- }
- incMap[dir] = true
+ incMap[dir] = true // この行が移動した
+ if _, ok := buildToolchain.(gccgoToolchain); ok {
+ dir = filepath.Join(dir, "gccgo")
+ } else {
+ dir = filepath.Join(dir, goos+"_"+goarch)
+ }
inc = append(inc, flag, dir)
}
}
コアとなるコードの解説
変更は非常にシンプルですが、その影響は重要です。
incMap[dir] = true の行が、if dir := a1.pkgdir; ... の条件が真であった直後、かつ dir 変数が filepath.Join によって変更される前に移動されました。
変更後:
if dir := a1.pkgdir; dir == a1.p.build.PkgRoot && !incMap[dir] {
incMap[dir] = true // ここに移動
if _, ok := buildToolchain.(gccgoToolchain); ok {
dir = filepath.Join(dir, "gccgo")
} else {
dir = filepath.Join(dir, goos+"_"+goarch)
}
inc = append(inc, flag, dir)
}
この変更により、以下のようになります。
if dir := a1.pkgdir; ...の条件が真となり、ブロックに入った直後、dirはまだa1.pkgdirの元の値(例:/path/to/pkg)を保持しています。- この時点で
incMap[dir] = trueが実行されるため、incMapにはa1.pkgdirの元の値が正しく「既に追加済み」として記録されます。 - その後、
dirはfilepath.Joinによってgccgoやgoos_goarchのサブディレクトリを含むパス(例:/path/to/pkg/gccgo)に変更されます。 - 最終的に、この変更された
dirの値がincスライスに追加されます。
これにより、incMap は a1.pkgdir の元の値を正確に追跡できるようになり、同じ a1.pkgdir から派生するインクルードパスが複数回処理されることを効果的に防ぎます。結果として、コンパイラに渡されるインクルードディレクトリのリストから重複が排除され、ビルドプロセスの堅牢性と効率性が向上します。
関連リンク
- Go Issue #3171: https://github.com/golang/go/issues/3171
- Go CL 5724045: https://golang.org/cl/5724045
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコードリポジトリ
- Go言語のIssueトラッカー
path/filepathパッケージのドキュメント- Go言語のマップとスライスに関する基本的な情報