Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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ツールはまだ成熟途上にあり、以下のような課題がありました。

  1. GOPATHの厳密な運用: GOPATH はGoプロジェクトの依存関係管理とビルドの基盤ですが、その運用ルールがまだ十分に確立されておらず、ユーザーが GOPATH 外で go install を実行しようとしたり、GOPATH に不適切なパス(例: ~ を含むパス)を設定したりするケースがありました。これらはビルドエラーや予期せぬ動作を引き起こすため、より明確なエラーメッセージや厳密なチェックが必要でした。
  2. コマンドの命名規則と実行: cmd/x/y のようなインポートパスは、Goの内部コマンドと衝突する可能性があり、混乱を招くことがありました。また、go run がテストファイルを直接実行しようとするなど、意図しない動作を防ぐ必要がありました。
  3. ビルドフラグの柔軟性: -ldflags のようなビルドフラグにスペースを含む引数を渡すことができないなど、コマンドライン引数のパースに制限がありました。これにより、複雑なビルド設定を行う際に不便が生じていました。
  4. ツールの連携と網羅性: go fix, go fmt, go vet といったGoツールは、ビルドタグによって無視されるファイルに対しては適用されませんでした。しかし、これらのツールはコードベース全体の一貫性を保つために、すべてのGoファイルに対して実行されるべきでした。
  5. テストツールの改善: go test はGo開発において非常に重要なツールですが、プロファイリング結果の解析のために一時バイナリを残す機能や、タイムアウト設定のバグ、シンボリックリンクの扱いの問題など、いくつかの改善点がありました。また、go list コマンドがビルドタグを考慮しないため、特定のビルド環境下でのパッケージ情報を正確に取得できない問題もありました。
  6. エラーメッセージの改善: ユーザーがGoツールを誤って使用した場合に、より分かりやすく、問題解決に役立つエラーメッセージを提供することが求められていました。特に go tool からの exit 1 出力は、ツールの内部エラーとユーザーの誤用によるエラーの区別がつきにくく、混乱を招いていました。

これらの背景から、Goツールの安定性、使いやすさ、そして開発者の生産性を向上させるために、本コミットで示される多岐にわたる修正が実施されました。

前提知識の解説

このコミットの変更内容を理解するためには、以下のGo言語およびGoツールの基本的な概念を理解しておく必要があります。

  1. GOPATH:

    • Go言語のワークスペースの概念です。Goのソースコード、コンパイルされたパッケージ、実行可能バイナリが配置されるディレクトリの集合を指します。
    • GOPATH 環境変数に設定されたパスは、Goがソースコードを検索し、依存関係を解決し、ビルド成果物をインストールする場所として機能します。
    • 通常、$HOME/go のようなディレクトリが設定されます。複数のパスを設定することも可能です(例: export GOPATH=$HOME/go:$HOME/work)。
    • GOPATH の各エントリは、src (ソースコード), pkg (コンパイル済みパッケージ), bin (実行可能バイナリ) のサブディレクトリを持ちます。
    • 重要性: Goのビルドシステムは GOPATH に強く依存しており、GOPATH の設定が正しくないと、パッケージのインポートやビルドが失敗します。
  2. Goコマンド (cmd/go):

    • Go言語の公式コマンドラインツールであり、Goプログラムの開発ライフサイクル全体を管理します。
    • go build: ソースコードをコンパイルして実行可能バイナリを生成します。
    • go install: ソースコードをコンパイルし、生成されたバイナリやパッケージを GOPATHbinpkg ディレクトリにインストールします。
    • 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)を実行します。
  3. ビルドタグ (+build directives):

    • Goソースファイルの先頭に記述される特殊なコメント行で、ファイルのコンパイル条件を制御します。
    • 例: // +build linux,amd64 は、このファイルがLinuxかつAMD64アーキテクチャの場合にのみコンパイルされることを意味します。
    • これにより、異なるOSやアーキテクチャ、または特定のビルド環境(例: テスト、デバッグ)向けに異なるコードを提供できます。
    • go build -tags "mytag" のように -tags フラグを使用して、特定のビルドタグを有効にできます。
  4. シンボリックリンク:

    • ファイルシステムにおける、別のファイルやディレクトリへの参照(ショートカットのようなもの)。
    • Goツールがファイルパスを解決する際に、シンボリックリンクを正しく追跡できないと、予期せぬエラーやファイルの不認識が発生する可能性があります。
  5. プロファイリング:

    • プログラムの実行中にパフォーマンスデータを収集するプロセス。
    • go test -cpuprofile=cpu.prof のように -cpuprofile などのフラグを使用すると、CPU使用率などのプロファイルデータがファイルに保存されます。
    • これらのプロファイルデータを解析するためには、通常、プロファイルデータが生成されたバイナリが必要になります。
  6. コマンドライン引数のパース:

    • シェルからプログラムに渡される引数を解析する処理。
    • スペースを含む引数を正しく扱うためには、引用符("')で囲むなどの特別な処理が必要です。

これらの概念は、Go開発における日常的な作業に密接に関連しており、本コミットの修正がGoツールの使いやすさと信頼性にどのように貢献しているかを理解する上で不可欠です。

技術的詳細

このコミットは、Goコマンドの内部実装に多岐にわたる変更を加えており、その技術的詳細は以下の通りです。

  1. cmd/x/y 形式のインポートパスの拒否:

    • src/cmd/go/pkg.goloadPackage 関数が変更され、cmd/ で始まるインポートパスが cmd/x/y の形式(つまり、cmd/ の後にスラッシュが2つ以上続く)である場合にエラーを返すようになりました。
    • これは、cmd/ がGoの標準コマンド(例: cmd/go, cmd/gofmt)のために予約されており、ユーザーがこのプレフィックスを持つ独自のパッケージを作成して混乱を避けるための措置です。
  2. go installGOPATH 外での実行拒否とエラーメッセージの改善:

    • src/cmd/go/build.gorunInstall 関数が修正され、GOPATH 外のディレクトリにあるパッケージに対して go install を実行しようとした際に、より明確なエラーメッセージ (go install: no install location for directory %s outside GOPATH) を表示するようになりました。
    • 以前は no install location for %s という一般的なメッセージでした。これにより、ユーザーは問題の原因を特定しやすくなります。
  3. 一時バイナリの命名規則の改善:

    • src/cmd/go/build.gobuilder.action メソッドが変更され、go rungo 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 サブディレクトリ内に配置されることで、名前の衝突を避ける工夫がされています。
  4. スペースを含む -ldflags 引数のパース:

    • src/cmd/go/build.gosplitQuotedFields 関数が追加されました。この関数は、スペースで区切られた文字列を解析し、単一引用符 (') または二重引用符 (") で囲まれた部分を単一のフィールドとして扱います。
    • src/cmd/go/testflag.gotestFlags 関数内で、-ldflags, -gcflags, -gccgoflags の値をパースする際に、この splitQuotedFields 関数が使用されるようになりました。
    • これにより、例えば -ldflags "-X main.version=1.0" のように、スペースを含む値をリンカフラグとして渡すことが可能になり、ビルドの柔軟性が向上しました。
  5. +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.goPackage 構造体に allgofiles フィールドが追加され、load メソッド内で IgnoredGoFilesgofiles を結合して allgofiles を生成するようになりました。
    • これにより、go fix, go fmt, go vet がコードベース全体に対して一貫して適用されるようになり、コード品質の維持に役立ちます。
  6. go run foo_test.go の拒否:

    • src/cmd/go/run.gorunRun 関数が修正され、_test.go で終わるファイルが go run コマンドの引数として渡された場合にエラー (go run: cannot run *_test.go files (%s)) を返すようになりました。
    • テストファイルは go test で実行されるべきであり、go run で直接実行することは意図されていません。この変更により、ユーザーの誤用を防ぎます。
  7. go tool 呼び出しからの exit 1 出力の抑制:

    • src/cmd/go/tool.gorunTool 関数が変更され、go tool が実行するサブコマンドが exit 1 で終了した場合でも、そのサブコマンドが既にエラーメッセージを出力していると判断される場合は、go tool 自身が重複してエラーメッセージを出力しないようになりました。
    • 具体的には、exec.ExitError であり、かつ Exited()true の場合は、go tool %s: %s の形式のエラーメッセージを出力しないように条件が追加されました。これにより、エラー出力が冗長になるのを防ぎ、ユーザーにとってよりクリーンな出力が提供されます。
  8. go test -xxxprofile によるバイナリの保持:

    • src/cmd/go/test.gorunTest 関数と builder.test 関数が修正され、-cpuprofile, -memprofile, -blockprofile などのプロファイリングフラグが指定された場合に、テスト実行後に生成されたテストバイナリ (pkg.test) が削除されずに残るようになりました。
    • testProfile という新しいフラグが導入され、プロファイリングフラグが指定された場合に true に設定されます。
    • testC || testProfile の条件で、テストバイナリをカレントディレクトリにコピーするアクションが実行されるようになり、プロファイル解析ツール(例: go tool pprof)がバイナリを見つけられるようになります。
  9. GOPATH 内の ~ の拒否 (Windows以外):

    • src/cmd/go/main.gomain 関数と src/pkg/go/build/build.gogopath メソッドが変更され、GOPATH のエントリに ~ (チルダ) が含まれている場合、Windows以外のOSではエラーを出すか、そのパスを無視するようになりました。
    • これは、Unix系システムで ~ がシェルによって $HOME に展開されることを期待して GOPATH に設定するユーザーがいたものの、シェルが展開しない環境(例: go env GOPATH)では ~ がリテラルとして扱われ、パスが正しく解決されないという混乱を避けるためです。Windowsでは ~ が短いファイル名(例: PROGRA~1)の一部として使用されるため、例外とされています。
  10. シンボリックリンクの扱いの改善:

    • src/pkg/go/build/build.gohasSubdir 関数が修正され、シンボリックリンクを含むパスの解決がより堅牢になりました。
    • filepath.EvalSymlinks を使用して、ルートパスとターゲットパスの両方でシンボリックリンクを展開し、展開されたパスと展開されていないパスの組み合わせでサブディレクトリ関係をチェックするようになりました。これにより、シンボリックリンクが絡むGoプロジェクトのビルドがより安定します。
  11. go test -timeout=0 の修正:

    • src/cmd/go/test.gorunTest 関数が修正され、testTimeout0 の場合にタイムアウト処理が適用されないように変更されました。
    • 以前は time.ParseDuration0 を有効な期間としてパースし、testKillTimeout1*time.Minute に設定されてしまう可能性がありました。dt > 0 の条件が追加されたことで、0 の場合はタイムアウトが無効になります。
  12. go list への -tags フラグの追加:

    • src/cmd/go/list.gosrc/cmd/go/doc.go が変更され、go list コマンドに -tags フラグが追加されました。
    • これにより、go build と同様に、go list も特定のビルドタグを考慮してパッケージ情報を表示できるようになり、条件付きコンパイルされたパッケージの情報を正確に取得できるようになりました。
  13. gccgo 出力ディレクトリの変更:

    • src/cmd/go/build.gosrc/cmd/go/pkg.go が変更され、gccgo コンパイラを使用する場合のパッケージインストールディレクトリが pkg/gccgo から pkg/gccgo_$GOOS_$GOARCH に変更されました。
    • これにより、異なるOSやアーキテクチャ向けの gccgo ビルド成果物が衝突することなく、適切に分離されるようになります。

これらの変更は、Goツールの内部ロジックを改善し、様々なエッジケースやユーザーの誤用に対応することで、Go開発体験全体の信頼性と効率性を高めています。

コアとなるコードの変更箇所

このコミットにおける主要なコード変更箇所は以下のファイルに集中しています。

  1. src/cmd/go/build.go:

    • splitQuotedFields 関数の追加: スペースを含む引用符付き文字列を正しく分割するためのロジックが実装されました。
    • stringsFlag 型の Set メソッドの変更: strings.Fields から splitQuotedFields を使用するように変更され、-ldflags などの引数でスペースを扱えるようになりました。
    • runInstall 関数でのエラーメッセージの改善: GOPATH 外のインストールに関するエラーメッセージがより具体的になりました。
    • builder.action メソッドでの一時バイナリの命名ロジックの変更: go rungo test で生成される実行可能ファイルの命名規則が改善され、exe サブディレクトリに配置されるようになりました。
    • builder.build メソッドでのターゲットディレクトリ作成ロジックの追加。
    • includeArgs メソッドでの gccgo パスの変更: gccgo_$GOOS_$GOARCH を使用するように修正。
  2. src/cmd/go/pkg.go:

    • Package 構造体への IgnoredGoFilesallgofiles, exeName フィールドの追加。
    • copyBuild メソッドでの IgnoredGoFiles のコピー。
    • load メソッドでの allgofiles の生成ロジックの追加: IgnoredGoFilesgofiles を結合。
    • load メソッドでの p.target の設定ロジックの修正。
    • swigDir メソッドでの gccgo パスの変更: gccgo_$GOOS_$GOARCH を使用するように修正。
    • loadPackage 関数での cmd/x/y 形式のインポートパスの拒否ロジックの追加。
  3. src/cmd/go/run.go:

    • runRun 関数での _test.go ファイルの実行拒否ロジックの追加。
    • p.exeName の設定: go run で実行される一時バイナリの名前を、最初のGoファイル名に基づいて設定。
  4. src/cmd/go/test.go:

    • testProfile グローバル変数の追加。
    • runTest 関数での testProfile フラグのチェックと、複数パッケージでのプロファイリングフラグ使用の拒否。
    • testTimeout0 の場合のタイムアウト処理の無効化。
    • builder.test 関数での一時バイナリの命名ロジックの変更: command-line-arguments の場合の elem の決定。
    • testC または testProfile の場合にテストバイナリをカレントディレクトリに残すロジックの追加。
  5. src/cmd/go/testflag.go:

    • testFlags 関数での -ldflags, -gcflags, -gccgoflags のパースに splitQuotedFields を使用するように変更。
    • プロファイリング関連のフラグ (-blockprofile, -cpuprofile, -memprofile) が指定された場合に testProfiletrue に設定。
  6. src/cmd/go/tool.go:

    • runTool 関数での go tool 呼び出しからの exit 1 出力の抑制ロジックの追加。
  7. 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.goPackage 構造体に allgofiles という新しいフィールドが追加されました。これは、ビルドタグによってコンパイル対象から除外されたGoファイル (IgnoredGoFiles) と、実際にコンパイルされるGoファイル (GoFiles, CgoFiles, TestGoFiles, XTestGoFiles) の両方を含む、パッケージ内のすべてのGoファイルの絶対パスのリストです。

src/cmd/go/pkg.goPackage.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.goreadDir 関数も変更され、ビルドタグや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.gobuilder.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.gorunRun 関数では、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 (コンパイルのみ) または testProfiletrue の場合に、生成されたテストバイナリ (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 rungo 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.gohasSubdir 関数は、filepath.EvalSymlinks を使用して、引数として渡された rootdir の両方についてシンボリックリンクを展開し、展開されたパスと展開されていないパスの様々な組み合わせでサブディレクトリ関係をチェックするようになりました。これにより、シンボリックリンクが絡む複雑なファイルシステム構造でも、Goツールがパスを正しく解決できるようになります。

また、src/pkg/go/build/build.goContext.gopath メソッドと src/cmd/go/main.gomain 関数では、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)へのリンクです。このリンクから、コミットがマージされる前の議論やレビューコメント、変更の経緯などを確認できます。

参考にした情報源リンク