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

[インデックス 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関数にエラーチェックを追加することで、このパニックを防ごうとしました。

変更の背景

このコミットは、以下の経緯で導入されました。

  1. 問題の発生 (Issue #7638): go getコマンドが、特定のインポートパス(例: cmd/something)を処理する際にパニックを起こすバグが存在しました。これは、go getツールがインポートパスの構造について誤った仮定をしていたため、予期せぬパス形式に遭遇した際に未処理のエラーやnilポインタ参照が発生し、クラッシュに至っていたと考えられます。
  2. 最初の修正 (CL 87300043): このパニックを修正するため、CL 87300043が導入されました。この変更は、src/cmd/go/get.godownload関数内で、パッケージのロード結果にエラーが含まれる場合に早期にエラーを捕捉し、errorf関数で出力して処理を中断するロジックを追加しました。
  3. 新たな問題の発生: しかし、CL 87300043の導入後、go get -d repo/path/...という形式のコマンドが正しく機能しなくなるという新たな問題が発覚しました。go get -dは、パッケージをビルドまたはインストールせずに依存関係をダウンロードする目的で使用されますが、元の修正が追加したエラーチェックが、この-dフラグの意図する挙動と衝突したためと考えられます。具体的には、go get -dは、たとえパッケージが見つからなくても、ダウンロードルートを特定しようと試みる場合がありますが、追加されたエラーチェックがこれを妨げ、早期に処理を終了させてしまった可能性があります。
  4. 今回のコミット (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 getgithub.com/foo/barリポジトリ全体をダウンロードしようとします。

しかし、CL 87300043が追加したエラーチェックは、loadPackagep.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.Errornilでない(つまりエラーが発生している)場合に、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言語のインポートパスに関する一般的な知識