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

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

このコミットは、Goコマンドラインツールにおけるgo vetツールの検出ロジックを更新し、その新しい場所をサポートするための変更です。また、expパッケージに対する特別な処理が不要になったため、そのコードを削除しています。これにより、go toolコマンドがvetツールを正しく見つけられるようになり、コードベースの保守性が向上します。

コミット

  • コミットハッシュ: 23dec8d1907f85d487b2adf3f0672c2cc02e4ce6
  • Author: Rob Pike r@golang.org
  • Date: Tue May 21 21:18:10 2013 -0700

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

https://github.com/golang/go/commit/23dec8d1907f85d487b2adf3f0672c2cc02e4ce6

元コミット内容

    cmd/go: support new location for vet
    Also delete the special case for exp, which isn't necessary any more.
    
    Fixes #5529.
    
    R=rsc, nightlyone
    CC=golang-dev
    https://golang.org/cl/9611048

変更の背景

このコミットの主な背景は、go vetツールの配置場所が変更されたことと、Go言語の実験的なパッケージを扱うexpプレフィックスに対する特別な処理が不要になったことです。

  1. go vetツールの場所変更: go vetはGoプログラムの静的解析を行う重要なツールです。以前はGoの標準ツールセットの一部として特定の場所に存在していましたが、このコミットの時点(2013年5月)で、go.toolsリポジトリ(code.google.com/p/go.tools、後にgolang.org/x/toolsへ移行)の一部として管理されるようになりました。この変更に伴い、goコマンドがvetツールを正しく見つけ、実行できるように、その検出ロジックを更新する必要がありました。
  2. expパッケージの特殊処理の削除: expプレフィックスを持つパッケージ(例: exp/foo)は、Goの初期段階で実験的な機能やライブラリを一時的に配置するために使用されていました。これらのパッケージは、Goの標準ライブラリに統合されるか、あるいはgolang.org/x/expのような独立したリポジトリに移動されることで、特別な扱いが不要になることがありました。このコミットでは、expプレフィックスに対する特別なパス解決ロジックがもはや必要ないと判断され、削除されました。これにより、コードベースが簡素化され、将来的な変更への対応が容易になります。
  3. Issue #5529の修正: コミットメッセージにはFixes #5529とありますが、Goの公式Issueトラッカーではこの番号のIssueに関する情報は見つかりませんでした。これは、内部的なトラッキング番号であるか、あるいは非常に古いIssueで現在はアクセスできないか、または特定のサブプロジェクトのIssueである可能性があります。しかし、コミット内容から推測すると、go vetの新しい場所への対応とexpの特殊処理の削除が、このIssueの解決に直接関連していると考えられます。

前提知識の解説

go toolコマンド

go toolコマンドは、Go言語のツールチェインに含まれる様々な補助ツールを実行するためのコマンドです。例えば、go tool vetgo tool pprofgo tool cgoなどがあります。これらのツールは、Goのビルドシステムや開発ワークフローをサポートするために設計されており、通常は$GOROOT/pkg/toolディレクトリに配置されます。go toolコマンドは、指定されたツール名に対応する実行可能ファイルを見つけ出し、引数を渡して実行します。

go vetツール

go vetは、Goプログラムのソースコードを静的に解析し、潜在的なバグや疑わしい構造を報告するツールです。例えば、Printfのフォーマット文字列と引数の不一致、到達不能なコード、構造体タグの誤りなどを検出できます。go vetはコンパイラでは検出できないような、実行時エラーにつながる可能性のある問題を早期に発見するのに役立ちます。Go 1.0のリリース当初から存在し、Go 1.12でanalysisパッケージを基盤として書き直され、拡張性が向上しました。

golang.org/x/expパッケージ

golang.org/x/expは、Go言語の実験的なライブラリや機能を集めたリポジトリです。これは、Goのメインリポジトリや他のサブモジュールに統合される可能性のある新しいアイデアや機能を試すためのテストベッドとして機能します。x/exp内のコードは、Go 1の互換性保証の対象外であり、予告なく変更、修正、または削除される可能性があります。そのため、本番環境での使用は推奨されません。このリポジトリは、以前はGoのメインリポジトリ内のpkg/expディレクトリに存在していましたが、バイナリGoインストールのユーザーも利用できるようにサブモジュールとして分離されました。

技術的詳細

このコミットは、主にsrc/cmd/go/pkg.gosrc/cmd/go/tool.goの2つのファイルに変更を加えています。

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

このファイルでは、Goのパッケージをロードする際のロジックが定義されています。変更点は以下の通りです。

  1. isGoToolマップの更新: isGoToolマップは、$GOROOT/pkg/toolにインストールされるGoプログラムのディレクトリをリストアップしています。 変更前:

    var isGoTool = map[string]bool{
    	"cmd/api":  true,
    	"cmd/cgo":  true,
    	"cmd/fix":  true,
    	"cmd/vet":  true,
    	"cmd/yacc": true,
    }
    

    変更後:

    var isGoTool = map[string]bool{
    	"cmd/api":                            true,
    	"cmd/cgo":                            true,
    	"cmd/fix":                            true,
    	"cmd/yacc":                           true,
    	"code.google.com/p/go.tools/cmd/vet": true,
    }
    

    ここで、"cmd/vet"が削除され、代わりに"code.google.com/p/go.tools/cmd/vet"が追加されています。これは、vetツールがGoのメインリポジトリ内のcmd/vetから、go.toolsリポジトリ内のcmd/vetに移動したことを反映しています。これにより、goコマンドはvetツールの新しいインポートパスを認識し、正しく処理できるようになります。

  2. exp/プレフィックスの特殊処理の削除: Package構造体のloadメソッド内で、パッケージのターゲットパスを決定するロジックが変更されています。 変更前:

    if p.Goroot && (isGoTool[p.ImportPath] || strings.HasPrefix(p.ImportPath, "exp/")) {
    	p.target = filepath.Join(gorootPkg, "tool", full)
    }
    

    変更後:

    if isGoTool[p.ImportPath] {
    	p.target = filepath.Join(gorootPkg, "tool", full)
    }
    

    strings.HasPrefix(p.ImportPath, "exp/")という条件が削除されました。これは、exp/プレフィックスを持つパッケージが$GOROOT/pkg/toolにインストールされるという特別なルールがもはや不要になったことを意味します。これにより、コードが簡素化され、expパッケージの管理方法の変更に対応しています。

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

このファイルは、go toolコマンドの実行ロジックを扱います。変更点は以下の通りです。

  1. tool関数の改善とエラーハンドリングの追加: tool関数は、指定されたツール名に対応する実行可能ファイルのパスを返します。 変更前は、ツールが見つからない場合のエラーメッセージがrunTool関数内で処理されていました。 変更後、tool関数自体がツールの存在チェックを行い、見つからない場合にはより詳細なエラーメッセージを出力し、プログラムを終了するようになりました。 特に注目すべきは、vetツールがgo.toolsリポジトリに移動したことを考慮したメッセージです。

    func tool(toolName string) string {
    	toolPath := filepath.Join(toolDir, toolName)
    	if toolIsWindows && toolName != "pprof" {
    		toolPath += toolWindowsExtension
    	}
    	// Give a nice message if there is no tool with that name.
    	if _, err := os.Stat(toolPath); err != nil {
    		if isInGoToolsRepo(toolName) {
    			fmt.Fprintf(os.Stderr, "go tool: no such tool %q; to install:\n\tgo install code.google.com/p/go.tools/cmd/%s\n", toolName, toolName)
    		} else {
    			fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName)
    		}
    		setExitStatus(3)
    		exit()
    	}
    	return toolPath
    }
    

    この変更により、ユーザーがgo tool vetを実行した際にvetが見つからない場合、go install code.google.com/p/go.tools/cmd/vetを実行してインストールするよう促す、より親切なメッセージが表示されるようになりました。

  2. isInGoToolsRepo関数の追加: この新しいヘルパー関数は、特定のツールがgo.toolsリポジトリに属しているかどうかを判定します。

    func isInGoToolsRepo(toolName string) bool {
    	switch toolName {
    	case "vet":
    		return true
    	}
    	return false
    }
    

    現在は"vet"のみを対象としていますが、将来的に他のツールがgo.toolsリポジトリに移動した場合に拡張できるようになっています。

  3. runTool関数の簡素化: tool関数がエラーハンドリングを行うようになったため、runTool関数内の重複するエラーチェックが削除されました。 変更前:

    toolPath := tool(toolName)
    // Give a nice message if there is no tool with that name.
    if _, err := os.Stat(toolPath); err != nil {
    	fmt.Fprintf(os.Stderr, "go tool: no such tool %q\\n", toolName)
    	setExitStatus(3)
    	return
    }
    

    変更後:

    toolPath := tool(toolName)
    if toolPath == "" { // tool関数がエラー時にexit()するため、この条件は通常到達しないが、念のため
    	return
    }
    

    これにより、コードの重複が解消され、ロジックがより明確になりました。

これらの変更は、go vetツールの新しい場所への移行と、expパッケージの特殊処理の廃止という、Goツールチェインの進化に対応するための重要なステップです。

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

diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
index b33d800bfb..a629d610f4 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -272,11 +272,11 @@ func reusePackage(p *Package, stk *importStack) *Package {
 // isGoTool is the list of directories for Go programs that are installed in
 // $GOROOT/pkg/tool.
 var isGoTool = map[string]bool{
-"cmd/api":  true,
-"cmd/cgo":  true,
-"cmd/fix":  true,
-"cmd/vet":  true,
-"cmd/yacc": true,
+"cmd/api":                            true,
+"cmd/cgo":                            true,
+"cmd/fix":                            true,
+"cmd/yacc":                           true,
+"code.google.com/p/go.tools/cmd/vet": true,
 }
 
 // expandScanner expands a scanner.List error into all the errors in the list.
@@ -329,7 +329,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
 		if p.build.BinDir != "" {
 			p.target = filepath.Join(p.build.BinDir, elem)
 		}
-"\t\tif p.Goroot && (isGoTool[p.ImportPath] || strings.HasPrefix(p.ImportPath, \"exp/\")) {"
+"\t\tif isGoTool[p.ImportPath] {"
 			p.target = filepath.Join(gorootPkg, "tool", full)
 		}
 		if p.target != "" && buildContext.GOOS == "windows" {
diff --git a/src/cmd/go/tool.go b/src/cmd/go/tool.go
index 299b94cb36..2d7db29152 100644
--- a/src/cmd/go/tool.go
+++ b/src/cmd/go/tool.go
@@ -45,12 +45,30 @@ func init() {
 
 const toolWindowsExtension = ".exe"
 
-func tool(name string) string {
-	p := filepath.Join(toolDir, name)
-	if toolIsWindows && name != "pprof" {
-		p += toolWindowsExtension
+func tool(toolName string) string {
+	toolPath := filepath.Join(toolDir, toolName)
+	if toolIsWindows && toolName != "pprof" {
+		toolPath += toolWindowsExtension
 	}
-	return p
+	// Give a nice message if there is no tool with that name.
+	if _, err := os.Stat(toolPath); err != nil {
+		if isInGoToolsRepo(toolName) {
+			fmt.Fprintf(os.Stderr, "go tool: no such tool %q; to install:\n\tgo install code.google.com/p/go.tools/cmd/%s\n", toolName, toolName)
+		} else {
+			fmt.Fprintf(os.Stderr, "go tool: no such tool %q\n", toolName)
+		}
+		setExitStatus(3)
+		exit()
+	}
+	return toolPath
+}
+
+func isInGoToolsRepo(toolName string) bool {
+	switch toolName {
+	case "vet":
+		return true
+	}
+	return false
 }
 
 func runTool(cmd *Command, args []string) {
@@ -70,10 +88,7 @@ func runTool(cmd *Command, args []string) {
 		}
 	}
 	toolPath := tool(toolName)
-	// Give a nice message if there is no tool with that name.
-	if _, err := os.Stat(toolPath); err != nil {
-		fmt.Fprintf(os.Stderr, "go tool: no such tool %q\\n", toolName)
-		setExitStatus(3)
+	if toolPath == "" {
 		return
 	}
 	if toolIsWindows && toolName == "pprof" {
@@ -86,7 +101,6 @@ func runTool(cmd *Command, args []string) {
 			return
 		}
 	}
-\n
 	if toolN {
 		fmt.Printf("%s %s\\n", toolPath, strings.Join(args[1:], " "))
 		return

コアとなるコードの解説

src/cmd/go/pkg.go

  • isGoToolマップの変更:

    • "cmd/vet": true, が削除され、代わりに "code.google.com/p/go.tools/cmd/vet": true, が追加されました。
    • これは、go vetツールがGoの標準配布物の一部としてではなく、go.toolsリポジトリ(後にgolang.org/x/toolsに移行)から提供されるようになったことを反映しています。goコマンドがvetツールを認識し、その新しいインポートパスに基づいて処理できるようにするための変更です。
  • exp/プレフィックスの特殊処理の削除:

    • if p.Goroot && (isGoTool[p.ImportPath] || strings.HasPrefix(p.ImportPath, "exp/")) { ... } の行が if isGoTool[p.ImportPath] { ... } に変更されました。
    • strings.HasPrefix(p.ImportPath, "exp/") という条件が削除されています。これは、exp/で始まる実験的なパッケージが$GOROOT/pkg/toolに特別にインストールされるというルールがもはや適用されないことを意味します。これにより、コードが簡素化され、expパッケージの管理方法の変更に対応しています。

src/cmd/go/tool.go

  • tool関数の変更:

    • 関数名がnameからtoolNameに、変数名がpからtoolPathに変更され、より明確になりました。
    • 最も重要な変更は、ツールが見つからない場合のエラーハンドリングがこの関数内に移動したことです。
    • os.Stat(toolPath)でツールの存在を確認し、エラーが発生した場合に処理を行います。
    • isInGoToolsRepo(toolName)という新しいヘルパー関数を呼び出し、ツールがgo.toolsリポジトリに属しているかどうかを判定します。
    • もしvetのようにgo.toolsリポジトリに属するツールが見つからない場合、go install code.google.com/p/go.tools/cmd/vetのように、インストール方法を具体的に指示するエラーメッセージを出力します。
    • それ以外のツールが見つからない場合は、一般的な「no such tool」メッセージを出力します。
    • エラー発生時にはsetExitStatus(3)で終了ステータスを設定し、exit()でプログラムを終了します。これにより、呼び出し元のrunTool関数で重複したエラーチェックが不要になります。
  • isInGoToolsRepo関数の追加:

    • func isInGoToolsRepo(toolName string) bool という新しい関数が追加されました。
    • この関数は、引数として受け取ったtoolName"vet"である場合にtrueを返します。
    • これは、vetツールがgo.toolsリポジトリに移動したことをgoコマンドが認識するためのメカニズムです。将来的に他のツールが同様に移動した場合、このswitch文にケースを追加することで対応できます。
  • runTool関数の簡素化:

    • tool関数がエラーハンドリングと終了処理を行うようになったため、runTool関数内のツールの存在チェックとエラーメッセージ出力のロジックが削除されました。
    • if toolPath == "" { return } という行が残されていますが、これはtool関数がエラー時にexit()するため、通常は到達しない防御的なコードです。

これらの変更により、goコマンドはgo vetツールの新しい場所を正しく認識し、ユーザーに対してより適切なガイダンスを提供できるようになりました。また、expパッケージの特殊処理を削除することで、コードベースの保守性が向上しています。

関連リンク

参考にした情報源リンク