[インデックス 15062] ファイルの概要
このコミットは、Go言語のコマンドラインツール cmd/go
における多数のバグ修正と改善を目的としています。Goコマンドのビルド、インストール、実行、テスト、およびその他のユーティリティ機能の堅牢性と使いやすさを向上させるための変更が含まれています。
コミット
commit 8b6534b78af791b80f371857a15d76bbc10fd012
Author: Russ Cox <rsc@golang.org>
Date: Thu Jan 31 08:06:38 2013 -0800
cmd/go: many bug fixes
* Reject import paths of the form cmd/x/y.
* Reject 'go install' of command outside GOPATH
* Clearer error rejecting 'go install' of package outside GOPATH.
* Name temporary binary for first file in 'go run' list or for test.
* Provide a way to pass -ldflags arguments with spaces.
* Pass all Go files (even +build ignored ones) to go fix, go fmt, go vet.
* Reject 'go run foo_test.go'.
* Silence 'exit 1' prints from 'go tool' invocations.
* Make go test -xxxprofile leave binary behind for analysis.
* Reject ~ in GOPATH except on Windows.
* Get a little less confused by symlinks.
* Document that go test x y z runs three test binaries.
* Fix go test -timeout=0.
* Add -tags flag to 'go list'.
* Use pkg/gccgo_$GOOS_$GOARCH for gccgo output.
Fixes #3389.
Fixes #3500.
Fixes #3503.
Fixes #3760.
Fixes #3941.
Fixes #4007.
Fixes #4032.
Fixes #4074.
Fixes #4127.
Fixes #4140.
Fixes #4311.
Fixes #4568.
Fixes #4576.
Fixes #4702.
R=adg
CC=golang-dev
https://golang.org/cl/7225074
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/8b6534b78af791b80f371857a15d76bbc10fd012
元コミット内容
このコミットは、Goコマンドラインツール cmd/go
の動作を改善するための複数の修正と機能強化をまとめています。主な変更点は以下の通りです。
cmd/x/y
形式のインポートパスの拒否。GOPATH
外のコマンドに対するgo install
の拒否。GOPATH
外のパッケージに対するgo install
のエラーメッセージの明確化。go run
またはテスト用のテンポラリバイナリに、リストの最初のファイル名またはテスト名を使用。- スペースを含む
-ldflags
引数を渡す方法の提供。 +build
タグで無視されたファイルを含むすべてのGoファイルをgo fix
,go fmt
,go vet
に渡すように変更。go run foo_test.go
の拒否。go tool
呼び出しからのexit 1
出力の抑制。go test -xxxprofile
が解析のためにバイナリを残すように変更。- Windows以外での
GOPATH
内の~
の拒否。 - シンボリックリンクの扱いの改善。
go test x y z
が3つのテストバイナリを実行することのドキュメント化。go test -timeout=0
の修正。go list
に-tags
フラグを追加。gccgo
の出力にpkg/gccgo_$GOOS_$GOARCH
を使用。
これらの変更は、Goツールの堅牢性、エラー報告、およびユーザーエクスペリエンスを向上させることを目的としています。
変更の背景
このコミットは、Goコマンドラインツール cmd/go
の初期開発段階における、ユーザーからのフィードバックや内部テストで発見された様々な問題に対処するために行われました。当時のGoツールはまだ成熟途上にあり、以下のような課題がありました。
- GOPATHの厳密な運用:
GOPATH
はGoプロジェクトの依存関係管理とビルドの基盤ですが、その運用ルールがまだ十分に確立されておらず、ユーザーがGOPATH
外でgo install
を実行しようとしたり、GOPATH
に不適切なパス(例:~
を含むパス)を設定したりするケースがありました。これらはビルドエラーや予期せぬ動作を引き起こすため、より明確なエラーメッセージや厳密なチェックが必要でした。 - コマンドの命名規則と実行:
cmd/x/y
のようなインポートパスは、Goの内部コマンドと衝突する可能性があり、混乱を招くことがありました。また、go run
がテストファイルを直接実行しようとするなど、意図しない動作を防ぐ必要がありました。 - ビルドフラグの柔軟性:
-ldflags
のようなビルドフラグにスペースを含む引数を渡すことができないなど、コマンドライン引数のパースに制限がありました。これにより、複雑なビルド設定を行う際に不便が生じていました。 - ツールの連携と網羅性:
go fix
,go fmt
,go vet
といったGoツールは、ビルドタグによって無視されるファイルに対しては適用されませんでした。しかし、これらのツールはコードベース全体の一貫性を保つために、すべてのGoファイルに対して実行されるべきでした。 - テストツールの改善:
go test
はGo開発において非常に重要なツールですが、プロファイリング結果の解析のために一時バイナリを残す機能や、タイムアウト設定のバグ、シンボリックリンクの扱いの問題など、いくつかの改善点がありました。また、go list
コマンドがビルドタグを考慮しないため、特定のビルド環境下でのパッケージ情報を正確に取得できない問題もありました。 - エラーメッセージの改善: ユーザーがGoツールを誤って使用した場合に、より分かりやすく、問題解決に役立つエラーメッセージを提供することが求められていました。特に
go tool
からのexit 1
出力は、ツールの内部エラーとユーザーの誤用によるエラーの区別がつきにくく、混乱を招いていました。
これらの背景から、Goツールの安定性、使いやすさ、そして開発者の生産性を向上させるために、本コミットで示される多岐にわたる修正が実施されました。
前提知識の解説
このコミットの変更内容を理解するためには、以下のGo言語およびGoツールの基本的な概念を理解しておく必要があります。
-
GOPATH:
- Go言語のワークスペースの概念です。Goのソースコード、コンパイルされたパッケージ、実行可能バイナリが配置されるディレクトリの集合を指します。
GOPATH
環境変数に設定されたパスは、Goがソースコードを検索し、依存関係を解決し、ビルド成果物をインストールする場所として機能します。- 通常、
$HOME/go
のようなディレクトリが設定されます。複数のパスを設定することも可能です(例:export GOPATH=$HOME/go:$HOME/work
)。 GOPATH
の各エントリは、src
(ソースコード),pkg
(コンパイル済みパッケージ),bin
(実行可能バイナリ) のサブディレクトリを持ちます。- 重要性: Goのビルドシステムは
GOPATH
に強く依存しており、GOPATH
の設定が正しくないと、パッケージのインポートやビルドが失敗します。
-
Goコマンド (
cmd/go
):- Go言語の公式コマンドラインツールであり、Goプログラムの開発ライフサイクル全体を管理します。
go build
: ソースコードをコンパイルして実行可能バイナリを生成します。go install
: ソースコードをコンパイルし、生成されたバイナリやパッケージをGOPATH
のbin
やpkg
ディレクトリにインストールします。go run
: ソースファイルをコンパイルして一時的な実行可能バイナリを作成し、それを実行します。go test
: パッケージ内のテストを実行します。go fmt
: Goソースコードを標準的なスタイルにフォーマットします。go fix
: 古いGoプログラムを新しいAPIに更新します。go vet
: Goソースコード内の疑わしい構成を報告します。go list
: 指定されたパッケージに関する情報を表示します。go tool
: Goツールチェーンに含まれる低レベルのツール(例:go tool compile
,go tool link
)を実行します。
-
ビルドタグ (
+build
directives):- Goソースファイルの先頭に記述される特殊なコメント行で、ファイルのコンパイル条件を制御します。
- 例:
// +build linux,amd64
は、このファイルがLinuxかつAMD64アーキテクチャの場合にのみコンパイルされることを意味します。 - これにより、異なるOSやアーキテクチャ、または特定のビルド環境(例: テスト、デバッグ)向けに異なるコードを提供できます。
go build -tags "mytag"
のように-tags
フラグを使用して、特定のビルドタグを有効にできます。
-
シンボリックリンク:
- ファイルシステムにおける、別のファイルやディレクトリへの参照(ショートカットのようなもの)。
- Goツールがファイルパスを解決する際に、シンボリックリンクを正しく追跡できないと、予期せぬエラーやファイルの不認識が発生する可能性があります。
-
プロファイリング:
- プログラムの実行中にパフォーマンスデータを収集するプロセス。
go test -cpuprofile=cpu.prof
のように-cpuprofile
などのフラグを使用すると、CPU使用率などのプロファイルデータがファイルに保存されます。- これらのプロファイルデータを解析するためには、通常、プロファイルデータが生成されたバイナリが必要になります。
-
コマンドライン引数のパース:
- シェルからプログラムに渡される引数を解析する処理。
- スペースを含む引数を正しく扱うためには、引用符(
"
や'
)で囲むなどの特別な処理が必要です。
これらの概念は、Go開発における日常的な作業に密接に関連しており、本コミットの修正がGoツールの使いやすさと信頼性にどのように貢献しているかを理解する上で不可欠です。
技術的詳細
このコミットは、Goコマンドの内部実装に多岐にわたる変更を加えており、その技術的詳細は以下の通りです。
-
cmd/x/y
形式のインポートパスの拒否:src/cmd/go/pkg.go
のloadPackage
関数が変更され、cmd/
で始まるインポートパスがcmd/x/y
の形式(つまり、cmd/
の後にスラッシュが2つ以上続く)である場合にエラーを返すようになりました。- これは、
cmd/
がGoの標準コマンド(例:cmd/go
,cmd/gofmt
)のために予約されており、ユーザーがこのプレフィックスを持つ独自のパッケージを作成して混乱を避けるための措置です。
-
go install
のGOPATH
外での実行拒否とエラーメッセージの改善:src/cmd/go/build.go
のrunInstall
関数が修正され、GOPATH
外のディレクトリにあるパッケージに対してgo install
を実行しようとした際に、より明確なエラーメッセージ (go install: no install location for directory %s outside GOPATH
) を表示するようになりました。- 以前は
no install location for %s
という一般的なメッセージでした。これにより、ユーザーは問題の原因を特定しやすくなります。
-
一時バイナリの命名規則の改善:
src/cmd/go/build.go
のbuilder.action
メソッドが変更され、go run
やgo test
で生成される一時的な実行可能バイナリのファイル名が、元のGoファイル名(go run
の場合)またはテスト対象のパッケージ名(go test
の場合)に基づいて決定されるようになりました。- 具体的には、
Package
構造体にexeName
フィールドが追加され、go run
の場合はp.exeName = src[:len(src)-len(".go")]
のように設定されます。これにより、ps
コマンドなどで実行中のプロセスを確認した際に、より分かりやすい名前が表示されるようになります。 - また、一時バイナリは
a.objdir + filepath.Join("exe", name) + exeSuffix
のようにexe
サブディレクトリ内に配置されることで、名前の衝突を避ける工夫がされています。
-
スペースを含む
-ldflags
引数のパース:src/cmd/go/build.go
にsplitQuotedFields
関数が追加されました。この関数は、スペースで区切られた文字列を解析し、単一引用符 ('
) または二重引用符 ("
) で囲まれた部分を単一のフィールドとして扱います。src/cmd/go/testflag.go
のtestFlags
関数内で、-ldflags
,-gcflags
,-gccgoflags
の値をパースする際に、このsplitQuotedFields
関数が使用されるようになりました。- これにより、例えば
-ldflags "-X main.version=1.0"
のように、スペースを含む値をリンカフラグとして渡すことが可能になり、ビルドの柔軟性が向上しました。
-
+build
無視ファイルを含むすべてのGoファイルをツールに渡す:src/cmd/go/fix.go
,src/cmd/go/fmt.go
,src/cmd/go/get.go
,src/cmd/go/vet.go
の各コマンドの実装が変更されました。- 以前は
pkg.gofiles
(ビルドタグによって選択されたGoファイルのみ) を対象としていましたが、pkg.allgofiles
(ビルドタグによって無視されたファイルも含むすべてのGoファイル) を対象とするように変更されました。 src/cmd/go/pkg.go
のPackage
構造体にallgofiles
フィールドが追加され、load
メソッド内でIgnoredGoFiles
とgofiles
を結合してallgofiles
を生成するようになりました。- これにより、
go fix
,go fmt
,go vet
がコードベース全体に対して一貫して適用されるようになり、コード品質の維持に役立ちます。
-
go run foo_test.go
の拒否:src/cmd/go/run.go
のrunRun
関数が修正され、_test.go
で終わるファイルがgo run
コマンドの引数として渡された場合にエラー (go run: cannot run *_test.go files (%s)
) を返すようになりました。- テストファイルは
go test
で実行されるべきであり、go run
で直接実行することは意図されていません。この変更により、ユーザーの誤用を防ぎます。
-
go tool
呼び出しからのexit 1
出力の抑制:src/cmd/go/tool.go
のrunTool
関数が変更され、go tool
が実行するサブコマンドがexit 1
で終了した場合でも、そのサブコマンドが既にエラーメッセージを出力していると判断される場合は、go tool
自身が重複してエラーメッセージを出力しないようになりました。- 具体的には、
exec.ExitError
であり、かつExited()
がtrue
の場合は、go tool %s: %s
の形式のエラーメッセージを出力しないように条件が追加されました。これにより、エラー出力が冗長になるのを防ぎ、ユーザーにとってよりクリーンな出力が提供されます。
-
go test -xxxprofile
によるバイナリの保持:src/cmd/go/test.go
のrunTest
関数とbuilder.test
関数が修正され、-cpuprofile
,-memprofile
,-blockprofile
などのプロファイリングフラグが指定された場合に、テスト実行後に生成されたテストバイナリ (pkg.test
) が削除されずに残るようになりました。testProfile
という新しいフラグが導入され、プロファイリングフラグが指定された場合にtrue
に設定されます。testC || testProfile
の条件で、テストバイナリをカレントディレクトリにコピーするアクションが実行されるようになり、プロファイル解析ツール(例:go tool pprof
)がバイナリを見つけられるようになります。
-
GOPATH
内の~
の拒否 (Windows以外):src/cmd/go/main.go
のmain
関数とsrc/pkg/go/build/build.go
のgopath
メソッドが変更され、GOPATH
のエントリに~
(チルダ) が含まれている場合、Windows以外のOSではエラーを出すか、そのパスを無視するようになりました。- これは、Unix系システムで
~
がシェルによって$HOME
に展開されることを期待してGOPATH
に設定するユーザーがいたものの、シェルが展開しない環境(例:go env GOPATH
)では~
がリテラルとして扱われ、パスが正しく解決されないという混乱を避けるためです。Windowsでは~
が短いファイル名(例:PROGRA~1
)の一部として使用されるため、例外とされています。
-
シンボリックリンクの扱いの改善:
src/pkg/go/build/build.go
のhasSubdir
関数が修正され、シンボリックリンクを含むパスの解決がより堅牢になりました。filepath.EvalSymlinks
を使用して、ルートパスとターゲットパスの両方でシンボリックリンクを展開し、展開されたパスと展開されていないパスの組み合わせでサブディレクトリ関係をチェックするようになりました。これにより、シンボリックリンクが絡むGoプロジェクトのビルドがより安定します。
-
go test -timeout=0
の修正:src/cmd/go/test.go
のrunTest
関数が修正され、testTimeout
が0
の場合にタイムアウト処理が適用されないように変更されました。- 以前は
time.ParseDuration
が0
を有効な期間としてパースし、testKillTimeout
が1*time.Minute
に設定されてしまう可能性がありました。dt > 0
の条件が追加されたことで、0
の場合はタイムアウトが無効になります。
-
go list
への-tags
フラグの追加:src/cmd/go/list.go
とsrc/cmd/go/doc.go
が変更され、go list
コマンドに-tags
フラグが追加されました。- これにより、
go build
と同様に、go list
も特定のビルドタグを考慮してパッケージ情報を表示できるようになり、条件付きコンパイルされたパッケージの情報を正確に取得できるようになりました。
-
gccgo
出力ディレクトリの変更:src/cmd/go/build.go
とsrc/cmd/go/pkg.go
が変更され、gccgo
コンパイラを使用する場合のパッケージインストールディレクトリがpkg/gccgo
からpkg/gccgo_$GOOS_$GOARCH
に変更されました。- これにより、異なるOSやアーキテクチャ向けの
gccgo
ビルド成果物が衝突することなく、適切に分離されるようになります。
これらの変更は、Goツールの内部ロジックを改善し、様々なエッジケースやユーザーの誤用に対応することで、Go開発体験全体の信頼性と効率性を高めています。
コアとなるコードの変更箇所
このコミットにおける主要なコード変更箇所は以下のファイルに集中しています。
-
src/cmd/go/build.go
:splitQuotedFields
関数の追加: スペースを含む引用符付き文字列を正しく分割するためのロジックが実装されました。stringsFlag
型のSet
メソッドの変更:strings.Fields
からsplitQuotedFields
を使用するように変更され、-ldflags
などの引数でスペースを扱えるようになりました。runInstall
関数でのエラーメッセージの改善:GOPATH
外のインストールに関するエラーメッセージがより具体的になりました。builder.action
メソッドでの一時バイナリの命名ロジックの変更:go run
やgo test
で生成される実行可能ファイルの命名規則が改善され、exe
サブディレクトリに配置されるようになりました。builder.build
メソッドでのターゲットディレクトリ作成ロジックの追加。includeArgs
メソッドでのgccgo
パスの変更:gccgo_$GOOS_$GOARCH
を使用するように修正。
-
src/cmd/go/pkg.go
:Package
構造体へのIgnoredGoFiles
とallgofiles
,exeName
フィールドの追加。copyBuild
メソッドでのIgnoredGoFiles
のコピー。load
メソッドでのallgofiles
の生成ロジックの追加:IgnoredGoFiles
とgofiles
を結合。load
メソッドでのp.target
の設定ロジックの修正。swigDir
メソッドでのgccgo
パスの変更:gccgo_$GOOS_$GOARCH
を使用するように修正。loadPackage
関数でのcmd/x/y
形式のインポートパスの拒否ロジックの追加。
-
src/cmd/go/run.go
:runRun
関数での_test.go
ファイルの実行拒否ロジックの追加。p.exeName
の設定:go run
で実行される一時バイナリの名前を、最初のGoファイル名に基づいて設定。
-
src/cmd/go/test.go
:testProfile
グローバル変数の追加。runTest
関数でのtestProfile
フラグのチェックと、複数パッケージでのプロファイリングフラグ使用の拒否。testTimeout
が0
の場合のタイムアウト処理の無効化。builder.test
関数での一時バイナリの命名ロジックの変更:command-line-arguments
の場合のelem
の決定。testC
またはtestProfile
の場合にテストバイナリをカレントディレクトリに残すロジックの追加。
-
src/cmd/go/testflag.go
:testFlags
関数での-ldflags
,-gcflags
,-gccgoflags
のパースにsplitQuotedFields
を使用するように変更。- プロファイリング関連のフラグ (
-blockprofile
,-cpuprofile
,-memprofile
) が指定された場合にtestProfile
をtrue
に設定。
-
src/cmd/go/tool.go
:runTool
関数でのgo tool
呼び出しからのexit 1
出力の抑制ロジックの追加。
-
src/pkg/go/build/build.go
:hasSubdir
関数の修正: シンボリックリンクを考慮したサブディレクトリチェックの改善。Context.gopath
メソッドでのGOPATH
内の~
の拒否ロジックの追加 (Windows以外)。Package
構造体へのIgnoredGoFiles
フィールドの追加。readDir
関数でのIgnoredGoFiles
の収集ロジックの追加: ビルドタグやOS/アーキテクチャによって無視されるGoファイルをIgnoredGoFiles
に格納。
これらのファイルは、Goコマンドのビルド、パッケージ管理、実行、テスト、およびユーティリティ機能の核心部分を構成しており、本コミットの変更がGoツールの動作に広範な影響を与えていることがわかります。
コアとなるコードの解説
ここでは、上記の「コアとなるコードの変更箇所」で挙げた主要な変更点について、その実装の詳細と意図を解説します。
1. splitQuotedFields
関数と引用符付き引数のパース
変更ファイル: src/cmd/go/build.go
, src/cmd/go/testflag.go
実装詳細:
src/cmd/go/build.go
に新しく追加された splitQuotedFields
関数は、以下のようなロジックで文字列を分割します。
func splitQuotedFields(s string) ([]string, error) {
var f []string
for len(s) > 0 {
// 先頭の空白をスキップ
for len(s) > 0 && isSpaceByte(s[0]) {
s = s[1:]
}
if len(s) == 0 {
break
}
// 引用符で囲まれた文字列の処理
if s[0] == '"' || s[0] == '\'' {
quote := s[0]
s = s[1:] // 引用符をスキップ
i := 0
for i < len(s) && s[i] != quote {
i++
}
if i >= len(s) {
return nil, fmt.Errorf("unterminated %c string", quote) // 閉じ引用符がない場合のエラー
}
f = append(f, s[:i]) // 引用符内の文字列を追加
s = s[i+1:] // 閉じ引用符をスキップ
continue
}
// 引用符で囲まれていない文字列の処理
i := 0
for i < len(s) && !isSpaceByte(s[i]) {
i++
}
f = append(f, s[:i]) // 空白までの文字列を追加
s = s[i:]
}
return f, nil
}
この関数は、strings.Fields
が単に空白で分割するのに対し、単一引用符 ('
) または二重引用符 ("
) で囲まれた部分を一つのフィールドとして扱います。これにより、-ldflags "-X main.version=1.0"
のように、スペースを含む値をフラグ引数として渡すことが可能になりました。
src/cmd/go/testflag.go
では、testFlags
関数内で -ldflags
, -gcflags
, -gccgoflags
の値をパースする際に、この splitQuotedFields
が呼び出されるように変更されています。
意図: Goコマンドのビルドフラグ(特にリンカフラグ)は、バージョン情報やビルド時の設定など、スペースを含む文字列を渡す必要がある場面があります。この変更により、より複雑なビルド設定をコマンドラインから直接指定できるようになり、Goツールの柔軟性と使いやすさが向上しました。
2. allgofiles
の導入とツールへの全Goファイルの適用
変更ファイル: src/cmd/go/pkg.go
, src/cmd/go/fix.go
, src/cmd/go/fmt.go
, src/cmd/go/get.go
, src/cmd/go/vet.go
, src/pkg/go/build/build.go
実装詳細:
src/cmd/go/pkg.go
の Package
構造体に allgofiles
という新しいフィールドが追加されました。これは、ビルドタグによってコンパイル対象から除外されたGoファイル (IgnoredGoFiles
) と、実際にコンパイルされるGoファイル (GoFiles
, CgoFiles
, TestGoFiles
, XTestGoFiles
) の両方を含む、パッケージ内のすべてのGoファイルの絶対パスのリストです。
src/cmd/go/pkg.go
の Package.load
メソッド内で、allgofiles
は以下のように生成されます。
p.allgofiles = stringList(p.IgnoredGoFiles) // まず無視されたファイルを追加
for i := range p.allgofiles {
p.allgofiles[i] = filepath.Join(p.Dir, p.allgofiles[i]) // 絶対パスに変換
}
p.allgofiles = append(p.allgofiles, p.gofiles...) // コンパイル対象ファイルを追加
sort.Strings(p.allgofiles) // ソート
src/pkg/go/build/build.go
の readDir
関数も変更され、ビルドタグやOS/アーキテクチャの制約によって無視されるGoファイルを Package.IgnoredGoFiles
に適切に格納するようになりました。
そして、src/cmd/go/fix.go
, src/cmd/go/fmt.go
, src/cmd/go/get.go
, src/cmd/go/vet.go
の各コマンドは、処理対象のファイルを pkg.gofiles
から pkg.allgofiles
に変更しました。
意図:
go fix
, go fmt
, go vet
といったツールは、コードの品質と一貫性を保つために、プロジェクト内のすべてのGoファイルに対して実行されるべきです。しかし、以前のバージョンでは、ビルドタグによってコンパイル対象から除外されたファイルはこれらのツールの対象外となっていました。この変更により、ビルドタグの有無にかかわらず、すべてのGoファイルがこれらのツールの恩恵を受けられるようになり、コードベース全体の一貫性が向上します。
3. 一時バイナリの命名と go test -xxxprofile
での保持
変更ファイル: src/cmd/go/build.go
, src/cmd/go/run.go
, src/cmd/go/test.go
実装詳細:
src/cmd/go/build.go
の builder.action
メソッドでは、実行可能バイナリのターゲットパスを決定する際に、Package
構造体の新しいフィールド exeName
を参照するようになりました。
if a.link {
// ...
name := "a.out"
if p.exeName != "" { // p.exeName が設定されていればそれを使用
name = p.exeName
}
a.target = a.objdir + filepath.Join("exe", name) + exeSuffix // exe サブディレクトリに配置
}
src/cmd/go/run.go
の runRun
関数では、go run
で実行される一時バイナリの exeName
を、引数として渡された最初のGoファイル名に基づいて設定します。
var src string
if len(p.GoFiles) > 0 {
src = p.GoFiles[0]
} else {
src = p.CgoFiles[0]
}
p.exeName = src[:len(src)-len(".go")] // 拡張子を除いたファイル名を exeName に設定
src/cmd/go/test.go
では、testProfile
という新しいブール型フラグが導入され、-cpuprofile
などのプロファイリングフラグが指定された場合に true
に設定されます。そして、builder.test
メソッド内で、testC
(コンパイルのみ) または testProfile
が true
の場合に、生成されたテストバイナリ (pkg.test
) をカレントディレクトリにコピーするアクションが追加されました。
if testC || testProfile { // -c またはプロファイリングフラグが指定された場合
runAction = &action{
f: (*builder).install,
deps: []*action{pmainAction},
p: pmain,
target: filepath.Join(cwd, testBinary+exeSuffix), // カレントディレクトリにコピー
}
pmainAction = runAction // in case we are running the test
}
意図:
- 一時バイナリの命名:
go run
やgo test
で生成される一時バイナリがa.out
のような汎用的な名前ではなく、元のファイル名やテスト名に由来する名前を持つことで、ps
コマンドなどでプロセスリストを見た際に、どのプログラムが実行されているのかを識別しやすくなります。また、exe
サブディレクトリに配置することで、一時ファイルの管理がしやすくなります。 - プロファイリングバイナリの保持: プロファイリングデータ(例:
cpu.prof
)を解析する際には、そのデータが生成された実行可能バイナリが必要です。以前はテスト実行後に一時バイナリが削除されてしまうため、プロファイル解析が困難でした。この変更により、プロファイリングをより効果的に活用できるようになりました。
4. シンボリックリンクの扱いの改善と GOPATH
の ~
拒否
変更ファイル: src/pkg/go/build/build.go
, src/cmd/go/main.go
実装詳細:
src/pkg/go/build/build.go
の hasSubdir
関数は、filepath.EvalSymlinks
を使用して、引数として渡された root
と dir
の両方についてシンボリックリンクを展開し、展開されたパスと展開されていないパスの様々な組み合わせでサブディレクトリ関係をチェックするようになりました。これにより、シンボリックリンクが絡む複雑なファイルシステム構造でも、Goツールがパスを正しく解決できるようになります。
また、src/pkg/go/build/build.go
の Context.gopath
メソッドと src/cmd/go/main.go
の main
関数では、GOPATH
環境変数の各エントリに ~
(チルダ) が含まれている場合、Windows以外のOSではそのエントリを無視するか、エラーを出すようになりました。
// src/pkg/go/build/build.go
if strings.Contains(p, "~") && runtime.GOOS != "windows" {
// Path segments containing ~ on Unix are almost always
// users who have incorrectly quoted ~ while setting GOPATH,
// preventing it from expanding to $HOME.
// ...
continue // このパスを無視
}
// src/cmd/go/main.go
if strings.Contains(p, "~") && runtime.GOOS != "windows" {
fmt.Fprintf(os.Stderr, "go: GOPATH entry cannot contain shell metacharacter '~': %q\n", p)
os.Exit(2) // エラーを出して終了
}
意図:
- シンボリックリンク: 開発環境ではシンボリックリンクが頻繁に使用されます。Goツールがシンボリックリンクを正しく扱えないと、ビルドエラーやファイルの不認識といった問題が発生します。この修正により、Goツールのファイルシステム操作の堅牢性が向上しました。
GOPATH
の~
拒否: Unix系システムでは~
がユーザーのホームディレクトリを表すメタ文字ですが、シェルがGOPATH
環境変数を展開しない場合、~
はリテラルとして扱われ、Goツールが正しいパスを見つけられないという混乱が生じていました。この変更は、このような一般的な誤用を検出し、ユーザーに明確なエラーメッセージを提供することで、問題解決を支援します。Windowsでは~
が短いファイル名の一部として使用されるため、このチェックの対象外とされています。
これらのコアとなるコードの変更は、Goツールの内部的な堅牢性を高めるとともに、開発者が直面する可能性のある一般的な問題を解決し、よりスムーズな開発体験を提供することを目的としています。
関連リンク
このコミットは、Go言語の公式リポジトリ golang/go
の一部として行われました。関連する情報源としては、以下のものが挙げられます。
- Go言語の公式ドキュメント: Go言語のインストール、GOPATHの設定、Goコマンドの使用方法など、基本的な情報が網羅されています。
- Goコマンドのヘルプ: コマンドラインで
go help <command>
を実行することで、各Goコマンドの詳細なヘルプ情報を参照できます。- 例:
go help build
,go help install
,go help test
- 例:
- Go言語のIssue Tracker: コミットメッセージに記載されている
Fixes #XXXX
の形式の番号は、Go言語の内部Issue Trackerの課題番号を指しています。これらの課題は、通常、GitHubのIssueとは異なる内部システムで管理されています。- https://go.dev/issue/ (ただし、直接アクセスしても詳細な情報は得られない場合があります)
- Go言語のコードレビューシステム (Gerrit): コミットメッセージの最後に記載されている
https://golang.org/cl/7225074
は、Go言語のコードレビューシステムであるGerritにおける変更リスト(Change-List)へのリンクです。このリンクから、コミットがマージされる前の議論やレビューコメント、変更の経緯などを確認できます。
参考にした情報源リンク
- Go言語の公式ドキュメント:
- Go言語のGerritコードレビューシステム:
- Go言語のGitHubリポジトリ:
- Go Modules (GOPATHの代替としての現代的な依存関係管理):
- このコミットはGOPATH時代のものですが、現代のGo開発ではGo Modulesが主流です。GOPATHの理解はGoの歴史的背景とツールの進化を理解する上で重要です。
- https://go.dev/blog/using-go-modules
- Goのビルドタグに関するドキュメント:
- Goのテストとプロファイリングに関するドキュメント: