[インデックス 11140] ファイルの概要
このコミットは、Go言語のcgo
ツールが生成する_cgo_export.h
ヘッダーファイルの出力先を、ソースディレクトリからオブジェクト(ビルド)ディレクトリに変更するものです。これにより、ビルドプロセスで生成されるファイルがソースコードと明確に分離され、プロジェクトのクリーンさとビルドシステムの整合性が向上します。
コミット
commit ba0e02b207930f3c4cda364d823820a40bb99f8e
Author: Russ Cox <rsc@golang.org>
Date: Thu Jan 12 15:04:31 2012 -0800
cgo: write _cgo_export.h to object directory, not source dir
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5540048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ba0e02b207930f3c4cda364d823820a40bb99f8e
元コミット内容
cgo: write _cgo_export.h to object directory, not source dir
変更の背景
この変更の背景には、ビルドプロセスにおける生成ファイルの管理と、ソースディレクトリのクリーンさの維持という重要な課題があります。
- ソースディレクトリの汚染: 以前の挙動では、
cgo
が生成する_cgo_export.h
ファイルが、Goのソースコードが置かれているディレクトリ(ソースディレクトリ)に直接出力されていました。これは、本来ソースコードのみを格納すべきディレクトリに、ビルド時に生成される一時的なファイルが混在することを意味し、ディレクトリ構造の乱雑さや視認性の低下を招いていました。 - バージョン管理システムとの整合性:
_cgo_export.h
のような生成ファイルは、通常、Gitなどのバージョン管理システムで管理されるべきではありません(.gitignore
などで無視されるべきです)。しかし、ソースディレクトリに直接生成されると、誤ってコミットされたり、他の開発者との間で不必要なマージコンフリクトを引き起こしたりするリスクがありました。 - クリーンビルドの困難さ: ビルドシステムでは、
make clean
のようなコマンドで生成されたファイルを一掃し、クリーンな状態から再ビルドできるようにすることが一般的です。ソースディレクトリに生成ファイルが残ると、この「クリーン」な状態を維持するのが難しくなり、ビルドの再現性や信頼性に影響を与える可能性がありました。 - ビルドシステムの標準的な慣習への準拠: 多くのビルドシステムでは、中間ファイルや最終的なビルド成果物を、ソースコードとは別の専用の「オブジェクトディレクトリ」や「ビルドディレクトリ」に格納するという慣習があります。この変更は、Goのビルドプロセスをこの標準的な慣習に合わせることで、より予測可能で管理しやすいビルド環境を提供することを目的としています。
これらの問題を解決し、Goのビルドシステム、特にcgo
を使用する際の堅牢性と保守性を高めるために、_cgo_export.h
の出力先をオブジェクトディレクトリに変更する必要がありました。
前提知識の解説
このコミットを理解するためには、以下の技術的な概念を把握しておく必要があります。
-
cgo:
cgo
はGo言語に標準で付属するツールの一つで、GoプログラムからC言語のコードを呼び出したり、逆にC言語のコードからGo言語の関数を呼び出したりするための橋渡しをします。GoとCの間のデータ型の変換や、関数呼び出しの規約の違いを吸収する役割を担います。cgo
を使用すると、既存のCライブラリをGoプロジェクトに統合したり、パフォーマンスが重要な部分をCで記述したりすることが可能になります。 -
_cgo_export.h
:cgo
がGoの関数をCから呼び出せるようにするために生成するヘッダーファイルです。Goのコード内で//export
ディレクティブを使ってCから呼び出したい関数をマークすると、cgo
はこの_cgo_export.h
ファイルを生成します。このファイルには、C言語のコンパイラがGoの関数シグネチャを理解できるように、対応するC言語の関数プロトタイプ宣言が含まれています。Cのコードはこのヘッダーファイルをインクルードすることで、Goで実装された関数を通常のC関数として呼び出すことができます。 -
ソースディレクトリ (Source Directory): プロジェクトのオリジナルのソースコードファイル(例:
.go
,.c
,.h
ファイルなど)が格納されているディレクトリです。開発者が直接編集するファイル群がここに置かれます。バージョン管理システムで管理されるのは主にこのディレクトリ内のファイルです。 -
オブジェクトディレクトリ (Object Directory / Build Directory): ビルドプロセス中に生成される中間ファイルや一時ファイルが格納されるディレクトリです。これには、コンパイルされたオブジェクトファイル(例:
.o
ファイル)、生成されたヘッダーファイル(例:_cgo_export.h
)、実行可能ファイル、ライブラリなどが含まれます。これらのファイルは通常、バージョン管理システムでは無視され、ビルドが完了すれば削除されるか、再ビルド時に上書きされます。ソースディレクトリとオブジェクトディレクトリを分離することは、ビルドのクリーンさとプロジェクトの管理のしやすさにとって非常に重要です。 -
Goのビルドプロセス:
go build
コマンドを実行すると、Goコンパイラはソースコードをコンパイルし、実行可能なバイナリやライブラリを生成します。cgo
が関与する場合、Goコンパイラはまずcgo
ツールを呼び出し、GoとCの間の結合コードを生成させます。この結合コードには、_cgo_export.h
のようなファイルも含まれます。その後、Goコンパイラは生成されたCコードをCコンパイラ(通常はGCCなど)でコンパイルし、その結果とGoのコードをリンクして最終的なバイナリを生成します。この一連のプロセスにおいて、中間ファイルの配置場所はビルドの効率性やクリーンさに影響を与えます。
技術的詳細
このコミットの技術的な核心は、cgo
ツールとGoのビルドシステムが連携して_cgo_export.h
ファイルを扱う方法を変更することにあります。具体的には、この生成されるヘッダーファイルが、Goのソースコードが存在するディレクトリではなく、ビルドプロセス中に使用される一時的なオブジェクトディレクトリに配置されるように修正されます。
変更は主に以下の2つのファイルにわたって行われています。
-
src/cmd/cgo/out.go
の変更: このファイルはcgo
ツールの内部実装の一部であり、cgo
がGoの関数をCにエクスポートする際に必要な_cgo_export.h
ファイルを実際に書き出すロジックを含んでいます。以前は、このファイルが現在の作業ディレクトリ(多くの場合、Goパッケージのソースディレクトリ)に直接作成されていました。変更後は、ファイル作成時にオブジェクトディレクトリのパスをプレフィックスとして付加することで、指定されたオブジェクトディレクトリ内に_cgo_export.h
が生成されるようになります。 -
src/pkg/go/build/build.go
の変更: このファイルはGoの標準ライブラリの一部であり、Goのビルドシステムの中核をなすgo/build
パッケージに属しています。go build
コマンドがGoパッケージをビルドする際に、cgo
によって生成された_cgo_export.h
ファイルを見つけるためのパスを決定するロジックが含まれています。以前は、ソースディレクトリと_cgo_export.h
のファイル名を結合してパスを構築していましたが、変更後は、オブジェクトディレクトリのパスと_cgo_export.h
のファイル名を結合するように修正されます。これにより、ビルドシステムは新しい場所にある_cgo_export.h
を正しく参照できるようになります。
この修正により、cgo
を使用するGoプロジェクトのビルドプロセスは、より標準的なソフトウェア開発の慣習に沿うことになります。生成ファイルとソースファイルの明確な分離は、ビルドの信頼性、プロジェクトの保守性、そしてバージョン管理の効率性を向上させます。例えば、go clean
コマンドがより効果的に機能し、開発者がソースディレクトリを手動でクリーンアップする必要がなくなります。
コアとなるコードの変更箇所
このコミットによるコードの変更は以下の通りです。
diff --git a/src/cmd/cgo/out.go b/src/cmd/cgo/out.go
index b1644d2b0e..3e25b2099c 100644
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -396,7 +396,7 @@ func (p *Package) writeOutputFunc(fgcc *os.File, n *Name) {
// from Go so that they are callable from C.
func (p *Package) writeExports(fgo2, fc, fm *os.File) {
fgcc := creat(*objDir + "_cgo_export.c")
- fgcch := creat("_cgo_export.h")
+ fgcch := creat(*objDir + "_cgo_export.h")
fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\\n")
fmt.Fprintf(fgcch, "%s\\n", gccExportHeaderProlog)
diff --git a/src/pkg/go/build/build.go b/src/pkg/go/build/build.go
index 5301ab53e5..9515a7e645 100644
--- a/src/pkg/go/build/build.go
+++ b/src/pkg/go/build/build.go
@@ -396,8 +396,7 @@ func (b *build) cgo(cgofiles, cgocfiles []string) (outGo, outObj []string) {
Output: output,
})\n \toutGo = append(outGo, gofiles...)\n-\texportH := filepath.Join(b.path, "_cgo_export.h")
-\tb.script.addIntermediate(defunC, exportH, b.obj+"_cgo_flags")
+\tb.script.addIntermediate(defunC, b.obj+"_cgo_export.h", b.obj+"_cgo_flags")
\tb.script.addIntermediate(cfiles...)\n \n \t// cc _cgo_defun.c
コアとなるコードの解説
変更された各コードブロックについて詳しく解説します。
src/cmd/cgo/out.go
の変更
// from Go so that they are callable from C.
func (p *Package) writeExports(fgo2, fc, fm *os.File) {
fgcc := creat(*objDir + "_cgo_export.c")
- fgcch := creat("_cgo_export.h")
+ fgcch := creat(*objDir + "_cgo_export.h")
fmt.Fprintf(fgcch, "/* Created by cgo - DO NOT EDIT. */\\n")
fmt.Fprintf(fgcch, "%s\\n", gccExportHeaderProlog)
- 変更前:
fgcch := creat("_cgo_export.h")
creat
関数は、指定された名前で新しいファイルを作成します。この行では、_cgo_export.h
という名前のファイルが、cgo
ツールが実行されている現在のディレクトリ(通常はGoパッケージのソースディレクトリ)に直接作成されていました。 - 変更後:
fgcch := creat(*objDir + "_cgo_export.h")
*objDir
は、cgo
が中間ファイルを格納するために使用するオブジェクトディレクトリのパスを指すポインタです。この変更により、_cgo_export.h
ファイルは、ソースディレクトリではなく、*objDir
が示すパス(例:/tmp/go-build.../_cgo_export.h
のような一時ディレクトリ)に作成されるようになります。これにより、生成ファイルがソースツリーから分離されます。
src/pkg/go/build/build.go
の変更
@@ -396,8 +396,7 @@ func (b *build) cgo(cgofiles, cgocfiles []string) (outGo, outObj []string) {
Output: output,
})\n \toutGo = append(outGo, gofiles...)\n-\texportH := filepath.Join(b.path, "_cgo_export.h")
-\tb.script.addIntermediate(defunC, exportH, b.obj+"_cgo_flags")
+\tb.script.addIntermediate(defunC, b.obj+"_cgo_export.h", b.obj+"_cgo_flags")
\tb.script.addIntermediate(cfiles...)\n \n \t// cc _cgo_defun.c
- 変更前:
exportH := filepath.Join(b.path, "_cgo_export.h") b.script.addIntermediate(defunC, exportH, b.obj+"_cgo_flags")
filepath.Join(b.path, "_cgo_export.h")
は、Goパッケージのパス(b.path
、つまりソースディレクトリ)とファイル名_cgo_export.h
を結合して、_cgo_export.h
のフルパスを生成していました。b.script.addIntermediate
は、ビルドプロセスの中間ファイルとしてこのパスを登録していました。これは、ビルドシステムが_cgo_export.h
をソースディレクトリ内で探すことを意味していました。 - 変更後:
b.script.addIntermediate(defunC, b.obj+"_cgo_export.h", b.obj+"_cgo_flags")
b.obj
は、Goのビルドシステムが使用するオブジェクトディレクトリのパスを指します。この変更により、_cgo_export.h
のパスがb.obj + "_cgo_export.h"
となり、ビルドシステムは_cgo_export.h
をオブジェクトディレクトリ内で探すように指示されます。これにより、cgo
がファイルを書き出す場所と、ビルドシステムがファイルを探す場所が一致し、ビルドが正しく行われるようになります。また、中間ファイルとして登録する際に、_cgo_export.h
のパスを直接オブジェクトディレクトリ内のパスとして指定することで、より簡潔かつ意図が明確なコードになっています。
これらの変更は、cgo
が生成する_cgo_export.h
ファイルのライフサイクルと配置を、Goのビルドシステム全体の設計思想と整合させるための重要なステップです。
関連リンク
- Go Code Review: https://golang.org/cl/5540048
参考にした情報源リンク
- Go言語公式ドキュメント: cgo (GoとCの連携について)
- Go言語のビルドシステムに関する一般的な情報
- ソフトウェア開発におけるビルド成果物の管理に関するベストプラクティス