[インデックス 12501] ファイルの概要
このコミットは、Go言語のビルドシステムとcmd/go
ツールに.syso
ファイルのサポートを追加するものです。.syso
ファイルは、パッケージアーカイブに直接コピーされるシステムオブジェクトファイルであり、主にCgoと連携して、特定のプラットフォームやアーキテクチャに依存するバイナリコードをGoプロジェクトに組み込むために使用されます。
コミット
commit b0996334c1cd2c14e07fb4f17c924cdf698ae48d
Author: Russ Cox <rsc@golang.org>
Date: Wed Mar 7 22:03:18 2012 -0500
go/build, cmd/go: add support for .syso files
.syso files are system objects copied directly
into the package archive.
Fixes #1552.
R=alex.brainman, iant, r, minux.ma, remyoudompheng
CC=golang-dev
https://golang.org/cl/5778043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b0996334c1cd2c14e07fb4f17c924cdf698ae48d
元コミット内容
go/build
パッケージとcmd/go
コマンドに.syso
ファイルのサポートを追加します。
.syso
ファイルは、パッケージアーカイブに直接コピーされるシステムオブジェクトです。
この変更は、Issue #1552を修正します。
変更の背景
この変更の背景には、GoプログラムがCgoを介してC/C++コードと連携する際に、特定のプラットフォームやアーキテクチャに特化したバイナリライブラリ(オブジェクトファイル)をGoのビルドプロセスに組み込む必要があったという課題があります。
Goのビルドシステムは、通常、Goのソースファイル(.go
)、Cgoのソースファイル(_cgo_export.c
など)、C/C++のソースファイル(.c
, .cc
, .cpp
)、アセンブリファイル(.s
, .S
)などを自動的にコンパイルし、リンクします。しかし、既にコンパイル済みのオブジェクトファイル(例えば、特定のハードウェアSDKが提供する.lib
や.o
ファイルなど)を直接Goのパッケージに含めたい場合、既存のビルドシステムではそのための明確なメカニズムが不足していました。
Issue #1552は、まさにこの問題、すなわち「Goのビルドプロセスで外部のオブジェクトファイルをどのように扱うか」という点に対処するためのものでした。特にWindows環境などでは、特定のシステムAPIを呼び出すために、コンパイル済みのオブジェクトファイルが必要となるケースが多く、Goのクロスプラットフォーム開発においてこの機能は不可欠でした。
.syso
ファイルという新しい概念を導入することで、Goのビルドツールは、これらのシステムオブジェクトファイルを自動的に検出し、Goパッケージのアーカイブに含めることができるようになり、Cgoを介した外部ライブラリとの連携がよりスムーズかつポータブルになりました。これにより、開発者はGoのコードベースに直接バイナリ依存性を含めることが可能になり、ビルドスクリプトの複雑さを軽減し、Goのビルドシステム内で完結した形で外部バイナリを利用できるようになります。
前提知識の解説
1. Go言語のビルドシステム
Go言語のビルドシステムは、go build
コマンドによって制御され、ソースコードをコンパイルして実行可能なバイナリやライブラリを生成します。このシステムは、依存関係の解決、ファイルのコンパイル順序の決定、リンクなどを自動的に行います。Goのパッケージは、通常、.go
ファイルで構成されますが、Cgoを使用することでC/C++コードをGoプログラムに組み込むことも可能です。
2. Cgo
Cgoは、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGoの機能です。Cgoを使用すると、Goの標準ライブラリでは提供されていない低レベルのシステムコールや、既存のC/C++ライブラリを利用することができます。Cgoを使用するGoファイルは、import "C"
という行を含み、Goのビルドプロセス中にCコンパイラ(通常はGCCやClang)が呼び出されてCコードがコンパイルされます。
3. オブジェクトファイル
オブジェクトファイル(.o
、.obj
、.lib
など)は、コンパイラによって生成された中間ファイルで、ソースコードを機械語に変換したものです。これらはまだ実行可能なプログラムではありませんが、リンカによって他のオブジェクトファイルやライブラリと結合され、最終的な実行可能ファイルや共有ライブラリが作成されます。
4. .syso
ファイル
.syso
ファイルは、Go言語のビルドシステムにおいて導入された特殊なオブジェクトファイルです。syso
は "system object" の略であり、Goのビルドプロセス中にパッケージアーカイブに直接コピーされることを意図しています。
- 目的: 主にCgoと連携して、特定のオペレーティングシステムやアーキテクチャに依存するコンパイル済みバイナリ(例えば、WindowsのCOMオブジェクトや特定のハードウェアSDKのライブラリなど)をGoプロジェクトに組み込むために使用されます。
- 命名規則: 通常、
foo_windows_amd64.syso
のように、ファイル名にターゲットOSとアーキテクチャを含めることで、Goのビルドシステムが適切な環境でのみそのファイルをリンクするように制御できます。これにより、クロスプラットフォームビルドの際に不要なオブジェクトがリンクされるのを防ぎます。 - ビルドプロセスでの扱い:
go build
コマンドは、パッケージ内の.syso
ファイルを自動的に検出し、それらを最終的な実行可能ファイルにリンクします。これにより、開発者は外部のビルドスクリプトを記述することなく、Goの標準的なビルドプロセス内でバイナリ依存性を管理できます。
このコミット以前は、Goプロジェクトに外部のコンパイル済みオブジェクトファイルを組み込むには、手動でリンカオプションを設定したり、外部のビルドツールを使用したりする必要がありましたが、.syso
ファイルの導入により、このプロセスが大幅に簡素化され、Goのビルドシステムに統合されました。
技術的詳細
このコミットは、Goのビルドシステムが.syso
ファイルを認識し、それらをビルドプロセスに組み込むための変更を導入しています。主要な変更点は以下の通りです。
-
go/build.Package
構造体へのSysoFiles
フィールドの追加:src/pkg/go/build/build.go
内のPackage
構造体に、SysoFiles []string
という新しいフィールドが追加されました。このフィールドは、パッケージに含まれる.syso
ファイルのリストを保持します。これにより、Goのビルドシステムは、どの.syso
ファイルが特定のパッケージに属しているかを追跡できるようになります。 -
go/build
パッケージでの.syso
ファイルの検出:src/pkg/go/build/build.go
内のファイル検出ロジックが更新され、.syso
拡張子を持つファイルが認識されるようになりました。goodOSArchFile
関数(コミットには直接示されていませんが、関連するロジック)によって、ファイル名が現在のOSとアーキテクチャに適合するかどうかがチェックされ、適合する場合にPackage.SysoFiles
リストに追加されます。これにより、プラットフォーム固有の.syso
ファイルが適切に選択されるようになります。 -
cmd/go
ツールでの.syso
ファイルの処理:cmd/go/list.go
とcmd/go/pkg.go
の更新:go list
コマンドがパッケージ情報を表示する際に、新しく追加されたSysoFiles
フィールドも含まれるように、Package
構造体の定義が更新されました。これにより、開発者はgo list -json
などのコマンドを通じて、パッケージに含まれる.syso
ファイルを確認できるようになります。cmd/go/build.go
でのリンク処理:cmd/go
のビルドロジック(具体的にはbuilder.build
関数)が変更され、パッケージのビルド時にPackage.SysoFiles
に含まれるすべての.syso
ファイルが、最終的なオブジェクトファイルのリストに追加されるようになりました。これにより、これらのシステムオブジェクトファイルがGoのパッケージアーカイブにパックされ、最終的な実行可能ファイルにリンクされることが保証されます。isStale
関数の更新:src/cmd/go/pkg.go
内のisStale
関数(パッケージが再ビルドを必要とするかどうかを判断する関数)が更新され、.syso
ファイルもソースファイルの一部として考慮されるようになりました。これにより、.syso
ファイルが変更された場合にも、パッケージが適切に再ビルドされるようになります。
これらの変更により、Goのビルドシステムは.syso
ファイルをファーストクラスの市民として扱い、Cgoを使用するプロジェクトにおいて、外部のコンパイル済みバイナリをよりシームレスに統合できるようになりました。
コアとなるコードの変更箇所
このコミットでは、主に以下の4つのファイルが変更されています。
-
src/cmd/go/build.go
:go build
コマンドのビルドロジックを定義するファイル。builder.build
関数内で、Cgoオブジェクトに加えて、パッケージの.syso
ファイルがオブジェクトリストに追加されるようになりました。
--- a/src/cmd/go/build.go +++ b/src/cmd/go/build.go @@ -727,6 +727,11 @@ func (b *builder) build(a *action) error { // http://golang.org/issue/2601 objects = append(objects, cgoObjects...) + // Add system object files. + for _, syso := range a.p.SysoFiles { + objects = append(objects, filepath.Join(a.p.Dir, syso)) + } + // Pack into archive in obj directory if err := buildToolchain.pack(b, a.p, obj, a.objpkg, objects); err != nil { return err
-
src/cmd/go/list.go
:go list
コマンドの出力形式と、パッケージ情報の構造を定義するファイル。Package
構造体にSysoFiles []string
フィールドが追加されました。
--- a/src/cmd/go/list.go +++ b/src/cmd/go/list.go @@ -41,11 +41,12 @@ being passed to the template is:\n Root string // Go root or Go path dir containing this package\n // Source files - GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)\n- CgoFiles []string // .go sources files that import "C"\n- CFiles []string // .c source files\n- HFiles []string // .h source files\n- SFiles []string // .s source files\n + GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)\n + CgoFiles []string // .go sources files that import "C"\n + CFiles []string // .c source files\n + HFiles []string // .h source files\n + SFiles []string // .s source files\n + SysoFiles []string // .syso object files to add to archive\n // Cgo directives CgoCFLAGS []string // cgo: flags for C compiler
-
src/cmd/go/pkg.go
:cmd/go
内部で使用されるPackage
構造体と、パッケージのメタデータ処理に関するロジックを定義するファイル。Package
構造体にSysoFiles []string
フィールドが追加され、JSONマーシャリングのタグも設定されました。copyBuild
関数でbuild.Package
からSysoFiles
がコピーされるようになりました。isStale
関数で、パッケージの鮮度をチェックする際に.syso
ファイルも考慮されるようになりました。
--- a/src/cmd/go/pkg.go +++ b/src/cmd/go/pkg.go @@ -35,11 +35,12 @@ type Package struct { Root string `json:",omitempty"` // Go root or Go path dir containing this package // Source files - GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)\n- CgoFiles []string `json:",omitempty"` // .go sources files that import "C"\n- CFiles []string `json:",omitempty"` // .c source files\n- HFiles []string `json:",omitempty"` // .h source files\n- SFiles []string `json:",omitempty"` // .s source files\n + GoFiles []string `json:",omitempty"` // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)\n + CgoFiles []string `json:",omitempty"` // .go sources files that import "C"\n + CFiles []string `json:",omitempty"` // .c source files\n + HFiles []string `json:",omitempty"` // .h source files\n + SFiles []string `json:",omitempty"` // .s source files\n + SysoFiles []string `json:",omitempty"` // .syso system object files added to package\n // Cgo directives CgoCFLAGS []string `json:",omitempty"` // cgo: flags for C compiler @@ -90,6 +91,7 @@ func (p *Package) copyBuild(pp *build.Package) { p.CFiles = pp.CFiles p.HFiles = pp.HFiles p.SFiles = pp.SFiles + p.SysoFiles = pp.SysoFiles p.CgoCFLAGS = pp.CgoCFLAGS p.CgoLDFLAGS = pp.CgoLDFLAGS p.CgoPkgConfig = pp.CgoPkgConfig @@ -487,7 +489,7 @@ func isStale(p *Package, topRoot map[string]bool) bool { return false } - srcs := stringList(p.GoFiles, p.CFiles, p.HFiles, p.SFiles, p.CgoFiles)\n + srcs := stringList(p.GoFiles, p.CFiles, p.HFiles, p.SFiles, p.CgoFiles, p.SysoFiles)\n for _, src := range srcs { if olderThan(filepath.Join(p.Dir, src)) { return true
-
src/pkg/go/build/build.go
: Goのパッケージビルドに関する低レベルのロジックと、パッケージ構造を定義するファイル。Package
構造体にSysoFiles []string
フィールドが追加されました。- ファイル検出ロジックが更新され、
.syso
ファイルがPackage.SysoFiles
に追加されるようになりました。
--- a/src/pkg/go/build/build.go +++ b/src/pkg/go/build/build.go @@ -279,11 +279,12 @@ type Package struct { PkgObj string // installed .a file // Source files - GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)\n- CgoFiles []string // .go source files that import "C"\n- CFiles []string // .c source files\n- HFiles []string // .h source files\n- SFiles []string // .s source files\n + GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles)\n + CgoFiles []string // .go source files that import "C"\n + CFiles []string // .c source files\n + HFiles []string // .h source files\n + SFiles []string // .s source files\n + SysoFiles []string // .syso system object files to add to archive\n // Cgo directives CgoPkgConfig []string // Cgo pkg-config directives @@ -476,7 +477,12 @@ Found:\n text := name[i:]\n switch ext {\n case ".go", ".c", ".s", ".h", ".S":\n -\t\t// tentatively okay\n +\t\t// tentatively okay - read to make sure\n +\tcase ".syso":\n +\t\t// binary objects to add to package archive\n +\t\t// Likely of the form foo_windows.syso, but\n +\t\t// the name was vetted above with goodOSArchFile.\n +\t\tp.SysoFiles = append(p.SysoFiles, name)\n default:\n // skip\n continue
コアとなるコードの解説
src/cmd/go/build.go
の変更
このファイルでは、builder.build
メソッドが変更されています。このメソッドは、Goパッケージをビルドする際の主要なロジックを含んでいます。
// Add system object files.
for _, syso := range a.p.SysoFiles {
objects = append(objects, filepath.Join(a.p.Dir, syso))
}
このコードブロックは、a.p.SysoFiles
(現在のパッケージに属する.syso
ファイルのリスト)をイテレートし、それぞれの.syso
ファイルの絶対パスをobjects
スライスに追加しています。objects
スライスは、最終的にリンカに渡されるすべてのオブジェクトファイル(Goのコンパイル済みオブジェクト、Cgoのオブジェクトなど)のリストです。これにより、.syso
ファイルがGoのビルドプロセスに組み込まれ、最終的な実行可能ファイルにリンクされることが保証されます。
src/cmd/go/list.go
および src/cmd/go/pkg.go
の変更
これらのファイルでは、Package
構造体にSysoFiles []string
フィールドが追加されています。
// src/cmd/go/list.go
type Package struct {
// ...
SysoFiles []string // .syso object files to add to archive
// ...
}
// src/cmd/go/pkg.go
type Package struct {
// ...
SysoFiles []string `json:",omitempty"` // .syso system object files added to package
// ...
}
この変更により、go list
コマンドがパッケージ情報を表示する際に、.syso
ファイルの情報も含まれるようになります。また、src/cmd/go/pkg.go
では、copyBuild
関数がbuild.Package
からSysoFiles
をコピーするように更新され、isStale
関数がパッケージの鮮度を判断する際に.syso
ファイルも考慮に入れるようになりました。これにより、.syso
ファイルが変更された場合に、Goツールが適切にパッケージを再ビルドするトリガーとなります。
src/pkg/go/build/build.go
の変更
このファイルは、Goのビルドシステムの中核となるパッケージ情報とファイル検出ロジックを定義しています。
type Package struct {
// ...
SysoFiles []string // .syso system object files to add to archive
// ...
}
ここでもPackage
構造体にSysoFiles
フィールドが追加されています。最も重要な変更は、ファイル検出ロジック内です。
case ".syso":
// binary objects to add to package archive
// Likely of the form foo_windows.syso, but
// the name was vetted above with goodOSArchFile.
p.SysoFiles = append(p.SysoFiles, name)
このswitch
文は、Goのビルドシステムがディレクトリ内のファイルをスキャンする際に、ファイルの拡張子に基づいて処理を分岐させる部分です。.syso
拡張子を持つファイルが検出された場合、そのファイル名がp.SysoFiles
スライスに追加されます。コメントにあるように、goodOSArchFile
のような関数によって、ファイル名が現在のOSとアーキテクチャに適合するかどうかが事前に検証されていることが示唆されており、これによりプラットフォーム固有の.syso
ファイルが正しく選択されるようになっています。
これらの変更により、Goのビルドシステムは.syso
ファイルを認識し、Goのビルドプロセスにシームレスに統合できるようになりました。
関連リンク
- Go Issue #1552: https://github.com/golang/go/issues/1552
- Go CL 5778043: https://golang.org/cl/5778043
参考にした情報源リンク
- Go言語の公式ドキュメント (Go 1.0当時の情報を含む)
- Go言語のソースコード (特に
go/build
パッケージとcmd/go
コマンド) - Go言語のIssueトラッカー (Issue #1552の議論)
- Cgoに関するGoのドキュメント
- Go言語のビルドプロセスに関する技術記事
.syso
ファイルに関するGoコミュニティの議論やブログ記事