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

[インデックス 11933] ファイルの概要

このコミットは、Go言語のビルドツールである cmd/dist におけるクロスコンパイル時のパッケージターゲットパスの指定に関する修正です。具体的には、Goライブラリ(パッケージ)のインストールパスを決定する際に、ホスト環境のOSとアーキテクチャ(gohostos, gohostarch)ではなく、ターゲット環境のOSとアーキテクチャ(goos, goarch)を使用するように変更しています。これにより、クロスコンパイルされたバイナリが正しいディレクトリに配置され、Goのビルドシステムが期待通りに動作するようになります。

コミット

commit def2022bc08f0326503df4f661eeaca3809dd5f0
Author: Alex Brainman <alex.brainman@gmail.com>
Date:   Wed Feb 15 09:02:45 2012 -0500

    cmd/dist: use correct package target when cross-compiling
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5672050
---
 src/cmd/dist/build.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/cmd/dist/build.c b/src/cmd/dist/build.c
index e115e3c322..67860cdf33 100644
--- a/src/cmd/dist/build.c
+++ b/src/cmd/dist/build.c
@@ -605,10 +605,10 @@ install(char *dir)
 		// Go library (package).
 		vadd(&link, bpathf(&b, "%s/pack", tooldir));
 		vadd(&link, "grc");
-		p = bprintf(&b, "%s/pkg/%s_%s/%s", goroot, gohostos, gohostarch, dir+4);
+		p = bprintf(&b, "%s/pkg/%s_%s/%s", goroot, goos, goarch, dir+4);
 		*xstrrchr(p, '/') = '\0';
 		xmkdirall(p);
-		vadd(&link, bpathf(&b, "%s/pkg/%s_%s/%s.a", goroot, gohostos, gohostarch, dir+4));
+		vadd(&link, bpathf(&b, "%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir+4));
 	} else if(streq(dir, "cmd/go") || streq(dir, "cmd/cgo")) {
 		// Go command.
 		vadd(&link, bpathf(&b, "%s/%sl", tooldir, gochar));

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/def2022bc08f0326503df4f661eeaca3809dd5f0

元コミット内容

このコミットの目的は、Go言語のビルドシステムの一部である cmd/dist ツールが、クロスコンパイル時にGoライブラリ(パッケージ)をインストールする際のターゲットディレクトリを正しく決定するように修正することです。以前は、ホスト環境のOSとアーキテクチャ(gohostos, gohostarch)に基づいてパスを構築していましたが、これはクロスコンパイルのシナリオでは誤りでした。クロスコンパイルでは、生成されるバイナリはターゲット環境向けであるため、そのパッケージはターゲット環境のOSとアーキテクチャ(goos, goarch)に対応するディレクトリに配置されるべきです。この修正により、Goのビルドプロセスにおけるクロスコンパイルの正確性が向上します。

変更の背景

Go言語は、異なるオペレーティングシステムやCPUアーキテクチャ向けにバイナリを生成する「クロスコンパイル」機能を強力にサポートしています。GoのソースコードからGoコンパイラや標準ライブラリをビルドする際には、cmd/dist ツールが中心的な役割を担います。

このコミットが行われた2012年当時、Goのビルドシステムはまだ進化の途中にありました。クロスコンパイルの際に、Goの標準ライブラリやユーザーが作成したパッケージのアーカイブファイル(.a ファイル)が GOROOT/pkg ディレクトリ以下に配置されます。この配置パスは通常、$GOROOT/pkg/$GOOS_$GOARCH/ の形式を取ります。

しかし、このコミット以前の cmd/dist の実装では、クロスコンパイル時にパッケージのインストールディレクトリを決定する際に、誤ってビルドを実行している「ホスト」環境のOSとアーキテクチャ(gohostos, gohostarch)を使用していました。例えば、Linux上でWindows向けのGoバイナリをクロスコンパイルする場合、生成されるパッケージは pkg/linux_amd64/ のようなホスト環境のディレクトリに置かれてしまっていました。これは、Windows向けのパッケージが pkg/windows_amd64/ のようなターゲット環境のディレクトリに置かれるべきというGoのビルドシステムの期待と矛盾します。

この不整合は、クロスコンパイルされたGoプログラムが正しくリンクできなかったり、Goツールチェインが期待通りに動作しなかったりする原因となります。このコミットは、この問題を解決し、クロスコンパイルされたパッケージが常に正しいターゲット環境のディレクトリに配置されるようにするために導入されました。

前提知識の解説

Goのビルドシステムと cmd/dist

Go言語のビルドシステムは非常に洗練されており、特にクロスコンパイルの容易さが特徴です。

  • GOROOT: Goのインストールディレクトリのルートパスを指す環境変数です。Goの標準ライブラリやツールチェインがここに配置されます。
  • GOPATH: Goのワークスペースのルートパスを指す環境変数です。ユーザーが開発するGoプロジェクトのソースコード、パッケージ、バイナリがここに配置されます。
  • cmd/dist: GoのソースコードからGoコンパイラ、アセンブラ、リンカ、標準ライブラリなどをビルドし、インストールするための内部ツールです。Goのリリースビルドや、ユーザーがGoのソースからGoをビルドする際に使用されます。make.bash (Unix系) や make.bat (Windows) スクリプトの内部で呼び出されます。

クロスコンパイルと環境変数

Goは、GOOSGOARCH という環境変数を設定することで、簡単にクロスコンパイルを行うことができます。

  • GOOS: ターゲットとなるオペレーティングシステム(例: linux, windows, darwin, freebsd など)。
  • GOARCH: ターゲットとなるCPUアーキテクチャ(例: amd64, arm, arm64, 386 など)。

これらの変数を設定して go buildgo install を実行すると、指定されたOSとアーキテクチャ向けのバイナリが生成されます。

一方で、Goのビルドプロセスには、ビルドを実行している「ホスト」環境を示すための内部的な変数も存在します。

  • GOHOSTOS: ビルドを実行しているホストのオペレーティングシステム。
  • GOHOSTARCH: ビルドを実行しているホストのCPUアーキテクチャ。

これらの変数は、Goツールチェイン自体をビルドする際に、ホスト環境に依存するツール(例: cmd/dist の一部)の動作を制御するために使用されます。しかし、生成される「ターゲット」バイナリやパッケージのパスを決定する際には、GOOSGOARCH を使用するのが正しい挙動です。

Goのパッケージパス

Goのビルドシステムでは、コンパイルされたGoパッケージのアーカイブファイル(通常 .a 拡張子を持つ)は、$GOROOT/pkg/$GOOS_$GOARCH/ ディレクトリ以下に配置されます。例えば、Linux AMD64向けのパッケージは $GOROOT/pkg/linux_amd64/ に、Windows AMD64向けのパッケージは $GOROOT/pkg/windows_amd64/ に格納されます。これにより、Goツールチェインは特定のターゲット環境向けのパッケージを効率的に見つけることができます。

技術的詳細

このコミットは、src/cmd/dist/build.c ファイル内の install 関数に焦点を当てています。この関数は、Goの標準ライブラリやその他のGoパッケージをビルドし、適切なディレクトリにインストールする役割を担っています。

問題の箇所は、Goライブラリ(パッケージ)のインストールパスを構築する部分です。Goのパッケージは、$GOROOT/pkg/$GOOS_$GOARCH/ という形式のディレクトリに配置されることが期待されます。ここで $GOOS$GOARCH は、そのパッケージが対象とするOSとアーキテクチャを示します。

しかし、修正前のコードでは、このパスを構築する際に gohostosgohostarch という変数が使用されていました。これらの変数は、Goのビルドを実行している「ホスト」環境のOSとアーキテクチャを表します。

例えば、Linux (ホスト) 上でWindows (ターゲット) 向けのGoをビルドする場合、

  • gohostoslinux
  • gohostarchamd64 (またはホストのアーキテクチャ)
  • gooswindows
  • goarchamd64 (またはターゲットのアーキテクチャ)

となります。

修正前のコードでは、パッケージのパスが $GOROOT/pkg/linux_amd64/... のように構築されてしまい、これはWindows向けのパッケージとしては不適切でした。

このコミットでは、gohostosgoos に、gohostarchgoarch に置き換えることで、生成されるパッケージが常にターゲット環境のOSとアーキテクチャに対応するディレクトリに配置されるように修正しています。これにより、クロスコンパイルされたGoツールチェインが、自身が生成するパッケージを正しく管理できるようになります。

bprintf は、Goの内部ビルドツールで使用される文字列フォーマット関数で、bpathf はパスをフォーマットするためのヘルパー関数です。xmkdirall はディレクトリを再帰的に作成する関数、xstrrchr は文字列内で指定された文字の最後の出現位置を検索する関数です。これらの関数は、Goのビルドシステム内でパスの構築やディレクトリの作成を行うために利用されています。

コアとなるコードの変更箇所

--- a/src/cmd/dist/build.c
+++ b/src/cmd/dist/build.c
@@ -605,10 +605,10 @@ install(char *dir)
 		// Go library (package).\n 		vadd(&link, bpathf(&b, "%s/pack", tooldir));\n 		vadd(&link, "grc");\n-		p = bprintf(&b, "%s/pkg/%s_%s/%s", goroot, gohostos, gohostarch, dir+4);\n+		p = bprintf(&b, "%s/pkg/%s_%s/%s", goroot, goos, goarch, dir+4);\n 		*xstrrchr(p, '/') = '\0';\n 		xmkdirall(p);\n-		vadd(&link, bpathf(&b, "%s/pkg/%s_%s/%s.a", goroot, gohostos, gohostarch, dir+4));\n+		vadd(&link, bpathf(&b, "%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir+4));

コアとなるコードの解説

変更は src/cmd/dist/build.c ファイルの install 関数内の2行に集中しています。

  1. ディレクトリパスの構築:

    -		p = bprintf(&b, "%s/pkg/%s_%s/%s", goroot, gohostos, gohostarch, dir+4);
    +		p = bprintf(&b, "%s/pkg/%s_%s/%s", goroot, goos, goarch, dir+4);
    

    この行は、Goパッケージがインストールされるディレクトリのパスを構築しています。

    • goroot: Goのインストールルートディレクトリ。
    • %s/pkg/: goroot の下の pkg ディレクトリ。
    • %s_%s/: ここが変更点です。
      • 変更前: gohostosgohostarch を使用していました。これはビルドを実行しているホストのOSとアーキテクチャです。
      • 変更後: goosgoarch を使用しています。これらはビルドのターゲットとなるOSとアーキテクチャです。
    • %s: パッケージの相対パス(dir+4src/ を取り除いた部分)。

    この変更により、例えばWindows向けのパッケージをLinux上でクロスコンパイルする場合、パスは GOROOT/pkg/windows_amd64/ のように正しく構築されるようになります。*xstrrchr(p, '/') = '\0'; は、パスの最後のスラッシュ以降をヌル終端してディレクトリ部分のみを取得し、xmkdirall(p); でそのディレクトリを作成しています。

  2. アーカイブファイルパスの構築:

    -		vadd(&link, bpathf(&b, "%s/pkg/%s_%s/%s.a", goroot, gohostos, gohostarch, dir+4));
    +		vadd(&link, bpathf(&b, "%s/pkg/%s_%s/%s.a", goroot, goos, goarch, dir+4));
    

    この行は、コンパイルされたGoパッケージのアーカイブファイル(.a)の最終的なパスを構築し、それをリンカに渡すための link リストに追加しています。 ここでも同様に、gohostosgohostarchgoosgoarch に置き換えられています。これにより、生成されるアーカイブファイルが、ターゲット環境に対応する正しいディレクトリに配置されることが保証されます。

これらの変更は、Goのクロスコンパイル機能の正確性と信頼性を向上させる上で非常に重要です。これにより、Goのビルドシステムは、異なるプラットフォーム向けのバイナリとパッケージをより一貫性のある方法で管理できるようになりました。

関連リンク

参考にした情報源リンク

  • Goの公式ドキュメントおよびソースコード
  • Goのビルドシステムに関するコミュニティの議論やブログ記事 (具体的なURLは時間の経過により変動するため、一般的な情報源として記載)
  • Goの環境変数に関するStack OverflowなどのQ&Aサイト