[インデックス 10950] ファイルの概要
このコミットは、Go言語のビルドシステムにおけるWindows環境での問題を修正し、パフォーマンスを改善することを目的としています。具体的には、リンカとcgoに関するバグの回避策、Windowsでのdeps.bash
スクリプトの実行条件の最適化、およびWindowsをcgoが有効なターゲットリストに追加する変更が含まれています。
コミット
commit 0509727b0d8c4175f3d8957b2066916e889da383
Author: Russ Cox <rsc@golang.org>
Date: Wed Dec 21 15:57:47 2011 -0500
build: fixes for Windows
* work around a linker/cgo bug
* do not run deps.bash on Windows unless we need it
(cuts a full minute off the build time)
* add windows to the list of cgo-enabled targets
The gopack problem is issue 2601.
R=golang-dev, r, bradfitz
CC=golang-dev
https://golang.org/cl/5504062
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0509727b0d8c4175f3d8957b2066916e889da383
元コミット内容
build: fixes for Windows
* work around a linker/cgo bug
* do not run deps.bash on Windows unless we need it
(cuts a full minute off the build time)
* add windows to the list of cgo-enabled targets
The gopack problem is issue 2601.
R=golang-dev, r, bradfitz
CC=golang-dev
https://golang.org/cl/5504062
変更の背景
このコミットは、Go言語のWindows環境におけるビルドプロセスが抱えていた複数の課題に対処するために行われました。
-
リンカ/cgoのバグ: Windows環境でcgo(C言語との相互運用機能)を使用する際に、リンカが正しく動作しないバグが存在していました。これは、Goのコンパイラ(5c/6c/8c)が生成するオブジェクトファイルと、GCCがcgo経由で生成するオブジェクトファイルの結合順序に起因する問題でした。特に
gopack
ツールがアーカイブを作成する際に、オブジェクトファイルの順序が重要であり、この順序が正しくないとリンクエラーが発生していました。コミットメッセージでは「issue 2601」として参照されていますが、これは当時の内部的な課題管理システムにおけるIDである可能性が高く、現在の公開されたGoリポジトリのIssueトラッカーでは直接参照できません。しかし、その内容はgopack
ツールにおけるオブジェクトファイルの順序問題であることが示唆されています。 -
deps.bash
の非効率な実行: Goのビルドプロセスでは、依存関係を解決するためにdeps.bash
スクリプトが実行されていました。しかし、Windows環境ではこのスクリプトの実行に約1分もの時間がかかっており、ビルド全体の時間を大幅に増加させていました。このスクリプトは、特定の条件下でのみ必要とされるものであり、常に実行する必要はありませんでした。 -
Windowsのcgoサポートの明示: Windows環境でcgoを公式にサポートするため、cgoが有効なターゲットプラットフォームのリストにWindowsが明示的に追加される必要がありました。
これらの問題は、WindowsユーザーがGo言語をビルド・利用する上での障壁となっており、開発体験の向上とWindows環境でのGoの普及のために解決が求められていました。
前提知識の解説
このコミットを理解するためには、以下のGo言語のビルドシステムと関連技術に関する知識が役立ちます。
- Go言語のビルドプロセス: Go言語のソースコードは、Goコンパイラ(
go tool compile
)によってオブジェクトファイルにコンパイルされます。これらのオブジェクトファイルは、Goのリンカ(go tool link
)によって実行可能ファイルやライブラリにリンクされます。Goのビルドツール(go build
など)は、これらのコンパイラやリンカ、その他のツールをオーケストレーションします。 - cgo: cgoは、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGoの機能です。cgoを使用すると、GoのコードとCのコードを混在させることができます。cgoが有効な場合、Goのビルドプロセスは、Cのコンパイラ(通常はGCC)を使用してCのコードをコンパイルし、その結果生成されたオブジェクトファイルをGoのオブジェクトファイルと結合します。
gopack
:gopack
は、Goのビルドシステム内部で使用されるツールで、複数のオブジェクトファイルをまとめてアーカイブ(ライブラリファイル)を作成する役割を担います。これは、Unix系のar
コマンドに似た機能を提供します。- オブジェクトファイルとリンカ: コンパイラはソースコードを機械語に変換し、オブジェクトファイル(
.o
や.obj
など)を生成します。これらのオブジェクトファイルには、コンパイルされたコード、データ、および他のオブジェクトファイルやライブラリへの参照(シンボル)が含まれています。リンカは、これらのオブジェクトファイルを結合し、必要なライブラリを解決して、最終的な実行可能ファイルや共有ライブラリを作成します。リンカによっては、オブジェクトファイルの結合順序が重要になる場合があります。特に、シンボル解決の順序や、特定のセクションの配置に影響を与えることがあります。 deps.bash
: Goの初期のビルドシステムで使用されていたシェルスクリプトで、Goの標準ライブラリやツールのビルドに必要な依存関係を準備する役割を担っていました。これには、外部ツールのダウンロードや、特定のファイルの生成などが含まれることがありました。- クロスコンパイル: あるプラットフォーム(例: Linux)で、別のプラットフォーム(例: Windows)向けの実行可能ファイルをビルドすることです。Goはクロスコンパイルを強力にサポートしており、
GOOS
やGOARCH
といった環境変数を設定することで、異なるOSやアーキテクチャ向けのバイナリを簡単に生成できます。
技術的詳細
このコミットは、主に以下の2つの技術的な問題に対処しています。
-
Windowsにおけるcgoオブジェクトファイルのリンカ順序問題: Windows環境でcgoを使用する際、Goのコンパイラ(5c/6c/8c)が生成するオブジェクトファイルと、GCCがcgo経由で生成するオブジェクトファイル(
cgoObjects
)をgopack
でアーカイブする際に、特定の順序で結合する必要がありました。コミットメッセージによると、GCCが生成したオブジェクトファイルは、Goコンパイラが生成した通常のオブジェクトファイルの「後に」リストされることが「極めて重要」でした。この理由については「なぜかは不明」とされていますが、これはWindowsのリンカ(またはgopack
の内部的な動作)の特性に起因するものでした。 さらに、cgoが生成するimportObj
(GoのコードからCの関数を呼び出すためのスタブが含まれるオブジェクトファイル)は、GCCが生成するオブジェクトファイルの「前に」処理される必要がありました。これは、importObj
がGoのコンパイラによって生成されるオブジェクトであり、GCCが生成するオブジェクトとは異なる特性を持つためと考えられます。この順序が守られないと、リンカがシンボルを正しく解決できず、ビルドエラーが発生していました。 -
Windowsにおける
deps.bash
の不要な実行:src/make.bash
スクリプトは、Goのビルドプロセス全体を管理する主要なスクリプトの一つです。このスクリプトは、Goの標準ライブラリのビルド前にdeps.bash
を実行していました。しかし、Windows環境では、deps.bash
が常に必要とされるわけではなく、その実行に時間がかかっていました。このコミットでは、$USE_GO_TOOL
という環境変数が設定されている場合にのみdeps.bash
を実行するように変更することで、不要な実行をスキップし、ビルド時間を短縮しました。$USE_GO_TOOL
は、Goツールチェーン自体がビルドされている場合に設定される変数であり、Goツールが利用可能であれば、deps.bash
が提供する機能の多くはGoツールによって代替できるため、この最適化が可能になりました。
コアとなるコードの変更箇所
このコミットによる主要なコード変更は以下の3つのファイルにわたります。
-
src/cmd/go/build.go
:gofiles
,cfiles
,sfiles
,objects
に加えて、cgoObjects
という新しいスライスが導入されました。これは、cgoによって生成されたオブジェクトファイルを個別に管理するためです。- cgoによって生成されたオブジェクトファイル(
outObj
)が、以前は直接objects
スライスに追加されていたのに対し、cgoObjects
スライスに追加されるように変更されました。 gopack
を呼び出す直前で、objects
スライスにcgoObjects
スライスが追加されるようになりました。これにより、GCCが生成したオブジェクトファイルがGoコンパイラが生成した通常のオブジェクトファイルの後に結合されることが保証されます。cgo
関数内で、outObj
スライスに空の文字列が追加され、importObj
を格納するためのプレースホルダーが確保されました。importObj
がoutObj
スライスの最初の要素として設定されるように変更されました。これにより、importObj
がGCCが生成するオブジェクトファイルの前に処理されることが保証されます。
-
src/make.bash
:deps.bash
の実行が$USE_GO_TOOL ||
という条件で囲まれました。これは、$USE_GO_TOOL
が真(つまり、Goツールが利用可能)でない場合にのみdeps.bash
が実行されることを意味します。
-
src/pkg/go/build/dir.go
:cgoEnabled
マップに"windows/386"
と"windows/amd64"
が追加されました。これにより、Windowsの32ビットおよび64ビットアーキテクチャがcgoが有効なターゲットとして明示的に認識されるようになりました。
コアとなるコードの解説
src/cmd/go/build.go
の変更
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -452,7 +452,7 @@ func (b *builder) build(a *action) error {
return err
}
- var gofiles, cfiles, sfiles, objects []string
+ var gofiles, cfiles, sfiles, objects, cgoObjects []string
gofiles = append(gofiles, a.p.GoFiles...)
cfiles = append(cfiles, a.p.CFiles...)
sfiles = append(sfiles, a.p.SFiles...)
@@ -487,7 +487,7 @@ func (b *builder) build(a *action) error {
if err != nil {
return err
}
- objects = append(objects, outObj...)
+ cgoObjects = append(cgoObjects, outObj...)
gofiles = append(gofiles, outGo...)
}
@@ -576,6 +576,12 @@ func (b *builder) build(a *action) error {
objects = append(objects, out)
}
+ // NOTE(rsc): On Windows, it is critically important that the
+ // gcc-compiled objects (cgoObjects) be listed after the ordinary
+ // objects in the archive. I do not know why this is.
+ // http://golang.org/issue/2601
+ objects = append(objects, cgoObjects...)
+
// pack into archive in obj directory
if err := b.gopack(a.p, obj, a.objpkg, objects); err != nil {
return err
@@ -917,6 +923,8 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,\
return nil, nil, errors.New("cannot use cgo when compiling for a different operating system")
}
+ outObj = append(outObj, "") // for importObj, at end of function
+
// cgo
// TODO: CGOPKGPATH, CGO_FLAGS?
gofiles := []string{obj + "_cgo_gotypes.go"}
@@ -983,7 +991,11 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,\
if err := b.cc(p, obj, importObj, importC); err != nil {
return nil, nil, err
}
- outObj = append(outObj, importObj)
+
+ // NOTE(rsc): The importObj is a 5c/6c/8c object and on Windows
+ // must be processed before the gcc-generated objects.
+ // Put it first. We left room above. http://golang.org/issue/2601
+ outObj[0] = importObj
return outGo, outObj, nil
}
cgoObjects
の導入と順序制御: 以前はcgoによって生成されたオブジェクトファイルもobjects
スライスに直接追加されていました。しかし、Windowsでのリンカの特性上、Goコンパイラが生成したオブジェクトとGCCが生成したオブジェクトの順序が重要であることが判明しました。そこで、cgoObjects
という専用のスライスを導入し、GCCが生成したオブジェクトを一時的にここに格納します。そして、gopack
を呼び出す直前に、objects = append(objects, cgoObjects...)
という行を追加することで、Goコンパイラが生成したオブジェクトの後にGCCが生成したオブジェクトが結合されるように明示的に順序を制御しています。これにより、Windowsでのリンカ/cgoバグが回避されます。importObj
の順序制御:cgo
関数内で、outObj = append(outObj, "")
という行で空の要素をスライスの末尾に追加し、その直後にoutObj[0] = importObj
とすることで、importObj
(Goのコンパイラが生成するcgo関連のオブジェクト)がoutObj
スライスの最初の要素として配置されるようにしています。これは、WindowsではimportObj
がGCCが生成するオブジェクトよりも先に処理される必要があるためです。この変更により、cgoが正しく機能するためのオブジェクトファイルの順序が保証されます。
src/make.bash
の変更
--- a/src/make.bash
+++ b/src/make.bash
@@ -71,6 +71,7 @@ do
fi
done
+$USE_GO_TOOL ||
(
cd "$GOROOT"/src/pkg;
bash deps.bash # do this here so clean.bash will work in the pkg directory
deps.bash
の条件付き実行:deps.bash
の実行が$USE_GO_TOOL ||
という条件で囲まれました。これはシェルスクリプトの論理OR演算子であり、「$USE_GO_TOOL
が真(0以外の終了コード)でなければ、deps.bash
を実行する」という意味になります。Goツールチェーンが既にビルドされ、$USE_GO_TOOL
が設定されている場合、deps.bash
はスキップされます。これにより、Windowsでのビルド時間が大幅に短縮されます。これは、Goツール自体が依存関係の解決やファイルの生成をより効率的に行えるようになったため、deps.bash
の役割が限定的になったことを示唆しています。
src/pkg/go/build/dir.go
の変更
--- a/src/pkg/go/build/dir.go
+++ b/src/pkg/go/build/dir.go
@@ -84,6 +84,8 @@ var cgoEnabled = map[string]bool{
"linux/amd64": true,
"freebsd/386": true,
"freebsd/amd64": true,
+ "windows/386": true,
+ "windows/amd64": true,
}
func defaultContext() Context {
- Windowsのcgo有効化:
cgoEnabled
マップは、どのOS/アーキテクチャの組み合わせでcgoが有効であるかを定義しています。この変更により、"windows/386"
(Windows 32ビット)と"windows/amd64"
(Windows 64ビット)が明示的にtrue
に設定されました。これにより、GoのビルドシステムがWindows環境でcgoを公式にサポートし、cgoを使用するGoプログラムをWindows上でビルドできるようになります。
これらの変更は、Go言語のWindowsサポートを強化し、開発者がよりスムーズにGoをWindows環境で利用できるようにするための重要なステップでした。
関連リンク
- GitHub上のコミットページ: https://github.com/golang/go/commit/0509727b0d8c4175f3d8957b2066916e889da383
- Go CL (Change List): https://golang.org/cl/5504062
- (参照されているGo issue 2601は、現在の公開されたGoリポジトリのIssueトラッカーでは直接参照できませんが、コミットメッセージから
gopack
におけるオブジェクトファイルの順序問題であることが示唆されています。)
参考にした情報源リンク
- Go言語の公式ドキュメント(cgo、ビルドプロセスに関する情報)
- Go言語のソースコード(特に
src/cmd/go/build.go
、src/make.bash
、src/pkg/go/build/dir.go
の関連部分) - 一般的なリンカの動作とオブジェクトファイルの構造に関する知識
- シェルスクリプトの構文と論理演算子に関する知識I have generated the commit explanation as requested.