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

[インデックス 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

変更の背景

このコミットには主に二つの背景があります。

  1. gccgoコンパイラへの対応と修正: Go言語には、公式のgcコンパイラ(Goコンパイラ)と、GCCをバックエンドとするgccgoコンパイラの二種類が存在します。gccgoは、特にC/C++との連携(Cgo)や、既存のGCCツールチェーンとの統合において異なる挙動を示すことがあります。このコミットは、gccgo使用時に発生していたビルドやリンクに関する問題を解決することを目的としています。特に、パッケージのアーカイブファイル(.a)のパス解決や、リンカへの引数渡し方においてgccgoの特性に合わせた調整が必要でした。

  2. go/buildパッケージによるインストール場所の定義: Goのビルドシステムは進化しており、go/buildパッケージがGoのソースコードやパッケージの構造、そしてそれらのビルド成果物の配置場所に関する情報をより詳細に提供するようになりました。これに伴い、以前はpkgpath関数がinstallというブール引数を使って「一時的なビルド成果物」と「最終的なインストール先」のパスを区別していましたが、go/buildパッケージが提供する情報(例: PkgObj)で十分になったため、このinstall引数が冗長になりました。このコミットは、この冗長な引数を削除し、コードベースを簡素化することを目的としています。

これらの変更は、Goのビルドシステムの堅牢性と柔軟性を高め、特にgccgoのような代替コンパイラを使用する際の互換性を向上させるために行われました。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびビルドシステムに関する知識が役立ちます。

  • Goツールチェーン (gcgccgo):
    • gc: Go言語の公式コンパイラであり、Goのソースコードをネイティブバイナリにコンパイルします。ほとんどのGo開発者が日常的に使用するものです。
    • gccgo: GCC(GNU Compiler Collection)をバックエンドとして使用するGoコンパイラです。gcとは異なる最適化や、C/C++ライブラリとの連携において異なるアプローチを取ることがあります。特に、リンカの挙動やライブラリの検索パスにおいてgcとは異なる要件を持つことがあります。
  • cmd/go:
    • Go言語のコマンドラインツールであり、Goのソースコードのビルド、テスト、インストール、フォーマットなど、Go開発における主要な操作を統括します。このコマンドは、内部的にコンパイラ(gcまたはgccgo)、アセンブラ、リンカなどのツールを呼び出して処理を実行します。
  • go/buildパッケージ:
    • Goの標準ライブラリの一部であり、Goのソースコードパッケージに関する情報(インポートパス、ソースファイルのリスト、依存関係、ビルドタグなど)をプログラム的に取得するための機能を提供します。このパッケージは、Goコマンドがパッケージを解決し、ビルドプランを立てる際に利用されます。特に、Package構造体には、ビルド成果物のパスに関する情報(例: PkgRoot, PkgObj)が含まれます。
  • pkgpath関数:
    • Goコマンドの内部で使用される関数で、特定のGoパッケージのビルド成果物(通常はアーカイブファイル.a)が配置されるべきパスを生成する役割を担います。このパスは、一時的なビルドディレクトリ内であったり、最終的なインストール場所であったりします。
  • Cgo:
    • GoプログラムからC言語のコードを呼び出すためのGoの機能です。Cgoを使用すると、GoとCのコードを混在させることができます。CgoでビルドされたGoパッケージは、Cのライブラリにリンクする必要があるため、リンカに対して追加のフラグ(LDFLAGS)を渡す必要があります。
  • $WORKディレクトリ:
    • Goコマンドがビルドプロセス中に一時的なファイル(オブジェクトファイル、アーカイブファイルなど)を生成するために使用する作業ディレクトリです。このディレクトリは通常、ビルドが完了すると削除されます。

技術的詳細

このコミットの技術的な変更点は多岐にわたりますが、主要なものは以下の通りです。

  1. pkgpath関数のシグネチャ変更:

    • toolchainインターフェース内のpkgpathメソッドのシグネチャが、pkgpath(basedir string, p *Package, install bool) stringからpkgpath(basedir string, p *Package) stringに変更されました。
    • これにより、installというブール引数が削除されました。これは、go/buildパッケージが提供するPackage構造体(特にPkgObjフィールド)が、パッケージのビルド成果物の適切なパスをすでに定義しているため、pkgpath関数内でインストールパスを別途計算する必要がなくなったためです。
    • goToolchaingccgoToolchainの両方で、この新しいシグネチャに合わせてpkgpathの実装が更新されました。goToolchain.pkgpathからはinstall引数に基づく条件分岐が削除され、常にfilepath.Join(basedir, end)を返すようになりました。gccgoToolchain.pkgpathも同様に簡素化されました。
  2. gccgoツールチェーンのリンカ処理の改善 (gccgoToolchain.ld):

    • gccgoは、gcとは異なり、リンカに対してすべてのパッケージ依存関係を明示的に渡す必要があります。また、Cgoを使用している場合、Cgo関連のリンカフラグ(CgoLDFLAGS)も適切に処理する必要があります。
    • このコミットでは、gccgoToolchain.ldメソッドが大幅にリファクタリングされました。
      • 以前はafiles(アーカイブファイル)とldflags(リンカフラグ)を単純なスライスで収集していましたが、新しい実装ではmap[*Package]stringを使用して、各パッケージのアーカイブファイルを重複なく収集するように変更されました。これにより、同じパッケージが複数回依存関係として現れても、リンカに渡されるアーカイブファイルは一つだけになります。
      • Cgoのリンカフラグ(a.p.CgoLDFLAGS)は、cgoldflagsという別のスライスに収集され、最終的にldflagsに結合されます。これにより、Cgoのリンカフラグが適切にリンカに渡されることが保証されます。
      • 最終的なb.run呼び出しでは、afilescgoldflagsが結合されたldflags-Wl,-(-Wl,-)で囲まれてリンカに渡されます。これは、GCCリンカがライブラリの依存関係を解決するための一般的な慣習です。
  3. buildContext.Gccgoフラグの設定:

    • src/cmd/go/build.goinit関数内で、環境変数GCgccgoに設定されている場合、buildContext.Gccgo = trueが設定されるようになりました。これにより、ビルドコンテキストがgccgoを使用していることを明示的に認識できるようになり、gccgo固有のビルドロジックの適用が容易になります。
  4. pkg.gotest.goでのpkgpath呼び出しの更新:

    • src/cmd/go/pkg.goPackage.load関数内で、p.targetを設定する際にbuildToolchain.pkgpath(p.build.PkgRoot, p, true)からp.build.PkgObjに直接変更されました。これは、go/buildパッケージが提供するPkgObjがすでに適切なターゲットパスを提供しているため、pkgpathを呼び出す必要がなくなったことを示しています。
    • src/cmd/go/test.gobuilder.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の変更点

  1. toolchainインターフェースのpkgpathメソッドの変更:

    • pkgpath(basedir string, p *Package, install bool) stringからpkgpath(basedir string, p *Package) stringへ変更されました。
    • これは、installというブール引数が不要になったことを示しています。以前は、この引数によって一時的なビルドパスと最終的なインストールパスを区別していましたが、go/buildパッケージが提供するPackage構造体(特にPkgObjフィールド)が、すでに適切なビルド成果物のパスを定義するようになったため、この区別が不要になりました。
  2. goToolchain.pkgpathの実装変更:

    • install引数に基づく条件分岐が削除されました。
    • 常にfilepath.Join(basedir, end)を返すようになり、パス生成ロジックが簡素化されました。これは、pkgpathが一時的なビルド成果物のパスを生成する役割に特化し、最終的なインストールパスの決定はgo/buildパッケージに委ねられるようになったことを意味します。
  3. gccgoToolchain.pkgpathの実装変更:

    • こちらもinstall引数が削除され、パス生成ロジックが簡素化されました。
    • 以前のコメント// NOTE: Apparently gccgo does not distinguish different trees // using goos_goarch, so install is ignored here.が示唆するように、gccgogoos_goarchによるツリーの区別をしないため、install引数の有無にかかわらず、そのロジックは無視されていました。今回の変更で、この冗長性が解消されました。
  4. gccgoToolchain.ldメソッドの大幅なリファクタリング:

    • この変更は、gccgoコンパイラでGoプログラムをリンクする際の重要な修正です。gccgoは、gcとは異なり、リンカに対してすべてのパッケージ依存関係(アーカイブファイル.a)とCgo関連のリンカフラグ(CgoLDFLAGS)を明示的に渡す必要があります。
    • afilesの収集: 以前は[]stringafilesを収集していましたが、新しいコードではmap[*Package]stringを使用しています。これにより、allactionsリストに同じパッケージが複数回現れても、afilesマップにはそのパッケージのアーカイブファイルパスが一度だけ(かつ最新のものが)格納されるようになり、リンカに重複して渡されることを防ぎます。
    • cgoldflagsの分離: Cgoのリンカフラグ(a.p.CgoLDFLAGS)は、cgoldflagsという別のスライスに収集されるようになりました。これにより、通常のアーカイブファイルパスとCgoのリンカフラグが明確に区別され、リンカへの引数として適切に結合されます。
    • リンカ引数の構築: 最終的なb.run呼び出しでは、afilesマップから抽出されたアーカイブファイルパスとcgoldflagsが結合され、ldflagsスライスとして構築されます。そして、これらのldflags-Wl,-(-Wl,-)で囲まれてgccgoリンカに渡されます。この-Wl,-(-Wl,-)は、GCCリンカにおいて、ライブラリの依存関係を解決するために使用される慣習的な方法です。これにより、循環参照を持つライブラリ依存関係も正しく解決されるようになります。
  5. init関数でのbuildContext.Gccgoの設定:

    • 環境変数GCgccgoに設定されている場合、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の一般的な知識に基づいています)