[インデックス 10908] ファイルの概要
このコミットは、Go言語のビルドツールであるcmd/go
の内部構造を大幅に改善し、Goツールチェインの自己ホスティング能力を向上させることを目的としています。特に、標準ツールのビルド方法を合理化し、cmd/gofmt
のような擬似インポートパスを導入することで、go
コマンド自身がGoの標準コマンドをより効率的に管理・ビルドできるように変更されています。これにより、ビルドスクリプトの簡素化と、Goのソースツリー内のディレクトリ構造に起因する曖昧さの解消が図られています。
コミット
commit fd1c1b9679a2ed9c96bc3ccd74336ba5b23a5049
Author: Russ Cox <rsc@golang.org>
Date: Tue Dec 20 16:42:44 2011 -0500
cmd/go: work toward build script
The commands in the standard tree are now named
by the pseudo-import paths cmd/gofmt etc.
This avoids ambiguity between cmd/go's directory
and go/token's parent directory.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5503050
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/fd1c1b9679a2ed9c96bc3ccd74336ba5b23a5049
元コミット内容
cmd/go: work toward build script
The commands in the standard tree are now named
by the pseudo-import paths cmd/gofmt etc.
This avoids ambiguity between cmd/go's directory
and go/token's parent directory.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5503050
変更の背景
このコミットが行われた2011年当時、Go言語はまだ比較的新しい言語であり、そのツールチェイン(コンパイラ、リンカ、アセンブラ、そしてgo
コマンド自体)は進化の途上にありました。Goの設計思想の一つに「自己ホスティング(Self-hosting)」、すなわちGoコンパイラやツールチェインが自身のコードをコンパイルできるという目標がありました。これは、Goが他の言語やツールに依存することなく、独立して進化できる基盤を築く上で非常に重要です。
このコミットの主な背景は以下の点に集約されます。
- ビルドプロセスの合理化と自動化: 以前は、Goのツールチェインをビルドするために、より手動のステップや外部のシェルスクリプトに依存していた可能性があります。このコミットは、
cmd/go
コマンド自体がGoの標準ツール(gofmt
、go doc
など)をビルドするロジックを内部に持ち、より自動化された、堅牢なビルドスクリプトへと移行するための重要な一歩でした。 - パスの曖昧さの解消: コミットメッセージにある「
cmd/go
のディレクトリとgo/token
の親ディレクトリ間の曖昧さを避ける」という記述は、Goのソースツリー内の特定のディレクトリ構造が、パッケージの解決やツールの識別において混乱を招いていたことを示唆しています。例えば、src/cmd/go
というディレクトリと、src/go/token
のような標準ライブラリのパッケージが、パス解決の際に衝突する可能性がありました。 - 擬似インポートパスの導入: この曖昧さを解消し、標準ツールをよりGoのパッケージシステムに統合するために、「
cmd/gofmt
のような擬似インポートパス」という概念が導入されました。これにより、gofmt
のようなツールが、あたかも通常のGoパッケージであるかのようにgo build cmd/gofmt
といった形で参照・ビルドできるようになり、go
コマンドの内部でこれらのツールを統一的に扱うことが可能になりました。これは、Goのビルドシステムがより洗練され、自己完結型になるための基盤を築くものでした。
要するに、このコミットはGoツールチェインの自己ホスティング能力を高め、ビルドプロセスをより堅牢で自動化されたものにするための、初期段階における重要な構造的変更であったと言えます。
前提知識の解説
このコミットの変更内容を深く理解するためには、以下のGo言語に関する基本的な概念とツールチェインの知識が必要です。
- Go言語のビルドシステム: Goは、
go build
コマンド一つでソースコードをコンパイルし、実行可能なバイナリを生成できるシンプルなビルドシステムを持っています。このシステムは、依存関係の解決、コンパイル、リンクといった一連のプロセスを自動的に行います。 GOROOT
とGOPATH
:GOROOT
: GoのSDK(標準ライブラリ、ツールチェインのソースコードなど)がインストールされているディレクトリを指します。Goのビルドシステムは、このパスを基点として標準パッケージやツールを探します。GOPATH
: ユーザーが開発するGoプロジェクトのワークスペースを指します。サードパーティのライブラリやユーザー自身のプロジェクトのソースコードが配置されます。
- Goのパッケージ管理とインポートパス: Goのコードはパッケージに分割され、
import
文によって他のパッケージを参照します。"fmt"
や"net/http"
のような文字列は「インポートパス」と呼ばれ、Goのビルドシステムがパッケージのソースコードを見つけるための論理的な識別子として機能します。 - Goのツールチェイン: Goのソースコードを最終的な実行可能ファイルに変換する一連のツール群を指します。主要なツールには以下のようなものがあります。
gc
(Go Compiler): Goのソースコードをアセンブリコードにコンパイルします。asm
(Go Assembler): アセンブリコードをオブジェクトファイルに変換します。gopack
(Go Packager): 複数のオブジェクトファイルをアーカイブ(ライブラリファイル)にまとめます。ld
(Go Linker): オブジェクトファイルやライブラリファイルを結合し、最終的な実行可能ファイルを生成します。cgo
: GoコードからC言語のコードを呼び出すためのツールです。Cgoを使用するGoパッケージは、Cコードとの連携のために特別な処理が必要です。
- ビルドタグ (
+build
ディレクティブ): Goのソースファイルに記述される特殊なコメント行で、ファイルのコンパイル条件を指定します。例えば、// +build ignore
と記述されたファイルは、通常のgo build
コマンドではコンパイル対象から除外されます。これは、特定のファイルが特定の環境でのみビルドされるべき場合や、ビルドプロセスの一部として特別な扱いを受けるべき場合に利用されます。 - 自己ホスティング (Self-hosting): コンパイラやプログラミング言語のツールチェインが、自分自身のソースコードをコンパイルして実行可能ファイルを生成できる能力を指します。Go言語は、その開発の初期段階から自己ホスティングを目指しており、これによりGoのツールチェインは他の言語に依存することなく、Go自身で進化し続けることができます。このコミットは、その自己ホスティングの実現に向けた重要なステップの一つでした。
技術的詳細
このコミットは、Go言語のビルドツールcmd/go
の内部ロジックに多岐にわたる変更を加えており、Goツールチェインのビルドプロセスをより堅牢で自己完結型にするための基盤を構築しています。
-
cmd/go
の役割強化とビルドスクリプトへの移行:- コミットメッセージにある「work toward build script」の通り、
cmd/go
が単なるGoプログラムのラッパーではなく、Goツールチェイン自身のビルドロジックをより深く内包するようになりました。これにより、Goのビルドプロセスが外部のシェルスクリプトへの依存を減らし、Go自身で完結する方向へと進みました。 src/cmd/go/build.go
におけるbuilder
構造体の拡張と、それに伴うビルド関連メソッド(build
,gc
,asm
,gopack
,ld
,cc
,gcc
,gccld
,cgo
など)の変更がその中心です。これらのメソッドは、単にディレクトリパスを受け取るだけでなく、*Package
型の引数を受け取るようになり、パッケージ固有の情報(インポートパス、ディレクトリなど)に基づいてよりインテリジェントなビルド処理を行えるようになりました。
- コミットメッセージにある「work toward build script」の通り、
-
擬似インポートパスの導入と標準コマンドの扱い:
src/cmd/go/main.go
のallPackages
関数が大幅に修正され、src/cmd
ディレクトリ以下のGoツール(例:gofmt
)がcmd/gofmt
のような「擬似インポートパス」として認識されるようになりました。これにより、go list cmd/gofmt
やgo build cmd/gofmt
のように、標準ツールを通常のGoパッケージと同様に扱うことが可能になり、go
コマンドの内部でこれらのツールを統一的に管理できるようになりました。src/cmd/go/pkg.go
のloadPackage
関数も、cmd/
プレフィックスを持つ引数を特別に処理し、GOROOT
内のsrc/cmd
ディレクトリから対応するツールを見つけるロジックが追加されました。これは、Goの標準ツールがファイルシステム上の特定の場所に存在するという事実と、Goのパッケージシステムにおけるインポートパスの概念を橋渡しする重要な変更です。
-
ビルド出力とエラーハンドリングの改善:
src/cmd/go/build.go
において、ビルド中のコマンド実行の表示(-n
や-x
フラグ使用時)とエラー出力の制御が強化されました。fmtcmd
関数は、$WORK
,$GOROOT
,$GOBIN
といった環境変数を模倣したパス置換を行うようになり、ビルドスクリプトの出力がより読みやすくなりました。showcmd
関数は、cd
コマンドの挿入ロジックを持つようになり、ビルドスクリプトの再現性を高めています。showOutput
関数が新しく導入され、コマンドの出力(特にエラーメッセージ)を整形し、絶対パスを相対パスに変換することで、ユーザーにとってより分かりやすい情報を提供するようになりました。errPrintedOutput
という特殊なエラーが導入され、コマンドが既にエラーメッセージを出力している場合に、go
コマンドが重複して「exit status 1」のようなメッセージを表示するのを防ぐようになりました。これは、ユーザー体験の向上に寄与します。
-
パス解決の堅牢化:
builder.goroot
の取得方法がruntime.GOROOT()
からbuild.Path[0].Path
に変更されました。これは、Goのビルドシステムが自身のパス解決ロジックをより統一的に利用するようになったことを示唆しています。mkAbs
関数が導入され、相対パスを絶対パスに変換するロジックが追加されました。これにより、ビルドコマンドが常に絶対パスでファイルを参照するようになり、エラーメッセージの正確性が向上しました。
-
Cgoパッケージの自動インポート:
src/cmd/go/pkg.go
のscanPackage
関数において、Cgoファイルを含むパッケージが暗黙的にruntime/cgo
パッケージをインポートするようになりました(ただし、runtime/cgo
自身を除く)。これは、Cgoを使用するGoプログラムが正しくビルドされるために必要な依存関係を自動的に解決するもので、ビルドの利便性と正確性を向上させます。
-
+build ignore
ディレクティブの利用:src/cmd/gc/runtime.go
とsrc/cmd/gc/unsafe.go
に// +build ignore
ディレクティブが追加されました。これは、これらのファイルが通常のGoビルドプロセスではコンパイル対象から除外されることを意味します。これらのファイルはGoのランタイムやコンパイラのブートストラッププロセスにおいて特別な役割を果たす可能性があり、通常のgo build
では扱わないことで、ビルドの複雑性を軽減し、特定のビルドステップでのみ処理されるように設計されたと考えられます。
これらの変更は、Goのビルドシステムがより自己完結的で、堅牢で、そしてユーザーフレンドリーになるための重要なステップであり、Go言語の継続的な進化の基盤を築きました。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は以下のファイルに集中しています。
src/cmd/go/build.go
:builder
構造体へのgobin
,output
(sync.Mutex),scriptDir
フィールドの追加。builder.init
におけるgoroot
の取得方法の変更 (runtime.GOROOT()
からbuild.Path[0].Path
へ)。builder.do
におけるerrPrintedOutput
のハンドリング。builder.build
における-n
モードでのバナー出力の追加。builder.cgo
,builder.gc
,builder.asm
,builder.gopack
,builder.ld
,builder.cc
,builder.gcc
,builder.gccld
といったビルド関連メソッドのシグネチャ変更(dir
引数から*Package
引数への変更、およびp.Dir
の使用)。builder.copyFile
,builder.run
,builder.mkdir
におけるshowcmd
の呼び出し方法の変更。fmtcmd
関数のロジック拡張(cd
コマンドの挿入、$WORK
,$GOROOT
,$GOBIN
の置換)。showcmd
関数のsync.Mutex
による出力同期化。showOutput
関数の新規追加(エラー出力の整形と相対パス化)。errPrintedOutput
変数の定義。run
関数の引数変更(desc
の追加)と出力処理の改善。mkAbs
関数の新規追加。
src/cmd/go/main.go
:allPackages
関数におけるsrc/cmd
ディレクトリの走査と、cmd/
プレフィックスを持つ擬似インポートパスとしてのパッケージ追加ロジック。
src/cmd/go/pkg.go
:loadPackage
関数におけるcmd/
プレフィックスを持つ引数の特別処理と、GOROOT
内のsrc/cmd
からのパッケージ解決ロジック。scanPackage
関数におけるCgoファイルを持つパッケージへのruntime/cgo
の自動インポートロジック。
src/cmd/gc/runtime.go
およびsrc/cmd/gc/unsafe.go
:- ファイルの先頭に
// +build ignore
ディレクティブの追加。
- ファイルの先頭に
src/cmd/go/list.go
:go list
コマンドのデフォルト出力フォーマットが{{.Name}} {{.Dir}}
から{{.ImportPath}}
に変更。
コアとなるコードの解説
src/cmd/go/build.go
の変更
-
builder
構造体の拡張とビルドメソッドのシグネチャ変更:builder
構造体にgobin
(Goバイナリの出力先),output
(並行処理時の出力同期のためのMutex),scriptDir
(ビルドスクリプト生成時の現在のディレクトリ追跡) が追加されました。これにより、ビルドプロセスがより詳細な状態を管理できるようになりました。cgo
,gc
,asm
,gopack
,ld
,cc
,gcc
,gccld
といったGoツールチェインの各ステップを実行するメソッドの引数が、従来のdir string
からp *Package
に変更されました。これは非常に重要な変更で、これにより各ビルドステップが、単なるディレクトリパスだけでなく、ビルド対象のパッケージに関する豊富な情報(インポートパス、ソースディレクトリ、Cgoフラグなど)にアクセスできるようになりました。これにより、ビルドロジックがよりコンテキストアウェアになり、正確で堅牢な処理が可能になります。例えば、gc(p *Package, ...)
はp.Dir
を使ってソースファイルの絶対パスを構築できるようになりました。
-
fmtcmd
,showcmd
,showOutput
による出力制御の強化:fmtcmd
は、ビルドコマンドの文字列を整形する際に、一時ディレクトリのパスを$WORK
に、GOROOT
を$GOROOT
に、GOBIN
を$GOBIN
に置換するようになりました。さらに、cd
コマンドを自動的に挿入するロジックが追加され、-n
(ドライラン)モードでの出力が、実際に実行可能なシェルスクリプトのように見えるようになりました。showcmd
は、fmtcmd
を使って整形されたコマンドを標準出力に出力します。sync.Mutex
(b.output
) を使用することで、並行ビルド時にも出力が混ざり合わないように同期が取られています。showOutput
は、ビルド中に発生したエラーやその他の出力を整形して表示するために導入されました。特に、エラーメッセージ内の絶対パスを、現在の作業ディレクトリからの相対パスに変換する機能があり、ユーザーがエラーの発生源を特定しやすくなっています。これは、Goのビルドエラーメッセージの可読性を大幅に向上させました。
-
run
関数の改善とerrPrintedOutput
:run
関数は、外部コマンドを実行し、その出力をキャプチャします。このコミットでは、desc
引数が追加され、コマンドの目的を説明する文字列を渡せるようになりました。- コマンドがエラーを返した場合、その出力が
showOutput
によって既に表示されていることを示すために、errPrintedOutput
という特別なエラーを返します。これにより、上位のbuilder.do
関数が、既に表示されたエラーに対してさらに「exit status 1」のような一般的なエラーメッセージを重複して表示するのを防ぎ、出力の冗長性を排除しています。
-
mkAbs
関数の導入:- このヘルパー関数は、与えられたディレクトリとファイル名から絶対パスを生成します。特に、
$WORK
のような擬似パスを適切に処理し、常に絶対パスを返すことで、ビルドコマンドがファイルを参照する際の曖昧さを排除し、エラーメッセージの正確性を保証します。
- このヘルパー関数は、与えられたディレクトリとファイル名から絶対パスを生成します。特に、
src/cmd/go/main.go
の変更
allPackages
関数におけるcmd/
パッケージの認識:allPackages
関数は、go list all
のように、Goが認識する全てのパッケージを列挙するために使用されます。このコミットでは、GOROOT
内のsrc/cmd
ディレクトリを特別に走査し、その中の各ツール(例:gofmt
,go doc
)をcmd/gofmt
のような擬似インポートパスとしてパッケージリストに追加するロジックが組み込まれました。これにより、go
コマンド自身がGoの標準ツールを「パッケージ」として認識し、管理できるようになりました。
src/cmd/go/pkg.go
の変更
loadPackage
関数におけるcmd/
プレフィックスの処理:loadPackage
関数は、与えられたインポートパスからGoパッケージの情報をロードします。このコミットでは、引数がcmd/
で始まる場合(例:cmd/gofmt
)、それをGOROOT
内のsrc/cmd
ディレクトリにある標準ツールとして特別に解決するロジックが追加されました。これにより、ユーザーがgo build cmd/gofmt
と入力した際に、go
コマンドが正しくgofmt
ツールを見つけてビルドできるようになります。
scanPackage
関数におけるruntime/cgo
の自動インポート:scanPackage
関数は、Goソースファイルをスキャンしてパッケージの依存関係を特定します。このコミットでは、パッケージがCgoファイル(.c
,.cc
,.cpp
,.m
,.s
ファイルなど)を含む場合、そのパッケージが暗黙的にruntime/cgo
パッケージをインポートするように変更されました(ただし、runtime/cgo
パッケージ自身を除く)。これは、Cgoを使用するGoプログラムが正しくリンクされるために必要な依存関係を自動的に解決するもので、開発者が手動でruntime/cgo
をインポートする必要がなくなりました。
src/cmd/gc/runtime.go
および src/cmd/gc/unsafe.go
の変更
// +build ignore
ディレクティブの追加:- これらのファイルはGoのランタイムやコンパイラの内部実装に関連するもので、通常のGoビルドプロセスでは直接コンパイルされるべきではありません。
// +build ignore
ディレクティブを追加することで、go build
コマンドがこれらのファイルを無視するようになり、Goツールチェインのブートストラップや特殊なビルドプロセスでのみ処理されるように設計されました。これにより、ビルドの依存関係が明確になり、通常の開発ワークフローでの誤ったコンパイルを防ぎます。
- これらのファイルはGoのランタイムやコンパイラの内部実装に関連するもので、通常のGoビルドプロセスでは直接コンパイルされるべきではありません。
src/cmd/go/list.go
の変更
go list
のデフォルト出力フォーマットの変更:go list
コマンドのデフォルト出力が、パッケージ名とディレクトリ ({{.Name}} {{.Dir}}
) からインポートパス ({{.ImportPath}}
) に変更されました。これは、cmd/gofmt
のような擬似インポートパスの導入と整合性を取るための変更であり、Goのパッケージをインポートパスで識別するというGoの設計思想をより強調するものです。
これらの変更は、Goのビルドシステムがより自己完結的で、堅牢で、そしてユーザーフレンドリーになるための重要なステップであり、Go言語の継続的な進化の基盤を築きました。
関連リンク
- GitHub上のコミットページ: https://github.com/golang/go/commit/fd1c1b9679a2ed9c96bc3ccd74336ba5b23a5049
- Go Code Review: https://golang.org/cl/5503050
参考にした情報源リンク
- Go言語の公式ドキュメント (Goのビルドシステム、パッケージ管理、ツールチェインに関する一般的な情報)
- Go言語のソースコード (特に
cmd/go
ディレクトリ内のファイル) - Go言語の自己ホスティングに関する議論や歴史的背景に関するウェブ記事 (例: Goのビルドプロセスの進化に関するブログ記事やフォーラムの議論)
- https://go.dev/doc/
- https://go.dev/blog/
- https://go.dev/wiki/GoBuild
- https://go.dev/wiki/GoModules (Go Modulesはコミット当時存在しませんが、インポートパスの概念を理解する上で参考になります)
- Goの自己ホスティングに関する歴史的な記事やGo開発者の発言など。 (今回のWeb検索結果も含む)
- https://go.dev/blog/go1.21-toolchain (Go 1.21以降のツールチェインに関する記事ですが、Goのビルドプロセスの進化の方向性を理解する上で参考になります)
- https://www.infoq.com/news/2023/08/go-1-21-release/ (Go 1.21のリリースに関する記事で、ビルドの再現性について触れられています)
- https://go.dev/blog/pseudo-versions (Go Modulesにおける擬似バージョンに関する記事ですが、Goがバージョン管理とインポートパスをどのように扱うかという概念を理解する上で参考になります)
- https://jfrog.com/blog/go-modules-and-pseudo-versions/ (Go Modulesと擬似バージョンに関する記事)