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

[インデックス 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ファイルを認識し、それらをビルドプロセスに組み込むための変更を導入しています。主要な変更点は以下の通りです。

  1. go/build.Package構造体へのSysoFilesフィールドの追加: src/pkg/go/build/build.go内のPackage構造体に、SysoFiles []stringという新しいフィールドが追加されました。このフィールドは、パッケージに含まれる.sysoファイルのリストを保持します。これにより、Goのビルドシステムは、どの.sysoファイルが特定のパッケージに属しているかを追跡できるようになります。

  2. go/buildパッケージでの.sysoファイルの検出: src/pkg/go/build/build.go内のファイル検出ロジックが更新され、.syso拡張子を持つファイルが認識されるようになりました。goodOSArchFile関数(コミットには直接示されていませんが、関連するロジック)によって、ファイル名が現在のOSとアーキテクチャに適合するかどうかがチェックされ、適合する場合にPackage.SysoFilesリストに追加されます。これにより、プラットフォーム固有の.sysoファイルが適切に選択されるようになります。

  3. cmd/goツールでの.sysoファイルの処理:

    • cmd/go/list.gocmd/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つのファイルが変更されています。

  1. 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
    
  2. 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
    
  3. 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
    
  4. 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言語の公式ドキュメント (Go 1.0当時の情報を含む)
  • Go言語のソースコード (特にgo/buildパッケージとcmd/goコマンド)
  • Go言語のIssueトラッカー (Issue #1552の議論)
  • Cgoに関するGoのドキュメント
  • Go言語のビルドプロセスに関する技術記事
  • .sysoファイルに関するGoコミュニティの議論やブログ記事