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

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

このコミットは、Go言語のコマンドラインツール cmd/go におけるエラーハンドリングの改善に関するものです。具体的には、インポートパスに "cmd/something" のような文字列が含まれる場合に、go get コマンドがエラーを適切に報告しない問題を修正しています。

コミット

commit eeb87c3660932cb0dcc6db2e3784a66b6d06a82a
Author: Jan Ziak <0xe2.0x9a.0x9b@gmail.com>
Date:   Tue May 27 23:58:03 2014 -0400

    cmd/go: do not miss an error if import path contains "cmd/something"
    
    Fixes #7638
    
    LGTM=rsc
    R=rsc, adg, robert.hencke, bradfitz
    CC=golang-codereviews
    https://golang.org/cl/89280043

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

https://github.com/golang/go/commit/eeb87c3660932cb0dcc6db2e3784a66b6d06a82a

元コミット内容

cmd/go: do not miss an error if import path contains "cmd/something"

このコミットは、インポートパスに "cmd/something" が含まれる場合にエラーを見逃さないようにするものです。

変更の背景

Go言語では、cmd/... で始まるインポートパスはGoの標準コマンドのために予約されています。これは、ユーザーが誤って標準ライブラリの内部パッケージと同じ名前のパッケージを作成し、それが衝突するのを防ぐための設計上の制約です。

しかし、このコミット以前の cmd/go ツールでは、go get コマンドがパッケージをダウンロードする際に、インポートパスがこの予約されたパターンに一致する場合でも、エラーを適切に伝播させない問題がありました。具体的には、loadPackage 関数が PackageError を返しても、download 関数がそのエラーを「ソフトエラー」として扱い、無視してしまうことがありました。これにより、ユーザーは無効なインポートパスを使用しているにもかかわらず、エラーメッセージを受け取ることができず、デバッグが困難になる可能性がありました。

この問題は、GoのIssue #7638として報告され、このコミットによって修正されました。

前提知識の解説

  • cmd/go ツール: Go言語のビルド、テスト、パッケージ管理などを行うための主要なコマンドラインツールです。go get はこのツールの一部で、リモートリポジトリからパッケージをダウンロードしてインストールするために使用されます。
  • インポートパス: Goのソースコード内でパッケージを識別するために使用される文字列です。例えば、"fmt""github.com/user/repo/package" などがあります。
  • cmd/... 予約語: Go言語の設計において、cmd/ で始まるインポートパスはGoの標準コマンド(例: cmd/go, cmd/vet など)のために予約されています。これは、ユーザーが独自のパッケージでこの命名規則を使用することを防ぎ、標準コマンドとの名前の衝突を避けるためのものです。
  • PackageError: cmd/go ツール内部で使用されるエラー構造体です。パッケージのロードや解析中に発生したエラーに関する情報(エラーメッセージ、スタックトレースなど)を保持します。
  • ソフトエラーとハードエラー: このコミットで導入された概念です。
    • ソフトエラー (soft error): cmd/go ツールが処理を続行できるような、比較的軽微なエラー。場合によっては無視されることがあります。
    • ハードエラー (hard error): cmd/go ツールが処理を続行できないような、致命的なエラー。通常、即座にエラーとして報告され、処理が中断されます。このコミットでは、cmd/... のインポートパスに関するエラーをハードエラーとして扱うように変更しています。

技術的詳細

このコミットの主要な変更点は、PackageError 構造体に hard フィールドを追加し、特定のタイプのエラー(特に予約された cmd/... インポートパスの使用に関するエラー)を「ハードエラー」として明示的にマークするようにしたことです。そして、download 関数が loadPackage から返された PackageError をチェックし、それが hard エラーである場合には、そのエラーを即座に報告して処理を中断するように変更されました。

具体的な変更は以下の2つのファイルで行われています。

  1. src/cmd/go/pkg.go:

    • PackageError 構造体に bool 型の hard フィールドが追加されました。このフィールドは、エラーがソフトエラーかハードエラーかを示します。
    • loadPackage 関数内で、インポートパスが cmd/... のパターンに一致する場合に生成される PackageError オブジェクトの hard フィールドが true に設定されるようになりました。これにより、この種のエラーがハードエラーとして識別されます。
  2. src/cmd/go/get.go:

    • download 関数内で、loadPackage から返された Package オブジェクトの Error フィールド(PackageError 型)がチェックされます。
    • もし p.Errornil でなく、かつ p.Error.hardtrue であれば、errorf 関数を使ってエラーメッセージを標準エラー出力に表示し、return することで関数の実行を即座に終了します。これにより、ハードエラーが適切に処理され、無視されることがなくなります。

この変更により、cmd/... のような予約されたインポートパスを使用しようとした際に、go get コマンドは明確なエラーメッセージをユーザーに提供し、不適切な動作を防ぐことができるようになりました。

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

src/cmd/go/get.go

--- a/src/cmd/go/get.go
+++ b/src/cmd/go/get.go
@@ -140,6 +140,10 @@ var downloadRootCache = map[string]bool{}
 // for the package named by the argument.
 func download(arg string, stk *importStack, getTestDeps bool) {
  	p := loadPackage(arg, stk)
+	if p.Error != nil && p.Error.hard {
+		errorf("%s", p.Error)
+		return
+	}
  
  	// There's nothing to do if this is a package in the standard library.
  	if p.Standard {

src/cmd/go/pkg.go

--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -139,6 +139,7 @@ type PackageError struct {
  	Pos           string   // position of error
  	Err           string   // the error itself
  	isImportCycle bool     // the error is an import cycle
+	hard          bool     // whether the error is soft or hard; soft errors are ignored in some places
  }
  
  func (p *PackageError) Error() string {
@@ -715,6 +716,7 @@ func loadPackage(arg string, stk *importStack) *Package {
  			\t\t\t\tError: &PackageError{
  			\t\t\t\t\tImportStack: stk.copy(),
  			\t\t\t\t\tErr:         fmt.Sprintf("invalid import path: cmd/... is reserved for Go commands"),
+				\t\t\t\thard:        true,
  			\t\t\t\t},\n \t\t\t}\n \t\t\treturn p\n

コアとなるコードの解説

src/cmd/go/get.go の変更

download 関数は、指定されたインポートパスのパッケージをダウンロードする役割を担っています。この関数はまず loadPackage を呼び出してパッケージ情報をロードします。

追加されたコードブロック:

	if p.Error != nil && p.Error.hard {
		errorf("%s", p.Error)
		return
	}

この if 文は、loadPackage がエラーを返した場合に、そのエラーが hard エラーであるかどうかをチェックします。

  • p.Error != nil: loadPackage が何らかのエラーを返したことを確認します。
  • p.Error.hard: 返されたエラーが PackageError 型であり、その hard フィールドが true であることを確認します。 もし両方の条件が真であれば、errorf 関数(Goツールの内部エラー報告関数)を使ってエラーメッセージを表示し、return で関数の実行を終了します。これにより、cmd/... のような予約されたパスに関するエラーが即座にユーザーに報告され、go get の処理が中断されるようになります。

src/cmd/go/pkg.go の変更

  1. PackageError 構造体への hard フィールドの追加:

    type PackageError struct {
    	// ... 既存のフィールド ...
    	hard          bool     // whether the error is soft or hard; soft errors are ignored in some places
    }
    

    hard フィールドは bool 型で、このエラーが「ハード」なエラー(無視すべきではないエラー)であるかどうかを示します。コメントにもあるように、ソフトエラーは一部の場所で無視される可能性があります。

  2. loadPackage 関数内での hard フィールドの設定:

    // ...
    			Error: &PackageError{
    				ImportStack: stk.copy(),
    				Err:         fmt.Sprintf("invalid import path: cmd/... is reserved for Go commands"),
    				hard:        true, // ここでhardをtrueに設定
    			},
    // ...
    

    loadPackage 関数は、インポートパスが cmd/... の予約されたパターンに一致する場合に、invalid import path: cmd/... is reserved for Go commands というエラーメッセージを含む PackageError を生成します。このコミットでは、この特定の PackageErrorhard フィールドを true に明示的に設定しています。これにより、このエラーが download 関数で適切に捕捉され、処理が中断されるようになります。

これらの変更により、Goツールは予約されたインポートパスの誤用に対してより堅牢なエラーハンドリングを提供するようになりました。

関連リンク

  • Go Issue #7638 (このコミットが修正した問題): https://github.com/golang/go/issues/7638 (※注: 2014年当時のGoのIssueトラッカーのリンクであり、現在のGitHubリポジトリのIssue番号とは直接対応しない可能性がありますが、コミットメッセージに記載されているため関連情報として記載します。)
  • Go Gerrit Change-ID 89280043 (このコミットのGerritレビューページ): https://golang.org/cl/89280043

参考にした情報源リンク

  • Go言語のソースコード (特に src/cmd/go/get.go および src/cmd/go/pkg.go の該当コミット時点のバージョン)
  • Go言語の公式ドキュメント (Goコマンドの動作に関する一般的な情報)
  • Go言語のIssueトラッカー (Issue #7638に関する情報)
  • Go Gerrit Code Review (Change-ID 89280043に関する情報)