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

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

このコミットは、Go言語のコマンドラインツール cmd/go において、SWIG (Simplified Wrapper and Interface Generator) のバージョン 2.0.9 以降で必要となる -intgosize フラグを渡すように変更を加えるものです。これにより、Goの int 型のサイズをSWIGに正確に伝え、C/C++コードとの連携における潜在的な問題を解決します。

コミット

commit 7c793826d44a6abb76f7846c51503f574eafe9e4
Author: Carlos Castillo <cookieo9@gmail.com>
Date:   Fri Mar 1 16:48:21 2013 -0800

    cmd/go: pass -intgosize to SWIG
    
    swig >= 2.0.9 requires the size of int values to be passed via a command line flag. Should swig complain about the -intgosize not being supported, then alert the user to their outdated version of swig.
    
    Fixes #4756.
    
    R=golang-dev, minux.ma, iant
    CC=golang-dev
    https://golang.org/cl/7331048

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

https://github.com/golang/go/commit/7c793826d44a6abb76f7846c51503f574eafe9e4

元コミット内容

cmd/go: pass -intgosize to SWIG

SWIG バージョン 2.0.9 以降では、int 型のサイズをコマンドラインフラグで渡す必要があります。もしSWIGが -intgosize をサポートしていないと文句を言ってきた場合、ユーザーにSWIGのバージョンが古いことを警告するべきです。

Go Issue #4756 を修正します。

変更の背景

この変更の背景には、Go言語とC/C++言語の相互運用性を提供するツールであるSWIGの進化があります。SWIGは、異なるプログラミング言語間でC/C++ライブラリを簡単に利用できるようにするためのラッパーコードを生成します。

Go言語の int 型は、実行環境のアーキテクチャ(32ビットまたは64ビット)によってサイズが異なります。例えば、32ビットシステムでは int は32ビット(4バイト)ですが、64ビットシステムでは64ビット(8バイト)になることがあります。

SWIGのバージョン2.0.9以降では、このGoの int 型の可変サイズに対応するため、明示的に int のサイズをコマンドライン引数 -intgosize で指定するようになりました。これにより、SWIGが生成するラッパーコードが、Goの int 型とC/C++の int 型の間のサイズ不一致による問題を回避できるようになります。

このコミットは、GoのビルドツールがSWIGを呼び出す際に、Goの int 型の実際のサイズを検出し、その情報を -intgosize フラグとしてSWIGに渡すようにすることで、この新しい要件に対応しています。もし古いバージョンのSWIGが使用されており、このフラグを認識しない場合は、適切なエラーメッセージをユーザーに表示し、SWIGのアップデートを促すように設計されています。

具体的には、Go Issue #4756 で報告された問題(SWIG 2.0.9以降でビルドが失敗する問題)を修正するためにこの変更が導入されました。

前提知識の解説

SWIG (Simplified Wrapper and Interface Generator)

SWIGは、C/C++/Objective-Cのコードを、Python, Perl, Ruby, Java, C#, Go, PHP, JavaScript, R, Lua, Octave, Scilab, Scheme, Tcl, Common Lisp, OCaml, Modula-3, Allegro CL, CLISP, Eiffel, Erlang, Guile, MzScheme, Pike, S-Lang, Smalltalk, V8 JavaScript, WxPython, XMLなど、様々なスクリプト言語や他のプログラミング言語から利用できるようにするためのオープンソースツールです。

SWIGは、C/C++のヘッダーファイルやインターフェース定義ファイル(.i ファイル)を読み込み、ターゲット言語(この場合はGo)からC/C++関数を呼び出したり、C/C++クラスのオブジェクトを操作したりするための「ラッパーコード」を自動生成します。これにより、開発者は手動で複雑なFII (Foreign Function Interface) コードを書く手間を省き、異なる言語間の連携を容易にすることができます。

Go言語の int

Go言語の int 型は、プラットフォームに依存する整数型です。これは、実行されているCPUアーキテクチャのワードサイズ(通常は32ビットまたは64ビット)に合わせてサイズが決定されます。

  • 32ビットシステム: int は32ビット(4バイト)
  • 64ビットシステム: int は64ビット(8バイト)

この特性は、Goプログラムが異なるアーキテクチャでコンパイル・実行される際に、int のサイズが自動的に最適化されるという利点がありますが、C/C++のような固定サイズの整数型(例: int は通常32ビット)と連携する際には、サイズ不一致による問題を引き起こす可能性があります。

Cgo

Cgoは、GoプログラムからCコードを呼び出すためのGoの機能です。Cgoを使用すると、Goのソースファイル内にCのコードを直接記述したり、既存のCライブラリをGoからリンクして利用したりできます。SWIGは、GoとC/C++の連携において、Cgoのメカニズムを利用してラッパーコードを生成します。

Goのビルドプロセス (cmd/go)

cmd/go は、Go言語の公式ビルドツールであり、Goプログラムのコンパイル、リンク、テスト、依存関係の管理など、様々なタスクを実行します。Goのソースコードをビルドする際、もしCgoやSWIGが関与するパッケージがある場合、cmd/go はこれらのツールを適切に呼び出し、生成されたC/C++コードやラッパーコードをGoのビルドプロセスに統合します。

技術的詳細

このコミットは、src/cmd/go/build.go ファイルに以下の主要な変更を加えています。

  1. processOutput 関数の導入: 以前は run 関数内で直接行われていた、コマンド実行結果の出力処理(末尾の改行追加、Cgo関連のエラーメッセージの整形)が processOutput という新しいヘルパー関数に切り出されました。これにより、コードの再利用性と可読性が向上しています。

  2. toolchain.gc メソッドの変更: toolchain インターフェースの gc (Goコンパイラ) メソッドのシグネチャが変更されました。以前は (ofile string, err error) を返していましたが、変更後は (ofile string, out []byte, err error) を返すようになりました。これにより、Goコンパイラの標準出力(out []byte)も呼び出し元に返されるようになり、コンパイラからの警告やエラーメッセージをより詳細に処理できるようになります。

  3. swigIntSize 関数の導入: Goの int 型のサイズ(32ビットまたは64ビット)を動的に判定するための swigIntSize 関数が追加されました。 この関数は、swig_intsize.go という一時的なGoソースファイルを生成します。このファイルには、const i int = 1 << 32 という定数宣言が含まれています。 この一時ファイルをGoコンパイラ (buildToolchain.gc) でコンパイルを試みます。

    • もしコンパイルが成功した場合、それは int が64ビットであることを意味します(1 << 32int に収まるため)。この場合、"64" を返します。
    • もしコンパイルが失敗した場合(例: constant 4294967296 overflows int のようなエラー)、それは int が32ビットであることを意味します(1 << 32int に収まらないため)。この場合、"32" を返します。 この巧妙な方法で、実行環境の int サイズをビルド時に判定しています。
  4. swigOne 関数への -intgosize フラグの追加: SWIGの単一の入力ファイルを処理する swigOne 関数が変更され、intgosize 引数が追加されました。 SWIGコマンドを実行する際に、"-intgosize", intgosize という引数が追加されるようになりました。これにより、Goの int 型のサイズがSWIGに正確に伝えられます。

  5. SWIGのエラーハンドリングの改善: swigOne 関数内でSWIGの実行結果をチェックする際に、SWIGの出力に "Unrecognized option -intgosize" という文字列が含まれているかどうかをチェックするようになりました。 もしこの文字列が含まれていれば、それはユーザーがSWIGバージョン2.0.9よりも古いものを使用していることを意味するため、"must have SWIG version >= 2.0.9\n" というエラーメッセージを生成し、ユーザーにSWIGのアップデートを促します。

これらの変更により、GoのビルドツールはSWIGの新しい要件に準拠し、異なるアーキテクチャ上でのGoとC/C++の相互運用性をより堅牢にしています。

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

変更の中心は src/cmd/go/build.go ファイルです。

src/cmd/go/build.go の変更点

  • build メソッド内の buildToolchain.gc 呼び出しの変更:

    --- a/src/cmd/go/build.go
    +++ b/src/cmd/go/build.go
    @@ -814,11 +814,17 @@ func (b *builder) build(a *action) (err error) {
     
     	// Compile Go.
     	if len(gofiles) > 0 {
    -		out, err := buildToolchain.gc(b, a.p, obj, inc, gofiles)
    +		ofile, out, err := buildToolchain.gc(b, a.p, obj, inc, gofiles)
    +		if len(out) > 0 {
    +			b.showOutput(a.p.Dir, a.p.ImportPath, b.processOutput(out))
    +			if err != nil {
    +				return errPrintedOutput
    +			}
    +		}
     		if err != nil {
     			return err
     		}
    -		objects = append(objects, out)
    +		objects = append(objects, ofile)
     	}
    

    buildToolchain.gc の戻り値が ofileout に増え、out (標準出力) が存在する場合に processOutput を通して表示されるようになりました。

  • run メソッド内の出力処理の変更:

    --- a/src/cmd/go/build.go
    +++ b/src/cmd/go/build.go
    @@ -1185,22 +1191,10 @@ var cgoLine = regexp.MustCompile(`\\[[^\\[\\]]+\\.cgo1\\.go:[0-9]+\\]`)\n func (b *builder) run(dir string, desc string, cmdargs ...interface{}) error {\n     out, err := b.runOut(dir, desc, cmdargs...)\n     if len(out) > 0 {\n-        if out[len(out)-1] != '\\n' {\n-            out = append(out, '\\n')\n-        }\n         if desc == "" {\n             desc = b.fmtcmd(dir, "%s", strings.Join(stringList(cmdargs...), " "))\n         }\n-        out := string(out)\n-        // Fix up output referring to cgo-generated code to be more readable.\n-        // Replace x.go:19[/tmp/.../x.cgo1.go:18] with x.go:19.\n-        // Replace _Ctype_foo with C.foo.\n-        // If we're using -x, assume we're debugging and want the full dump, so disable the rewrite.\n-        if !buildX && cgoLine.MatchString(out) {\n-            out = cgoLine.ReplaceAllString(out, "")\n-            out = strings.Replace(out, "type _Ctype_", "type C.", -1)\n-        }\n-        b.showOutput(dir, desc, out)\n+        b.showOutput(dir, desc, b.processOutput(out))\n         if err != nil {\n             err = errPrintedOutput\n         }\n    ```
    `run` メソッドから出力整形ロジックが削除され、`processOutput` 関数に委譲されました。
    
    
  • processOutput 関数の追加:

    --- a/src/cmd/go/build.go
    +++ b/src/cmd/go/build.go
    @@ -1208,6 +1202,23 @@ func (b *builder) run(dir string, desc string, cmdargs ...interface{}) error {\n     return err\n }\n     \n+// processOutput prepares the output of runOut to be output to the console.\n+func (b *builder) processOutput(out []byte) string {\n+    if out[len(out)-1] != '\\n' {\n+        out = append(out, '\\n')\n+    }\n+    messages := string(out)\n+    // Fix up output referring to cgo-generated code to be more readable.\n+    // Replace x.go:19[/tmp/.../x.cgo1.go:18] with x.go:19.\n+    // Replace _Ctype_foo with C.foo.\n+    // If we're using -x, assume we're debugging and want the full dump, so disable the rewrite.\n+    if !buildX && cgoLine.MatchString(messages) {\n+        messages = cgoLine.ReplaceAllString(messages, "")\n+        messages = strings.Replace(messages, "type _Ctype_", "type C.", -1)\n+    }\n+    return messages\n+}\n+\n // runOut runs the command given by cmdline in the directory dir.\n // It returns the command output and any errors that occurred.\n func (b *builder) runOut(dir string, desc string, cmdargs ...interface{}) ([]byte, error) {\
    

    コマンドの出力を整形するための新しい関数が追加されました。

  • toolchain インターフェースの gc メソッドのシグネチャ変更:

    --- a/src/cmd/go/build.go
    +++ b/src/cmd/go/build.go
    @@ -1325,7 +1336,7 @@ type toolchain interface {\n     // gc runs the compiler in a specific directory on a set of files\n     // and returns the name of the generated output file.\n     // The compiler runs in the directory dir.\n-    gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error)\n+    gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, out []byte, err error)\
    

    gc メソッドが out []byte も返すようになりました。

  • noToolchain および gcToolchaingc メソッドの実装変更: noToolchaingcToolchaingc メソッドが、新しいシグネチャに合わせて out []byte を返すように変更されました。

  • swig メソッド内の swigIntSize 呼び出し:

    --- a/src/cmd/go/build.go
    +++ b/src/cmd/go/build.go
    @@ -1897,8 +1912,14 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,\n     \n     // Run SWIG on all SWIG input files.\n     func (b *builder) swig(p *Package, obj string, gccfiles []string) (outGo, outObj []string, err error) {\n    +\n    +\tintgosize, err := b.swigIntSize(obj)\n    +\tif err != nil {\n    +\t\treturn nil, nil, err\n    +\t}\n    +\n     	for _, f := range p.SwigFiles {\n    -		goFile, objFile, err := b.swigOne(p, f, obj, false)\n    +		goFile, objFile, err := b.swigOne(p, f, obj, false, intgosize)\
    

    swig 関数内で swigIntSize を呼び出し、その結果を swigOne に渡すようになりました。

  • swigIntSize 関数の追加:

    --- a/src/cmd/go/build.go
    +++ b/src/cmd/go/build.go
    @@ -1924,8 +1945,31 @@ func (b *builder) swig(p *Package, obj string, gccfiles []string) (outGo, outObj\n     	return outGo, outObj, nil\n     }\n     \n    +// This code fails to build if sizeof(int) <= 32\n    +const swigIntSizeCode = `\n    +package main\n    +const i int = 1 << 32\n    +`\n    +\n    +// Determine the size of int on the target system for the -intgosize option\n    +// of swig >= 2.0.9\n    +func (b *builder) swigIntSize(obj string) (intsize string, err error) {\n    +\tsrc := filepath.Join(b.work, "swig_intsize.go")\n    +\tif err = ioutil.WriteFile(src, []byte(swigIntSizeCode), 0644); err != nil {\n    +\t\treturn\n    +\t}\n    +\tsrcs := []string{src}\n    +\n    +\tp := goFilesPackage(srcs)\n    +\n    +\tif _, _, e := buildToolchain.gc(b, p, obj, nil, srcs); e != nil {\n    +\t\treturn "32", nil\n    +\t}\n    +\treturn "64", nil\n    +}\n    +\n     // Run SWIG on one SWIG input file.\n    -func (b *builder) swigOne(p *Package, file, obj string, cxx bool) (outGo, outObj string, err error) {\
    

    Goの int サイズを判定するための新しい関数が追加されました。

  • swigOne メソッドへの -intgosize フラグの追加とエラーハンドリングの改善:

    --- a/src/cmd/go/build.go
    +++ b/src/cmd/go/build.go
    @@ -1945,6 +1989,7 @@ func (b *builder) swigOne(p *Package, file, obj string, cxx bool) (outGo, outObj\n     	// swig\n     	args := []string{\n     		"-go",\n    +		"-intgosize", intgosize,\n     		"-module", base,\n     		"-soname", soname,\n     		"-o", obj + gccBase + gccExt,\
    

    SWIGコマンドに -intgosize フラグが追加されました。

    --- a/src/cmd/go/build.go
    +++ b/src/cmd/go/build.go
    @@ -1957,7 +2002,14 @@ func (b *builder) swigOne(p *Package, file, obj string, cxx bool) (outGo, outObj\n     		args = append(args, "-c++")\n     	}\n     \n    -	if err := b.run(p.Dir, p.ImportPath, "swig", args, file); err != nil {\n    +	if out, err := b.runOut(p.Dir, p.ImportPath, "swig", args, file); err != nil {\n    +		if len(out) > 0 {\n    +			if bytes.Contains(out, []byte("Unrecognized option -intgosize")) {\n    +				return "", "", errors.New("must have SWIG version >= 2.0.9\\n")\n    +			}\n    +			b.showOutput(p.Dir, p.ImportPath, b.processOutput(out))\n    +			return "", "", errPrintedOutput\n    +		}\n     		return "", "", err\n     	}\
    

    SWIGの出力に "Unrecognized option -intgosize" が含まれる場合のエラーハンドリングが追加されました。

コアとなるコードの解説

このコミットの核心は、GoのビルドプロセスがSWIGと連携する際に、Goの int 型のサイズを正確にSWIGに伝えるメカニズムを導入した点にあります。

  1. swigIntSize 関数による int サイズの動的判定: この関数は、Goの int が32ビットか64ビットかをビルド時に自動的に判定します。これは、const i int = 1 << 32 というコードを含む一時的なGoファイルをコンパイルすることで実現されます。もし int が32ビットであれば 1 << 32 はオーバーフローするためコンパイルエラーとなり、64ビットであれば成功します。この結果に基づいて、SWIGに渡すべき -intgosize の値("32" または "64")が決定されます。この動的な判定は、異なるアーキテクチャでGoコードをビルドする際の移植性を高めます。

  2. swigOne 関数での -intgosize フラグの追加: swigOne 関数は、個々のSWIG入力ファイルを処理し、SWIGコマンドを実行します。この関数に intgosize 引数が追加され、SWIGコマンドの引数リストに "-intgosize", intgosize が追加されるようになりました。これにより、SWIGはGoの int 型の正確なサイズを知ることができ、GoとC/C++間の型変換を正しく行えるようになります。

  3. SWIGのバージョンチェックとユーザーへの警告: swigOne 関数内でSWIGの実行結果をチェックし、もしSWIGが -intgosize オプションを認識しないというエラーメッセージ("Unrecognized option -intgosize")を出力した場合、Goのビルドツールはユーザーに対して「SWIGバージョン2.0.9以上が必要です」という明確なエラーメッセージを表示します。これは、ユーザーが古いSWIGバージョンを使用している場合に、問題の原因を特定し、解決策を提示するための重要なユーザーフレンドリーな改善です。

  4. 出力処理の共通化 (processOutput): processOutput 関数の導入は、コードの重複を減らし、GoコンパイラやSWIGなどの外部ツールの出力メッセージを整形するロジックを一元化することで、保守性を向上させています。特にCgoが生成する一時ファイルに関するパスを整形する機能は、ユーザーにとってより読みやすいエラーメッセージを提供するために重要です。

これらの変更は、Goのビルドシステムが外部ツール(SWIG)の進化に追従し、GoとC/C++の相互運用性をより堅牢かつユーザーフレンドリーにするための重要なステップです。

関連リンク

参考にした情報源リンク

  • SWIG 公式サイト: http://www.swig.org/
  • Go言語の int 型に関するドキュメント (Go言語の公式ドキュメントや仕様を参照)
  • Cgoに関するGo言語のドキュメント (Go言語の公式ドキュメントを参照)
  • SWIGの変更履歴やリリースノート (特に2.0.9に関する情報)

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

このコミットは、Go言語のコマンドラインツール cmd/go において、SWIG (Simplified Wrapper and Interface Generator) のバージョン 2.0.9 以降で必要となる -intgosize フラグを渡すように変更を加えるものです。これにより、Goの int 型のサイズをSWIGに正確に伝え、C/C++コードとの連携における潜在的な問題を解決します。

コミット

commit 7c793826d44a6abb76f7846c51503f574eafe9e4
Author: Carlos Castillo <cookieo9@gmail.com>
Date:   Fri Mar 1 16:48:21 2013 -0800

    cmd/go: pass -intgosize to SWIG
    
    swig >= 2.0.9 requires the size of int values to be passed via a command line flag. Should swig complain about the -intgosize not being supported, then alert the user to their outdated version of swig.
    
    Fixes #4756.
    
    R=golang-dev, minux.ma, iant
    CC=golang-dev
    https://golang.org/cl/7331048

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

https://github.com/golang/go/commit/7c793826d44a6abb76f7846c51503f574eafe9e4

元コミット内容

cmd/go: pass -intgosize to SWIG

SWIG バージョン 2.0.9 以降では、int 型のサイズをコマンドラインフラグで渡す必要があります。もしSWIGが -intgosize をサポートしていないと文句を言ってきた場合、ユーザーにSWIGのバージョンが古いことを警告するべきです。

Go Issue #4756 を修正します。

変更の背景

この変更の背景には、Go言語とC/C++言語の相互運用性を提供するツールであるSWIGの進化があります。SWIGは、異なるプログラミング言語間でC/C++ライブラリを簡単に利用できるようにするためのラッパーコードを生成します。

Go言語の int 型は、実行環境のアーキテクチャ(32ビットまたは64ビット)によってサイズが異なります。例えば、32ビットシステムでは int は32ビット(4バイト)ですが、64ビットシステムでは64ビット(8バイト)になることがあります。

SWIGのバージョン2.0.9以降では、このGoの int 型の可変サイズに対応するため、明示的に int のサイズをコマンドライン引数 -intgosize で指定するようになりました。これにより、SWIGが生成するラッパーコードが、Goの int 型とC/C++の int 型の間のサイズ不一致による問題を回避できるようになります。

このコミットは、GoのビルドツールがSWIGを呼び出す際に、Goの int 型の実際のサイズを検出し、その情報を -intgosize フラグとしてSWIGに渡すようにすることで、この新しい要件に対応しています。もし古いバージョンのSWIGが使用されており、このフラグを認識しない場合は、適切なエラーメッセージをユーザーに表示し、SWIGのアップデートを促すように設計されています。

具体的には、Go Issue #4756 で報告された問題(SWIG 2.0.9以降でビルドが失敗する問題)を修正するためにこの変更が導入されました。

前提知識の解説

SWIG (Simplified Wrapper and Interface Generator)

SWIGは、C/C++/Objective-Cのコードを、Python, Perl, Ruby, Java, C#, Go, PHP, JavaScript, R, Lua, Octave, Scilab, Scheme, Tcl, Common Lisp, OCaml, Modula-3, Allegro CL, CLISP, Eiffel, Erlang, Guile, MzScheme, Pike, S-Lang, Smalltalk, V8 JavaScript, WxPython, XMLなど、様々なスクリプト言語や他のプログラミング言語から利用できるようにするためのオープンソースツールです。

SWIGは、C/C++のヘッダーファイルやインターフェース定義ファイル(.i ファイル)を読み込み、ターゲット言語(この場合はGo)からC/C++関数を呼び出したり、C/C++クラスのオブジェクトを操作したりするための「ラッパーコード」を自動生成します。これにより、開発者は手動で複雑なFII (Foreign Function Interface) コードを書く手間を省き、異なる言語間の連携を容易にすることができます。

Go言語の int

Go言語の int 型は、プラットフォームに依存する整数型です。これは、実行されているCPUアーキテクチャのワードサイズ(通常は32ビットまたは64ビット)に合わせてサイズが決定されます。

  • 32ビットシステム: int は32ビット(4バイト)
  • 64ビットシステム: int は64ビット(8バイト)

この特性は、Goプログラムが異なるアーキテクチャでコンパイル・実行される際に、int のサイズが自動的に最適化されるという利点がありますが、C/C++のような固定サイズの整数型(例: int は通常32ビット)と連携する際には、サイズ不一致による問題を引き起こす可能性があります。

Cgo

Cgoは、GoプログラムからCコードを呼び出すためのGoの機能です。Cgoを使用すると、Goのソースファイル内にCのコードを直接記述したり、既存のCライブラリをGoからリンクして利用したりできます。SWIGは、GoとC/C++の連携において、Cgoのメカニズムを利用してラッパーコードを生成します。

Goのビルドプロセス (cmd/go)

cmd/go は、Go言語の公式ビルドツールであり、Goプログラムのコンパイル、リンク、テスト、依存関係の管理など、様々なタスクを実行します。Goのソースコードをビルドする際、もしCgoやSWIGが関与するパッケージがある場合、cmd/go はこれらのツールを適切に呼び出し、生成されたC/C++コードやラッパーコードをGoのビルドプロセスに統合します。

技術的詳細

このコミットは、src/cmd/go/build.go ファイルに以下の主要な変更を加えています。

  1. processOutput 関数の導入: 以前は run 関数内で直接行われていた、コマンド実行結果の出力処理(末尾の改行追加、Cgo関連のエラーメッセージの整形)が processOutput という新しいヘルパー関数に切り出されました。これにより、コードの再利用性と可読性が向上しています。

  2. toolchain.gc メソッドの変更: toolchain インターフェースの gc (Goコンパイラ) メソッドのシグネチャが変更されました。以前は (ofile string, err error) を返していましたが、変更後は (ofile string, out []byte, err error) を返すようになりました。これにより、Goコンパイラの標準出力(out []byte)も呼び出し元に返されるようになり、コンパイラからの警告やエラーメッセージをより詳細に処理できるようになります。

  3. swigIntSize 関数の導入: Goの int 型のサイズ(32ビットまたは64ビット)を動的に判定するための swigIntSize 関数が追加されました。 この関数は、swig_intsize.go という一時的なGoソースファイルを生成します。このファイルには、const i int = 1 << 32 という定数宣言が含まれています。 この一時ファイルをGoコンパイラ (buildToolchain.gc) でコンパイルを試みます。

    • もしコンパイルが成功した場合、それは int が64ビットであることを意味します(1 << 32int に収まるため)。この場合、"64" を返します。
    • もしコンパイルが失敗した場合(例: constant 4294967296 overflows int のようなエラー)、それは int が32ビットであることを意味します(1 << 32int に収まらないため)。この巧妙な方法で、実行環境の int サイズをビルド時に判定しています。
  4. swigOne 関数への -intgosize フラグの追加: SWIGの単一の入力ファイルを処理する swigOne 関数が変更され、intgosize 引数が追加されました。 SWIGコマンドを実行する際に、"-intgosize", intgosize という引数が追加されるようになりました。これにより、Goの int 型のサイズがSWIGに正確に伝えられます。

  5. SWIGのエラーハンドリングの改善: swigOne 関数内でSWIGの実行結果をチェックする際に、SWIGの出力に "Unrecognized option -intgosize" という文字列が含まれているかどうかをチェックするようになりました。 もしこの文字列が含まれていれば、それはユーザーがSWIGバージョン2.0.9よりも古いものを使用していることを意味するため、"must have SWIG version >= 2.0.9\n" というエラーメッセージを生成し、ユーザーにSWIGのアップデートを促します。

これらの変更により、GoのビルドツールはSWIGの新しい要件に準拠し、異なるアーキテクチャ上でのGoとC/C++の相互運用性をより堅牢にしています。

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

変更の中心は src/cmd/go/build.go ファイルです。

src/cmd/go/build.go の変更点

  • build メソッド内の buildToolchain.gc 呼び出しの変更:

    --- a/src/cmd/go/build.go
    +++ b/src/cmd/go/build.go
    @@ -814,11 +814,17 @@ func (b *builder) build(a *action) (err error) {
     
     	// Compile Go.
     	if len(gofiles) > 0 {
    -		out, err := buildToolchain.gc(b, a.p, obj, inc, gofiles)
    +		ofile, out, err := buildToolchain.gc(b, a.p, obj, inc, gofiles)
    +		if len(out) > 0 {
    +			b.showOutput(a.p.Dir, a.p.ImportPath, b.processOutput(out))
    +			if err != nil {
    +				return errPrintedOutput
    +			}
    +		}
     		if err != nil {
     			return err
     		}
    -		objects = append(objects, out)
    +		objects = append(objects, ofile)
     	}
    

    buildToolchain.gc の戻り値が ofileout に増え、out (標準出力) が存在する場合に processOutput を通して表示されるようになりました。

  • run メソッド内の出力処理の変更:

    --- a/src/cmd/go/build.go
    +++ b/src/cmd/go/build.go
    @@ -1185,22 +1191,10 @@ var cgoLine = regexp.MustCompile(`\\[[^\\[\\]]+\\.cgo1\\.go:[0-9]+\\]`)\n func (b *builder) run(dir string, desc string, cmdargs ...interface{}) error {\n     out, err := b.runOut(dir, desc, cmdargs...)\n     if len(out) > 0 {\n-        if out[len(out)-1] != '\\n' {\n-            out = append(out, '\\n')\n-        }\n         if desc == "" {\n             desc = b.fmtcmd(dir, "%s", strings.Join(stringList(cmdargs...), " "))\n         }\n-        out := string(out)\n-        // Fix up output referring to cgo-generated code to be more readable.\n-        // Replace x.go:19[/tmp/.../x.cgo1.go:18] with x.go:19.\n-        // Replace _Ctype_foo with C.foo.\n-        // If we're using -x, assume we're debugging and want the full dump, so disable the rewrite.\n-        if !buildX && cgoLine.MatchString(out) {\n-            out = cgoLine.ReplaceAllString(out, "")\n-            out = strings.Replace(out, "type _Ctype_", "type C.", -1)\n-        }\n-        b.showOutput(dir, desc, out)\n+        b.showOutput(dir, desc, b.processOutput(out))\n         if err != nil {\n             err = errPrintedOutput\n         }\n    ```
    `run` メソッドから出力整形ロジックが削除され、`processOutput` 関数に委譲されました。
    
    
  • processOutput 関数の追加:

    --- a/src/cmd/go/build.go
    +++ b/src/cmd/go/build.go
    @@ -1208,6 +1202,23 @@ func (b *builder) run(dir string, desc string, cmdargs ...interface{}) error {\n     return err\n }\n     \n+// processOutput prepares the output of runOut to be output to the console.\n+func (b *builder) processOutput(out []byte) string {\n+    if out[len(out)-1] != '\\n' {\n+        out = append(out, '\\n')\n+    }\n+    messages := string(out)\n+    // Fix up output referring to cgo-generated code to be more readable.\n+    // Replace x.go:19[/tmp/.../x.cgo1.go:18] with x.go:19.\n+    // Replace _Ctype_foo with C.foo.\n+    // If we're using -x, assume we're debugging and want the full dump, so disable the rewrite.\n+    if !buildX && cgoLine.MatchString(messages) {\n+        messages = cgoLine.ReplaceAllString(messages, "")\n+        messages = strings.Replace(messages, "type _Ctype_", "type C.", -1)\n+    }\n+    return messages\n+}\n+\n // runOut runs the command given by cmdline in the directory dir.\n // It returns the command output and any errors that occurred.\n func (b *builder) runOut(dir string, desc string, cmdargs ...interface{}) ([]byte, error) {\
    

    コマンドの出力を整形するための新しい関数が追加されました。

  • toolchain インターフェースの gc メソッドのシグネチャ変更:

    --- a/src/cmd/go/build.go
    +++ b/src/cmd/go/build.go
    @@ -1325,7 +1336,7 @@ type toolchain interface {\n     // gc runs the compiler in a specific directory on a set of files\n     // and returns the name of the generated output file.\n     // The compiler runs in the directory dir.\n-    gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, err error)\n+    gc(b *builder, p *Package, obj string, importArgs []string, gofiles []string) (ofile string, out []byte, err error)\
    

    gc メソッドが out []byte も返すようになりました。

  • noToolchain および gcToolchaingc メソッドの実装変更: noToolchaingcToolchaingc メソッドが、新しいシグネチャに合わせて out []byte を返すように変更されました。

  • swig メソッド内の swigIntSize 呼び出し:

    --- a/src/cmd/go/build.go
    +++ b/src/cmd/go/build.go
    @@ -1897,8 +1912,14 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,\n     \n     // Run SWIG on all SWIG input files.\n     func (b *builder) swig(p *Package, obj string, gccfiles []string) (outGo, outObj []string, err error) {\n    +\n    +\tintgosize, err := b.swigIntSize(obj)\n    +\tif err != nil {\n    +\t\treturn nil, nil, err\n    +\t}\n    +\n     	for _, f := range p.SwigFiles {\n    -		goFile, objFile, err := b.swigOne(p, f, obj, false)\n    +		goFile, objFile, err := b.swigOne(p, f, obj, false, intgosize)\
    

    swig 関数内で swigIntSize を呼び出し、その結果を swigOne に渡すようになりました。

  • swigIntSize 関数の追加:

    --- a/src/cmd/go/build.go
    +++ b/src/cmd/go/build.go
    @@ -1924,8 +1945,31 @@ func (b *builder) swig(p *Package, obj string, gccfiles []string) (outGo, outObj\n     	return outGo, outObj, nil\n     }\n     \n    +// This code fails to build if sizeof(int) <= 32\n    +const swigIntSizeCode = `\n    +package main\n    +const i int = 1 << 32\n    +`\n    +\n    +// Determine the size of int on the target system for the -intgosize option\n    +// of swig >= 2.0.9\n    +func (b *builder) swigIntSize(obj string) (intsize string, err error) {\n    +\tsrc := filepath.Join(b.work, "swig_intsize.go")\n    +\tif err = ioutil.WriteFile(src, []byte(swigIntSizeCode), 0644); err != nil {\n    +\t\treturn\n    +\t}\n    +\tsrcs := []string{src}\n    +\n    +\tp := goFilesPackage(srcs)\n    +\n    +\tif _, _, e := buildToolchain.gc(b, p, obj, nil, srcs); e != nil {\n    +\t\treturn "32", nil\n    +\t}\n    +\treturn "64", nil\n    +}\n    +\n     // Run SWIG on one SWIG input file.\n    -func (b *builder) swigOne(p *Package, file, obj string, cxx bool) (outGo, outObj string, err error) {\
    

    Goの int サイズを判定するための新しい関数が追加されました。

  • swigOne メソッドへの -intgosize フラグの追加とエラーハンドリングの改善:

    --- a/src/cmd/go/build.go
    +++ b/src/cmd/go/build.go
    @@ -1945,6 +1989,7 @@ func (b *builder) swigOne(p *Package, file, obj string, cxx bool) (outGo, outObj\n     	// swig\n     	args := []string{\n     		"-go",\n    +		"-intgosize", intgosize,\n     		"-module", base,\n     		"-soname", soname,\n     		"-o", obj + gccBase + gccExt,\
    

    SWIGコマンドに -intgosize フラグが追加されました。

    --- a/src/cmd/go/build.go
    +++ b/cmd/go/build.go
    @@ -1957,7 +2002,14 @@ func (b *builder) swigOne(p *Package, file, obj string, cxx bool) (outGo, outObj\n     		args = append(args, "-c++")\n     	}\n     \n    -	if err := b.run(p.Dir, p.ImportPath, "swig", args, file); err != nil {\n    +	if out, err := b.runOut(p.Dir, p.ImportPath, "swig", args, file); err != nil {\n    +		if len(out) > 0 {\n    +			if bytes.Contains(out, []byte("Unrecognized option -intgosize")) {\n    +				return "", "", errors.New("must have SWIG version >= 2.0.9\\n")\n    +			}\n    +			b.showOutput(p.Dir, p.ImportPath, b.processOutput(out))\n    +			return "", "", errPrintedOutput\n    +		}\n     		return "", "", err\n     	}\
    

    SWIGの出力に "Unrecognized option -intgosize" が含まれる場合のエラーハンドリングが追加されました。

コアとなるコードの解説

このコミットの核心は、GoのビルドプロセスがSWIGと連携する際に、Goの int 型のサイズを正確にSWIGに伝えるメカニズムを導入した点にあります。

  1. swigIntSize 関数による int サイズの動的判定: この関数は、Goの int が32ビットか64ビットかをビルド時に自動的に判定します。これは、const i int = 1 << 32 というコードを含む一時的なGoファイルをコンパイルすることで実現されます。もし int が32ビットであれば 1 << 32 はオーバーフローするためコンパイルエラーとなり、64ビットであれば成功します。この結果に基づいて、SWIGに渡すべき -intgosize の値("32" または "64")が決定されます。この動的な判定は、異なるアーキテクチャでGoコードをビルドする際の移植性を高めます。

  2. swigOne 関数での -intgosize フラグの追加: swigOne 関数は、個々のSWIG入力ファイルを処理し、SWIGコマンドを実行します。この関数に intgosize 引数が追加され、SWIGコマンドの引数リストに "-intgosize", intgosize が追加されるようになりました。これにより、SWIGはGoの int 型の正確なサイズを知ることができ、GoとC/C++間の型変換を正しく行えるようになります。

  3. SWIGのバージョンチェックとユーザーへの警告: swigOne 関数内でSWIGの実行結果をチェックし、もしSWIGが -intgosize オプションを認識しないというエラーメッセージ("Unrecognized option -intgosize")を出力した場合、Goのビルドツールはユーザーに対して「SWIGバージョン2.0.9以上が必要です」という明確なエラーメッセージを表示します。これは、ユーザーが古いSWIGバージョンを使用している場合に、問題の原因を特定し、解決策を提示するための重要なユーザーフレンドリーな改善です。

  4. 出力処理の共通化 (processOutput): processOutput 関数の導入は、コードの重複を減らし、GoコンパイラやSWIGなどの外部ツールの出力メッセージを整形するロジックを一元化することで、保守性を向上させています。特にCgoが生成する一時ファイルに関するパスを整形する機能は、ユーザーにとってより読みやすいエラーメッセージを提供するために重要です。

これらの変更は、Goのビルドシステムが外部ツール(SWIG)の進化に追従し、GoとC/C++の相互運用性をより堅牢かつユーザーフレンドリーにするための重要なステップです。

関連リンク

参考にした情報源リンク

  • SWIG 公式サイト: http://www.swig.org/
  • SWIG Go Language Module Documentation: http://www.swig.org/Doc2.0/Go.html (特に -intgosize オプションに関する記述)
  • Go言語の int 型に関するドキュメント (Go言語の公式ドキュメントや仕様を参照)
  • Cgoに関するGo言語のドキュメント (Go言語の公式ドキュメントを参照)
  • SWIGの変更履歴やリリースノート (特に2.0.9に関する情報)