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

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

このコミットは、Go言語のビルドツールであるgoコマンドに、使用するコンパイラを指定するための-compilerフラグを追加し、それに伴いgo/buildパッケージにContext.Compilerフィールドを導入する変更です。これにより、gc(標準Goコンパイラ)とgccgo(GCCベースのGoコンパイラ)の切り替えがより柔軟に行えるようになります。

コミット

commit 347cc981f043193dc9b29e92b485b158aa6c85f8
Author: Russ Cox <rsc@golang.org>
Date:   Tue Mar 6 00:36:24 2012 -0500

    cmd/go: add -compiler
    go/build: add Context.Compiler
    
    Fixes #3157.
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/5756047

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

https://github.com/golang/go/commit/347cc981f043193dc9b29e92b485b158aa6c85f8

元コミット内容

cmd/go: add -compiler go/build: add Context.Compiler

Fixes #3157.

変更の背景

Go言語の初期のビルドシステムでは、コンパイラの選択が環境変数GCに依存していました。具体的には、GC=gccgoを設定することでgccgoコンパイラを使用し、それ以外の場合はデフォルトのgcコンパイラが使用されていました。しかし、このような環境変数による制御は、ビルドの柔軟性やコマンドラインからの直接的な制御の点で限界がありました。

このコミットは、Goのビルドプロセスにおいて、より明示的かつコマンドラインから直接コンパイラを指定できるようにすることを目的としています。これにより、ユーザーはgo buildgo installなどのコマンド実行時に、-compilerフラグを使ってgcまたはgccgoを簡単に切り替えられるようになります。これは、異なるコンパイラでのテストや、特定のコンパイラに依存するビルド環境での作業において、開発者の利便性を向上させます。

コミットメッセージにあるFixes #3157は、この変更がGoプロジェクトのIssue 3157を解決することを示唆しています。このIssueは、Goツールチェーンにおけるコンパイラ選択の柔軟性に関する議論や要望であったと推測されます。

前提知識の解説

Go言語のビルドシステム

Go言語のビルドシステムは、ソースコードをコンパイルし、実行可能なバイナリやライブラリを生成する一連のツールとプロセスを指します。主要なツールはgoコマンドであり、go buildgo installgo testなどのサブコマンドを通じて、コンパイル、リンク、テストなどの操作を行います。

Goコンパイラ(gc)

gcは、Go言語の公式かつデフォルトのコンパイラです。Go言語のソースコードを機械語に変換し、実行可能なバイナリを生成します。Go言語の進化に合わせて開発されており、Goのランタイムと密接に連携しています。

GCCベースのGoコンパイラ(gccgo)

gccgoは、GCC(GNU Compiler Collection)をバックエンドとして使用するGo言語のコンパイラです。gcとは異なる実装であり、GCCの最適化機能や、C/C++とのより深い連携(Cgoを介した既存のC/C++ライブラリとのリンクなど)を利用できる場合があります。gccgoは、特に既存のC/C++プロジェクトとの統合や、特定のプラットフォームでのパフォーマンス要件がある場合に選択肢となり得ます。

Goのツールチェーン

Goのツールチェーンとは、Goプログラムの開発に必要な一連のツール(コンパイラ、リンカ、アセンブラ、パッケージマネージャなど)の集合体を指します。goコマンドは、これらのツールを統合的に管理し、ビルドプロセスを自動化します。

go/buildパッケージ

go/buildパッケージは、Goのソースコードパッケージの構造を解析し、ビルドに必要な情報を取得するための標準ライブラリです。このパッケージは、Goのビルドシステムがソースファイルを特定し、依存関係を解決し、適切なコンパイラやツールチェーンのパスを決定するために使用されます。Context構造体は、ビルド環境に関する情報(OS、アーキテクチャ、ビルドタグなど)を保持します。

技術的詳細

このコミットの技術的な核心は、Goのビルドシステムがコンパイラを動的に選択できるようにするためのメカニズムの導入です。

  1. cmd/goにおける-compilerフラグの追加: src/cmd/go/build.goにおいて、goコマンドのビルドフラグとして-compiler nameが追加されました。nameにはgcまたはgccgoを指定できます。このフラグは、buildCompilerというflag.Varインターフェースを実装するカスタム型によって処理されます。Setメソッドが呼び出されると、指定されたコンパイラ名に基づいてbuildToolchain変数が適切なツールチェーン実装(gcToolchainまたはgccgcToolchain)に設定され、同時にbuildContext.Compilerも更新されます。

  2. go/buildパッケージへのContext.Compilerの追加: src/pkg/go/build/build.goContext構造体にCompiler stringフィールドが追加されました。これは、ビルドコンテキストが現在どのコンパイラを使用しているかを追跡するためのものです。以前はGccgo boolというブーリアンフラグでgccgoの使用を判断していましたが、より汎用的なCompiler文字列に置き換えられました。これにより、将来的に他のコンパイラが追加された場合でも、既存の構造体を拡張することなく対応できるようになります。

  3. ツールチェーン抽象化の改善: src/cmd/go/build.goには、toolchainインターフェースが定義されており、compiler()linker()gc()asm()などのメソッドを通じて、各コンパイラ(gcgccgo)固有のビルド操作を抽象化しています。このコミットでは、goToolchaingcToolchainに、gccgoToolchaingccgcToolchainにリネームされ、より明確な命名になりました。また、noToolchainという新しい型が追加され、コンパイラが未設定の場合のエラーハンドリングが強化されました。

  4. オブジェクトパス計算の変更: src/pkg/go/build/build.goContext.Importメソッド内で、パッケージのオブジェクトパス(.aファイルのパス)を計算するロジックがctxt.Gccgoブーリアンからctxt.Compiler文字列に依存するように変更されました。これにより、gcgccgoで異なるパス構造(例: pkg/gccgo/ vs pkg/GOOS_GOARCH/)が適切に処理されます。

  5. testflag.goの更新: src/cmd/go/testflag.goでは、テストコマンドに渡されるフラグの定義に-compilerが追加され、テスト実行時にもコンパイラを指定できるようになりました。

これらの変更により、Goのビルドシステムは、コンパイラの選択に関してより柔軟で、将来の拡張性を持つ設計へと進化しました。

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

src/cmd/go/build.go

  • buildCompilerというflag.Varを実装する新しい型が追加され、-compilerフラグの処理を担う。
  • addBuildFlags関数にcmd.Flag.Var(buildCompiler{}, "compiler", "")が追加され、-compilerフラグを登録。
  • buildToolchainの初期化ロジックが、環境変数GCからbuild.Default.Compilerの値に基づいて行われるように変更。
  • actionメソッドやincludeArgsメソッド内で、buildToolchain.(gccgoToolchain)の型アサーションがbuildToolchain.(gccgcToolchain)に修正。
  • toolchainインターフェースの実装として、noToolchaingcToolchaingccgcToolchainが定義され、それぞれがcompiler(), linker(), gc(), asm(), pkgpath(), pack(), ld(), cc()などのメソッドを実装。特に、goToolchaingcToolchainに、gccgoToolchaingccgcToolchainにリネームされた。
  • cgo関数内で、buildToolchain.(gccgoToolchain)の型アサーションがbuildToolchain.(gccgcToolchain)に修正。

src/cmd/go/testflag.go

  • testFlagDefn{name: "compiler"}が追加され、テストコマンドで-compilerフラグが認識されるように。
  • testFlags関数内で、case "compiler": buildContext.Compiler = valueが追加され、テスト実行時のコンパイラ設定を反映。

src/pkg/go/build/build.go

  • Context構造体からGccgo boolフィールドが削除され、代わりにCompiler stringフィールドが追加。
  • defaultContext()関数で、c.Compiler = runtime.Compilerが追加され、デフォルトのコンパイラが設定されるように。
  • Context.Importメソッド内で、パッケージのアーカイブファイル(.a)のパスを決定するロジックがctxt.Gccgoからctxt.Compilerに依存するように変更。switch ctxt.Compiler文が導入され、gcgccgoで異なるパス生成ロジックが適用される。
  • binaryOnlyの条件式にpkga != ""が追加され、pkgaが空でない場合にのみバイナリのみのインポートが許可されるように。
  • p.PkgObjの設定がif pkga != ""の条件付きになり、pkgaが空の場合は設定されないように。
  • Context.Importの戻り値で、pkgerrが返されるように修正され、不明なコンパイラが指定された場合にエラーが伝播されるように。

コアとなるコードの解説

このコミットの最も重要な変更は、Goのビルドシステムがコンパイラを抽象化し、コマンドラインから選択できるようにした点です。

src/cmd/go/build.gobuildCompiler型は、flag.Varインターフェースを実装しています。これにより、goコマンドは-compilerフラグを受け取ることができます。Setメソッドが呼び出されると、ユーザーが指定したコンパイラ名(例: "gc"や"gccgo")に基づいて、グローバル変数buildToolchainが適切なツールチェーン実装(gcToolchainまたはgccgcToolchain)に設定されます。同時に、go/buildパッケージのbuildContext.Compilerフィールドも更新され、ビルドコンテキスト全体で選択されたコンパイラが認識されるようになります。

// src/cmd/go/build.go
type buildCompiler struct{}

func (c buildCompiler) Set(value string) error {
	switch value {
	case "gc":
		buildToolchain = gcToolchain{}
	case "gccgo":
		buildToolchain = gccgcToolchain{}
	default:
		return fmt.Errorf("unknown compiler %q", value)
	}
	buildContext.Compiler = value
	return nil
}

func (c buildCompiler) String() string {
	return buildContext.Compiler
}

toolchainインターフェースは、コンパイラ、リンカ、アセンブラなどの各ツールが提供すべき操作を定義しています。gcToolchaingccgcToolchainはそれぞれこのインターフェースを実装し、各コンパイラ固有のコマンドや引数をラップしています。これにより、goコマンドのビルドロジックは、具体的なコンパイラの実装に依存することなく、抽象化されたtoolchainインターフェースを通じて操作を実行できます。

// src/cmd/go/build.go (抜粋)
type toolchain interface {
	compiler() string
	linker() string
	gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err 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
	cc(b *builder, p *Package, objdir, ofile, cfile string) error
}

src/pkg/go/build/build.goContext構造体へのCompilerフィールドの追加は、ビルドプロセス全体でコンパイラの選択を伝播させるために重要です。特に、Context.Importメソッドは、パッケージのインポートパスに基づいて、対応するアーカイブファイル(.a)のパスを決定します。このパスはコンパイラによって異なる場合があるため、Compilerフィールドの値に基づいて適切なパスが生成されるようにロジックが変更されました。

// src/pkg/go/build/build.go (抜粋)
type Context struct {
	// ...
	Compiler    string   // compiler to assume when computing target paths
	// ...
}

func (ctxt *Context) Import(path string, src string, mode ImportMode) (*Package, error) {
	// ...
	var pkga string
	var pkgerr error
	switch ctxt.Compiler {
	case "gccgo":
		dir, elem := pathpkg.Split(p.ImportPath)
		pkga = "pkg/gccgo/" + dir + "lib" + elem + ".a"
	case "gc":
		pkga = "pkg/" + ctxt.GOOS + "_" + ctxt.GOARCH + "/" + p.ImportPath + ".a"
	default:
		// Save error for end of function.
		pkgerr = fmt.Errorf("import %q: unknown compiler %q", path, ctxt.Compiler)
	}
	// ...
	return p, pkgerr
}

これらの変更により、Goのビルドシステムは、コンパイラの選択をより柔軟に、かつ明示的に制御できるようになり、異なるコンパイラ環境での開発とテストが容易になりました。

関連リンク

参考にした情報源リンク