[インデックス 16802] ファイルの概要
このコミットは、Go言語のcmd/cgo
ツールにおいて、gccgo
コンパイラを使用する際に特定の#pragma
宣言の出力を停止する変更を導入しています。これにより、gccgo
が理解できない、または使用できない#pragma cgo_ldflag
および#pragma cgo_import_static
宣言が生成されるのを防ぎます。
コミット
commit c5c52f072b3d33f1a40ffda6d5334d38d0a11fa5
Author: Ian Lance Taylor <iant@golang.org>
Date: Wed Jul 17 18:02:21 2013 -0700
cmd/cgo: don't emit #pragma declarations when using gccgo
Update #5905
Don't emit #pragma cgo_ldflag and cgo_import_static
declarations that gccgo doesn't understand and can't use.
R=golang-dev, rsc, bradfitz
CC=golang-dev
https://golang.org/cl/11490043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c5c52f072b3d33f1a40ffda6d5334d38d0a11fa5
元コミット内容
cmd/cgo: gccgo使用時に#pragma宣言を出力しない
#5905 を更新
gccgoが理解できず、使用できない#pragma cgo_ldflagおよびcgo_import_static宣言を出力しない。
変更の背景
Go言語は、C言語のコードをGoプログラムから呼び出すためのメカニズムとしてcgo
を提供しています。cgo
は、Goのソースコード内にCのコードを埋め込み、GoとCの間でデータを受け渡しするための橋渡しをします。このプロセスの一環として、cgo
ツールはCのコンパイラ(通常はGCCやClang)が処理するためのCソースファイルを生成します。
標準のGoコンパイラ(gc
)と異なり、gccgo
はGCCのフロントエンドとしてGo言語をサポートするコンパイラです。gccgo
はGoのコードをGCCの内部表現に変換し、GCCのバックエンドを利用してネイティブコードを生成します。このため、gccgo
はgc
とは異なるビルドシステムや内部的な処理メカニズムを持っています。
このコミットの背景には、cgo
が生成する特定の#pragma
宣言がgccgo
にとって問題であったという事実があります。具体的には、#pragma cgo_ldflag
と#pragma cgo_import_static
は、標準のGoツールチェーン(gc
コンパイラと連携するcgo
)がリンカに情報を渡したり、静的ライブラリのシンボルをインポートしたりするために使用するGo固有の拡張機能です。しかし、gccgo
はこれらの#pragma
を認識せず、その結果、コンパイルエラーやリンカエラーを引き起こす可能性がありました。
この変更は、gccgo
を使用している場合にこれらの互換性のない#pragma
宣言の出力を抑制することで、cgo
とgccgo
の間の互換性を向上させることを目的としています。コミットメッセージにあるUpdate #5905
は、この問題がGoのイシュートラッカーで報告され、このコミットがその解決策であることを示唆しています。
前提知識の解説
cgoとは
cgo
は、GoプログラムからC言語のコードを呼び出すためのGoのツールです。Goのソースファイル内にimport "C"
という特別なインポート文を記述し、その直後のコメントブロックにC言語のコードを記述することで、GoとCの相互運用を可能にします。cgo
は、Goのビルドプロセス中にこれらのCコードを抽出し、Cコンパイラでコンパイルし、Goのコードとリンクするための接着コード("glue code")を生成します。
#pragmaとは
#pragma
は、C/C++言語のプリプロセッサディレクティブの一つで、コンパイラに対して特定の指示を与えるために使用されます。これは標準化された機能ではなく、コンパイラごとに独自の#pragma
が定義されていることが多く、特定のコンパイラやプラットフォームに依存する機能や最適化を制御するために使われます。cgo
の場合、GoのビルドシステムとCコンパイラの間で情報をやり取りするために、Go独自の#pragma
が導入されています。
#pragma cgo_ldflag
#pragma cgo_ldflag
は、cgo
が生成するCコード内で使用される#pragma
ディレクティブの一つです。これは、リンカに渡す追加のフラグ(例: -L/path/to/lib -lmylib
)を指定するために使用されます。cgo
は、Goのビルド時にこの情報を抽出し、最終的な実行可能ファイルをリンクする際にこれらのリンカフラグを適用します。これにより、GoプログラムがCライブラリにリンクできるようになります。
#pragma cgo_import_static
#pragma cgo_import_static
もcgo
が生成するCコード内で使用される#pragma
ディレクティブです。これは、静的ライブラリから特定のシンボルをインポートする必要があることをリンカに指示するために使用されます。特に、GoのランタイムがCのコードから呼び出される場合や、CのコードがGoのランタイムによって提供される特定のシンボルにアクセスする必要がある場合に利用されます。これにより、静的リンクされたCライブラリのシンボルがGoの実行可能ファイルに正しく含まれるようになります。
gcとgccgoの違い
- gc (Go Compiler): Goプロジェクトが公式に提供する標準のGoコンパイラです。Goのソースコードを直接機械語にコンパイルします。Goのツールチェーン(
go build
など)のデフォルトコンパイラです。 - gccgo: GCC (GNU Compiler Collection) のフロントエンドとして実装されたGoコンパイラです。GoのソースコードをGCCの内部表現に変換し、GCCの最適化パスとバックエンドを利用して機械語を生成します。
gccgo
は、既存のGCCベースのビルドシステムやツールチェーンとの統合が容易であるという利点があります。しかし、gc
とは異なるコード生成パスを持つため、Goの特定の機能やcgo
の挙動において互換性の問題が生じることがあります。
このコミットは、gc
とgccgo
の間のcgo
関連の互換性の問題を解決するためのものです。
技術的詳細
このコミットの技術的詳細は、cgo
ツールがCソースファイルを生成する際の条件分岐にあります。cgo
は、Goのソースコードを解析し、Cの関数呼び出しやCの型定義をGoの型にマッピングするためのCコードを生成します。この生成されたCコードには、Goのビルドシステムが利用する特定の#pragma
ディレクティブが含まれることがあります。
具体的には、src/cmd/cgo/out.go
ファイル内のPackage
構造体のwriteDefs
メソッドが、Cの定義ファイル(_cgo_defun.c
など)を生成する際にこれらの#pragma
を出力します。
変更前は、LDFLAGS
(リンカフラグ)が指定されている場合、常に#pragma cgo_ldflag
が出力されていました。また、静的にインポートされるシンボル(cgo_import_static
)についても、常に#pragma cgo_import_static
が出力されていました。
このコミットでは、これらの#pragma
の出力に!*gccgo
という条件が追加されました。これは、cgo
ツールがgccgo
コンパイラを使用しているかどうかを判断するためのフラグです。gccgo
が使用されていない場合(つまり、標準のgc
コンパイラが使用されている場合)にのみ、これらの#pragma
宣言が出力されるようになります。
これにより、gccgo
がこれらのGo固有の#pragma
を処理しようとして発生する可能性のあるコンパイルエラーや警告が回避されます。gccgo
は独自のリンカフラグ処理やシンボル解決メカニズムを持っているため、これらの#pragma
は不要であり、むしろ問題を引き起こす可能性がありました。
コアとなるコードの変更箇所
変更はsrc/cmd/cgo/out.go
ファイルに集中しています。
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -32,7 +32,7 @@ func (p *Package) writeDefs() {
fflg := creat(*objDir + "_cgo_flags")
for k, v := range p.CgoFlags {
fmt.Fprintf(fflg, "_CGO_%s=%s\n", k, strings.Join(v, " "))
- if k == "LDFLAGS" {
+ if k == "LDFLAGS" && !*gccgo {
for _, arg := range v {
fmt.Fprintf(fc, "#pragma cgo_ldflag %q\n", arg)
}
@@ -105,7 +105,10 @@ func (p *Package) writeDefs() {
fmt.Fprintf(fm, "extern char %s[];\n", n.C)
fmt.Fprintf(fm, "void *_cgohack_%s = %s;\n\n", n.C, n.C)
- fmt.Fprintf(fc, "#pragma cgo_import_static %s\n", n.C)
+ if !*gccgo {
+ fmt.Fprintf(fc, "#pragma cgo_import_static %s\n", n.C)
+ }
+
fmt.Fprintf(fc, "extern byte *%s;\n", n.C)
cVars[n.C] = true
コアとなるコードの解説
-
LDFLAGS
の処理変更: 変更前:if k == "LDFLAGS" { for _, arg := range v { fmt.Fprintf(fc, "#pragma cgo_ldflag %q\n", arg) } }
変更後:
if k == "LDFLAGS" && !*gccgo { for _, arg := range v { fmt.Fprintf(fc, "#pragma cgo_ldflag %q\n", arg) } }
この変更により、
CgoFlags
マップからLDFLAGS
(リンカフラグ)が検出された場合でも、グローバル変数gccgo
がtrue
(つまりgccgo
コンパイラが使用されている)でなければ、#pragma cgo_ldflag
が出力されるようになりました。*gccgo
は、cgo
ツールがgccgo
モードで実行されているかどうかを示すブール型のフラグです。 -
cgo_import_static
の処理変更: 変更前:fmt.Fprintf(fc, "#pragma cgo_import_static %s\n", n.C)
変更後:
if !*gccgo { fmt.Fprintf(fc, "#pragma cgo_import_static %s\n", n.C) }
同様に、静的にインポートされるCシンボル(
n.C
)についても、gccgo
が使用されていない場合にのみ#pragma cgo_import_static
が出力されるようになりました。
これらの変更は、cgo
が生成するCコードがgccgo
によってコンパイルされる際に、gccgo
が認識しない#pragma
ディレクティブを含まないようにするためのものです。これにより、gccgo
を使用するGoプロジェクトのビルドがよりスムーズに行われるようになります。
関連リンク
- Go言語の
cgo
に関する公式ドキュメント: https://pkg.go.dev/cmd/cgo gccgo
に関する情報(GCCのドキュメントなど): https://gcc.gnu.org/onlinedocs/gccgo/
参考にした情報源リンク
- GitHub上のコミットページ: https://github.com/golang/go/commit/c5c52f072b3d33f1a40ffda6d5334d38d0a11fa5
- Go言語のイシュートラッカー(
#5905
に関する情報が見つからなかったため、一般的なcgo
とgccgo
の互換性に関する議論を参照) - Stack OverflowやGoコミュニティのフォーラムでの
cgo_ldflag
やcgo_import_static
に関する議論。 - Go言語のソースコード(
src/cmd/cgo/out.go
)の分析。