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

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

コミット

commit 6fa38e5e0a2ceb83871d22e27a80fca271f26a73
Author: Ian Lance Taylor <iant@golang.org>
Date:   Fri Aug 3 18:08:43 2012 -0700

    cmd/go, go/build, misc/swig: add SWIG support to Go tool
    
    R=adg, rsc, franciscossouza, seb.binet, gen.battle
    CC=golang-dev
    https://golang.org/cl/5845071

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

https://github.com/golang/go/commit/6fa38e5e0a2ceb83871d22e27a80fca271f26a73

元コミット内容

cmd/go, go/build, misc/swig: add SWIG support to Go tool

このコミットは、GoツールにSWIGのサポートを追加することを目的としています。具体的には、cmd/go(Goコマンドラインツール)、go/build(Goのビルドシステム)、そしてmisc/swig(SWIG関連の雑多なファイルやテスト)に変更が加えられています。

変更の背景

Go言語は、その設計思想としてシンプルさと効率性を重視していますが、既存のC/C++ライブラリとの連携は、多くのシステムプログラミングにおいて不可欠です。GoにはCgoというC言語との連携メカニズムが標準で提供されていますが、C++やより複雑なCの構造体、コールバックなどを扱う場合には、SWIG(Simplified Wrapper and Interface Generator)のようなツールが非常に強力な選択肢となります。

このコミットが行われた2012年8月時点では、Go言語はまだ発展途上にあり、エコシステムも現在ほど成熟していませんでした。既存のC/C++資産をGoから利用したいというニーズは高く、SWIGはそのギャップを埋めるための重要なツールでした。Goツール自体にSWIGのサポートを組み込むことで、開発者はGoプロジェクト内でSWIGを利用したC/C++バインディングをよりシームレスにビルドできるようになります。これにより、Go言語の適用範囲が広がり、既存の豊富なC/C++ライブラリ資産をGoプロジェクトで活用する道が開かれました。

具体的には、GoのビルドプロセスにSWIGの実行ステップを統合し、SWIGが生成するGoコードとC/C++コードを適切に処理できるようにすることが、この変更の主要な動機と考えられます。これにより、Go開発者は外部のビルドスクリプトや手動でのSWIG実行なしに、Goの標準ビルドコマンド(go build)を通じてSWIGを利用できるようになります。

前提知識の解説

Go言語

Go(Golang)は、Googleによって開発されたオープンソースのプログラミング言語です。静的型付け、コンパイル型、並行処理のサポート(GoroutineとChannel)、ガベージコレクションなどの特徴を持ちます。システムプログラミング、Webサービス、CLIツールなど幅広い分野で利用されています。

SWIG (Simplified Wrapper and Interface Generator)

SWIGは、C/C++/Objective-Cのプログラムやライブラリを、Perl、Python、Ruby、Java、C#、Go、PHP、JavaScript、Lua、R、Octave、Scheme、Tcl、Allegro CLなどのスクリプト言語や他の高水準言語から呼び出せるようにするためのインターフェースコードを生成するツールです。SWIGは、C/C++のヘッダーファイルやインターフェース定義ファイル(.iファイル)を解析し、ターゲット言語(この場合はGo)からC/C++関数やクラス、変数にアクセスするためのラッパーコードを自動生成します。これにより、異なる言語間での相互運用性を容易にします。

SWIGの主な機能は以下の通りです。

  • ラッパーコード生成: C/C++のAPIをターゲット言語のネイティブなデータ型や関数呼び出し規約に変換するコードを生成します。
  • 型マッピング: C/C++のデータ型をターゲット言語の対応する型に自動的にマッピングします。カスタムの型マッピングを定義することも可能です。
  • コールバック: ターゲット言語からC/C++コードにコールバック関数を渡すメカニズムを提供します。
  • ディレクター: C++の仮想関数をターゲット言語でオーバーライドできるようにする機能です。

Cgo

Cgoは、GoプログラムからCコードを呼び出すためのGoの機能です。Goのソースファイル内でimport "C"と記述することで、Cの関数や型をGoコードから直接利用できるようになります。Cgoは主にC言語との連携に特化しており、C++の複雑な機能(クラス、テンプレート、例外など)を直接扱うのは困難です。SWIGは、Cgoの基盤の上に構築されることもあり、Cgoだけでは難しいC++のバインディングを生成する際に特に有用です。

ビルドシステムとツールチェーン

Goのビルドシステムは、go buildコマンドを中心に構成されており、ソースファイルのコンパイル、リンク、パッケージの管理などを自動的に行います。ツールチェーンとは、コンパイラ(gcまたはgccgo)、リンカ(go tool link)、アセンブラなどのGoプログラムをビルドするために必要な一連のツールのことです。このコミットでは、GoのビルドシステムがSWIGによって生成されたファイルを認識し、適切にコンパイル・リンクするよう拡張されています。

技術的詳細

このコミットの技術的な核心は、GoのビルドプロセスにSWIGの実行と、SWIGが生成するC/C++およびGoのラッパーコードの統合です。

  1. go/buildパッケージの拡張:

    • go/build.Package構造体にSwigFilesSwigCXXFilesという新しいフィールドが追加されました。これらはそれぞれ、SWIGのインターフェースファイル(.swig)とC++用のSWIGインターフェースファイル(.swigcxx)のリストを保持します。
    • build.Contextがファイルタイプを認識するロジックに.swig.swigcxxが追加され、Goのビルドシステムがこれらのファイルをパッケージの一部として認識できるようになりました。
  2. cmd/goコマンドの変更:

    • ビルドプロセスの変更: src/cmd/go/build.go内のビルドロジックが修正され、パッケージがSWIGファイルを使用している場合(p.usesSwig()がtrueの場合)、SWIGが実行されるようになりました。
      • SWIGは、.swigまたは.swigcxxファイルからGoのラッパーファイル(.go)とC/C++のラッパーファイル(.cまたは.cxx)を生成します。
      • 生成されたC/C++ファイルは、GoのCgoプロセスと同様に、GCCなどのC/C++コンパイラによってコンパイルされます。
      • 生成されたGoファイルは、通常のGoソースファイルとしてGoコンパイラによってコンパイルされます。
      • SWIGによって生成された共有ライブラリ(.soファイルなど)は、最終的な実行可能ファイルにリンクされるか、実行時にロードされるように処理されます。
    • クリーンアップ機能の拡張: src/cmd/go/clean.goが更新され、go cleanコマンドがSWIGによって生成されたファイル(特に共有ライブラリ.soファイル)も削除対象に含めるようになりました。
    • パッケージ情報の表示: src/cmd/go/doc.gosrc/cmd/go/list.goが更新され、go docgo listコマンドがパッケージのSWIGファイル情報を表示できるようになりました。
    • リンカの変更: go tool linkがSWIGによって生成された共有ライブラリを適切にリンクするための引数(-rや共有ライブラリのパス)を受け取るように変更されました。特に、gccgoツールチェーンを使用する場合、SWIGによって生成された共有ライブラリもリンカに渡されるようになりました。
    • テスト実行時の環境変数設定: src/cmd/go/test.goが更新され、SWIGを使用するパッケージのテストを実行する際に、共有ライブラリのパス(LD_LIBRARY_PATHなど)が適切に設定されるようになりました。これにより、テスト実行時にSWIGが生成した共有ライブラリが正しくロードされることが保証されます。
    • swig関数の追加: builder構造体にswigおよびswigOneという新しいメソッドが追加され、SWIGの実行ロジックがカプセル化されました。これらの関数は、SWIGコマンドの呼び出し、生成されるファイルの命名規則、およびC/C++コンパイラ(gcc)とリンカの呼び出しを管理します。
  3. misc/swigディレクトリの変更:

    • SWIGのコールバック機能と標準I/O(stdio)のファイル操作に関するサンプルコードが更新されました。特に、misc/swig/callback/Makefilemisc/swig/callback/run.goが削除され、Goツールによるビルドシステムへの統合を示唆しています。代わりに、misc/swig/callback/callback.gomisc/swig/callback/callback_test.goが追加され、Goの標準テストフレームワークでSWIGのコールバック機能がテストされるようになりました。
    • misc/swig/stdio/file.swigが変更され、%typemap(gotype) const char * "string"などのGo固有の型マッピングが追加されました。これは、Cのconst char *型をGoのstring型に自動的に変換するためのSWIGディレクティブです。また、mallocmemcpyfreeを使ったメモリ管理のティップマップも追加され、GoとCの間での文字列の受け渡しがより安全かつ効率的に行われるようになりました。
    • misc/swig/stdio/file_test.goが追加され、SWIGを介したCのファイルI/O関数のテストがGoから実行されるようになりました。

これらの変更により、GoのビルドシステムはSWIGファイルを自動的に検出し、必要なラッパーコードを生成し、それをGoおよびC/C++のコンパイル・リンクプロセスに統合できるようになりました。これにより、Go開発者はSWIGをGoプロジェクト内でより簡単に利用できるようになります。

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

このコミットの主要な変更は、以下のファイルに集中しています。

  • src/cmd/go/build.go: Goのビルドロジックの核心部分。SWIGの実行と生成されたファイルの処理が追加されました。
  • src/cmd/go/clean.go: go cleanコマンドのロジック。SWIGが生成する共有ライブラリの削除が追加されました。
  • src/cmd/go/doc.go, src/cmd/go/list.go, src/cmd/go/pkg.go: パッケージ情報の構造体(Package)にSWIG関連のフィールドが追加され、それらの情報がGoコマンドで扱えるようになりました。
  • src/cmd/go/test.go: go testコマンドのロジック。SWIGが生成する共有ライブラリのパスをテスト実行環境に設定する処理が追加されました。
  • src/pkg/go/build/build.go: Goのビルドパッケージの定義。Package構造体にSWIG関連のフィールドが追加されました。
  • api/next.txt: GoのAPI変更を記録するファイル。go/build.PackageSwigCXXFilesSwigFilesが追加されたことが記録されています。
  • misc/swig/callback/: SWIGコールバックのサンプルとテストが更新されました。
  • misc/swig/stdio/: SWIGによる標準I/Oのサンプルとテストが更新されました。特にfile.swigにGoの型マッピングが追加されました。

コアとなるコードの解説

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

 // Run SWIG.
 if a.p.usesSwig() {
 	// In a package using SWIG, any .c or .s files are
 	// compiled with gcc.
 	gccfiles := append(cfiles, sfiles...)
 	cfiles = nil
 	sfiles = nil
 	outGo, outObj, err := b.swig(a.p, obj, gccfiles)
 	if err != nil {
 		return err
 	}
 	cgoObjects = append(cgoObjects, outObj...)
 	gofiles = append(gofiles, outGo...)
 }

このコードブロックは、GoのビルドプロセスにおいてSWIGの実行を統合する中心的な部分です。

  • a.p.usesSwig(): 現在のパッケージがSWIGファイル(.swigまたは.swigcxx)を含んでいるかどうかをチェックします。
  • gccfiles := append(cfiles, sfiles...): SWIGを使用するパッケージでは、通常のCファイル(.c)とアセンブリファイル(.s)もGCCでコンパイルされるため、これらをgccfilesにまとめます。
  • cfiles = nil; sfiles = nil: 元のCファイルとアセンブリファイルのリストをクリアし、SWIGによって生成されるC/C++ファイルがGCCで処理されるようにします。
  • b.swig(a.p, obj, gccfiles): builderswigメソッドを呼び出し、SWIGを実行します。このメソッドは、SWIGによって生成されたGoソースファイル(outGo)とオブジェクトファイル(outObj)のパスを返します。
  • cgoObjects = append(cgoObjects, outObj...): SWIGによって生成されたオブジェクトファイルは、Cgoによって生成されたオブジェクトファイルと同様に、最終的なリンクステップに含められます。
  • gofiles = append(gofiles, outGo...): SWIGによって生成されたGoソースファイルは、通常のGoソースファイルとしてコンパイル対象に追加されます。

src/cmd/go/pkg.go の変更

 type Package struct {
 	// ... 既存のフィールド ...
 	SwigFiles    []string `json:",omitempty"` // .swig files
 	SwigCXXFiles []string `json:",omitempty"` // .swigcxx files
 }

 // usesSwig returns whether the package needs to run SWIG.
 func (p *Package) usesSwig() bool {
 	return len(p.SwigFiles) > 0 || len(p.SwigCXXFiles) > 0
 }

 // swigSoname returns the name of the shared library we create for a
 // SWIG input file.
 func (p *Package) swigSoname(file string) string {
 	return strings.Replace(p.ImportPath, "/", "-", -1) + "-" + strings.Replace(file, ".", "-", -1) + ".so"
 }

 // swigDir returns the name of the shared SWIG directory for a
 // package.
 func (p *Package) swigDir(ctxt *build.Context) string {
 	dir := p.build.PkgRoot
 	if ctxt.Compiler == "gccgo" {
 		dir = filepath.Join(dir, "gccgo")
 	} else {
 		dir = filepath.Join(dir, ctxt.GOOS+"_"+ctxt.GOARCH)
 	}
 	return filepath.Join(dir, "swig")
 }

Package構造体へのSwigFilesSwigCXXFilesの追加は、GoのビルドシステムがSWIG関連のファイルを認識し、管理するための基盤となります。

  • usesSwig(): パッケージがSWIGを使用しているかどうかの簡単なチェックを提供します。
  • swigSoname(): SWIGによって生成される共有ライブラリの命名規則を定義します。これにより、パッケージのインポートパスとSWIGファイル名に基づいて一意な共有ライブラリ名が生成されます。
  • swigDir(): SWIGによって生成される共有ライブラリが配置されるディレクトリのパスを決定します。これは、コンパイラ(gcまたはgccgo)とOS/アーキテクチャに基づいてパスを構築します。

src/pkg/go/build/build.go の変更

 type Package struct {
 	// ... 既存のフィールド ...
 	SwigFiles    []string // .swig files
 	SwigCXXFiles []string // .swigcxx files
 }

 // ...
 case ".swig":
 	p.SwigFiles = append(p.SwigFiles, name)
 	continue
 case ".swigcxx":
 	p.SwigCXXFiles = append(p.SwigCXXFiles, name)
 	continue

go/buildパッケージのPackage構造体にも同様のフィールドが追加され、ファイルシステムをスキャンしてパッケージを構築する際に、.swig.swigcxxファイルが適切に認識され、Package構造体の対応するフィールドに格納されるようになりました。これにより、Goのビルドシステム全体でSWIGファイルが認識されるようになります。

misc/swig/stdio/file.swig の変更

 %typemap(gotype) const char * "string"
 %typemap(in) const char * %{
 	$1 = malloc($input.n + 1);
 	memcpy($1, $input.p, $input.n);
 	$1[$input.n] = '\0';
 %}
 %typemap(freearg) const char * %{
 	free($1);
 %}

これはSWIGのインターフェースファイルにおける重要な変更です。

  • %typemap(gotype) const char * "string": Cのconst char *型がGoのstring型にマッピングされることをSWIGに指示します。
  • %typemap(in) const char * %{...%}: GoからC関数にconst char *を渡す際の「in」マッピングを定義します。Goの文字列データ($input.p$input.nでアクセス可能)をCのヒープメモリにコピーし、ヌル終端します。
  • %typemap(freearg) const char * %{...%}: inマッピングで割り当てられたCのメモリを解放するための「freearg」マッピングを定義します。これにより、メモリリークを防ぎます。

これらの型マッピングは、GoとCの間で文字列を安全かつ効率的に受け渡すためのSWIGの強力な機能を示しており、GoとC/C++の相互運用性を向上させます。

関連リンク

参考にした情報源リンク