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

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

このコミットは、Go言語のツールチェインにおけるgodocコマンドのインストールパスと、go docコマンドのユーザーエクスペリエンスを改善するものです。具体的には、go.tools/cmd/godoc$GOROOT/bin/godocにインストールされるように変更し、さらにgo docコマンド実行時にgodocが見つからない場合に、go getコマンドを使ったインストール方法をユーザーに提示するように改善しています。

コミット

  • コミットハッシュ: e011ac5420e1bda1feb87bc398a61ebbb52c0332
  • 作者: Andrew Gerrand adg@golang.org
  • 日付: 2013年8月1日 木曜日 11:17:42 +1000

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

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

元コミット内容

cmd/go: install go.tools/cmd/godoc to $GOROOT/bin/godoc

Also suggest "go get" if godoc not found when running "go doc".

R=golang-dev, r, rsc
CC=golang-dev
https://golang.org/cl/12214043

変更の背景

Go言語の初期のバージョンでは、godocツールはGoの標準配布物には含まれておらず、別途go getコマンドを使ってインストールする必要がありました。しかし、godocはGoのドキュメントを参照するための非常に重要なツールであり、ユーザーがGoのドキュメントを簡単に利用できるようにすることが望まれていました。

このコミット以前は、godoc$GOROOT/pkg/toolディレクトリにインストールされる可能性がありました。しかし、godocは一般的なコマンドラインツールとして利用されることが多いため、ユーザーのPATHに含まれる$GOROOT/binにインストールされる方が、よりアクセスしやすく、使い勝手が良いと考えられました。

また、go docコマンドは内部的にgodocツールを呼び出してドキュメントを表示します。もしユーザーのシステムにgodocがインストールされていない場合、go docコマンドは単にエラーを返すだけで、ユーザーは何をすべきか分かりませんでした。このユーザーエクスペリエンスを改善するため、godocが見つからない場合に、そのインストール方法(go getコマンドの使用)を具体的に提示することが必要とされました。

この変更は、Goツールのインストールと利用の利便性を向上させ、特に新規ユーザーがGoのエコシステムにスムーズに慣れることを支援することを目的としています。

前提知識の解説

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

  1. go docコマンド: go docはGoの標準ツールの一つで、Goのパッケージ、関数、型、メソッドなどのドキュメントを表示するために使用されます。これは内部的にgodocツールを呼び出して機能を提供します。例えば、go doc fmt.Printlnと入力すると、fmtパッケージのPrintln関数のドキュメントが表示されます。

  2. godocツール: godocはGoのソースコードからドキュメントを生成し、表示するためのツールです。Goのコードコメントは特定の形式で記述することで、godocによって自動的にドキュメントとして抽出されます。godocはコマンドラインツールとしてだけでなく、Webサーバーとしても機能し、ブラウザを通じてGoのドキュメントを閲覧することもできます。

  3. go getコマンド: go getは、Goのパッケージやコマンドをリモートリポジトリ(GitHubなど)からダウンロードし、ビルドしてインストールするためのコマンドです。例えば、go get example.com/some/packageと実行すると、指定されたパッケージが$GOPATH/srcにダウンロードされ、そのパッケージ内のコマンドが$GOPATH/binにインストールされます。

  4. $GOROOT: $GOROOTはGoのインストールディレクトリを指す環境変数です。Goの標準ライブラリやツールチェインのバイナリ(goコマンド自体など)がこのディレクトリ以下に配置されます。

    • $GOROOT/bin: Goの標準ツール(goコマンドなど)の実行可能ファイルが置かれる場所です。
    • $GOROOT/pkg/tool: Goのビルドプロセスで使用される内部ツールや、特定のプラットフォーム向けのツールが置かれる場所です。通常、ユーザーのPATHには含まれません。
  5. $GOPATH: $GOPATHはGoのワークスペースディレクトリを指す環境変数です。ユーザーが開発するGoのプロジェクトや、go getでダウンロード・インストールされたサードパーティのパッケージやコマンドがこのディレクトリ以下に配置されます。

    • $GOPATH/src: ソースコードが置かれる場所です。
    • $GOPATH/bin: go installgo getでインストールされたコマンドの実行可能ファイルが置かれる場所です。
  6. Goのツールインストールパス: Goのツールは、その性質によってインストールされる場所が異なります。

    • 一般的なコマンドラインツールとしてユーザーが直接実行するものは、通常$GOPATH/binまたは$GOROOT/binにインストールされることが望ましいです。
    • Goのビルドシステムや他のツールから内部的に呼び出される補助的なツールは、$GOROOT/pkg/toolにインストールされることがあります。

このコミットは、godocが後者の「内部ツール」から前者の「一般的なコマンドラインツール」としての位置づけに移行し、よりアクセスしやすい場所にインストールされるように変更するものです。

技術的詳細

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

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

このファイルでは、go docコマンドが実行された際にgodocツールが見つからない場合の挙動が変更されています。

  • os/execパッケージのインポート: exec.LookPath関数を使用するために、os/execパッケージがインポートされています。
  • runDoc関数の変更:
    • exec.LookPath("godoc")を呼び出して、システムPATHからgodoc実行可能ファイルを探します。
    • もしgodocが見つからずエラーが発生した場合(err != nil)、errorf関数を使ってユーザーにエラーメッセージを表示します。
    • 表示されるエラーメッセージは、「go doc: can't find godoc; to install:\n\tgo get code.google.com/p/go.tools/cmd/godoc」という形式で、godocが見つからないことと、go getコマンドを使ったインストール方法を具体的に指示します。
    • エラーメッセージを表示した後、関数はreturnして処理を終了します。

この変更により、ユーザーはgodocがインストールされていない場合に、その解決策をすぐに知ることができます。

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

このファイルでは、Goのツールがどこにインストールされるかを決定するロジックが変更されています。

  • isGoTool変数の削除とtargetDir型およびgoToolsマップの導入:

    • 以前はisGoToolというmap[string]bool型の変数があり、$GOROOT/pkg/toolにインストールされるGoツールのインポートパスを単純にリストアップしていました。
    • このコミットでは、targetDirという新しい列挙型(iotaを使用)が導入されました。
      • toRoot: パッケージのルート内のbinディレクトリ(デフォルト)
      • toTool: $GOROOT/pkg/tool
      • toBin: $GOROOT/bin
    • goToolsというmap[string]targetDir型の新しいマップが導入されました。このマップは、Goツールのインポートパスと、そのツールがインストールされるべきターゲットディレクトリのタイプを関連付けます。
    • 注目すべきは、"code.google.com/p/go.tools/cmd/godoc"のエントリがtoBinに設定されている点です。これにより、godoc$GOROOT/binにインストールされるようになります。他のツール(cmd/api, cmd/cgo, cmd/fix, cmd/yacc, cmd/cover, cmd/vet)は引き続きtoToolに設定されています。
  • Package.load関数の変更:

    • パッケージのターゲットパスを決定するロジックが、新しいgoToolsマップとtargetDir型を使用するように変更されました。
    • 以前はisGoTool[p.ImportPath]という単純なブール値のチェックに基づいていましたが、新しいswitchステートメントが導入されました。
    • switch goTools[p.ImportPath]によって、ツールのインポートパスに対応するtargetDirの値に基づいて、インストールパスが決定されます。
      • toRootの場合(デフォルト):p.build.BinDir(通常は$GOPATH/bin)にインストールされます。
      • toToolの場合:filepath.Join(gorootPkg, "tool", full)、つまり$GOROOT/pkg/toolにインストールされます。
      • toBinの場合:filepath.Join(gorootBin, elem)、つまり$GOROOT/binにインストールされます。

この変更により、godocはGoの標準バイナリが置かれる$GOROOT/binにインストールされるようになり、ユーザーのPATHを通じて直接実行できるようになります。これは、godocがGoのドキュメント参照において中心的な役割を果たすことを考慮した、論理的な配置変更と言えます。

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

src/cmd/go/fmt.go

--- a/src/cmd/go/fmt.go
+++ b/src/cmd/go/fmt.go
@@ -4,6 +4,8 @@
 
 package main
 
+import "os/exec"
+
 func init() {
 	addBuildFlagsNX(cmdFmt)
 	addBuildFlagsNX(cmdDoc)
@@ -59,6 +61,11 @@ See also: go fix, go fmt, go vet.
 }
 
 func runDoc(cmd *Command, args []string) {
+	_, err := exec.LookPath("godoc")
+	if err != nil {
+		errorf("go doc: can't find godoc; to install:\n\tgo get code.google.com/p/go.tools/cmd/godoc")
+		return
+	}
 	for _, pkg := range packages(args) {
 		if pkg.ImportPath == "command-line arguments" {
 			errorf("go doc: cannot use package file list")

src/cmd/go/pkg.go

--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -283,15 +283,23 @@ func reusePackage(p *Package, stk *importStack) *Package {
 	return p
 }
 
-// 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/yacc":                             true,
-	"code.google.com/p/go.tools/cmd/cover": true,
-	"code.google.com/p/go.tools/cmd/vet":   true,
+type targetDir int
+
+const (
+	toRoot targetDir = iota // to bin dir inside package root (default)
+	toTool                  // GOROOT/pkg/tool
+	toBin                   // GOROOT/bin
+)
+
+// goTools is a map of Go program import path to install target directory.
+var goTools = map[string]targetDir{
+	"cmd/api":                              toTool,
+	"cmd/cgo":                              toTool,
+	"cmd/fix":                              toTool,
+	"cmd/yacc":                             toTool,
+	"code.google.com/p/go.tools/cmd/cover": toTool,
+	"code.google.com/p/go.tools/cmd/godoc": toBin,
+	"code.google.com/p/go.tools/cmd/vet":   toTool,
 }
 
 // expandScanner expands a scanner.List error into all the errors in the list.
@@ -341,11 +349,15 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
 			// Install cross-compiled binaries to subdirectories of bin.
 			elem = full
 		}\n-		if p.build.BinDir != "" {\n-			p.target = filepath.Join(p.build.BinDir, elem)\n-		}\n-		if isGoTool[p.ImportPath] {\n+		switch goTools[p.ImportPath] {\n+		case toRoot: // default, if p.ImportPath not in goTools
+			if p.build.BinDir != "" {
+				p.target = filepath.Join(p.build.BinDir, elem)
+			}
+		case toTool:
 			p.target = filepath.Join(gorootPkg, "tool", full)
+		case toBin:
+			p.target = filepath.Join(gorootBin, elem)
 		}
 		if p.target != "" && buildContext.GOOS == "windows" {
 			p.target += ".exe"

コアとなるコードの解説

src/cmd/go/fmt.goの変更点

runDoc関数はgo docコマンドの実行ロジックを担っています。この変更では、godoc実行可能ファイルがシステムPATH上に存在するかどうかをexec.LookPath("godoc")で確認しています。もしgodocが見つからなかった場合(err != nil)、ユーザーに対して「godocが見つからないので、go get code.google.com/p/go.tools/cmd/godocでインストールしてください」という具体的な指示を含むエラーメッセージを表示し、処理を中断します。これにより、ユーザーはgodocが不足している場合に、その解決策をすぐに理解できるようになります。

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

このファイルは、Goパッケージのビルドとインストールに関するロジックを含んでいます。

  1. isGoToolからtargetDirgoToolsへの移行: 以前はisGoToolという単純なブールマップで、特定のツールが$GOROOT/pkg/toolにインストールされるかどうかを管理していました。このコミットでは、より柔軟なインストールパスの指定を可能にするために、targetDirという列挙型とgoToolsというマップが導入されました。

    • targetDirは、ツールのインストール先として考えられる3つの主要な場所(toRoottoTooltoBin)を定義します。
    • goToolsマップは、各Goツールのインポートパスと、そのツールがインストールされるべきtargetDirのタイプを関連付けます。
    • 特に重要なのは、"code.google.com/p/go.tools/cmd/godoc"toBinに設定されたことです。これは、godoc$GOROOT/bin、つまりGoの主要な実行可能ファイルが置かれるディレクトリにインストールされることを意味します。これにより、godocはユーザーのPATHを通じて直接実行できるようになり、利便性が向上します。
  2. Package.load関数のインストールパス決定ロジックの変更: Package.load関数内の、パッケージの最終的なインストールターゲットパス(p.target)を決定する部分が変更されました。以前はisGoToolのブール値に基づいていましたが、新しいロジックではgoTools[p.ImportPath]の値(targetDir型)に基づいてswitch文で分岐します。

    • toRootの場合(デフォルト):p.build.BinDir(通常は$GOPATH/bin)にインストールされます。これは、一般的なGoのコマンドやアプリケーションのデフォルトのインストール場所です。
    • toToolの場合:$GOROOT/pkg/toolにインストールされます。これは、Goの内部ツールや、他のGoツールから呼び出される補助的なツールに適した場所です。
    • toBinの場合:$GOROOT/binにインストールされます。これは、godocのようにGoの標準配布物の一部として提供され、ユーザーが直接実行することを意図したツールに適した場所です。

これらの変更により、godocはよりアクセスしやすい$GOROOT/binにインストールされるようになり、go docコマンドはgodocが見つからない場合にユーザーに具体的な解決策を提示することで、Goツールの使いやすさが大幅に向上しました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント (上記「関連リンク」に記載)
  • Go言語のソースコード (このコミットが属するリポジトリ)
  • Go言語のIssueトラッカーやメーリングリスト (コミットメッセージに記載されているCLリンクなど)
    • https://golang.org/cl/12214043 (現在はhttps://go.dev/cl/12214043にリダイレクトされます)
  • 一般的なGo言語のチュートリアルや解説記事 (Goのツールチェインや環境変数に関する説明)
  • os/execパッケージのGoドキュメント: https://pkg.go.dev/os/exec
  • path/filepathパッケージのGoドキュメント: https://pkg.go.dev/path/filepath
  • go/buildパッケージのGoドキュメント: https://pkg.go.dev/go/build
  • go.toolsリポジトリ (現在はgolang.org/x/toolsに移行): https://pkg.go.dev/golang.org/x/tools
  • code.google.com/p/go.tools (古いパス、現在はgolang.org/x/toolsに移行)