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

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

このコミットは、Go言語のビルドシステムの中核を担う src/cmd/go/build.go ファイルに対する変更です。このファイルは、Goプログラムのコンパイル、リンク、パッケージ管理など、ビルドプロセス全体をオーケストレーションする go コマンドの内部ロジックを定義しています。特に、Cgo(GoとC/C++コードを連携させるためのメカニズム)に関連するオブジェクトファイルの取り扱いが変更されています。

コミット

このコミットは、gccgo コンパイラによって生成されるオブジェクトファイルの拡張子を、従来のアーキテクチャ固有の拡張子(例: .5, .6, .8)から標準的な .o に変更することを目的としています。これにより、gccgo を使用したビルドプロセスにおけるオブジェクトファイルの命名規則が統一され、より一般的なツールチェーンとの互換性が向上します。

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

https://github.com/golang/go/commit/c5b45aa9913dbad6596a3fcaa001be0cca23c9e0

元コミット内容

cmd/go: use .o, not .{5,6,8}, for gccgo created object files

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5861044

変更の背景

Go言語には、主に2つの主要なコンパイラ実装が存在します。

  1. gc (Go Compiler): Goプロジェクトの公式かつデフォルトのコンパイラです。これはGo言語で書かれており、Goのソースコードを直接機械語にコンパイルします。gc は、オブジェクトファイルにターゲットアーキテクチャを示す独自の拡張子を使用します。例えば、x86 (32-bit) では .8x86-64 (64-bit) では .6ARM では .5 といった形式です。これは、異なるアーキテクチャ向けのオブジェクトファイルを区別しやすくするための gc 独自の慣習でした。

  2. gccgo: GCC (GNU Compiler Collection) のフロントエンドとして実装されたGoコンパイラです。gccgo はGCCのバックエンドを利用するため、CやC++などの他の言語と同様に、標準的なオブジェクトファイル拡張子である .o を使用します。

このコミットが行われた2012年当時、Goのエコシステムはまだ発展途上であり、gcgccgo の両方が活発に開発されていました。go コマンドは、内部的にどちらのコンパイラが使用されているかに応じて、生成されるオブジェクトファイルの拡張子を適切に処理する必要がありました。

変更の背景にある問題は、cgo を使用してGoとC/C++を連携させる際に、gccgo が生成するオブジェクトファイルの拡張子が gc の慣習に従って .5, .6, .8 となることで、標準的なビルドツールやリンカが期待する .o 拡張子と齟齬が生じる可能性があったことです。この不整合は、ビルドプロセスの複雑性を増し、特にクロスコンパイル環境や、既存のC/C++プロジェクトとの統合において問題を引き起こす可能性がありました。

このコミットは、gccgo が生成するオブジェクトファイルについては、その性質上、標準的な .o 拡張子を使用するように go コマンドのビルドロジックを調整することで、この問題を解決しようとしています。これにより、gccgo を使用した場合でも、オブジェクトファイルの命名規則がより予測可能で、一般的なツールチェーンと互換性のあるものになります。

前提知識の解説

  • cmd/go: Go言語の公式ツールチェーンの中核をなすコマンドラインツールです。Goソースコードのコンパイル、テスト、パッケージ管理、依存関係の解決など、Go開発におけるほとんどの操作を担います。src/cmd/go/build.go は、この go コマンドのビルドロジックを実装しているファイルの一つです。
  • gccgo: GCC (GNU Compiler Collection) の一部として提供されるGo言語のコンパイラです。gc コンパイラとは異なり、GCCの最適化バックエンドを利用するため、C/C++コンパイラと共通の最適化パスやコード生成機能を利用できます。これにより、特定の環境でのパフォーマンスや、既存のC/C++ライブラリとの連携において利点を持つことがあります。
  • オブジェクトファイル (.o, .5, .6, .8): コンパイラがソースコードを機械語に変換した中間ファイルです。これらのファイルは、最終的な実行可能ファイルを生成するためにリンカによって結合されます。
    • .o: 多くのUnix系システムやGCCツールチェーンで標準的に使用されるオブジェクトファイルの拡張子です。
    • .5, .6, .8: Goの gc コンパイラが使用する、アーキテクチャ固有のオブジェクトファイル拡張子です。
      • .5: ARMアーキテクチャ (例: GOARCH=arm)
      • .6: AMD64 (x86-64) アーキテクチャ (例: GOARCH=amd64)
      • .8: 386 (x86) アーキテクチャ (例: GOARCH=386)
  • cgo: Go言語の機能の一つで、GoプログラムからC言語の関数を呼び出したり、C言語のコードをGoプログラムに組み込んだりするためのメカニズムです。cgo を使用すると、Goのビルドプロセス中にC/C++コンパイラ(通常はGCC)が呼び出され、C/C++ソースコードがコンパイルされてオブジェクトファイルが生成されます。

技術的詳細

この変更の技術的な核心は、go コマンドが cgo を介して外部のC/C++コンパイラ(特に gccgo が関与する場合)を呼び出す際に、生成されるオブジェクトファイルの拡張子を動的に決定するロジックにあります。

従来の gc コンパイラでは、オブジェクトファイルの拡張子は $GOARCH 環境変数(ターゲットアーキテクチャ)に基づいて決定され、archChar という変数に格納されていました。例えば、GOARCH=amd64 であれば archChar'6' となり、オブジェクトファイルは .6 という拡張子を持つことになります。

しかし、gccgo はGCCの慣習に従い、常に .o 拡張子を使用します。このコミットは、go コマンドが gccgo を使用していることを検出した場合に、オブジェクトファイルの拡張子を強制的に .o に上書きするように変更します。

具体的には、build.go 内の cgo 関数において、objExt という新しい変数が導入されます。この変数はデフォルトで archChar の値(gc の慣習)に初期化されますが、もし gccgo が使用されていると判断された場合(buildToolchain.IsGccgotrue の場合)、objExt"o" に上書きされます。その後、_cgo_defun.c_cgo_import.ARCH といったCgo関連の内部生成ファイルのオブジェクトファイル名を構築する際に、この objExt 変数が使用されるようになります。

これにより、gccgo を使用してCgoを含むGoプログラムをビルドする際に、生成される中間オブジェクトファイルが .o 拡張子を持つようになり、GCCツールチェーンの他の部分(リンカなど)との互換性が確保されます。これは、特にクロスコンパイルや、既存のC/C++ライブラリをGoプロジェクトに組み込む際に、ビルドエラーや予期せぬ動作を防ぐ上で重要です。

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

--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -1466,6 +1466,8 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,\n    	cgoflags := []string{}\n    	// TODO: make cgo not depend on $GOARCH?\n    \n    +	objExt := archChar\n    +\n    	if p.Standard && p.ImportPath == \"runtime/cgo\" {\n    	\tcgoflags = append(cgoflags, \"-import_runtime_cgo=false\")\n    	}\n    @@ -1474,6 +1476,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,\n    		if prefix := gccgoPrefix(p); prefix != \"\" {\n    		\tcgoflags = append(cgoflags, \"-gccgoprefix=\"+gccgoPrefix(p))\n    		}\n    +		objExt = \"o\"\n    \t}\n    \tif err := b.run(p.Dir, p.ImportPath, cgoExe, \"-objdir\", obj, cgoflags, \"--\", cgoCFLAGS, p.CgoFiles); err != nil {\n    \t\treturn nil, nil, err\n    @@ -1481,7 +1484,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,\n    \toutGo = append(outGo, gofiles...)\n    \n    \t// cc _cgo_defun.c\n    -\tdefunObj := obj + \"_cgo_defun.\" + archChar\n    +\tdefunObj := obj + \"_cgo_defun.\" + objExt\n    \tif err := buildToolchain.cc(b, p, obj, defunObj, defunC); err != nil {\n    \t\treturn nil, nil, err\n    \t}\n    @@ -1524,7 +1527,7 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,\n    \t}\n    \n    \t// cc _cgo_import.ARCH\n    -\timportObj := obj + \"_cgo_import.\" + archChar\n    +\timportObj := obj + \"_cgo_import.\" + objExt\n    \tif err := buildToolchain.cc(b, p, obj, importObj, importC); err != nil {\n    \t\treturn nil, nil, err\n    \t}\n```

## コアとなるコードの解説

変更は `src/cmd/go/build.go` ファイル内の `cgo` 関数に集中しています。この関数は、Cgoに関連するビルドステップを処理します。

1.  **`objExt := archChar` の追加**:
    `cgo` 関数の冒頭で、`objExt` という新しい文字列変数が宣言され、`archChar` の値で初期化されます。`archChar` は、現在のターゲットアーキテクチャに対応する `gc` スタイルのオブジェクトファイル拡張子(例: `5`, `6`, `8`)を保持しています。これは、デフォルトの動作として `gc` コンパイラの慣習に従うことを意味します。

2.  **`if buildToolchain.IsGccgo() { objExt = "o" }` の追加**:
    `cgo` 関数内で、`buildToolchain.IsGccgo()` という条件が追加されました。この関数は、現在使用されているGoコンパイラが `gccgo` であるかどうかを判定します。もし `gccgo` が使用されている場合、`objExt` の値は `"o"` に上書きされます。これにより、`gccgo` が生成するオブジェクトファイルは `.o` 拡張子を持つことが保証されます。

3.  **`defunObj` と `importObj` の構築における `objExt` の使用**:
    変更前は、`_cgo_defun.c` と `_cgo_import.ARCH` というCgoが内部的に生成するCソースファイルから作成されるオブジェクトファイルの名前は、直接 `archChar` を使って構築されていました(例: `_cgo_defun.6`)。
    変更後は、これらのオブジェクトファイル名を構築する際に、新しく導入された `objExt` 変数が使用されるようになりました。
    *   `defunObj := obj + "_cgo_defun." + objExt`
    *   `importObj := obj + "_cgo_import." + objExt`
    これにより、`gccgo` が使用されている場合は `.o` 拡張子(例: `_cgo_defun.o`)、`gc` が使用されている場合は従来のアーキテクチャ固有の拡張子(例: `_cgo_defun.6`)が適用されるようになります。

この変更により、`go` コマンドは、使用されているコンパイラ(`gc` または `gccgo`)に応じて、Cgoが生成するオブジェクトファイルの拡張子を適切に調整できるようになり、ビルドプロセスの堅牢性と互換性が向上しました。

## 関連リンク

*   Go issue tracker: [https://github.com/golang/go/issues](https://github.com/golang/go/issues) (このコミットに関連する特定のissueはコミットメッセージに記載されていませんが、Goの変更は通常issueと関連付けられています)
*   Go CL (Change List): [https://golang.org/cl/5861044](https://golang.org/cl/5861044) (元のコミットメッセージに記載されているChange Listへのリンク)

## 参考にした情報源リンク

*   Go言語の公式ドキュメント: [https://golang.org/doc/](https://golang.org/doc/)
*   Go言語のビルドプロセスに関する情報 (Go Wikiなど): [https://go.dev/wiki/](https://go.dev/wiki/)
*   GCCgoに関する情報: [https://gcc.gnu.org/onlinedocs/gccgo/](https://gcc.gnu.org/onlinedocs/gccgo/)
*   Cgoに関する情報: [https://go.dev/blog/cgo](https://go.dev/blog/cgo)
*   Goのオブジェクトファイル拡張子に関する議論 (Stack Overflow, メーリングリストなど)
    *   `go build` のオブジェクトファイル拡張子に関する一般的な情報源を検索しました。
    *   `gc` と `gccgo` の違いに関する情報源を検索しました。
    *   `cgo` のビルドプロセスに関する情報源を検索しました。