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

[インデックス 17537] ファイルの概要

このコミットは、Go言語のコマンドラインツール cmd/go における go get コマンドの挙動を修正するものです。具体的には、ワイルドカード (...) を使用してパッケージを取得する際にエラーが発生した場合、エラーメッセージに実際のパッケージパスとワイルドカードの両方を報告するように改善しています。これにより、ユーザーはどのパッケージで問題が発生したのか、そしてそれがワイルドカード指定によるものなのかをより明確に理解できるようになります。

コミット

commit a547ad6ac0d7cb83198b1144ae0e87442b746fd9
Author: Ian Lance Taylor <iant@golang.org>
Date:   Tue Sep 10 11:27:29 2013 -0700

    cmd/go: report real package in errors for go get with wildcard
    
    Fixes #5054.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/13609043
---
 src/cmd/go/get.go | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/cmd/go/get.go b/src/cmd/go/get.go
index 83244b2531..b6a3d5ba05 100644
--- a/src/cmd/go/get.go
+++ b/src/cmd/go/get.go
@@ -157,6 +157,7 @@ func download(arg string, stk *importStack, getTestDeps bool) {\n 
 	pkgs := []*Package{p}\n 	wildcardOkay := len(*stk) == 0\n+\tisWildcard := false\n 
 	// Download if the package is missing, or update if we\'re using -u.\n 	if p.Dir == "" || *getU {\n@@ -179,6 +180,7 @@ func download(arg string, stk *importStack, getTestDeps bool) {\n 			} else {\n 				args = matchPackages(arg)\n 			}\n+\t\t\tisWildcard = true\n \t\t}\n \n 		// Clear all relevant package cache entries before\n@@ -218,6 +220,12 @@ func download(arg string, stk *importStack, getTestDeps bool) {\n 			}\n 		}\n \n+\t\tif isWildcard {\n+\t\t\t// Report both the real package and the\n+\t\t\t// wildcard in any error message.\n+\t\t\tstk.push(p.ImportPath)\n+\t\t}\n+\n \t\t// Process dependencies, now that we know what they are.\n \t\tfor _, dep := range p.deps {\n \t\t\t// Don\'t get test dependencies recursively.\n@@ -233,6 +241,10 @@ func download(arg string, stk *importStack, getTestDeps bool) {\n \t\t\t\tdownload(path, stk, false)\n \t\t\t}\n \t\t}\n+\n+\t\tif isWildcard {\n+\t\t\tstk.pop()\n+\t\t}\n \t}\n }\n \n```

## GitHub上でのコミットページへのリンク

[https://github.com/golang/go/commit/a547ad6ac0d7cb83198b1144ae0e87442b746fd9](https://github.com/golang/go/commit/a547ad6ac0d7cb83198b1144ae0e87442b746fd9)

## 元コミット内容

cmd/go: report real package in errors for go get with wildcard

Fixes #5054.

R=golang-dev, rsc CC=golang-dev https://golang.org/cl/13609043


## 変更の背景

この変更は、Go Issue #5054 を修正するために行われました。Goの `go get` コマンドは、指定されたパッケージとその依存関係をダウンロードしてインストールするために使用されます。このコマンドは、`go get example.com/foo/...` のようにワイルドカード (`...`) を使用して、特定のパスプレフィックスに一致する複数のパッケージを一度に取得する機能を持っています。

しかし、ワイルドカードを使用して複数のパッケージを取得しようとした際に、その中のいずれかのパッケージでエラー(例えば、リポジトリが見つからない、ビルドに失敗する、依存関係の解決に失敗するなど)が発生した場合、従来のエラーメッセージでは、どの具体的なパッケージが問題を引き起こしたのかが不明瞭でした。エラーメッセージにはワイルドカード指定全体が表示されるだけで、ユーザーは問題の根本原因を特定するために手動で各パッケージを調査する必要がありました。

この問題は、特に大規模なプロジェクトや多数のサブパッケージを持つリポジトリを扱う際に、デバッグの労力を増大させる要因となっていました。ユーザーは、エラーが発生した際に、ワイルドカード指定によって取得しようとした多数のパッケージの中から、実際に問題が発生した特定のパッケージを迅速に特定できるような、より詳細なエラー報告を求めていました。このコミットは、このユーザーエクスペリエンスの課題を解決し、デバッグプロセスを効率化することを目的としています。

## 前提知識の解説

このコミットを理解するためには、以下のGo言語および`go`コマンドに関する基本的な知識が必要です。

*   **`go get` コマンド**: Go言語のパッケージ管理ツールの一部であり、リモートリポジトリからGoパッケージをダウンロードし、ローカルの`GOPATH`にインストールするために使用されます。また、パッケージの依存関係も自動的に解決してダウンロードします。
*   **パッケージパス**: Go言語では、パッケージは通常、そのソースコードが配置されているリポジトリのパスによって識別されます。例えば、`github.com/user/repo/package` のようになります。
*   **ワイルドカード (`...`)**: `go get` コマンドにおいて、パッケージパスの末尾に `...` を追加することで、そのパスプレフィックスに一致するすべてのサブパッケージを対象とすることができます。例えば、`example.com/foo/...` は `example.com/foo` 以下のすべてのパッケージ(`example.com/foo/bar`, `example.com/foo/baz/qux` など)を対象とします。
*   **`importStack`**: `go`コマンド内部で使用されるデータ構造で、パッケージのインポートパスのスタックを管理します。これは、依存関係の解決やエラー報告の際に、どのパッケージがどのパッケージをインポートしようとしていたか、というコンテキストを追跡するために利用されます。エラーメッセージにおいて、このスタックは「インポートパス」として表示され、エラーが発生した場所までのパッケージの連鎖を示します。
*   **`Package` 構造体**: `go`コマンド内部でGoパッケージのメタデータを表現するために使用される構造体です。これには、パッケージのインポートパス (`ImportPath`) やソースコードのディレクトリ (`Dir`) などの情報が含まれます。
*   **エラーハンドリング**: Go言語では、エラーは関数の戻り値として明示的に扱われます。`go`コマンドも同様に、パッケージのダウンロードやビルド中に発生した問題をエラーとして報告します。

このコミットの変更は、`go get` コマンドがワイルドカードを処理する際の内部ロジック、特にエラー報告のメカニズムに焦点を当てています。`importStack`に一時的に実際のパッケージパスを追加することで、エラー発生時にそのパッケージパスがエラーメッセージのコンテキストに含まれるようにしています。

## 技術的詳細

このコミットは、`src/cmd/go/get.go` ファイル内の `download` 関数に修正を加えています。`download` 関数は、`go get` コマンドの主要なロジックを担っており、指定されたパッケージのダウンロードと依存関係の解決を行います。

変更の核心は、ワイルドカード (`...`) を使用してパッケージが指定された場合に、エラーメッセージのコンテキストを改善することにあります。

1.  **`isWildcard` フラグの導入**:
    `download` 関数内に `isWildcard` という新しいブーリアン変数が導入されました。このフラグは、現在の `download` 呼び出しがワイルドカード指定の結果として行われたものであるかどうかを追跡します。
    `args = matchPackages(arg)` の呼び出し(ワイルドカードパターンに一致するパッケージを解決する部分)の直後に `isWildcard = true` が設定されます。これにより、ワイルドカードが展開されて個々のパッケージが処理される際に、その処理がワイルドカードに起因するものであることが識別されます。

2.  **`importStack` の操作**:
    Goのエラー報告メカニズムでは、`importStack` を使用して、エラーが発生した際のインポートパスの連鎖を構築します。これにより、ユーザーはどのパッケージがどのパッケージをインポートしようとしてエラーになったのかを追跡できます。
    このコミットでは、`isWildcard` が `true` の場合、つまりワイルドカードによってパッケージが処理されている場合に、以下の操作が行われます。
    *   **`stk.push(p.ImportPath)`**: パッケージの依存関係を処理するループに入る直前に、現在のパッケージの実際のインポートパス (`p.ImportPath`) が `importStack` (`stk`) にプッシュされます。これにより、このパッケージでエラーが発生した場合、その実際のパスがエラーメッセージのスタックトレースに含まれるようになります。コメントには「Report both the real package and the wildcard in any error message.」とあり、これが目的であることを示しています。
    *   **`stk.pop()`**: パッケージの依存関係の処理が完了した後、`importStack` からプッシュされたパスがポップされます。これは、`importStack` の状態をクリーンアップし、次の処理に影響を与えないようにするためです。これにより、`download` 関数の呼び出しスタックが正しく維持されます。

この変更により、例えば `go get example.com/foo/...` を実行し、`example.com/foo/bar` パッケージでエラーが発生した場合、エラーメッセージには `example.com/foo/...` という元のワイルドカード指定だけでなく、`example.com/foo/bar` という具体的なパッケージパスも含まれるようになります。これにより、ユーザーはエラーの発生源を迅速に特定できるようになります。

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

変更は `src/cmd/go/get.go` ファイルの `download` 関数に集中しています。

1.  **`isWildcard` 変数の追加**:
    ```go
    // src/cmd/go/get.go (抜粋)
    func download(arg string, stk *importStack, getTestDeps bool) {
        // ...
        pkgs := []*Package{p}
        wildcardOkay := len(*stk) == 0
        isWildcard := false // <-- ここが追加
        // ...
    }
    ```

2.  **`isWildcard` の設定**:
    ワイルドカードが展開されるロジック内で `isWildcard` が `true` に設定されます。
    ```go
    // src/cmd/go/get.go (抜粋)
    // ...
    		} else {
    			args = matchPackages(arg)
    		}
    		isWildcard = true // <-- ここが追加
    // ...
    ```

3.  **`importStack` の `push` と `pop`**:
    パッケージの依存関係処理の前後で `importStack` を操作するロジックが追加されます。
    ```go
    // src/cmd/go/get.go (抜粋)
    // ...
    		}
    	}

    	if isWildcard { // <-- ここが追加
    		// Report both the real package and the
    		// wildcard in any error message.
    		stk.push(p.ImportPath) // <-- ここが追加
    	}

    	// Process dependencies, now that we know what they are.
    	for _, dep := range p.deps {
    		// Don't get test dependencies recursively.
    // ...
    	}

    	if isWildcard { // <-- ここが追加
    		stk.pop() // <-- ここが追加
    	}
    }
    ```

## コアとなるコードの解説

`download` 関数は、`go get` コマンドの中核をなす関数であり、パッケージのダウンロードと依存関係の解決を担当します。

*   **`isWildcard := false`**:
    `download` 関数の冒頭で `isWildcard` という新しいブーリアン変数が導入され、初期値は `false` に設定されます。これは、デフォルトでは現在のダウンロード操作がワイルドカードによるものではないことを示します。

*   **`isWildcard = true` の設定**:
    `download` 関数内で、`matchPackages(arg)` が呼び出される箇所があります。この関数は、`go get` に渡された引数がワイルドカードパターンを含んでいる場合に、そのパターンに一致する実際のパッケージパスのリストを生成します。`matchPackages` が呼び出された後、`isWildcard` が `true` に設定されます。これは、現在の `download` 呼び出しが、ワイルドカードによって展開された個々のパッケージの処理の一部であることを示します。

*   **`if isWildcard { stk.push(p.ImportPath) }`**:
    このブロックは、`isWildcard` が `true` の場合に実行されます。`stk` は `importStack` のインスタンスであり、Goコマンドがエラーメッセージを生成する際に使用するインポートパスの履歴を保持します。`p.ImportPath` は現在処理中のパッケージの実際のインポートパスです。
    `stk.push(p.ImportPath)` は、この実際のパッケージパスを `importStack` に追加します。これにより、もしこのパッケージの処理中にエラーが発生した場合、エラーメッセージのスタックトレースにこの具体的なパッケージパスが含まれるようになります。これは、ユーザーがワイルドカード指定のどの部分で問題が発生したのかを正確に把握できるようにするための重要なステップです。

*   **`if isWildcard { stk.pop() }`**:
    このブロックも `isWildcard` が `true` の場合に実行されます。`stk.pop()` は、以前に `push` された `p.ImportPath` を `importStack` から削除します。これは、`download` 関数の呼び出しが完了した後に `importStack` をクリーンアップし、次の処理やエラー報告に不要なコンテキストが残らないようにするために重要です。これにより、`importStack` の整合性が保たれ、正確なエラー報告が保証されます。

これらの変更により、`go get` コマンドがワイルドカードを使用してパッケージを処理する際に、より詳細で役立つエラーメッセージを提供するようになり、デバッグの効率が向上します。

## 関連リンク

*   **Go Issue #5054**: [https://github.com/golang/go/issues/5054](https://github.com/golang/go/issues/5054)
*   **Gerrit Change-Id (CL) 13609043**: [https://golang.org/cl/13609043](https://golang.org/cl/13609043)

## 参考にした情報源リンク

*   Go言語の公式ドキュメント (`go get` コマンドに関する情報)
*   Go言語のソースコード (`src/cmd/go/get.go` の変更履歴)
*   Go Issue Tracker (`#5054` の議論)
*   Gerrit Code Review (`CL 13609043` の詳細)
*   Go言語のパッケージ管理に関する一般的な情報源