[インデックス 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と擬似バージョンに関する記事)