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

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

このコミットは、Goコマンドラインツール(cmd/go)の機能改善とユーザビリティ向上を目的としています。具体的には、go docコマンドがgodocツールを呼び出す際の挙動を改善し、go docgo fmtgo vetの各コマンドに-n(実行されるコマンドを表示)と-x(実行中のコマンドを表示)のデバッグフラグを追加しています。さらに、不明なコマンドが入力された際のエラーメッセージをより簡潔にしています。

コミット

commit 0c8f4694282aebd131fee8400cfc6ca0b0c41775
Author: Russ Cox <rsc@golang.org>
Date:   Tue May 15 12:53:57 2012 -0400

    cmd/go: invoke godoc with import path when possible
    Also add -n -x flags to doc, fmt, vet.
    Also shorten unknown command error.
    
    Fixes #3612.
    Fixes #3613.
    
    R=golang-dev, bradfitz, r
    CC=golang-dev
    https://golang.org/cl/6211053

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

https://github.com/golang/go/commit/0c8f4694282aebd131fee8400cfc6ca0b0c41775

元コミット内容

cmd/go: invoke godoc with import path when possible
Also add -n -x flags to doc, fmt, vet.
Also shorten unknown command error.

Fixes #3612.
Fixes #3613.

変更の背景

このコミットは、Goツールの使いやすさとデバッグ能力を向上させるために行われました。

  1. go docgodoc呼び出し改善: 以前のgo docコマンドは、godocツールを呼び出す際に、常にパッケージのローカルディレクトリパスを渡していました。しかし、Goのパッケージはインポートパス(例: github.com/user/repo/package)によって識別されるため、ローカルディレクトリパスだけではgodocが正確なドキュメントを特定できない場合がありました。特に、GOPATH外のモジュールや、リモートリポジトリから取得したパッケージのドキュメントを参照する際に問題が生じる可能性がありました。この変更により、可能な場合はインポートパスを使用してgodocを呼び出すことで、より堅牢で正確なドキュメント参照が可能になります。

  2. -nおよび-xフラグの追加: goコマンドは、内部で他のGoツール(godoc, gofmt, go vetなど)を呼び出して処理を実行します。開発者がこれらの内部的なコマンド呼び出しを理解し、デバッグできるようにするために、-n(実行されるコマンドを表示するが実行しない)と-x(実行されるコマンドを表示し、実行する)フラグが導入されました。これは、ビルドシステムやスクリプトのデバッグにおいて非常に一般的な機能であり、Goツールにも同様の機能が求められていました。

  3. 不明なコマンドエラーメッセージの短縮: goコマンドに存在しないサブコマンドが入力された際のエラーメッセージが冗長でした。この変更は、ユーザーエクスペリエンスを向上させ、エラーメッセージをより簡潔で分かりやすくすることを目的としています。

コミットメッセージに記載されているFixes #3612Fixes #3613は、このコミットが解決したGoのIssueトラッカー上の問題を指しています。ただし、現在のGoのIssueトラッカー(GitHub)ではこれらの番号のIssueは存在しないか、異なる内容を示しているため、このコミットが作成された2012年当時のGoのIssueトラッカー(Google Codeなど)のIssueを参照している可能性が高いです。

前提知識の解説

このコミットを理解するためには、以下のGoの基本的な概念とツールに関する知識が必要です。

  • Goコマンド (go): Go言語のビルド、テスト、実行、ドキュメント生成など、様々なタスクを管理するための主要なコマンドラインツールです。go build, go run, go test, go getなどのサブコマンドを持ちます。
  • godoc: Goのソースコードからドキュメントを生成・表示するためのツールです。Goのコメント規約に従って書かれたコメントや、エクスポートされた識別子(関数、変数、型など)から自動的にドキュメントを生成します。
  • gofmt: Goのソースコードを標準的なスタイルに自動整形するためのツールです。Goコミュニティ全体で一貫したコードスタイルを維持するために広く利用されています。
  • go vet: Goのソースコード内で潜在的なバグや疑わしい構造を検出するための静的解析ツールです。例えば、フォーマット文字列の不一致や、到達不能なコードなどを検出します。
  • パッケージとインポートパス: Goのコードはパッケージに分割され、他のパッケージから利用する際にはインポートパス(例: fmt, net/http, github.com/user/repo/package)を指定します。インポートパスは、パッケージのソースコードがどこにあるかを特定するための論理的な識別子です。
  • GOPATH: Go 1.11以前のGoのワークスペースの概念で、Goのソースコード、コンパイル済みバイナリ、パッケージが配置されるディレクトリ構造を定義していました。Go Modulesの導入により、GOPATHの重要性は低下しましたが、古いコードベースや特定の環境では依然として関連性があります。
  • Go Modules: Go 1.11以降で導入された依存関係管理システムです。プロジェクトのルートディレクトリにあるgo.modファイルで依存関係を定義し、GOPATHに依存せずにプロジェクトをビルドできるようになりました。
  • コマンドラインフラグ (-n, -x): 多くのコマンドラインツールで一般的に使用されるオプションで、ツールの挙動を制御します。
    • -n (dry run): 実際にコマンドを実行せずに、実行されるであろうコマンドを表示します。スクリプトのデバッグや、予期せぬ変更を防ぐために有用です。
    • -x (verbose execution): コマンドが実行される際に、そのコマンド自体を表示します。内部で複数のサブコマンドを呼び出すような複雑なツールで、何が実行されているかを追跡するのに役立ちます。

技術的詳細

このコミットは、主にsrc/cmd/goディレクトリ内のGoコマンドのサブコマンド処理ロジックとフラグ処理に影響を与えています。

  1. godoc呼び出しの改善 (src/cmd/go/doc.go):

    • runDoc関数内で、godocを呼び出す際に渡す引数を変更しています。
    • pkg.local(パッケージがローカルファイルシステム上にあるかどうかを示すブール値)をチェックし、trueの場合はこれまで通りpkg.Dir(パッケージのディレクトリパス)をgodocに渡します。
    • pkg.localfalseの場合(例: リモートパッケージや標準ライブラリパッケージ)、pkg.ImportPath(パッケージのインポートパス)をgodocに渡すように変更されました。これにより、godocはインポートパスに基づいて正確なドキュメントを特定できるようになります。
  2. -n, -xフラグの追加と処理 (src/cmd/go/build.go, src/cmd/go/doc.go, src/cmd/go/fmt.go, src/cmd/go/vet.go, src/cmd/go/main.go):

    • src/cmd/go/build.goaddBuildFlagsNXという新しいヘルパー関数が追加されました。この関数は、Command構造体に-n-xのブール型フラグを登録します。
    • src/cmd/go/doc.go, src/cmd/go/fmt.go, src/cmd/go/vet.goの各ファイルで、それぞれのCommand構造体のinit関数内でaddBuildFlagsNXが呼び出され、対応するサブコマンドに-n-xフラグが追加されます。
    • これらのフラグの動作は、src/cmd/go/main.gorun関数で実装されています。run関数は、Goコマンドが内部で外部コマンド(godoc, gofmt, go vetなど)を実行する際に使用される共通のヘルパー関数です。
    • run関数内で、グローバル変数buildN-nフラグが設定されているか)またはbuildV-vフラグ、このコミットでは変更なしだが既存の冗長出力フラグ)がtrueの場合、実行されるコマンドラインが標準出力にfmt.Printfで表示されます。
    • 特にbuildNtrueの場合、コマンドは実際に実行されずにreturnするため、ドライラン機能が実現されます。
  3. 不明なコマンドエラーメッセージの短縮 (src/cmd/go/main.go):

    • main関数内で、不明なサブコマンドが入力された際のエラー出力ロジックが変更されました。
    • 以前のfmt.Fprintf(os.Stderr, "Unknown command %#q\\n\\n", args[0])usage()の呼び出しが、より簡潔なfmt.Fprintf(os.Stderr, "go: unknown subcommand %#q\\nRun 'go help' for usage.\\n", args[0])setExitStatus(2)exit()に置き換えられました。これにより、エラーメッセージが短くなり、go helpを実行するように促す明確な指示が追加されました。また、終了ステータスが2に設定されることで、スクリプトなどからのエラーハンドリングが容易になります。

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

src/cmd/go/build.go

--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -152,6 +152,11 @@ func addBuildFlags(cmd *Command) {
 	cmd.Flag.Var(buildCompiler{}, "compiler", "")
 }
 
+func addBuildFlagsNX(cmd *Command) {
+	cmd.Flag.BoolVar(&buildN, "n", false, "")
+	cmd.Flag.BoolVar(&buildX, "x", false, "")
+}
+
 type stringsFlag []string
 
 func (v *stringsFlag) Set(s string) error {

src/cmd/go/doc.go

--- a/src/cmd/go/doc.go
+++ b/src/cmd/go/doc.go
@@ -145,7 +145,7 @@ Run godoc on package sources
 
 Usage:
 
-\tgo doc [packages]
+\tgo doc [-n] [-x] [packages]
 
 Doc runs the godoc command on the packages named by the
 import paths.
@@ -153,6 +153,9 @@ import paths.
 For more about godoc, see 'godoc godoc'.
 For more about specifying packages, see 'go help packages'.
 
+The -n flag prints commands that would be executed.
+The -x flag prints commands as they are executed.
+
 To run godoc with specific options, run godoc itself.
 
 See also: go fix, go fmt, go vet.
@@ -414,7 +420,7 @@ Run go tool vet on packages
 
 Usage:
 
-\tgo vet [packages]
+\tgo vet [-n] [-x] [packages]
 
 Vet runs the Go vet command on the packages named by the import paths.
 
@@ -423,6 +429,9 @@ For more about specifying packages, see 'go help packages'.
 
 To run the vet tool with specific options, run 'go tool vet'.
 
+The -n flag prints commands that would be executed.
+The -x flag prints commands as they are executed.
+
 See also: go fmt, go fix.
 
 

src/cmd/go/fmt.go

--- a/src/cmd/go/fmt.go
+++ b/src/cmd/go/fmt.go
@@ -4,9 +4,14 @@
 
 package main
 
+func init() {
+	addBuildFlagsNX(cmdFmt)
+	addBuildFlagsNX(cmdDoc)
+}
+
 var cmdFmt = &Command{
 	Run:       runFmt,
-\tUsageLine: "fmt [packages]",
+\tUsageLine: "fmt [-n] [-x] [packages]",
 	Short:     "run gofmt on package sources",
 	Long: `
 Fmt runs the command 'gofmt -l -w' on the packages named
@@ -15,6 +20,9 @@ by the import paths.  It prints the names of the files that are modified.\n For more about gofmt, see 'godoc gofmt'.\n For more about specifying packages, see 'go help packages'.\n \n+The -n flag prints commands that would be executed.\n+The -x flag prints commands as they are executed.\n+\n To run gofmt with specific options, run gofmt itself.\n \n See also: go doc, go fix, go vet.\n@@ -32,7 +40,7 @@ func runFmt(cmd *Command, args []string) {\n \n var cmdDoc = &Command{\n 	Run:       runDoc,\n-\tUsageLine: "doc [packages]",
+\tUsageLine: "doc [-n] [-x] [packages]",
 	Short:     "run godoc on package sources",
 	Long: `
 Doc runs the godoc command on the packages named by the
@@ -41,6 +49,9 @@ import paths.\n For more about godoc, see 'godoc godoc'.\n For more about specifying packages, see 'go help packages'.\n \n+The -n flag prints commands that would be executed.\n+The -x flag prints commands as they are executed.\n+\n To run godoc with specific options, run godoc itself.\n \n See also: go fix, go fmt, go vet.\n@@ -53,6 +64,10 @@ func runDoc(cmd *Command, args []string) {\n \t\t\terrorf("go doc: cannot use package file list")\n \t\t\tcontinue\n \t\t}\n-\t\trun("godoc", pkg.Dir)\n+\t\tif pkg.local {\n+\t\t\trun("godoc", pkg.Dir)\n+\t\t} else {\n+\t\t\trun("godoc", pkg.ImportPath)\n+\t\t}\n \t}\n }\n```

### `src/cmd/go/main.go`

```diff
--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -144,8 +144,9 @@ func main() {
 		}
 	}
 
-\tfmt.Fprintf(os.Stderr, "Unknown command %#q\\n\\n", args[0])
-\tusage()
+\tfmt.Fprintf(os.Stderr, "go: unknown subcommand %#q\\nRun 'go help' for usage.\\n", args[0])
+\tsetExitStatus(2)
+\texit()
 }
 
 var usageTemplate = `Go is a tool for managing Go source code.
@@ -339,6 +340,13 @@ func exitIfErrors() {
 
 func run(cmdargs ...interface{}) {
 	cmdline := stringList(cmdargs...)
+\tif buildN || buildV {
+\t\tfmt.Printf("%s\\n", strings.Join(cmdline, " "))
+\t\tif buildN {
+\t\t\treturn
+\t\t}\n+\t}\n+\n 	cmd := exec.Command(cmdline[0], cmdline[1:]...)
 	cmd.Stdout = os.Stdout
 	cmd.Stderr = os.Stderr

src/cmd/go/vet.go

--- a/src/cmd/go/vet.go
+++ b/src/cmd/go/vet.go
@@ -4,9 +4,13 @@
 
 package main
 
+func init() {
+	addBuildFlagsNX(cmdVet)
+}
+
 var cmdVet = &Command{
 	Run:       runVet,
-\tUsageLine: "vet [packages]",
+\tUsageLine: "vet [-n] [-x] [packages]",
 	Short:     "run go tool vet on packages",
 	Long: `
 Vet runs the Go vet command on the packages named by the import paths.
@@ -16,6 +20,9 @@ For more about specifying packages, see 'go help packages'.
 
 To run the vet tool with specific options, run 'go tool vet'.
 
+The -n flag prints commands that would be executed.
+The -x flag prints commands as they are executed.
+
 See also: go fmt, go fix.
 	`,
 }

コアとなるコードの解説

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

addBuildFlagsNX関数が新しく追加されました。この関数は、Command構造体(Goコマンドのサブコマンドを表す)を受け取り、そのFlagセットに-n-xという2つのブール型フラグを登録します。これらのフラグはそれぞれグローバル変数buildNbuildXにバインドされ、コマンドラインでこれらのフラグが指定された場合にtrueになります。これにより、Goコマンドの各サブコマンドが共通の方法でこれらのデバッグフラグをサポートできるようになります。

src/cmd/go/doc.go, src/cmd/go/fmt.go, src/cmd/go/vet.go の変更

  • UsageLineの更新: 各コマンドのUsageLineフィールドが更新され、新しく追加された-n-xフラグが使用法に明示的に含まれるようになりました。これにより、ユーザーはgo help docなどのコマンドを実行した際に、これらの新しいフラグの存在を知ることができます。
  • Long説明の追加: 各コマンドのLongフィールド(詳細な説明)に、-n-xフラグの機能に関する説明が追加されました。
  • init関数でのフラグ登録: cmdFmt, cmdDoc, cmdVetの各Command構造体に対応するinit関数内で、addBuildFlagsNXが呼び出されるようになりました。これにより、Goプログラムの初期化時に、これらのサブコマンドに-n-xフラグが自動的に登録されます。
  • runDoc関数の変更 (src/cmd/go/fmt.go 内の cmdDocRun フィールド): runDoc関数は、go docコマンドが実行されたときに実際にgodocツールを呼び出すロジックを含んでいます。この変更の最も重要な部分は、godocを呼び出す際の引数の選択ロジックです。
    if pkg.local {
        run("godoc", pkg.Dir)
    } else {
        run("godoc", pkg.ImportPath)
    }
    
    このコードは、処理中のパッケージpkgがローカルファイルシステム上に存在するかどうか(pkg.local)をチェックします。
    • もしpkg.localtrueであれば、godocはパッケージの物理的なディレクトリパス(pkg.Dir)を引数として実行されます。これは、ローカルで開発中のパッケージや、GOPATH内のパッケージに対して適切です。
    • もしpkg.localfalseであれば(例: 標準ライブラリのパッケージや、Go Modulesで管理されているリモートパッケージ)、godocはパッケージのインポートパス(pkg.ImportPath)を引数として実行されます。これにより、godocはインポートパスに基づいてパッケージを正確に解決し、適切なドキュメントを表示できるようになります。この変更は、go docの堅牢性と汎用性を大幅に向上させました。

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

  • 不明なコマンドエラーメッセージの改善: main関数内で、Goコマンドに認識されないサブコマンドが渡された場合のエラー処理が変更されました。
    -	fmt.Fprintf(os.Stderr, "Unknown command %#q\\n\\n", args[0])
    -	usage()
    +	fmt.Fprintf(os.Stderr, "go: unknown subcommand %#q\\nRun 'go help' for usage.\\n", args[0])
    +	setExitStatus(2)
    +	exit()
    
    以前は「Unknown command」というメッセージと、Goコマンド全体のusage(使用法)が表示されていましたが、新しいメッセージは「go: unknown subcommand」とより具体的になり、go helpを実行するように促す簡潔な指示に変わりました。また、setExitStatus(2)exit()が追加され、プログラムがエラー終了コード2で終了するようになりました。これは、スクリプトなどからGoコマンドの実行結果を判断する際に役立ちます。
  • run関数の変更: run関数は、Goコマンドが内部で他の外部コマンドを実行する際の共通のエントリーポイントです。
    if buildN || buildV {
        fmt.Printf("%s\\n", strings.Join(cmdline, " "))
        if buildN {
            return
        }
    }
    
    このコードブロックが追加され、-nまたは-xフラグ(buildNまたはbuildXtrueの場合、buildVは既存の冗長フラグ)が設定されている場合に、実行されるコマンドライン(cmdline)が標準出力に表示されるようになりました。
    • buildNtrueの場合(-nフラグが指定された場合)、コマンドラインが表示された後、returnによって実際のコマンド実行がスキップされます。これがドライラン機能の実装です。
    • buildXtrueの場合(-xフラグが指定された場合)、コマンドラインが表示された後、通常のコマンド実行が続行されます。これにより、実行中のコマンドをリアルタイムで追跡できます。

これらの変更により、Goコマンドはよりデバッグしやすく、ユーザーフレンドリーなツールへと進化しました。

関連リンク

参考にした情報源リンク