[インデックス 17532] ファイルの概要
このコミットは、Go言語のコマンドラインツール cmd/go
における、go build -n
コマンドの出力に関するバグ修正です。具体的には、SWIG (Simplified Wrapper and Interface Generator) を使用しているプロジェクトを go build -n
でビルドする際に、$INTBITS
という変数が未定義のまま出力されてしまう問題を解決します。
コミット
commit 7d249ef28f5566f77706ccf1b0b5f181e0d873ae
Author: Russ Cox <rsc@golang.org>
Date: Tue Sep 10 12:55:07 2013 -0400
cmd/go: fix build -n output when using swig
$INTBITS will not be defined, of course, but that's the best we can do.
Fixes #5978.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13253048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/7d249ef28f5566f77706ccf1b0b5f181e0d873ae
元コミット内容
cmd/go: fix build -n output when using swig
このコミットは、SWIGを使用している場合に go build -n
の出力が正しくない問題を修正します。
$INTBITS
はもちろん定義されませんが、これが現状で最善の解決策です。
Issue #5978 を修正します。
変更の背景
Go言語のビルドシステム cmd/go
は、Goプログラムのコンパイル、リンク、テストなどを管理する中心的なツールです。go build -n
コマンドは、実際にビルドを実行せずに、ビルドプロセスで実行されるコマンドを標準出力に表示する「ドライラン」機能を提供します。これは、ビルドがどのように行われるかをデバッグしたり、理解したりするのに非常に役立ちます。
SWIG (Simplified Wrapper and Interface Generator) は、C/C++で書かれたライブラリを、Goを含む様々なスクリプト言語から呼び出すためのインターフェースコードを自動生成するツールです。GoとC/C++を連携させる際には、CgoというGoの機能が使われますが、SWIGはCgoをさらに抽象化し、より簡単にC/C++ライブラリをGoから利用できるようにします。
このコミットが修正する問題は、SWIGがGoのビルドプロセスに組み込まれた際に発生しました。SWIGは、ターゲットシステムの int
型のサイズを決定するために、swig_intsize.go
という一時的なGoファイルを生成し、それをコンパイルして実行します。このプロセスは、SWIGの -intgosize
オプションで使用され、SWIG 2.0.9以降で導入されました。
しかし、go build -n
を実行した場合、実際にコードがコンパイル・実行されるわけではないため、swig_intsize.go
が生成されず、int
のサイズを決定するロジックが実行されません。その結果、$INTBITS
というプレースホルダーがそのまま出力されてしまい、go build -n
の出力が不正確になるというバグがありました。ユーザーは go build -n
の出力を見て、実際に実行されるコマンドを把握しようとするため、このような不正確な出力は混乱を招きます。
前提知識の解説
cmd/go
: Go言語の公式ビルドツールチェーンのフロントエンドです。go build
,go run
,go test
,go get
などのコマンドを提供し、Goプロジェクトの依存関係解決、コンパイル、リンク、実行、テストなどを自動化します。go build -n
:go build
コマンドのオプションの一つで、"dry run" モードを有効にします。このオプションが指定されると、go build
は実際にバイナリをビルドする代わりに、ビルドプロセス中に実行されるであろうすべてのコマンドを標準出力に表示します。これは、ビルドスクリプトのデバッグや、ビルドの内部動作を理解するのに非常に有用です。- SWIG (Simplified Wrapper and Interface Generator): C/C++で書かれたライブラリのAPIを、Go、Python、Java、Rubyなど、様々なスクリプト言語から呼び出せるようにするためのラッパーコードを自動生成するツールです。SWIGは、C/C++のヘッダーファイルを解析し、ターゲット言語のインターフェース定義を生成します。
- Cgo: Go言語の機能の一つで、GoコードからC言語の関数を呼び出したり、C言語のコードをGoプログラムに組み込んだりすることを可能にします。SWIGは、Go言語のラッパーを生成する際に、内部的にCgoを利用します。
swig_intsize.go
と-intgosize
: SWIG 2.0.9以降では、Goのint
型のサイズ(32ビットか64ビットか)を正確に知る必要があります。これは、C/C++のint
型との互換性を保つためです。SWIGは、この情報を得るために一時的なGoファイル (swig_intsize.go
) を生成し、それをコンパイル・実行してint
のサイズを動的に決定します。このサイズは、SWIGの-intgosize
オプションに渡されます。- Issue #5978: このコミットが修正する具体的なバグ報告です。GoプロジェクトのIssueトラッカーで管理されており、
go build -n
がSWIGを使用する際に不正確な出力を生成するという問題が記述されています。
技術的詳細
この問題の根本原因は、go build -n
がドライランであるにもかかわらず、SWIGが int
のサイズを決定するために実行する動的なコード生成・コンパイル・実行のプロセスが考慮されていなかった点にあります。
cmd/go
の内部では、builder
構造体がビルドプロセスを管理しており、その中に swigIntSize
というメソッドがあります。このメソッドの役割は、SWIGが要求するGoの int
型のサイズを文字列として返すことです。通常、このメソッドは一時ファイル swig_intsize.go
を作成し、それをコンパイル・実行して実際の int
サイズ(例: "32" または "64")を取得します。
しかし、go build -n
が有効な場合(buildN
フラグが true
の場合)、実際のビルドは行われません。したがって、swig_intsize.go
を生成してコンパイル・実行しようとすると、それは無意味であり、またエラーを引き起こす可能性があります。このコミット以前は、buildN
フラグが考慮されていなかったため、swigIntSize
メソッドは常に実際の int
サイズを取得しようとし、結果として go build -n
の出力に swig_intsize.go
のコンパイル・実行に関するコマンドが含まれていました。しかし、これらのコマンドは実際に実行されるわけではないため、$INTBITS
のようなプレースホルダーが解決されずにそのまま出力されてしまっていたのです。
この修正は、swigIntSize
メソッドの冒頭に buildN
フラグのチェックを追加することで、この問題を解決します。buildN
が true
の場合、つまり go build -n
が実行されている場合は、実際の int
サイズを計算する代わりに、単に $INTBITS
という文字列を返します。これは、go build -n
の目的が「実行されるであろうコマンドを表示する」ことであり、実際に値を計算することではないため、適切な振る舞いです。$INTBITS
はSWIGの内部で使われる変数であり、ドライランの出力ではそのプレースホルダーを示すだけで十分と判断されました。
コアとなるコードの変更箇所
変更は src/cmd/go/build.go
ファイルの builder
構造体の swigIntSize
メソッド内で行われています。
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -2162,6 +2162,9 @@ const i int = 1 << 32
// Determine the size of int on the target system for the -intgosize option
// of swig >= 2.0.9
func (b *builder) swigIntSize(obj string) (intsize string, err error) {
+ if buildN {
+ return "$INTBITS", nil
+ }
src := filepath.Join(b.work, "swig_intsize.go")
if err = ioutil.WriteFile(src, []byte(swigIntSizeCode), 0644); err != nil {
return
コアとなるコードの解説
変更は非常にシンプルですが、効果的です。
-
if buildN { ... }
:buildN
はcmd/go
の内部で使われるブール型のフラグで、go build -n
コマンドが実行された場合にtrue
に設定されます。- この
if
文は、現在のビルドがドライランモードであるかどうかをチェックします。
-
return "$INTBITS", nil
:- もし
buildN
がtrue
であれば、swigIntSize
メソッドは直ちに文字列"$INTBITS"
とnil
エラーを返します。 - これは、実際の
int
サイズを計算する複雑なロジック(一時ファイルの生成、コンパイル、実行)をスキップすることを意味します。 "$INTBITS"
は、SWIGが内部的に使用するプレースホルダーであり、ドライランの出力ではこのプレースホルダーが存在することを示すだけで十分です。実際に値が解決されるのは、ドライランではない通常のビルド時のみです。
- もし
この変更により、go build -n
が実行された際に、swigIntSize
メソッドは実際の int
サイズを決定しようとせず、代わりに $INTBITS
という文字列を返すようになります。これにより、go build -n
の出力は、実際に実行されるコマンドのプレースホルダーを正確に反映するようになり、不正確な出力が修正されます。
関連リンク
- Go Issue #5978: cmd/go: build -n output when using swig
- Go CL 13253048: cmd/go: fix build -n output when using swig
- SWIG 公式サイト: http://www.swig.org/
- Go言語のCgoに関するドキュメント: https://go.dev/blog/c-go-is-not-go (Cgoの基本的な概念を理解するのに役立ちます)
参考にした情報源リンク
- 上記のGitHub IssueおよびGo CLのページ
- SWIGのドキュメント (特にGoのバインディングと
-intgosize
オプションに関する情報) - Go言語の
cmd/go
のソースコード (特にbuild.go
ファイル) - Go言語のビルドプロセスに関する一般的な知識
go build -n
の動作に関する情報