[インデックス 11871] ファイルの概要
このコミットは、Go言語のビルドシステム、特にクロスコンパイル機能に関する重要な改善を導入しています。主な目的は、ホストシステムとは異なるOSやアーキテクチャ(ターゲットシステム)向けにGoプログラムをビルドする際の堅牢性と柔軟性を向上させることです。これには、ビルドツールの配置場所の変更(bin/tool/
からpkg/tool/goos_goarch/
へ)、共有ファイルシステム上でのGOROOT
のサポート強化、およびビルドスクリプトと内部パスの調整が含まれます。
コミット
commit 7b848c69647c52d69127ccef79cc7d01c0ec02c6
Author: Russ Cox <rsc@golang.org>
Date: Mon Feb 13 22:31:51 2012 -0500
cmd/dist: cross-compiling fixes
This CL makes it possible to run make.bash with
GOOS and GOARCH set to something other than
the native host GOOS and GOARCH.
As part of the CL, the tool directory moves from bin/tool/
to pkg/tool/goos_goarch where goos and goarch are
the values for the host system (running the build), not
the target. pkg/ is not technically appropriate, but C objects
are there now tool (pkg/obj/) so this puts all the generated
binaries in one place (rm -rf $GOROOT/pkg cleans everything).
Including goos_goarch in the name allows different systems
to share a single $GOROOT on a shared file system.
Fixes #2920.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5645093
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/7b848c69647c52d69127ccef79cc7d01c0ec02c6
元コミット内容
cmd/dist: cross-compiling fixes
This CL makes it possible to run make.bash with
GOOS and GOARCH set to something other than
the native host GOOS and GOARCH.
As part of the CL, the tool directory moves from bin/tool/
to pkg/tool/goos_goarch where goos and goarch are
the values for the host system (running the build), not
the target. pkg/ is not technically appropriate, but C objects
are there now tool (pkg/obj/) so this puts all the generated
binaries in one place (rm -rf $GOROOT/pkg cleans everything).
Including goos_goarch in the name allows different systems
to share a single $GOROOT on a shared file system.
Fixes #2920.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5645093
変更の背景
このコミットが行われた背景には、Go言語のビルドシステムが抱えていたクロスコンパイルに関する課題がありました。以前のシステムでは、make.bash
スクリプトを実行する際に、ビルドを実行するホストシステムのOSとアーキテクチャ(GOHOSTOS
/GOHOSTARCH
)と、生成されるバイナリのターゲットOSとアーキテクチャ(GOOS
/GOARCH
)が異なる場合に、ビルドが正しく機能しない、または非常に困難であるという問題がありました。
具体的には、以下の点が問題視されていました。
- ツールの配置場所の硬直性: ビルドプロセスで使用されるコンパイラ(
gc
,cc
など)、リンカ(gl
など)、アセンブラ(ga
など)、pack
などの補助ツールは、$GOROOT/bin/tool/
という固定されたパスに配置されていました。これは、ホストシステムとターゲットシステムが異なる場合に、どのツールを使用すべきかという曖昧さを生じさせ、特にクロスコンパイル環境でのツールの管理を複雑にしていました。 - 共有
GOROOT
環境での問題: 複数の異なるホストシステム(例: Linux x86-64とmacOS x86-64)が同じ$GOROOT
ディレクトリを共有するような環境では、それぞれのホストシステムが生成するツールやオブジェクトファイルが衝突する可能性がありました。これは、ビルドの整合性を損ない、予期せぬエラーを引き起こす原因となっていました。 - クリーンアップの複雑さ: 生成されたバイナリやオブジェクトファイルが複数の場所に散らばっていると、ビルド環境をクリーンアップする際に手間がかかり、完全にクリーンな状態に戻すのが困難でした。
これらの問題を解決し、Go言語のクロスコンパイル機能をより堅牢で使いやすいものにすることが、このコミットの主要な動機となっています。特に、Issue 2920で報告されたクロスコンパイルの問題への対応が求められていました。
前提知識の解説
このコミットの変更内容を理解するためには、以下のGo言語のビルドシステムと関連する概念についての知識が必要です。
-
Goのビルドシステム:
GOROOT
: Goのインストールディレクトリを指す環境変数です。Goのソースコード、標準ライブラリ、ツールチェインなどが含まれます。GOOS
/GOARCH
: ビルドされるGoプログラムが実行されるターゲットのオペレーティングシステム(GOOS
、例:linux
,windows
,darwin
)とアーキテクチャ(GOARCH
、例:amd64
,arm
,386
)を指定する環境変数です。GOHOSTOS
/GOHOSTARCH
: ビルドプロセス自体が実行されるホストシステムのオペレーティングシステムとアーキテクチャを指します。通常、GOOS
/GOARCH
が設定されていない場合、これらはGOHOSTOS
/GOHOSTARCH
と同じ値になります。クロスコンパイルを行う際に、GOOS
/GOARCH
をGOHOSTOS
/GOHOSTARCH
とは異なる値に設定します。make.bash
/make.bat
: GoのソースコードからGoのツールチェイン全体をビルドするためのシェルスクリプト(Unix系)およびバッチファイル(Windows)です。Goの初期ブートストラップビルドや、Goのバージョンアップ時に使用されます。cmd/dist
:make.bash
やmake.bat
によって最初にビルドされるGoの内部ツールです。Goのビルドプロセス全体を管理し、コンパイラやリンカなどのツールを呼び出します。環境変数の設定や、ビルド順序の管理などを行います。go tool
:go
コマンドのサブコマンドで、Goの内部ツール(コンパイラ、リンカ、アセンブラ、pack
など)を実行するためのインターフェースを提供します。例えば、go tool compile
はGoコンパイラを、go tool pack
はpack
ツールを呼び出します。
-
クロスコンパイル:
- あるプラットフォーム(ホスト)上で、別のプラットフォーム(ターゲット)向けの実行可能ファイルを生成するプロセスです。Go言語は、
GOOS
とGOARCH
環境変数を設定するだけで簡単にクロスコンパイルができることで知られています。 - クロスコンパイルでは、ホストシステム上で動作するコンパイラやリンカ(これらはホストの
GOHOSTOS
/GOHOSTARCH
向けにビルドされている)を使用して、ターゲットシステム向けのバイナリを生成します。
- あるプラットフォーム(ホスト)上で、別のプラットフォーム(ターゲット)向けの実行可能ファイルを生成するプロセスです。Go言語は、
-
Goのツールチェイン:
- Goのソースコードをコンパイルし、実行可能ファイルを生成するために必要な一連のツール群です。これには、Goコンパイラ(
gc
)、Cコンパイラ(cc
)、アセンブラ(as
)、リンカ(ld
)、アーカイブツール(pack
)などが含まれます。 - これらのツールは、Goのビルドプロセス中に生成され、通常は
$GOROOT/pkg/tool/
(このコミット以前は$GOROOT/bin/tool/
)以下に配置されます。
- Goのソースコードをコンパイルし、実行可能ファイルを生成するために必要な一連のツール群です。これには、Goコンパイラ(
技術的詳細
このコミットの技術的な変更は多岐にわたりますが、その中心はGoのビルドツールの配置戦略の変更と、それに伴うビルドプロセスの適応です。
-
ツールディレクトリの移動と命名規則の変更:
- 変更前: ビルドツールは
$GOROOT/bin/tool/
に配置されていました。 - 変更後: ビルドツールは
$GOROOT/pkg/tool/$GOHOSTOS_$GOHOSTARCH/
に配置されるようになりました。$GOHOSTOS_$GOHOSTARCH
というサフィックスが付加されることで、異なるホストシステムでビルドされたツールが同じGOROOT
を共有する際に衝突するのを防ぎます。これは、特に共有ファイルシステム上で開発を行う場合に非常に有効です。pkg/
ディレクトリは、元々Goのパッケージアーカイブ(.a
ファイル)が配置される場所でしたが、コミットメッセージにあるように「技術的には適切ではない」とされつつも、C言語のオブジェクトファイル(pkg/obj/
)もここに置かれていたため、生成される全てのバイナリをpkg/
以下に集約するという方針が取られました。これにより、rm -rf $GOROOT/pkg
で全ての生成物をクリーンアップできるという利点があります。
- 変更前: ビルドツールは
-
GOTOOLDIR
環境変数の導入:- 新しいツールディレクトリのパスは、
GOTOOLDIR
という新しい環境変数によって参照されるようになりました。これにより、ビルドスクリプトや内部コードでツールのパスをハードコードする代わりに、この変数を使用することで柔軟性が向上します。 make.bash
やmake.bat
では、./cmd/dist/dist env
を実行することで、このGOTOOLDIR
を含む環境変数を設定するようになりました。
- 新しいツールディレクトリのパスは、
-
cmd/dist
の変更:src/cmd/dist/build.c
内のinit()
関数で、tooldir
変数が$GOROOT/pkg/tool/$GOHOSTOS_$GOHOSTARCH
として初期化されるようになりました。setup()
関数では、古いbin/tool
ディレクトリの削除と、新しいpkg/tool/$GOHOSTOS_$GOHOSTARCH
ディレクトリの作成が処理されます。また、pkg/obj
ディレクトリもpkg/obj/$GOHOSTOS_$GOHOSTARCH
のようなホスト固有のサブディレクトリを持つように変更され、クリーンアップがより細かく制御できるようになりました。install()
関数では、コンパイラ、リンカ、アセンブラ、pack
などのツールを呼び出す際のパスが、ハードコードされた../bin/tool/
から$tooldir/
を使用するように変更されました。- クロスコンパイルの場合(
goos != gohostos
またはgoarch != gohostarch
)、install()
関数内で実際のビルドステップをスキップするロジックが追加されました。これは、クロスコンパイルではホストのツールチェインでターゲット向けのソースファイルを生成するだけで、最終的なバイナリのビルドはgo
コマンドに任せるという方針を示唆しています。
-
make.bash
/make.bat
のビルドフローの変更:dist
ツール自体が、一時的にcmd/dist/dist
としてビルドされ、その後に$GOTOOLDIR
へ移動されるようになりました。これは、bootstrap
プロセスが$GOTOOLDIR
をクリーンアップするため、dist
ツールが削除されないようにするための工夫です。bootstrap
コマンドに-a
フラグ(rebuildall
を意味する)が追加され、初期ビルド時に全てのコンポーネントが確実に再ビルドされるようになりました。- クロスコンパイルのシナリオを考慮し、
make.bash
とmake.bat
は、まずホストシステム(GOHOSTOS
/GOHOSTARCH
)向けのツールと標準ライブラリをビルドし、その後、もしGOOS
/GOARCH
がホストと異なる場合は、ターゲットシステム向けの標準ライブラリをビルドするようになりました。これにより、クロスコンパイル環境でのビルドがよりスムーズに行えるようになります。
-
cmd/go
の変更:src/cmd/go/build.go
では、cgo
ツールの依存関係の扱いが変更されました。クロスビルドでない場合(つまり、ホストとターゲットが同じ場合)にのみ、cgo
ツールがビルドの依存関係として追加されるようになりました。これは、クロスコンパイル時にホストのcgo
ツールがターゲットのcgo
ツールによって上書きされるのを防ぐためです。src/cmd/go/pkg.go
では、クロスコンパイルされたバイナリのインストールパスが、bin/tool/
のサブディレクトリではなく、pkg/tool/GOOS_GOARCH/
のサブディレクトリに配置されるように調整されました。src/cmd/go/tool.go
では、toolDir
変数がbuild.ToolDir
を参照するように変更され、パスの定義が一元化されました。
-
src/pkg/go/build/path.go
でのToolDir
の定義:- このファイルに
ToolDir
というグローバル変数が導入され、filepath.Join(runtime.GOROOT(), "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
として定義されました。これは、Goのビルドシステム全体でツールのパスを一貫して参照するための中心的な定義となります。
- このファイルに
これらの変更により、Goのビルドシステムはクロスコンパイルに対してより柔軟で堅牢になり、異なる環境間でのGOROOT
の共有も容易になりました。
コアとなるコードの変更箇所
このコミットにおける主要なコード変更箇所は以下の通りです。
src/cmd/dist/build.c
:init()
関数:tooldir
変数の初期化ロジックが追加され、$GOROOT/pkg/tool/$GOHOSTOS_$GOHOSTARCH
を指すように変更。setup()
関数:bin/tool
の作成ロジックが削除され、pkg/tool/$GOHOSTOS_$GOHOSTARCH
およびpkg/obj/$GOHOSTOS_$GOHOSTARCH
の作成とクリーンアップロジックが追加。古いbin/tool
ディレクトリの削除もここで行われる。install()
関数:pack
や各種コンパイラ/リンカ(5l
,6l
,8l
,5g
,6g
,8g
,5c
,6c
,8c
,5a
,6a
,8a
など)のパスが$GOROOT/bin/tool/
からtooldir
を使用するように変更。- クロスコンパイル時のビルドスキップロジックが追加。
pkg/obj
内のライブラリパス(libcc.a
,libgc.a
など)が$GOROOT/pkg/obj/$GOOS_$GOARCH/
を含むように変更。
src/pkg/go/build/path.go
:ToolDir
という新しいグローバル変数が定義され、Goのツールディレクトリの標準パス($GOROOT/pkg/tool/$GOHOSTOS_$GOHOSTARCH
)が設定される。
src/make.bash
/src/make.bat
:dist
ツールのビルドパスが一時的なものに変更され、その後$GOTOOLDIR
へ移動するロジックが追加。eval $(./cmd/dist/dist env)
による環境変数(特にGOTOOLDIR
)の設定が追加。bootstrap
コマンドに-a
フラグが追加。- クロスコンパイルを考慮したビルドフロー(ホスト向けビルド後にターゲット向けビルド)が導入。
go_bootstrap
やdist banner
の呼び出しパスが$GOTOOLDIR
を使用するように変更。
src/cmd/go/build.go
:action()
関数内で、cgo
ツールの依存関係が、ホストとターゲットが同じ場合にのみ追加されるように条件が変更。
src/cmd/go/pkg.go
:scanPackage()
関数内で、クロスコンパイルされたmain
パッケージのターゲットパスがpkg/tool/GOOS_GOARCH/
以下になるように調整。
コアとなるコードの解説
src/cmd/dist/build.c
このファイルはGoのビルドプロセスの中核を担うdist
ツールの主要な部分です。
-
init()
関数:// ... bpathf(&b, "%s/pkg/tool/%s_%s", goroot, gohostos, gohostarch); tooldir = btake(&b); // ...
ここで、新しい
tooldir
変数が初期化されます。このパスは、$GOROOT/pkg/tool/
の後に、ビルドを実行しているホストシステムのOSとアーキテクチャ(gohostos_gohostarch
)が続く形になります。これにより、異なるホスト環境でビルドされたツールが互いに干渉しないようになります。 -
setup()
関数:// ... // Create bin directory. p = bpathf(&b, "%s/bin", goroot); if(!isdir(p)) xmkdir(p); // ... (old bin/tool creation removed) // Create tool directory. // We keep it in pkg/, just like the object directory above. xremoveall(tooldir); xmkdirall(tooldir); // ...
以前は
$GOROOT/bin/tool
を作成していましたが、このコミットでそのロジックが削除され、代わりにtooldir
(つまり$GOROOT/pkg/tool/$GOHOSTOS_$GOHOSTARCH
)を再帰的に作成し、古いツールディレクトリを削除するようになりました。これにより、ツールの配置場所が新しい規則に準拠し、クリーンな状態が保たれます。 -
install()
関数:// ... } else if(ispkg) { // Go library (package). vadd(&link, bpathf(&b, "%s/pack", tooldir)); // packツールのパス変更 // ... } else if(streq(dir, "cmd/go") || streq(dir, "cmd/cgo")) { // Go command. vadd(&link, bpathf(&b, "%s/%sl", tooldir, gochar)); // リンカのパス変更 // ... } else { // C command. vadd(&link, bpathf(&b, "%s/%s%s", tooldir, name, exe)); // Cコマンドのパス変更 // ... } // ... if(!streq(goos, gohostos) || !streq(goarch, gohostarch)) { // We've generated the right files; the go command can do the build. if(vflag > 1) xprintf("skip build for cross-compile %s\n", dir); goto nobuild; // クロスコンパイル時のビルドスキップ } // ...
この関数は、Goのパッケージやコマンドをビルドし、インストールする役割を担います。変更点として、
pack
ツールや各種コンパイラ/リンカなどのパスが、ハードコードされた$GOROOT/bin/tool/
から、新しく定義されたtooldir
を使用するように変更されました。これにより、ビルドツールへの参照が一貫性を持ちます。 また、クロスコンパイルの場合(goos
とgohostos
、またはgoarch
とgohostarch
が異なる場合)、実際のビルドステップをスキップするロジックが追加されました。これは、クロスコンパイルではホストのツールチェインでターゲット向けのソースファイルを生成するだけで、最終的なバイナリのビルドはgo
コマンドに任せるという方針を反映しています。
src/pkg/go/build/path.go
このファイルは、Goのビルドパスに関するユーティリティ関数や変数を定義しています。
// ToolDir is the directory containing build tools.
var ToolDir = filepath.Join(runtime.GOROOT(), "pkg/tool/"+runtime.GOOS+"_"+runtime.GOARCH)
この変更は非常に重要です。ToolDir
という新しいグローバル変数が導入され、Goのツールチェインが配置される標準的なディレクトリパスが定義されました。このパスは、$GOROOT/pkg/tool/
の後に、runtime.GOOS
とruntime.GOARCH
(これはビルドを実行しているGoプログラム自身のOSとアーキテクチャ、つまりホストのOSとアーキテクチャを指します)が続く形になります。これにより、Goのビルドシステム全体でツールのパスを一貫して参照できるようになり、パスの管理が大幅に簡素化されます。
src/make.bash
/ src/make.bat
これらのスクリプトは、Goのツールチェイン全体をビルドするためのエントリポイントです。
# make.bashの関連部分
# ...
gcc -O2 -Wall -Werror -ggdb -o cmd/dist/dist -Icmd/dist "$DEFGOROOT" cmd/dist/*.c
eval $(./cmd/dist/dist env) # 環境変数の設定
echo
if [ "$1" = "--dist-tool" ]; then
# Stop after building dist tool.
mv cmd/dist/dist $GOTOOLDIR/dist # distツールを新しい場所へ移動
exit 0
fi
echo "# Building compilers and Go bootstrap tool for host, $GOHOSTOS/$GOHOSTARCH."
./cmd/dist/dist bootstrap -a -v # bootstrap実行
# Delay move of dist tool to now, because bootstrap cleared tool directory.
mv cmd/dist/dist $GOTOOLDIR/dist # bootstrap後に再度distツールを移動
$GOTOOLDIR/go_bootstrap clean -i std
echo
if [ "$GOHOSTARCH" != "$GOARCH" -o "$GOHOSTOS" != "$GOOS" ]; then
echo "# Building packages and commands for host, $GOHOSTOS/$GOHOSTARCH."
GOOS=$GOHOSTOS GOARCH=$GOHOSTARCH \
$GOTOOLDIR/go_bootstrap install -v std
echo
fi
echo "# Building packages and commands for $GOOS/$GOARCH."
$GOTOOLDIR/go_bootstrap install -v std
echo
# ...
これらのスクリプトは、dist
ツールのビルドと配置、およびGoツールチェイン全体のビルドフローを制御します。
dist
ツールはまず一時的な場所(cmd/dist/dist
)にビルドされ、その後eval $(./cmd/dist/dist env)
によってGOTOOLDIR
などの環境変数が設定されます。bootstrap
コマンドが-a
フラグ(rebuildall
)付きで実行されます。このbootstrap
プロセスは、ツールのディレクトリをクリーンアップする可能性があるため、dist
ツールはbootstrap
の実行後に改めて$GOTOOLDIR
に移動されます。- 最も重要な変更は、クロスコンパイルのサポートです。
GOHOSTARCH
とGOARCH
、またはGOHOSTOS
とGOOS
が異なる場合、まずホストシステム向けの標準ライブラリとコマンドがビルドされ、その後、ターゲットシステム向けの標準ライブラリとコマンドがビルドされるようになりました。これにより、クロスコンパイル環境でのビルドがより堅牢かつ自動化されます。
src/cmd/go/build.go
このファイルはgo
コマンドのビルドロジックを扱います。
// ...
// If we are not doing a cross-build, then record the binary we'll
// generate for cgo as a dependency of the build of any package
// using cgo, to make sure we do not overwrite the binary while
// a package is using it. If this is a cross-build, then the cgo we
// are writing is not the cgo we need to use.
if b.goos == runtime.GOOS && b.goarch == runtime.GOARCH {
if len(p.CgoFiles) > 0 || p.Standard && p.ImportPath == "runtime/cgo" {
var stk importStack
p1 := loadPackage("cmd/cgo", &stk)
if p1.Error != nil {
fatalf("load cmd/cgo: %v", p1.Error)
}
a.cgo = b.action(depMode, depMode, p1)
a.deps = append(a.deps, a.cgo)
}
}
// ...
このコードスニペットは、cgo
ツールのビルド依存関係の扱いを調整しています。以前は常にcgo
が依存関係として追加されていましたが、この変更により、ホストとターゲットが同じ場合(つまりクロスビルドでない場合)にのみcgo
がビルド依存関係として記録されるようになりました。これは、クロスコンパイル時にホストのcgo
ツールがターゲットのcgo
ツールによって上書きされるのを防ぐための重要な修正です。クロスビルドの場合、go
コマンドはホストのcgo
ツールを使用し、ターゲット向けのcgo
ツールをビルドする必要がないため、この最適化が導入されました。
関連リンク
- Go Issue #2920: cmd/dist: cross-compiling fixes
- Go Change List (CL) 5645093: https://golang.org/cl/5645093
参考にした情報源リンク
- Go言語のソースコード (特に
src/cmd/dist
、src/make.bash
、src/pkg/go/build
ディレクトリ) - Go言語の公式ドキュメント (クロスコンパイルに関する情報)
- Gitのコミット履歴と差分表示