[インデックス 12330] ファイルの概要
このコミットは、Goコマンド(cmd/go
)におけるgccgo
コンパイラ関連の修正と、pkgpath
関数から不要になったinstall
引数を削除する変更を含んでいます。これにより、go/build
パッケージがパッケージのインストール場所をより適切に定義するようになったことに対応し、gccgo
使用時のビルドプロセスにおけるリンカの挙動が改善されています。
コミット
cmd/go: fixes for gccgo.
Also remove useless "install" argument to pkgpath now that go/build
defines package install locations.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5714059
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/11e7eabb7ec76cd1ba43058234627fa938ff9b48
元コミット内容
commit 11e7eabb7ec76cd1ba43058234627fa938ff9b48
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date: Fri Mar 2 08:36:53 2012 +0100
cmd/go: fixes for gccgo.
Also remove useless "install" argument to pkgpath now that go/build
defines package install locations.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5714059
変更の背景
このコミットには主に二つの背景があります。
-
gccgo
コンパイラへの対応と修正: Go言語には、公式のgc
コンパイラ(Goコンパイラ)と、GCCをバックエンドとするgccgo
コンパイラの二種類が存在します。gccgo
は、特にC/C++との連携(Cgo)や、既存のGCCツールチェーンとの統合において異なる挙動を示すことがあります。このコミットは、gccgo
使用時に発生していたビルドやリンクに関する問題を解決することを目的としています。特に、パッケージのアーカイブファイル(.a
)のパス解決や、リンカへの引数渡し方においてgccgo
の特性に合わせた調整が必要でした。 -
go/build
パッケージによるインストール場所の定義: Goのビルドシステムは進化しており、go/build
パッケージがGoのソースコードやパッケージの構造、そしてそれらのビルド成果物の配置場所に関する情報をより詳細に提供するようになりました。これに伴い、以前はpkgpath
関数がinstall
というブール引数を使って「一時的なビルド成果物」と「最終的なインストール先」のパスを区別していましたが、go/build
パッケージが提供する情報(例:PkgObj
)で十分になったため、このinstall
引数が冗長になりました。このコミットは、この冗長な引数を削除し、コードベースを簡素化することを目的としています。
これらの変更は、Goのビルドシステムの堅牢性と柔軟性を高め、特にgccgo
のような代替コンパイラを使用する際の互換性を向上させるために行われました。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびビルドシステムに関する知識が役立ちます。
- Goツールチェーン (
gc
とgccgo
):gc
: Go言語の公式コンパイラであり、Goのソースコードをネイティブバイナリにコンパイルします。ほとんどのGo開発者が日常的に使用するものです。gccgo
: GCC(GNU Compiler Collection)をバックエンドとして使用するGoコンパイラです。gc
とは異なる最適化や、C/C++ライブラリとの連携において異なるアプローチを取ることがあります。特に、リンカの挙動やライブラリの検索パスにおいてgc
とは異なる要件を持つことがあります。
cmd/go
:- Go言語のコマンドラインツールであり、Goのソースコードのビルド、テスト、インストール、フォーマットなど、Go開発における主要な操作を統括します。このコマンドは、内部的にコンパイラ(
gc
またはgccgo
)、アセンブラ、リンカなどのツールを呼び出して処理を実行します。
- Go言語のコマンドラインツールであり、Goのソースコードのビルド、テスト、インストール、フォーマットなど、Go開発における主要な操作を統括します。このコマンドは、内部的にコンパイラ(
go/build
パッケージ:- Goの標準ライブラリの一部であり、Goのソースコードパッケージに関する情報(インポートパス、ソースファイルのリスト、依存関係、ビルドタグなど)をプログラム的に取得するための機能を提供します。このパッケージは、Goコマンドがパッケージを解決し、ビルドプランを立てる際に利用されます。特に、
Package
構造体には、ビルド成果物のパスに関する情報(例:PkgRoot
,PkgObj
)が含まれます。
- Goの標準ライブラリの一部であり、Goのソースコードパッケージに関する情報(インポートパス、ソースファイルのリスト、依存関係、ビルドタグなど)をプログラム的に取得するための機能を提供します。このパッケージは、Goコマンドがパッケージを解決し、ビルドプランを立てる際に利用されます。特に、
pkgpath
関数:- Goコマンドの内部で使用される関数で、特定のGoパッケージのビルド成果物(通常はアーカイブファイル
.a
)が配置されるべきパスを生成する役割を担います。このパスは、一時的なビルドディレクトリ内であったり、最終的なインストール場所であったりします。
- Goコマンドの内部で使用される関数で、特定のGoパッケージのビルド成果物(通常はアーカイブファイル
- Cgo:
- GoプログラムからC言語のコードを呼び出すためのGoの機能です。Cgoを使用すると、GoとCのコードを混在させることができます。CgoでビルドされたGoパッケージは、Cのライブラリにリンクする必要があるため、リンカに対して追加のフラグ(
LDFLAGS
)を渡す必要があります。
- GoプログラムからC言語のコードを呼び出すためのGoの機能です。Cgoを使用すると、GoとCのコードを混在させることができます。CgoでビルドされたGoパッケージは、Cのライブラリにリンクする必要があるため、リンカに対して追加のフラグ(
$WORK
ディレクトリ:- Goコマンドがビルドプロセス中に一時的なファイル(オブジェクトファイル、アーカイブファイルなど)を生成するために使用する作業ディレクトリです。このディレクトリは通常、ビルドが完了すると削除されます。
技術的詳細
このコミットの技術的な変更点は多岐にわたりますが、主要なものは以下の通りです。
-
pkgpath
関数のシグネチャ変更:toolchain
インターフェース内のpkgpath
メソッドのシグネチャが、pkgpath(basedir string, p *Package, install bool) string
からpkgpath(basedir string, p *Package) string
に変更されました。- これにより、
install
というブール引数が削除されました。これは、go/build
パッケージが提供するPackage
構造体(特にPkgObj
フィールド)が、パッケージのビルド成果物の適切なパスをすでに定義しているため、pkgpath
関数内でインストールパスを別途計算する必要がなくなったためです。 goToolchain
とgccgoToolchain
の両方で、この新しいシグネチャに合わせてpkgpath
の実装が更新されました。goToolchain.pkgpath
からはinstall
引数に基づく条件分岐が削除され、常にfilepath.Join(basedir, end)
を返すようになりました。gccgoToolchain.pkgpath
も同様に簡素化されました。
-
gccgo
ツールチェーンのリンカ処理の改善 (gccgoToolchain.ld
):gccgo
は、gc
とは異なり、リンカに対してすべてのパッケージ依存関係を明示的に渡す必要があります。また、Cgoを使用している場合、Cgo関連のリンカフラグ(CgoLDFLAGS
)も適切に処理する必要があります。- このコミットでは、
gccgoToolchain.ld
メソッドが大幅にリファクタリングされました。- 以前は
afiles
(アーカイブファイル)とldflags
(リンカフラグ)を単純なスライスで収集していましたが、新しい実装ではmap[*Package]string
を使用して、各パッケージのアーカイブファイルを重複なく収集するように変更されました。これにより、同じパッケージが複数回依存関係として現れても、リンカに渡されるアーカイブファイルは一つだけになります。 - Cgoのリンカフラグ(
a.p.CgoLDFLAGS
)は、cgoldflags
という別のスライスに収集され、最終的にldflags
に結合されます。これにより、Cgoのリンカフラグが適切にリンカに渡されることが保証されます。 - 最終的な
b.run
呼び出しでは、afiles
とcgoldflags
が結合されたldflags
が-Wl,-(
と-Wl,-)
で囲まれてリンカに渡されます。これは、GCCリンカがライブラリの依存関係を解決するための一般的な慣習です。
- 以前は
-
buildContext.Gccgo
フラグの設定:src/cmd/go/build.go
のinit
関数内で、環境変数GC
がgccgo
に設定されている場合、buildContext.Gccgo = true
が設定されるようになりました。これにより、ビルドコンテキストがgccgo
を使用していることを明示的に認識できるようになり、gccgo
固有のビルドロジックの適用が容易になります。
-
pkg.go
とtest.go
でのpkgpath
呼び出しの更新:src/cmd/go/pkg.go
のPackage.load
関数内で、p.target
を設定する際にbuildToolchain.pkgpath(p.build.PkgRoot, p, true)
からp.build.PkgObj
に直接変更されました。これは、go/build
パッケージが提供するPkgObj
がすでに適切なターゲットパスを提供しているため、pkgpath
を呼び出す必要がなくなったことを示しています。src/cmd/go/test.go
のbuilder.test
関数内でも、pkgpath
の呼び出しが新しいシグネチャに合わせて更新されました。
これらの変更は、Goのビルドシステムがgccgo
のような代替コンパイラをよりスムーズにサポートし、同時にコードベースの簡素化とgo/build
パッケージの機能活用を進めるための重要なステップです。
コアとなるコードの変更箇所
src/cmd/go/build.go
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -432,7 +432,7 @@ func (b *builder) action(mode buildMode, depMode buildMode, p *Package) *action
prefix = "local"
}
a.objdir = filepath.Join(b.work, prefix, a.p.ImportPath, "_obj") + string(filepath.Separator)
- a.objpkg = buildToolchain.pkgpath(b.work+"/"+prefix, a.p, false)
+ a.objpkg = buildToolchain.pkgpath(b.work+"/"+prefix, a.p)
a.link = p.Name == "main"
switch mode {
@@ -1083,8 +1083,8 @@ type toolchain interface {
// 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, install bool) string
+ // pkgpath builds an appropriate path for a temporary 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.
@@ -1104,6 +1104,7 @@ var buildToolchain toolchain
func init() {
// TODO(rsc): Decide how to trigger gccgo. Issue 3157.
if os.Getenv("GC") == "gccgo" {
+ buildContext.Gccgo = true
buildToolchain = gccgoToolchain{}
} else {
buildToolchain = goToolchain{}
@@ -1142,11 +1143,8 @@ func (goToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) error {
return b.run(p.Dir, p.Dir, p.ImportPath, tool(archChar+"a"), "-I", obj, "-o", ofile, "-DGOOS_"+goos, "-DGOARCH_"+goarch, sfile)
}
-func (goToolchain) pkgpath(basedir string, p *Package, install bool) string {
+func (goToolchain) pkgpath(basedir string, p *Package) string {
end := filepath.FromSlash(p.ImportPath + ".a")
- if install {
- return filepath.Join(basedir, buildContext.GOOS+"_"+buildContext.GOARCH, end)
- }
return filepath.Join(basedir, end)
}
@@ -1206,10 +1204,9 @@ func (gccgoToolchain) asm(b *builder, p *Package, obj, ofile, sfile string) erro
return b.run(p.Dir, p.Dir, p.ImportPath, "gccgo", "-I", obj, "-o", ofile, "-DGOOS_"+goos, "-DGOARCH_"+goarch, sfile)
}
-func (gccgoToolchain) pkgpath(basedir string, p *Package, install bool) string {
- // NOTE: Apparently gccgo does not distinguish different trees
- // using goos_goarch, so install is ignored here.
- afile := filepath.Join(basedir, "gccgo", filepath.FromSlash(p.ImportPath+".a"))
+func (gccgoToolchain) pkgpath(basedir string, p *Package) string {
+ end := filepath.FromSlash(p.ImportPath + ".a")
+ afile := filepath.Join(basedir, end)
// add "lib" to the final element
return filepath.Join(filepath.Dir(afile), "lib"+filepath.Base(afile))
}
@@ -1224,21 +1221,25 @@ func (gccgoToolchain) pack(b *builder, p *Package, objDir, afile string, ofiles
func (tools gccgoToolchain) ld(b *builder, p *Package, out string, allactions []*action, mainpkg string, ofiles []string) error {
// gccgo needs explicit linking with all package dependencies,
- // and all LDFLAGS from cgo dependencies
- afiles := []string{}
+ // and all LDFLAGS from cgo dependencies.
+ afiles := make(map[*Package]string)
ldflags := []string{}
- seen := map[*Package]bool{}
+ cgoldflags := []string{}
for _, a := range allactions {
- if a.p != nil && !seen[a.p] {
- seen[a.p] = true
+ if a.p != nil {
if !a.p.Standard {
- afiles = append(afiles, a.target)
+ if afiles[a.p] == "" || a.objpkg != a.target {
+ afiles[a.p] = a.target
+ }
}
- ldflags = append(ldflags, a.p.CgoLDFLAGS...)
+ cgoldflags = append(cgoldflags, a.p.CgoLDFLAGS...)
}
}
-
- return b.run(p.Dir, p.Dir, p.ImportPath, "gccgo", "-o", out, buildGccgoflags, ofiles, "-Wl,-(\", afiles, ldflags, "-Wl,-)")
+ for _, afile := range afiles {
+ ldflags = append(ldflags, afile)
+ }
+ ldflags = append(ldflags, cgoldflags...)
+ return b.run(p.Dir, p.Dir, p.ImportPath, "gccgo", "-o", out, buildGccgoflags, ofiles, "-Wl,-(\", ldflags, "-Wl,-)")
}
func (gccgoToolchain) cc(b *builder, p *Package, objdir, ofile, cfile string) error {
src/cmd/go/pkg.go
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -295,7 +295,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
// No permanent install target.
p.target = ""
} else {
- p.target = buildToolchain.pkgpath(p.build.PkgRoot, p, true)
+ p.target = p.build.PkgObj
}
importPaths := p.Imports
src/cmd/go/test.go
--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -424,7 +424,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\
// We write the external test package archive to
// $WORK/unicode/utf8/_test/unicode/utf8_test.a.
testDir := filepath.Join(b.work, filepath.FromSlash(p.ImportPath+"/_test"))
- ptestObj := buildToolchain.pkgpath(testDir, p, false)
+ ptestObj := buildToolchain.pkgpath(testDir, p)
// Create the directory for the .a files.
ptestDir, _ := filepath.Split(ptestObj)
@@ -483,7 +483,7 @@ func (b *builder) test(p *Package) (buildAction, runAction, printAction *action,\
}
a := b.action(modeBuild, modeBuild, pxtest)
a.objdir = testDir + string(filepath.Separator)
- a.objpkg = buildToolchain.pkgpath(testDir, pxtest, false)
+ a.objpkg = buildToolchain.pkgpath(testDir, pxtest)
a.target = a.objpkg
}
コアとなるコードの解説
src/cmd/go/build.go
の変更点
-
toolchain
インターフェースのpkgpath
メソッドの変更:pkgpath(basedir string, p *Package, install bool) string
からpkgpath(basedir string, p *Package) string
へ変更されました。- これは、
install
というブール引数が不要になったことを示しています。以前は、この引数によって一時的なビルドパスと最終的なインストールパスを区別していましたが、go/build
パッケージが提供するPackage
構造体(特にPkgObj
フィールド)が、すでに適切なビルド成果物のパスを定義するようになったため、この区別が不要になりました。
-
goToolchain.pkgpath
の実装変更:install
引数に基づく条件分岐が削除されました。- 常に
filepath.Join(basedir, end)
を返すようになり、パス生成ロジックが簡素化されました。これは、pkgpath
が一時的なビルド成果物のパスを生成する役割に特化し、最終的なインストールパスの決定はgo/build
パッケージに委ねられるようになったことを意味します。
-
gccgoToolchain.pkgpath
の実装変更:- こちらも
install
引数が削除され、パス生成ロジックが簡素化されました。 - 以前のコメント
// NOTE: Apparently gccgo does not distinguish different trees // using goos_goarch, so install is ignored here.
が示唆するように、gccgo
はgoos_goarch
によるツリーの区別をしないため、install
引数の有無にかかわらず、そのロジックは無視されていました。今回の変更で、この冗長性が解消されました。
- こちらも
-
gccgoToolchain.ld
メソッドの大幅なリファクタリング:- この変更は、
gccgo
コンパイラでGoプログラムをリンクする際の重要な修正です。gccgo
は、gc
とは異なり、リンカに対してすべてのパッケージ依存関係(アーカイブファイル.a
)とCgo関連のリンカフラグ(CgoLDFLAGS
)を明示的に渡す必要があります。 afiles
の収集: 以前は[]string
でafiles
を収集していましたが、新しいコードではmap[*Package]string
を使用しています。これにより、allactions
リストに同じパッケージが複数回現れても、afiles
マップにはそのパッケージのアーカイブファイルパスが一度だけ(かつ最新のものが)格納されるようになり、リンカに重複して渡されることを防ぎます。cgoldflags
の分離: Cgoのリンカフラグ(a.p.CgoLDFLAGS
)は、cgoldflags
という別のスライスに収集されるようになりました。これにより、通常のアーカイブファイルパスとCgoのリンカフラグが明確に区別され、リンカへの引数として適切に結合されます。- リンカ引数の構築: 最終的な
b.run
呼び出しでは、afiles
マップから抽出されたアーカイブファイルパスとcgoldflags
が結合され、ldflags
スライスとして構築されます。そして、これらのldflags
が-Wl,-(
と-Wl,-)
で囲まれてgccgo
リンカに渡されます。この-Wl,-(
と-Wl,-)
は、GCCリンカにおいて、ライブラリの依存関係を解決するために使用される慣習的な方法です。これにより、循環参照を持つライブラリ依存関係も正しく解決されるようになります。
- この変更は、
-
init
関数でのbuildContext.Gccgo
の設定:- 環境変数
GC
がgccgo
に設定されている場合、buildContext.Gccgo = true
が設定されるようになりました。これにより、Goコマンドのビルドコンテキストがgccgo
を使用していることを明示的に認識し、gccgo
固有のビルドロジックを適用するためのフラグとして機能します。
- 環境変数
src/cmd/go/pkg.go
の変更点
Package.load
関数内で、パッケージのターゲットパスp.target
を設定する際に、buildToolchain.pkgpath(p.build.PkgRoot, p, true)
の呼び出しがp.build.PkgObj
に直接置き換えられました。- これは、
go/build
パッケージのPackage
構造体に含まれるPkgObj
フィールドが、すでにビルド成果物の適切なパスを提供しているため、pkgpath
関数を呼び出してパスを生成する必要がなくなったことを意味します。これにより、コードがより直接的で効率的になりました。
src/cmd/go/test.go
の変更点
builder.test
関数内で、buildToolchain.pkgpath
の呼び出しが、新しいシグネチャに合わせてinstall
引数なしで更新されました。- テストパッケージのアーカイブファイルパスを生成する際に、
pkgpath
の新しいインターフェースが使用されるようになりました。
これらの変更は、Goのビルドシステムがgccgo
のような代替コンパイラをより堅牢にサポートし、同時にgo/build
パッケージの進化に合わせて内部ロジックを簡素化するための重要なステップです。特にgccgoToolchain.ld
のリファクタリングは、gccgo
使用時のリンカエラーを減らし、Cgoを含むプロジェクトのビルド信頼性を向上させる上で不可欠な変更です。
関連リンク
- Go言語公式ドキュメント: https://go.dev/doc/
go/build
パッケージドキュメント: https://pkg.go.dev/go/build- Cgoドキュメント: https://go.dev/blog/cgo
- GCC (GNU Compiler Collection) 公式サイト: https://gcc.gnu.org/
参考にした情報源リンク
- 特になし(提供されたコミット情報とGoの一般的な知識に基づいています)