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

[インデックス 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 フラグのチェックを追加することで、この問題を解決します。buildNtrue の場合、つまり 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

コアとなるコードの解説

変更は非常にシンプルですが、効果的です。

  1. if buildN { ... }:

    • buildNcmd/go の内部で使われるブール型のフラグで、go build -n コマンドが実行された場合に true に設定されます。
    • この if 文は、現在のビルドがドライランモードであるかどうかをチェックします。
  2. return "$INTBITS", nil:

    • もし buildNtrue であれば、swigIntSize メソッドは直ちに文字列 "$INTBITS"nil エラーを返します。
    • これは、実際の int サイズを計算する複雑なロジック(一時ファイルの生成、コンパイル、実行)をスキップすることを意味します。
    • "$INTBITS" は、SWIGが内部的に使用するプレースホルダーであり、ドライランの出力ではこのプレースホルダーが存在することを示すだけで十分です。実際に値が解決されるのは、ドライランではない通常のビルド時のみです。

この変更により、go build -n が実行された際に、swigIntSize メソッドは実際の int サイズを決定しようとせず、代わりに $INTBITS という文字列を返すようになります。これにより、go build -n の出力は、実際に実行されるコマンドのプレースホルダーを正確に反映するようになり、不正確な出力が修正されます。

関連リンク

参考にした情報源リンク

  • 上記のGitHub IssueおよびGo CLのページ
  • SWIGのドキュメント (特にGoのバインディングと -intgosize オプションに関する情報)
  • Go言語の cmd/go のソースコード (特に build.go ファイル)
  • Go言語のビルドプロセスに関する一般的な知識
  • go build -n の動作に関する情報