[インデックス 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)を実行します。
-
ビルドタグ (
+builddirectives):- 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のテストとプロファイリングに関するドキュメント: