[インデックス 19140] ファイルの概要
このコミットは、Go言語のツールチェインにおけるgo get
コマンドの挙動に関する以前の変更(CL 87300043)を元に戻すものです。具体的には、go get -d repo/path/...
コマンドが正しく機能しなくなる問題が発生したため、その原因となっていたエラーチェックの追加を取り消しています。
コミット
commit 85ddc68921052dbe214ed78b679e896d69f13db0
Author: Andrew Gerrand <adg@golang.org>
Date: Tue Apr 15 10:20:04 2014 +1000
undo CL 87300043 / 1dc800571456
This breaks "go get -d repo/path/...".
««« original CL description
cmd/go: do not miss an error if import path contains "cmd/something"
Fixes #7638
LGTM=rsc
R=rsc
CC=golang-codereviews
https://golang.org/cl/87300043
»»»
LGTM=bradfitz
R=golang-codereviews, bradfitz
CC=golang-codereviews
https://golang.org/cl/87890043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/85ddc68921052dbe214ed78b679e896d69f13db0
元コミット内容
このコミットが元に戻しているのは、CL 87300043という変更です。その元のコミットの目的は、「cmd/go: do not miss an error if import path contains "cmd/something"
」と説明されており、Goのgo get
コマンドがインポートパスに「cmd/something
」のような文字列を含む場合に発生するパニック(ランタイムクラッシュ)を修正することでした。これはIssue #7638として報告されていました。元の変更は、src/cmd/go/get.go
内のdownload
関数にエラーチェックを追加することで、このパニックを防ごうとしました。
変更の背景
このコミットは、以下の経緯で導入されました。
- 問題の発生 (Issue #7638):
go get
コマンドが、特定のインポートパス(例:cmd/something
)を処理する際にパニックを起こすバグが存在しました。これは、go get
ツールがインポートパスの構造について誤った仮定をしていたため、予期せぬパス形式に遭遇した際に未処理のエラーやnil
ポインタ参照が発生し、クラッシュに至っていたと考えられます。 - 最初の修正 (CL 87300043): このパニックを修正するため、CL 87300043が導入されました。この変更は、
src/cmd/go/get.go
のdownload
関数内で、パッケージのロード結果にエラーが含まれる場合に早期にエラーを捕捉し、errorf
関数で出力して処理を中断するロジックを追加しました。 - 新たな問題の発生: しかし、CL 87300043の導入後、
go get -d repo/path/...
という形式のコマンドが正しく機能しなくなるという新たな問題が発覚しました。go get -d
は、パッケージをビルドまたはインストールせずに依存関係をダウンロードする目的で使用されますが、元の修正が追加したエラーチェックが、この-d
フラグの意図する挙動と衝突したためと考えられます。具体的には、go get -d
は、たとえパッケージが見つからなくても、ダウンロードルートを特定しようと試みる場合がありますが、追加されたエラーチェックがこれを妨げ、早期に処理を終了させてしまった可能性があります。 - 今回のコミット (CL 87890043): 上記の新たな問題に対応するため、このコミット(CL 87890043)が導入されました。このコミットは、CL 87300043で行われた変更を完全に元に戻すことで、
go get -d
の機能を回復させました。これにより、元のパニック(Issue #7638)が再発する可能性はありますが、go get -d
の重要な機能が優先されました。
前提知識の解説
go get
コマンドと-d
フラグ
go get
: Go言語のパッケージ管理ツールであり、指定されたパッケージとその依存関係をリモートリポジトリ(GitHubなど)からダウンロードし、ビルドしてインストールするコマンドです。通常、Goのプロジェクトで外部ライブラリを使用する際に利用されます。go get -d
:-d
フラグは「download only」を意味します。このフラグを使用すると、go get
は指定されたパッケージとその依存関係のソースコードをダウンロードしますが、ビルドやインストールは行いません。これは、プロジェクトの依存関係を事前にフェッチしておきたい場合や、ビルドプロセスを別途制御したい場合に非常に便利です。例えば、CI/CDパイプラインで依存関係をキャッシュする目的などで利用されます。
Go言語におけるpanic
とエラーハンドリング
panic
: Go言語におけるpanic
は、プログラムの回復不可能なエラーを示すメカニズムです。panic
が発生すると、現在の関数の実行が中断され、遅延関数(defer
)が実行された後、呼び出し元の関数に制御が戻ります。このプロセスはスタックを遡り、最終的にプログラム全体がクラッシュします。panic
は通常、プログラマーの論理的な誤りや、予期せぬ致命的な状況(例:nil
ポインタ参照、配列の範囲外アクセス)で発生します。- エラーハンドリング: Go言語では、エラーは通常、関数の戻り値として明示的に扱われます。慣例として、関数は最後の戻り値として
error
型の値を返し、呼び出し元はそのエラーをチェックして適切に処理します。panic
は、この通常のエラーハンドリングでは対処できない、より深刻な状況のために予約されています。
Goのインポートパス
Goのインポートパスは、パッケージを一意に識別するための文字列です。通常、バージョン管理システムのURL(例: github.com/user/repo/package
)や、標準ライブラリのパス(例: fmt
, net/http
)に対応します。cmd/something
のようなパスは、Goの標準ツールや内部コマンドに関連する特殊なパス構造を持つことがあります。go get
のようなツールは、これらのパスを適切に解析し、リモートリポジトリの場所を特定する必要があります。
技術的詳細
このコミットの技術的詳細は、go get
コマンドの内部ロジック、特にパッケージのダウンロードとエラー処理に関するものです。
元のCL 87300043は、src/cmd/go/get.go
ファイル内のdownload
関数に以下のコードを追加しました。
if p.Error != nil {
errorf("%s", p.Error)
return
}
このコードは、loadPackage
関数が返したp
(パッケージ情報)にエラーが含まれている場合、即座にerrorf
関数を呼び出してエラーメッセージを出力し、download
関数の実行を中断するものでした。
この変更がgo get -d repo/path/...
を壊した理由は、go get -d
の特定のユースケースと関連しています。通常、go get -d
は、指定されたパスが直接ビルド可能なパッケージでなくても、そのパスが属するリポジトリのルートを特定し、そのリポジトリ全体をダウンロードしようとします。例えば、go get -d github.com/foo/bar/...
のように、ワイルドカードを含むパスを指定した場合、go get
はgithub.com/foo/bar
リポジトリ全体をダウンロードしようとします。
しかし、CL 87300043が追加したエラーチェックは、loadPackage
がp.Error
を返した場合に、download
関数の残りのロジック(特にダウンロードルートの特定と実際のダウンロード処理)が実行されるのを妨げました。go get -d
のシナリオでは、loadPackage
が一時的にエラーを返すことがあっても、その後の処理でダウンロードルートを特定できる場合があり、そのエラーは致命的ではないと見なされるべきでした。早期のエラー終了が、この期待される挙動を阻害したため、go get -d
が機能しなくなったのです。
このコミットは、上記のif p.Error != nil
ブロックを削除することで、download
関数が以前の挙動に戻り、p.Error
が存在する場合でも、ダウンロードルートの特定やその他の処理を続行できるようにしました。これにより、go get -d
の機能が回復しました。
コアとなるコードの変更箇所
--- a/src/cmd/go/get.go
+++ b/src/cmd/go/get.go
@@ -143,10 +143,6 @@ var downloadRootCache = map[string]bool{}\n // for the package named by the argument.\n func download(arg string, stk *importStack, getTestDeps bool) {\n p := loadPackage(arg, stk)\n-\tif p.Error != nil {\n-\t\terrorf("%s", p.Error)\n-\t\treturn\n-\t}\n \n // There's nothing to do if this is a package in the standard library.\n if p.Standard {\n```
## コアとなるコードの解説
上記の`diff`は、`src/cmd/go/get.go`ファイルの`download`関数から4行のコードが削除されたことを示しています。
削除されたコードは以下の部分です。
```go
if p.Error != nil {
errorf("%s", p.Error)
return
}
このコードブロックは、loadPackage(arg, stk)
の呼び出し直後に配置されていました。loadPackage
関数は、指定されたインポートパスarg
に対応するパッケージ情報をロードしようとします。もしロード中に何らかの問題(例: パッケージが見つからない、パスが不正など)が発生した場合、p.Error
フィールドにエラー情報が設定されます。
削除されたif
文は、このp.Error
がnil
でない(つまりエラーが発生している)場合に、errorf
関数を使ってエラーメッセージを標準エラー出力に表示し、直ちにdownload
関数の実行を終了させていました。
このコミットによってこのコードが削除されたことで、loadPackage
がエラーを返した場合でも、download
関数は処理を続行するようになります。これは、go get -d
のようなシナリオにおいて、loadPackage
が一時的にエラーを返しても、その後のロジックでダウンロードすべきリポジトリのルートを特定できる場合に、処理を中断せずに続行できるようにするために必要でした。この変更により、go get -d repo/path/...
が期待通りに動作するようになりました。
関連リンク
参考にした情報源リンク
- 元の変更(CL 87300043)のGerritリンク:
https://golang.org/cl/87300043
(直接アクセスは困難だが、コミットメッセージに記載) - この変更(CL 87890043)のGerritリンク:
https://golang.org/cl/87890043
(直接アクセスは困難だが、コミットメッセージに記載) - 関連するGitHub Issue #7638に関する情報:
cmd/go: go get panics when import path contains 'cmd/something'
(Web検索により特定) go get -d
コマンドに関する情報 (Web検索により特定)- Go言語における
panic
とエラーハンドリングに関する一般的な知識 - Go言語のインポートパスに関する一般的な知識