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

[インデックス 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環境におけるビルドプロセスが抱えていた複数の課題に対処するために行われました。

  1. リンカ/cgoのバグ: Windows環境でcgo(C言語との相互運用機能)を使用する際に、リンカが正しく動作しないバグが存在していました。これは、Goのコンパイラ(5c/6c/8c)が生成するオブジェクトファイルと、GCCがcgo経由で生成するオブジェクトファイルの結合順序に起因する問題でした。特にgopackツールがアーカイブを作成する際に、オブジェクトファイルの順序が重要であり、この順序が正しくないとリンクエラーが発生していました。コミットメッセージでは「issue 2601」として参照されていますが、これは当時の内部的な課題管理システムにおけるIDである可能性が高く、現在の公開されたGoリポジトリのIssueトラッカーでは直接参照できません。しかし、その内容はgopackツールにおけるオブジェクトファイルの順序問題であることが示唆されています。

  2. deps.bashの非効率な実行: Goのビルドプロセスでは、依存関係を解決するためにdeps.bashスクリプトが実行されていました。しかし、Windows環境ではこのスクリプトの実行に約1分もの時間がかかっており、ビルド全体の時間を大幅に増加させていました。このスクリプトは、特定の条件下でのみ必要とされるものであり、常に実行する必要はありませんでした。

  3. 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はクロスコンパイルを強力にサポートしており、GOOSGOARCHといった環境変数を設定することで、異なるOSやアーキテクチャ向けのバイナリを簡単に生成できます。

技術的詳細

このコミットは、主に以下の2つの技術的な問題に対処しています。

  1. Windowsにおけるcgoオブジェクトファイルのリンカ順序問題: Windows環境でcgoを使用する際、Goのコンパイラ(5c/6c/8c)が生成するオブジェクトファイルと、GCCがcgo経由で生成するオブジェクトファイル(cgoObjects)をgopackでアーカイブする際に、特定の順序で結合する必要がありました。コミットメッセージによると、GCCが生成したオブジェクトファイルは、Goコンパイラが生成した通常のオブジェクトファイルの「後に」リストされることが「極めて重要」でした。この理由については「なぜかは不明」とされていますが、これはWindowsのリンカ(またはgopackの内部的な動作)の特性に起因するものでした。 さらに、cgoが生成するimportObj(GoのコードからCの関数を呼び出すためのスタブが含まれるオブジェクトファイル)は、GCCが生成するオブジェクトファイルの「前に」処理される必要がありました。これは、importObjがGoのコンパイラによって生成されるオブジェクトであり、GCCが生成するオブジェクトとは異なる特性を持つためと考えられます。この順序が守られないと、リンカがシンボルを正しく解決できず、ビルドエラーが発生していました。

  2. 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つのファイルにわたります。

  1. src/cmd/go/build.go:

    • gofiles, cfiles, sfiles, objectsに加えて、cgoObjectsという新しいスライスが導入されました。これは、cgoによって生成されたオブジェクトファイルを個別に管理するためです。
    • cgoによって生成されたオブジェクトファイル(outObj)が、以前は直接objectsスライスに追加されていたのに対し、cgoObjectsスライスに追加されるように変更されました。
    • gopackを呼び出す直前で、objectsスライスにcgoObjectsスライスが追加されるようになりました。これにより、GCCが生成したオブジェクトファイルがGoコンパイラが生成した通常のオブジェクトファイルの後に結合されることが保証されます。
    • cgo関数内で、outObjスライスに空の文字列が追加され、importObjを格納するためのプレースホルダーが確保されました。
    • importObjoutObjスライスの最初の要素として設定されるように変更されました。これにより、importObjがGCCが生成するオブジェクトファイルの前に処理されることが保証されます。
  2. src/make.bash:

    • deps.bashの実行が$USE_GO_TOOL ||という条件で囲まれました。これは、$USE_GO_TOOLが真(つまり、Goツールが利用可能)でない場合にのみdeps.bashが実行されることを意味します。
  3. 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環境で利用できるようにするための重要なステップでした。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント(cgo、ビルドプロセスに関する情報)
  • Go言語のソースコード(特にsrc/cmd/go/build.gosrc/make.bashsrc/pkg/go/build/dir.goの関連部分)
  • 一般的なリンカの動作とオブジェクトファイルの構造に関する知識
  • シェルスクリプトの構文と論理演算子に関する知識I have generated the commit explanation as requested.