[インデックス 11441] ファイルの概要
このコミットは、Go言語のビルドツールである go
コマンドに gccgo
コンパイラをサポートするための変更を導入しています。具体的には、GC=gccgo
環境変数を設定することで、go build
, go test
, go run
, go install
などのサブコマンドが gccgo
を利用して動作するように拡張されました。これにより、標準のGoディストリビューションと連携しつつ、gccgo
の利用が可能になります。
コミット
commit 45a8fae996700a40bc671bc48e78931d277dee0a
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date: Fri Jan 27 17:05:51 2012 -0500
go: introduce support for "go build" with gccgo.
The use of gccgo is triggered by GC=gccgo in environment. It
still needs the standard distribution to behave properly, but
allows using the test, build, run, install subcommands with
gccgo.
R=rsc, iant, fullung
CC=golang-dev, remy
https://golang.org/cl/5562045
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/45a8fae996700a40bc671bc48e78931d277dee0a
元コミット内容
go: introduce support for "go build" with gccgo.
The use of gccgo is triggered by GC=gccgo in environment. It still needs the standard distribution to behave properly, but allows using the test, build, run, install subcommands with gccgo.
変更の背景
Go言語には、公式のコンパイラである gc
(Go Compiler) と、GCC (GNU Compiler Collection) のフロントエンドとして実装された gccgo
の2つの主要なコンパイラが存在します。gc
はGoチームによって開発され、Go言語の最新機能に迅速に対応し、高速なコンパイルが特徴です。一方、gccgo
はGCCの豊富な最適化機能を活用できるため、生成されるバイナリの実行速度が向上する可能性があります。また、GCCがサポートする幅広いアーキテクチャに対応できるという利点もあります。
このコミットが作成された2012年当時、go
コマンドは主に gc
ツールチェイン(6g
, 6a
, 6l
など)を前提として設計されていました。しかし、gccgo
の利用を望む開発者も存在し、go
コマンドが gccgo
を透過的に利用できるようにすることは、Goエコシステムの柔軟性を高め、より多様な開発環境やデプロイメントシナリオに対応するために重要でした。
この変更の背景には、以下の目的があったと考えられます。
gccgo
の統合:go
コマンドがgccgo
をビルドツールチェインとして認識し、利用できるようにする。- 開発体験の向上:
gccgo
を使用する際にも、go build
,go test
,go run
,go install
といった標準的なgo
コマンドのサブコマンドをそのまま利用できるようにし、開発者の利便性を高める。 - 柔軟なビルド環境の提供: ユーザーが環境変数
GC
を設定するだけで、使用するコンパイラを簡単に切り替えられるようにする。
前提知識の解説
Go言語のビルドプロセス
Go言語のビルドプロセスは、go build
コマンドを中心に非常にシンプルかつ効率的に設計されています。
- 依存関係の解析:
go build
はまず、プロジェクトのソースコードを解析し、必要なパッケージの依存関係を特定します。Go Modules(Go 1.11以降)が導入される前は、GOPATH
というワークスペースの概念に基づいて依存関係が解決されていました。 - コンパイル: 各Goソースファイルは、選択されたGoコンパイラ(通常は
gc
)によって機械語にコンパイルされ、オブジェクトファイルが生成されます。 - リンク: コンパイルされたオブジェクトファイルと、必要なライブラリ(標準ライブラリやサードパーティライブラリ)がリンカによって結合され、単一の実行可能バイナリが生成されます。Goのバイナリは静的にリンクされるため、実行時に外部の依存関係を必要としない自己完結型であることが特徴です。
- クロスコンパイル: Goは、異なるOSやアーキテクチャ向けのバイナリを簡単に生成できるクロスコンパイル機能を標準でサポートしています。
Goツールチェイン (6g
, 6a
, 6l
など)
Go 1.5より前のバージョンでは、Goのビルドプロセスは、特定のアーキテクチャに対応する個別のツール(コンパイラ、アセンブラ、リンカ)を直接呼び出すことで行われていました。これらは通常、GOHOSTARCH
環境変数に基づいて命名されていました。
6g
:amd64
(64-bit) アーキテクチャ向けのGoコンパイラ。Goソースコードをオブジェクトファイルにコンパイルします。6a
:amd64
アーキテクチャ向けのアセンブラ。Goのアセンブリ言語(.s
ファイル)をオブジェクトファイルに変換します。6l
:amd64
アーキテクチャ向けのリンカ。オブジェクトファイルを結合して実行可能バイナリを生成します。- 同様に、
5g
,5a
,5l
はarm
アーキテクチャ向け、8g
,8a
,8l
は386
アーキテクチャ向けでした。
Go 1.5以降では、go build
コマンドがこれらのツールを内部で自動的に呼び出すようになり、開発者が直接これらのツールを操作する必要はほとんどなくなりました。しかし、このコミットが作成された時点では、これらのツールがビルドプロセスの中心的な役割を担っていました。
gc
と gccgo
-
gc
(Go Compiler):- Go言語の公式コンパイラであり、Goチームによって開発されています。
- Go言語の最新の仕様や機能に最も早く対応します。
- コンパイル速度が非常に高速です。
- 生成されるバイナリは、
gccgo
に比べて最適化の面で劣る場合がありますが、多くの場合十分なパフォーマンスを発揮します。 - Goのランタイムと密接に統合されています。
-
gccgo
:- GCC (GNU Compiler Collection) のフロントエンドとして実装されたGoコンパイラです。
- GCCの強力な最適化パイプラインを利用できるため、生成されるバイナリの実行時パフォーマンスが
gc
よりも優れる場合があります。 - GCCがサポートする幅広いCPUアーキテクチャ(SPARC, MIPS, PowerPCなど)に対応できます。
- GCCのリリースサイクルに依存するため、Go言語の最新機能への対応が
gc
よりも遅れる傾向があります。 - バイナリサイズが小さくなる可能性があります(
libgo
ライブラリを動的にリンクする場合)。 gc
とは異なり、エスケープ解析を行いません。
GC
環境変数
このコミットでは、GC
という新しい環境変数が導入されています。この変数は、go
コマンドがどのGoコンパイラツールチェインを使用するかを決定するために利用されます。
GC
が設定されていない、またはGC=go
の場合: デフォルトのgc
ツールチェインが使用されます。GC=gccgo
の場合:gccgo
ツールチェインが使用されます。
これは、Goのガベージコレクタ (GC) の動作を制御する GOGC
環境変数とは異なる、ビルドツールチェインを選択するための環境変数です。
技術的詳細
このコミットの主要な技術的変更点は、Goのビルドプロセスにおけるコンパイラ、アセンブラ、リンカ、パッカーといったツールチェインの呼び出しを抽象化し、gc
と gccgo
の両方に対応できるようにした点です。
具体的には、以下の変更が行われました。
-
toolchain
インターフェースの導入:src/cmd/go/build.go
にtoolchain
という新しいインターフェースが定義されました。このインターフェースは、Goのビルドプロセスで必要となる主要な操作(コンパイル、アセンブル、リンク、パッケージング、パッケージパスの決定)を抽象化します。type toolchain interface { // gc runs the compiler in a specific directory on a set of files // and returns the name of the generated output file. gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error) // cc runs the toolchain's C compiler in a directory on a C file // to produce an output file. cc(b *builder, p *Package, objdir, ofile, cfile string) error // asm runs the assembler in a specific directory on a specific file // to generate the named output file. asm(b *builder, p *Package, obj, ofile, sfile string) error // pkgpath creates the appropriate destination path for a package file. pkgpath(basedir string, p *Package) string // pack runs the archive packer in a specific directory to create // an archive from a set of object files. // typically it is run in the object directory. pack(b *builder, p *Package, objDir, afile string, ofiles []string) error // ld runs the linker to create a package starting at mainpkg. ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error }
-
goToolchain
とgccgoToolchain
の実装:toolchain
インターフェースを実装する2つの構造体goToolchain
とgccgoToolchain
が導入されました。goToolchain
: 従来の6g
,6a
,6l
などのGoツールチェインのコマンドを呼び出す実装を提供します。gccgoToolchain
:gccgo
,gcc
,ar
などのGCC関連のコマンドを呼び出す実装を提供します。特に、pkgpath
メソッドではgccgo
のライブラリ命名規則に合わせてlib
プレフィックスを追加するロジックが含まれています。また、ld
メソッドではgccgo
が明示的なパッケージ依存関係のリンクを必要とするため、allactions
からすべての依存パッケージのアーカイブファイルとCgoのリンカフラグを収集して渡すように変更されています。
-
buildToolchain
変数による動的なツールチェイン選択:src/cmd/go/build.go
のinit
関数内で、GC
環境変数の値に基づいてbuildToolchain
グローバル変数がgoToolchain
またはgccgoToolchain
のいずれかに初期化されるようになりました。var buildToolchain toolchain func init() { if os.Getenv("GC") == "gccgo" { buildToolchain = gccgoToolchain{} } else { buildToolchain = goToolchain{} } }
-
既存のビルドロジックの変更:
src/cmd/go/build.go
内のbuilder
構造体のメソッド(gc
,cc
,asm
,gopack
,ld
など)が、直接ツールチェインコマンドを呼び出す代わりに、buildToolchain
インターフェースの対応するメソッドを呼び出すように変更されました。これにより、ビルドロジック自体はツールチェインの実装に依存せず、抽象化されたインターフェースを通じて操作されるようになりました。 -
パッケージパスとターゲットパスの調整:
src/cmd/go/pkg.go
とsrc/cmd/go/test.go
において、パッケージのターゲットパス (p.target
) やテスト関連のオブジェクトファイルのパスが、buildToolchain.pkgpath
メソッドを通じて決定されるように変更されました。これにより、gccgo
が期待するライブラリの命名規則(例:lib<package>.a
)に適切に対応できるようになりました。 -
Cgoのサポート:
src/cmd/go/build.go
のcgo
関数において、gccgo
ツールチェインが選択されている場合にcgo
コマンドに-gccgo
フラグを渡すように変更されました。これにより、Cgoの処理もgccgo
に対応できるようになります。
これらの変更により、go
コマンドは、ユーザーが GC=gccgo
を設定するだけで、内部的に gccgo
ツールチェインを透過的に利用してGoプログラムをビルド、テスト、実行、インストールできるようになりました。
コアとなるコードの変更箇所
このコミットによる主要なコード変更は以下の3つのファイルに集中しています。
-
src/cmd/go/build.go
:toolchain
インターフェースの定義と、goToolchain
およびgccgoToolchain
構造体の実装が追加されました。init
関数内でGC
環境変数に基づいてbuildToolchain
が初期化されるロジックが追加されました。builder
構造体のgc
,cc
,asm
,gopack
(→pack
),ld
メソッドが、直接ツールコマンドを呼び出す代わりに、buildToolchain
インターフェースの対応するメソッドを呼び出すように変更されました。includeArgs
関数で、gccgo
の場合にパッケージディレクトリのパスにgccgo
サブディレクトリを追加するロジックが追加されました。cgo
関数で、gccgo
ツールチェインの場合にcgo
コマンドに-gccgo
フラグを渡すように変更されました。
-
src/cmd/go/pkg.go
:scanPackage
関数内で、非標準パッケージのターゲットパス (p.target
) がbuildToolchain.pkgpath
を使用して決定されるように変更されました。これにより、gccgo
の命名規則に対応できるようになります。
-
src/cmd/go/test.go
:- テスト関連のオブジェクトファイルパス (
ptestObj
) がbuildToolchain.pkgpath
を使用して決定されるように変更されました。
- テスト関連のオブジェクトファイルパス (
コアとなるコードの解説
toolchain
インターフェース
このインターフェースは、Goのビルドプロセスにおける各ステップ(コンパイル、アセンブル、リンク、パッケージング、パス解決)を抽象化します。これにより、異なるコンパイラツールチェイン(gc
と gccgo
)がこれらの操作をそれぞれ独自の方法で実装できるようになります。
type toolchain interface {
gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error)
cc(b *builder, p *Package, objdir, ofile, cfile string) error
asm(b *builder, p *Package, obj, ofile, sfile string) error
pkgpath(basedir string, p *Package) string
pack(b *builder, p *Package, objDir, afile string, ofiles []string) error
ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error
}
gc
: Goソースファイルをコンパイルします。cc
: Cソースファイルをコンパイルします(Cgo用)。asm
: アセンブリファイルをアセンブルします。pkgpath
: パッケージのアーカイブファイルのパスを生成します。gccgo
の場合、lib
プレフィックスが付くなどの違いを吸収します。pack
: オブジェクトファイルをアーカイブ(ライブラリ)にまとめます。ld
: リンクを行い、実行可能バイナリまたは共有ライブラリを生成します。
goToolchain
の実装
goToolchain
は、従来のGoツールチェイン(6g
, 6a
, 6l
など)を呼び出す実装を提供します。
gc
:b.arch + "g"
(例:6g
) コマンドを呼び出します。runtime
パッケージのコンパイル時には-+
フラグを追加します。asm
:b.arch + "a"
(例:6a
) コマンドを呼び出します。pkgpath
:filepath.Join(basedir, filepath.FromSlash(p.ImportPath+".a"))
の形式でパッケージアーカイブパスを生成します。pack
:gopack
コマンドを呼び出します。ld
:b.arch + "l"
(例:6l
) コマンドを呼び出します。cc
:b.arch + "c"
(例:6c
) コマンドを呼び出します。
gccgoToolchain
の実装
gccgoToolchain
は、gccgo
, gcc
, ar
などのGCC関連のコマンドを呼び出す実装を提供します。
gc
:gccgo
コマンドを呼び出します。パッケージ名に応じて-fgo-prefix
フラグを追加します。asm
:gccgo
コマンドを呼び出します。pkgpath
:filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile))
の形式でパッケージアーカイブパスを生成します。これはgccgo
が生成するライブラリファイル名がlib
で始まる慣例に対応するためです。pack
:ar
コマンドを呼び出します。ld
:gccgo
コマンドを呼び出します。gccgo
は明示的な依存関係のリンクを必要とするため、allactions
からすべてのパッケージのアーカイブファイルとCgoのリンカフラグを収集し、-Wl,-(
と-Wl,-)
で囲んで渡します。cc
:gcc
コマンドを呼び出します。
buildToolchain
の動的な選択
src/cmd/go/build.go
の init
関数で、os.Getenv("GC")
の値に基づいて buildToolchain
が goToolchain
または gccgoToolchain
のいずれかに設定されます。これにより、go
コマンドの実行時に使用するツールチェインが動的に切り替わります。
この設計により、go
コマンドのビルドロジックは、特定のツールチェインの実装に依存することなく、抽象化されたインターフェースを通じて動作するようになり、将来的に他のツールチェインをサポートする際にも拡張が容易になります。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/45a8fae996700a40bc671bc48e78931d277dee0a
- Go Code Review (CL): https://golang.org/cl/5562045
参考にした情報源リンク
gccgo
の概要:- https://go.dev/doc/install/gccgo
- https://stackoverflow.com/questions/10079493/what-is-the-difference-between-gc-and-gccgo
- Goビルドプロセス:
- https://codedamn.com/news/go/go-build-process
- https://www.digitalocean.com/community/tutorials/how-to-build-go-applications-for-multiple-platforms-on-ubuntu-20-04
- Goツールチェイン (
6g
,6a
,6l
):- https://www.cheney.net/articles/go-toolchain-6g-6a-6l
- https://www.cs.cmu.edu/~410/doc/go-toolchain.pdf
- Go環境変数 (
GOGC
など):- https://englyk.com/go-garbage-collector-gogc-gomemlimit/
- https://dev.to/joshuabaker/understanding-go-garbage-collection-and-gogc-300k
- https://go.dev/doc/diagnostics#godebug