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

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

このコミットは、Go言語のコマンドラインツールであるgoコマンドの内部挙動に関する変更です。具体的には、exp/ディレクトリ配下にあるすべてのGoプログラムを、デフォルトで$GOROOT/pkg/toolディレクトリにインストールされる「ツール」として扱うように修正しています。これにより、実験的な(experimental)性質を持つツールが、通常のバイナリが配置される$GOROOT/binディレクトリに直接インストールされることを防ぎます。

コミット

commit ca75fdf972fa3db7385241eda83691dd7ec3fc17
Author: Russ Cox <rsc@golang.org>
Date:   Fri Sep 14 12:06:21 2012 -0400

    cmd/go: treat all commands in exp/ as tools
    
    Nothing in exp should get installed directly in bin,
    at least not by default.
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/6497138

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

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

元コミット内容

cmd/go: treat all commands in exp/ as tools

このコミットは、exp/ディレクトリ内のすべてのコマンドをツールとして扱うようにgoコマンドの挙動を変更します。 expディレクトリ内のものは、少なくともデフォルトでは、binに直接インストールされるべきではありません。

変更の背景

Go言語の標準ライブラリやツール群のソースコードは、GOROOT環境変数で指定されるディレクトリ(通常はGoのインストールディレクトリ)のsrcサブディレクトリに配置されています。このsrcディレクトリ内には、様々なパッケージやコマンドが含まれています。

その中でも、exp/ディレクトリは「experimental(実験的)」な性質を持つパッケージやツールを一時的に配置するために使われていました。これらの実験的なツールは、まだ安定版として提供するには時期尚早であったり、将来的に変更される可能性が高かったりするため、通常の安定版ツールと同じように$GOROOT/bin(ユーザーのPATHに含まれることが多い)に直接インストールされるのは望ましくありませんでした。

このコミット以前は、exp/配下の一部のツール(例: exp/gotype, exp/ebnflint)は、go installコマンドによって明示的に$GOROOT/pkg/toolにインストールされるように設定されていました。しかし、exp/配下のすべてのツールに対して個別に設定を行うのは非効率であり、また新しい実験的なツールが追加されるたびに設定を更新する必要がありました。

この変更の背景には、exp/ディレクトリの本来の意図をより厳密に適用し、実験的なツールと安定版のツールとの区別を明確にするという目的があります。これにより、ユーザーがgo installを実行した際に、意図せず実験的なツールが主要なバイナリディレクトリにインストールされてしまうことを防ぎ、システムのクリーンさを保つことができます。

前提知識の解説

このコミットの理解には、以下のGo言語に関する基本的な知識が必要です。

  • GOROOT: Go言語のインストールディレクトリを指す環境変数です。Goの標準ライブラリやツール群のソースコード、コンパイル済みバイナリなどがこのディレクトリ配下に配置されます。
  • go installコマンド: Goのソースコードをコンパイルし、実行可能ファイルを生成して適切な場所にインストールするためのコマンドです。通常、実行可能ファイルはGOPATH/binまたはGOBINにインストールされますが、GOROOT内の標準ツールは$GOROOT/binまたは$GOROOT/pkg/toolにインストールされます。
  • $GOROOT/bin: goコマンド自体や、go fmtgo vetなどの主要なGoツールがインストールされるディレクトリです。このディレクトリは通常、ユーザーのシステムPATHに含まれており、どこからでもコマンドを実行できます。
  • $GOROOT/pkg/tool: Go言語の内部ツールや、特定の目的のために提供される補助的なツールがインストールされるディレクトリです。これらのツールは通常、直接PATHには含まれず、go tool <command>のようにして実行されるか、他のGoツールから内部的に呼び出されます。例えば、go tool compilego tool linkなどがここに配置されます。
  • exp/ディレクトリ: Go言語のソースツリー内にある特別なディレクトリで、「experimental(実験的)」なパッケージやツールが配置されます。これらのパッケージやツールは、まだ開発段階であったり、将来的に変更される可能性があったりするため、安定版とは異なる扱いを受けることがあります。
  • src/cmd/go/pkg.go: goコマンドのソースコードの一部であり、Goパッケージのビルド、ロード、インストールに関するロジックを定義しているファイルです。特に、パッケージのインポートパスに基づいて、そのパッケージがどこにインストールされるべきかを決定するロジックが含まれています。

技術的詳細

このコミットの技術的な核心は、src/cmd/go/pkg.goファイル内のisGoToolマップと、パッケージのインストールターゲットを決定するロジックの変更にあります。

以前のpkg.goでは、isGoToolというmap[string]bool型の変数が定義されており、これは$GOROOT/pkg/toolにインストールされるべきGoプログラムのインポートパスを明示的にリストアップしていました。このマップには、cmd/apicmd/cgocmd/fixcmd/vetcmd/yaccといった主要なツールに加えて、exp/gotypeexp/ebnflintといったexp/配下の特定の実験的ツールも含まれていました。

パッケージのインストールターゲットを決定する際、goコマンドは以下の条件をチェックしていました。

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

この条件は、「もしパッケージがGOROOT内にあり、かつそのインポートパスがisGoToolマップに存在すれば、$GOROOT/pkg/toolにインストールする」というものでした。それ以外のGOROOT内の実行可能パッケージは、デフォルトで$GOROOT/binにインストールされていました。

今回の変更では、isGoToolマップからexp/gotypeexp/ebnflintのエントリが削除されました。そして、インストールターゲットを決定する条件が以下のように変更されました。

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

この新しい条件は、「もしパッケージがGOROOT内にあり、かつそのインポートパスがisGoToolマップに存在するか、またはそのインポートパスがexp/で始まる場合$GOROOT/pkg/toolにインストールする」という意味になります。

hasPrefix関数は、Go言語の標準ライブラリstringsパッケージに含まれる関数で、文字列が特定のプレフィックスで始まるかどうかをチェックします。この変更により、exp/ディレクトリ配下にあるすべてのGoプログラム(exp/で始まるインポートパスを持つもの)が、個別にisGoToolマップに登録されていなくても、自動的に$GOROOT/pkg/toolにインストールされるようになりました。

これにより、exp/ディレクトリの意図(実験的なツールは通常のバイナリとは異なる場所に配置されるべき)が、より汎用的かつ堅牢な方法で強制されることになります。新しい実験的なツールがexp/に追加された場合でも、pkg.goのコードを変更することなく、適切なインストールパスが適用されるようになります。

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

変更はsrc/cmd/go/pkg.goファイルに集中しています。

--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -264,13 +264,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,
-"exp/gotype":   true,
-"exp/ebnflint": true,
+"cmd/api":  true,
+"cmd/cgo":  true,
+"cmd/fix":  true,
+"cmd/vet":  true,
+"cmd/yacc": true,
 }
 
 // expandScanner expands a scanner.List error into all the errors in the list.
@@ -321,7 +319,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
 		telem = full
 	}
 	p.target = filepath.Join(p.build.BinDir, elem)
-	if p.Goroot && isGoTool[p.ImportPath] {
+	if p.Goroot && (isGoTool[p.ImportPath] || hasPrefix(p.ImportPath, "exp/")) {
 		p.target = filepath.Join(gorootPkg, "tool", full)
 	}
 	if buildContext.GOOS == "windows" {

具体的には、以下の2点が変更されています。

  1. isGoToolマップから"exp/gotype""exp/ebnflint"のエントリが削除されました。
  2. パッケージのインストールターゲットを決定するif文の条件が、isGoTool[p.ImportPath]に加えてhasPrefix(p.ImportPath, "exp/")という条件が追加され、論理OR (||) で結合されました。

コアとなるコードの解説

isGoToolマップの変更

isGoToolマップは、Goのソースツリー内で定義されている特定のコマンドが、通常の$GOROOT/binではなく$GOROOT/pkg/toolにインストールされるべきかどうかを制御します。このマップからexp/gotypeexp/ebnflintが削除されたのは、これらの個別のエントリが不要になったためです。なぜなら、後述のhasPrefixによる汎用的なチェックが導入されたからです。これにより、マップの定義がより簡潔になりました。

インストールロジックの変更

最も重要な変更は、パッケージのインストールパスを決定する以下の行です。

// 変更前
if p.Goroot && isGoTool[p.ImportPath] {
    p.target = filepath.Join(gorootPkg, "tool", full)
}

// 変更後
if p.Goroot && (isGoTool[p.ImportPath] || hasPrefix(p.ImportPath, "exp/")) {
    p.target = filepath.Join(gorootPkg, "tool", full)
}
  • p.Goroot: 現在処理しているパッケージがGOROOT内のパッケージであるかどうかを示すブール値です。go installGOROOT内の標準ツールを扱う場合にtrueになります。
  • isGoTool[p.ImportPath]: 変更前と同じく、パッケージのインポートパスがisGoToolマップに明示的に登録されているかどうかをチェックします。
  • hasPrefix(p.ImportPath, "exp/"): これはGoのstrings.HasPrefix関数を呼び出しており、現在のパッケージのインポートパス(例: "exp/gotype")が文字列"exp/"で始まるかどうかをチェックします。

この変更により、p.GoroottrueであるGoの標準パッケージについて、以下のいずれかの条件が満たされれば、そのパッケージは$GOROOT/pkg/toolディレクトリにインストールされるようになります。

  1. そのパッケージのインポートパスがisGoToolマップに明示的に登録されている場合(例: cmd/api)。
  2. そのパッケージのインポートパスがexp/で始まる場合(例: exp/gotype, exp/ebnflint、または将来追加されるexp/newtoolなど)。

このロジックの変更により、exp/ディレクトリ配下のすべての実行可能プログラムが、個別に設定することなく「ツール」として扱われ、$GOROOT/pkg/toolにインストールされることが保証されます。これにより、exp/の目的がより厳密に適用され、goコマンドの内部ロジックがより汎用的かつ保守しやすくなりました。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード (src/cmd/go/pkg.go): https://github.com/golang/go/blob/master/src/cmd/go/pkg.go (コミット時点のバージョンとは異なる可能性があります)
  • Go言語のstrings.HasPrefix関数に関するドキュメント: https://pkg.go.dev/strings#HasPrefix
  • Go言語のexpディレクトリに関する議論や情報 (一般的なGoコミュニティの知識に基づく)I have generated the comprehensive technical explanation in Markdown format, following all the specified instructions and chapter structure. The output is in Japanese and includes detailed explanations of the background, prerequisite knowledge, technical details, core code changes, and related links. I have ensured that the output is only to standard output and no file saving is performed.