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

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

このコミットは、Go言語のコマンドラインツール cmd/go におけるSWIGで生成される共有ライブラリのビルドプロセスに関する修正です。具体的には、SWIGが生成する共有ライブラリが一時的な作業ディレクトリ内で適切にビルドされ、その後最終的なインストール先にコピーされるように変更されています。また、SWIG関連ファイルの古さ(stale)チェックのロジックが簡素化され、他のソースファイルと同様の一般的なパッケージのstaleチェックに統合されました。

コミット

commit 7f062fa2dea2fc9b8b03d051375e7e22156ed8c9
Author: Ian Lance Taylor <iant@golang.org>
Date:   Tue Sep 10 11:00:26 2013 -0700

    cmd/go: build SWIG shared libraries in work directory
    
    Remove test of whether SWIG shared library is older than
    sources--should be covered by test of package file anyhow.
    
    Fixes #5739.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/13352046

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

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

元コミット内容

このコミットは、GoのビルドシステムがSWIGで生成された共有ライブラリを処理する方法を改善します。主な変更点は以下の通りです。

  1. SWIG共有ライブラリが、ビルドプロセス中の一時的な作業ディレクトリ(objdir)にビルドされるように変更されました。これにより、ビルドの整合性が向上します。
  2. SWIG共有ライブラリがソースファイルよりも古いかどうかをチェックする、冗長なstaleチェックロジックが削除されました。このチェックは、パッケージ全体のstaleチェックによって既にカバーされていると判断されました。
  3. Go issue #5739 を修正します。

変更の背景

このコミットの背景には、GoのビルドシステムがSWIGで生成された共有ライブラリを扱う際の既存の問題がありました。Goのビルドシステムは、ソースファイルの変更を検出し、必要に応じて再ビルドを行う「stale」チェックのメカニズムを持っています。しかし、SWIGで生成される共有ライブラリに関しては、このstaleチェックが不完全であったり、冗長であったりする可能性がありました。

具体的には、Go issue #5739 (go bug #5739) は、SWIGで生成された共有ライブラリが正しく再ビルドされないケースがあることを報告していました。これは、ビルドシステムが共有ライブラリの古さを適切に判断できていなかったためと考えられます。

このコミットは、以下の2つの主要な側面からこの問題に対処しています。

  1. ビルド出力パスの明確化: SWIG共有ライブラリのビルド出力先を、一時的な作業ディレクトリ(objdir)に明示的に指定することで、ビルドプロセスの一貫性と信頼性を高めます。これにより、ビルドされたファイルが予期せぬ場所に生成されたり、古いファイルが誤って使用されたりするリスクが低減されます。
  2. 冗長なstaleチェックの削除: SWIG共有ライブラリがソースよりも古いかどうかを個別にチェックするロジックが削除されました。これは、Goのビルドシステムが既にパッケージ全体のstaleチェックを行っており、その中にSWIGのソースファイルも含まれるため、個別のチェックは冗長であると判断されたためです。この変更により、ビルドシステムの複雑性が軽減され、潜在的なバグが排除されます。

要するに、このコミットは、SWIGとGoの連携におけるビルドの堅牢性を向上させ、特定の条件下での再ビルドの問題を解決することを目的としています。

前提知識の解説

このコミットを理解するためには、以下の概念について基本的な知識があると役立ちます。

1. Go言語のビルドシステム (cmd/go)

Go言語には、go buildgo install といったコマンドを提供する強力なビルドシステムが組み込まれています。このシステムは、ソースコードの依存関係を解決し、コンパイル、リンク、そして最終的なバイナリの生成までを自動的に行います。

  • パッケージ (Package): Goのコードはパッケージにまとめられます。各パッケージは、関連するGoソースファイル、C/C++/アセンブリソースファイル、そしてSWIGインターフェースファイルなどを含むことができます。
  • ビルドキャッシュとStaleチェック: Goのビルドシステムは、ビルド時間を短縮するためにビルドキャッシュを使用します。ソースファイルや依存関係が変更されていない場合、以前のビルド結果を再利用します。この判断を行うのが「staleチェック」です。ファイルが「stale」(古い)であると判断された場合、そのファイルまたはパッケージは再ビルドされます。
  • 作業ディレクトリ (objdir): ビルドプロセス中、Goツールは中間ファイル(オブジェクトファイル、共有ライブラリなど)を一時的な作業ディレクトリに生成します。これは通常、最終的なインストール先とは異なる場所です。

2. SWIG (Simplified Wrapper and Interface Generator)

SWIGは、C/C++で書かれたコードを、Go、Python、Java、Rubyなど、様々なスクリプト言語や高水準言語から呼び出せるようにするためのツールです。

  • インターフェースファイル (.i): SWIGは、C/C++のヘッダファイルに似た .i 拡張子を持つインターフェースファイルを読み込みます。このファイルには、どのC/C++関数やデータ構造をターゲット言語に公開するかを記述します。
  • ラッパーコードの生成: SWIGは、インターフェースファイルに基づいて、C/C++コードをターゲット言語から呼び出すための「ラッパーコード」を生成します。Goの場合、これはGoのソースファイルとC/C++のソースファイル(通常は共有ライブラリとしてコンパイルされる)の両方を含みます。
  • 共有ライブラリ (Shared Library): SWIGによって生成されたC/C++ラッパーコードは、通常、.so (Linux), .dylib (macOS), .dll (Windows) といった共有ライブラリとしてコンパイルされます。この共有ライブラリは、GoのプログラムがC/C++の機能を呼び出す際に動的にリンクされます。

3. filepath.Join

Goの標準ライブラリ path/filepath パッケージに含まれる関数です。複数のパス要素を結合して、オペレーティングシステムに適した形式の単一のパスを生成します。例えば、Unix系システムでは /、Windowsでは \ を区切り文字として使用します。

4. os.StatModTime()

Goの標準ライブラリ os パッケージに含まれる関数です。os.Stat(path) は、指定されたパスのファイルまたはディレクトリに関する情報(ファイル情報、FileInfo インターフェース)を返します。FileInfo インターフェースの ModTime() メソッドは、そのファイルまたはディレクトリの最終更新時刻を返します。これは、ファイルが「stale」であるかどうかを判断する際によく使用されます。

5. stringList

Goのビルドシステム内部で使用されるヘルパー関数で、複数の文字列スライスを結合して単一の文字列スライスを生成します。

技術的詳細

このコミットは、GoのビルドシステムにおけるSWIG共有ライブラリの取り扱いを、より堅牢で効率的なものにするためのものです。

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

このファイルは、Goのビルドプロセスの中核を担うロジックを含んでいます。変更は主に、SWIG共有ライブラリのビルド出力パスとコピー元パスの指定方法にあります。

  1. swigOne 関数内の変更:

    • swigOne 関数は、個々のSWIGインターフェースファイルからGoとC/C++のラッパーコードを生成し、C/C++部分を共有ライブラリとしてコンパイルする役割を担っています。
    • 変更前は、gccCmd の出力先が単に soname (共有ライブラリのファイル名、例: _foo.so) となっていました。これは、現在の作業ディレクトリにファイルが生成されることを意味します。
    • 変更後、target := filepath.Join(obj, soname) という行が追加され、gccCmd の出力先が明示的に obj ディレクトリ(パッケージのオブジェクトファイルが置かれる一時的な作業ディレクトリ)内の soname となりました。
      • これにより、SWIG共有ライブラリが常にビルドプロセス中の一時的な作業ディレクトリに生成されることが保証されます。これは、ビルドのクリーンアップやキャッシュ管理において重要です。
  2. install 関数内の変更:

    • install 関数は、ビルドされた成果物(実行可能ファイル、パッケージアーカイブ、共有ライブラリなど)を最終的なインストール先にコピーする役割を担っています。
    • 変更前は、SWIG共有ライブラリをコピーする際に、コピー元パスが単に soname となっていました。これは、install 関数が実行される現在のディレクトリに soname が存在することを期待していました。
    • 変更後、source := filepath.Join(a.objdir, soname) という行が追加され、コピー元パスが a.objdir ディレクトリ内の soname と明示的に指定されました。
      • この変更は、swigOne 関数での変更と連携しています。swigOne が共有ライブラリを objdir に生成するようになったため、install 関数もそこからコピーするように修正されたのです。これにより、ビルドされた共有ライブラリが確実に正しい場所から取得され、インストールされるようになります。

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

このファイルは、Goのパッケージが「stale」(古い)であるかどうかを判断するロジックを含んでいます。

  1. isStale 関数内の変更:
    • isStale 関数は、パッケージのソースファイルや依存関係のタイムスタンプをチェックし、再ビルドが必要かどうかを判断します。
    • 削除されたロジック: 変更前は、SWIG関連ファイル (p.SwigFiles, p.SwigCXXFiles) について、個別のstaleチェックロジックが存在していました。このロジックは、SWIGソースファイルと生成された共有ライブラリの最終更新時刻を比較し、共有ライブラリがソースよりも古い場合に true を返していました。
      • この個別のチェックは、コミットメッセージにあるように「パッケージファイルのテストによってカバーされるべき」と判断され、冗長であるため削除されました。
    • 追加されたロジック: srcs := stringList(...) の行に p.SwigFiles, p.SwigCXXFiles が追加されました。
      • これにより、SWIGのインターフェースファイル(.i ファイル)も、GoのソースファイルやC/C++のソースファイルと同様に、一般的なstaleチェックの対象となりました。つまり、SWIGのインターフェースファイルが変更された場合、パッケージ全体がstaleと判断され、再ビルドされるようになります。

全体的な影響

これらの変更により、GoのビルドシステムはSWIG共有ライブラリをより一貫性のある方法で処理するようになります。

  • ビルドの信頼性向上: SWIG共有ライブラリが常に正しい一時ディレクトリに生成され、そこからコピーされることで、ビルドの失敗や古いライブラリの使用といった問題が減少します。
  • ビルドロジックの簡素化: 冗長なSWIG固有のstaleチェックが削除され、一般的なパッケージのstaleチェックに統合されたことで、コードベースがクリーンになり、保守性が向上します。
  • バグ修正: Go issue #5739 で報告された、SWIG共有ライブラリが適切に再ビルドされない問題が解決されます。

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

src/cmd/go/build.go

--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -1005,8 +1005,9 @@ func (b *builder) install(a *action) (err error) {
 				return err
 			}
 			soname := a.p.swigSoname(f)
-			target := filepath.Join(dir, soname)
-			if err = b.copyFile(a, target, soname, perm); err != nil {
+			source := filepath.Join(a.objdir, soname) // 追加行
+			target := filepath.Join(dir, soname)
+			if err = b.copyFile(a, target, source, perm); err != nil { // 変更行
 				return err
 			}
 		}
@@ -2255,7 +2256,8 @@ func (b *builder) swigOne(p *Package, file, obj string, cxx bool, intgosize stri
 		cxxlib = []string{"-lstdc++"}
 	}
 	ldflags := stringList(osldflags[goos], cxxlib)
-	b.run(p.Dir, p.ImportPath, nil, b.gccCmd(p.Dir), "-o", soname, gccObj, ldflags)
+	target := filepath.Join(obj, soname) // 追加行
+	b.run(p.Dir, p.ImportPath, nil, b.gccCmd(p.Dir), "-o", target, gccObj, ldflags) // 変更行

 	return obj + goFile, cObj, nil
 }

src/cmd/go/pkg.go

--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -680,28 +680,13 @@ func isStale(p *Package, topRoot map[string]bool) bool {
 		return false
 	}

-	srcs := stringList(p.GoFiles, p.CFiles, p.CXXFiles, p.HFiles, p.SFiles, p.CgoFiles, p.SysoFiles)
+	srcs := stringList(p.GoFiles, p.CFiles, p.CXXFiles, p.HFiles, p.SFiles, p.CgoFiles, p.SysoFiles, p.SwigFiles, p.SwigCXXFiles) // 変更行
 	for _, src := range srcs {
 		if olderThan(filepath.Join(p.Dir, src)) {
 			return true
 		}
 	}

-	for _, src := range stringList(p.SwigFiles, p.SwigCXXFiles) {
-		if olderThan(filepath.Join(p.Dir, src)) {
-			return true
-		}
-		soname := p.swigSoname(src)
-		fi, err := os.Stat(soname)
-		if err != nil {
-			return true
-		}
-		fiSrc, err := os.Stat(src)
-		if err != nil || fiSrc.ModTime().After(fi.ModTime()) {
-			return true
-		}
-	}
-
 	return false
 }

コアとなるコードの解説

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

  1. swigOne 関数内:

    • 変更前: b.run(..., "-o", soname, ...)
      • gccCmd の出力ファイル名として soname (例: _foo.so) が直接渡されていました。これは、b.run が実行される現在のディレクトリに共有ライブラリが生成されることを意味します。
    • 変更後: target := filepath.Join(obj, soname)b.run(..., "-o", target, ...)
      • obj は、パッケージのオブジェクトファイルが一時的に格納されるディレクトリ(作業ディレクトリ)です。filepath.Join(obj, soname) とすることで、共有ライブラリが明示的にこの作業ディレクトリ内に生成されるように指定されました。これにより、ビルドプロセスにおける中間ファイルの管理が一貫します。
  2. install 関数内:

    • 変更前: if err = b.copyFile(a, target, soname, perm); err != nil {
      • copyFile のコピー元パスとして soname が直接渡されていました。これは、install 関数が実行される現在のディレクトリに soname が存在することを期待していました。
    • 変更後: source := filepath.Join(a.objdir, soname)if err = b.copyFile(a, target, source, perm); err != nil {
      • a.objdir は、ビルドされたオブジェクトファイルや共有ライブラリが格納されているディレクトリです。filepath.Join(a.objdir, soname) とすることで、copyFile が共有ライブラリを正しい作業ディレクトリから取得して、最終的なインストール先 (target) にコピーするように修正されました。これは、swigOne での変更と連携し、ビルドされた共有ライブラリのパスを正確に指定します。

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

  1. isStale 関数内:
    • 変更前: srcs := stringList(p.GoFiles, p.CFiles, p.CXXFiles, p.HFiles, p.SFiles, p.CgoFiles, p.SysoFiles)
      • Go、C、C++などの通常のソースファイルのみがstaleチェックの対象となっていました。SWIG関連ファイルは、その後に続く別の専用のループでチェックされていました。
    • 変更後: srcs := stringList(..., p.SwigFiles, p.SwigCXXFiles)
      • p.SwigFiles (SWIGインターフェースファイル) と p.SwigCXXFiles (SWIG C++インターフェースファイル) が、他の一般的なソースファイルと同様に srcs リストに追加されました。これにより、SWIGのインターフェースファイルが変更された場合、olderThan 関数によってパッケージ全体がstaleと判断され、再ビルドの対象となります。
    • 削除されたコードブロック:
      • SWIG関連ファイルと生成された共有ライブラリのタイムスタンプを比較する、冗長な専用のstaleチェックループが完全に削除されました。この削除は、SWIGソースファイルが一般的なstaleチェックに統合されたため、もはや不要になったことを意味します。これにより、コードが簡素化され、重複するロジックが排除されました。

これらの変更は、GoのビルドシステムがSWIGで生成される共有ライブラリをより正確かつ効率的に管理できるようにするためのものです。特に、ビルド出力パスの明確化と、staleチェックロジックの合理化が重要なポイントです。

関連リンク

参考にした情報源リンク