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

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

このコミットは、Go言語のビルドシステムにおいて、Cgoを有効にした状態でのクロスコンパイルを改善するための重要な変更を導入しています。具体的には、ターゲットアーキテクチャ向けのC/C++コンパイラを指定するための新しい環境変数CC_FOR_TARGETCXX_FOR_TARGETを導入し、クロスコンパイル時にはCGO_ENABLEDがデフォルトで無効になるように挙動を変更しています。これにより、より柔軟かつ安全なクロスコンパイル環境が提供されます。

コミット

commit 2dc759d7c69cbb0800de53d6ca391c703ad42d9c
Author: Elias Naur <elias.naur@gmail.com>
Date:   Thu Feb 6 09:11:00 2014 -0800

    cmd/go, cmd/cgo, make.bash: cross compiling with cgo enabled
    
    Introduce two new environment variables, CC_FOR_TARGET and CXX_FOR_TARGET.
    CC_FOR_TARGET defaults to CC and is used when compiling for GOARCH, while
    CC remains for compiling for GOHOSTARCH.
    CXX_FOR_TARGET defaults to CXX and is used when compiling C++ code for
    GOARCH.
    
    CGO_ENABLED defaults to disabled when cross compiling and has to be
    explicitly enabled.
    
    Update #4714
    
    LGTM=minux.ma, iant
    R=golang-codereviews, minux.ma, iant, rsc, dominik.honnef
    CC=golang-codereviews
    https://golang.org/cl/57100043

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

https://github.com/golang/go/commit/2dc759d7c69cbb0800de53d6ca391c703ad42d9c

元コミット内容

このコミットは、Goのビルドシステム、特にcmd/gocmd/cgomake.bashに関連するもので、Cgoを有効にした状態でのクロスコンパイルのサポートを改善します。主な変更点は以下の通りです。

  1. 新しい環境変数の導入:
    • CC_FOR_TARGET: ターゲットアーキテクチャ (GOARCH) 向けのCコードをコンパイルする際に使用されるコンパイラを指定します。デフォルトはCCです。
    • CXX_FOR_TARGET: ターゲットアーキテクチャ (GOARCH) 向けのC++コードをコンパイルする際に使用されるコンパイラを指定します。デフォルトはCXX、または設定されていない場合はg++clang++です。
    • 既存のCCはホストアーキテクチャ (GOHOSTARCH) 向けのコンパイルに引き続き使用されます。
  2. CGO_ENABLEDのデフォルト挙動の変更:
    • クロスコンパイル時には、CGO_ENABLEDがデフォルトで無効になります。Cgoを有効にするには、明示的にCGO_ENABLED=1を設定する必要があります。
  3. Issue #4714の更新: この変更は、GoのIssueトラッカーで報告されていたクロスコンパイル時のCgoに関する問題を解決します。

変更の背景

Go言語は、その設計思想の一つとして、静的リンクされたバイナリを生成し、デプロイの容易さを追求しています。しかし、Cgo(GoとC言語の相互運用機能)を使用する場合、GoのコードがC/C++のコードとリンクされるため、外部のC/C++コンパイラやリンカが必要になります。

従来のGoのビルドシステムでは、クロスコンパイル(例えば、LinuxマシンでWindows向けのバイナリをビルドする)を行う際に、Cgoが絡むと問題が発生することがありました。具体的には、Goのビルドツールが、ホストシステム(ビルドを実行しているマシン)のC/C++コンパイラを、ターゲットシステム(生成されるバイナリが実行されるマシン)のC/C++コードのコンパイルにも使用しようとする傾向がありました。これは、異なるアーキテクチャやOS向けのC/C++コードをコンパイルする際に、適切なクロスコンパイラが使用されないという問題を引き起こしました。

このコミットは、この問題を解決するために、ターゲットアーキテクチャ向けのC/C++コンパイラを明示的に指定できるメカニズムを提供します。これにより、Goのビルドシステムが、ホストとターゲットで異なるC/C++コンパイラを適切に使い分けられるようになり、Cgoを有効にした状態でのクロスコンパイルがより堅牢になります。また、クロスコンパイル時にCGO_ENABLEDをデフォルトで無効にすることで、意図しないCgoの利用を防ぎ、ビルドの失敗や予期せぬ挙動を避けるための安全策を講じています。

前提知識の解説

このコミットを理解するためには、以下のGo言語のビルドに関する概念と環境変数について理解しておく必要があります。

  • Cgo: Go言語の機能の一つで、GoのコードからC言語の関数を呼び出したり、C言語のコードをGoのプログラムに組み込んだりすることを可能にします。Cgoを使用すると、Goのプログラムは外部のC/C++ライブラリに依存するようになり、その結果、ビルドプロセスにC/C++コンパイラとリンカが必要になります。
  • クロスコンパイル (Cross-compilation): あるプラットフォーム(ホスト)で、別のプラットフォーム(ターゲット)向けの実行可能ファイルをビルドするプロセスです。例えば、macOS上でLinux向けのGoバイナリをビルドする場合などがこれに当たります。
  • GOARCH: Goのプログラムが実行されるターゲットのCPUアーキテクチャ(例: amd64, arm, arm64)を指定する環境変数です。
  • GOHOSTARCH: ビルドを実行しているホストのCPUアーキテクチャを指定する環境変数です。通常はGOARCHと同じですが、クロスコンパイル時には異なります。
  • GOOS: Goのプログラムが実行されるターゲットのオペレーティングシステム(例: linux, windows, darwin)を指定する環境変数です。
  • GOHOSTOS: ビルドを実行しているホストのオペレーティングシステムを指定する環境変数です。通常はGOOSと同じですが、クロスコンパイル時には異なります。
  • CC: C言語のコンパイラを指定する環境変数です。通常、ホストシステム上でCコードをコンパイルする際に使用されます。
  • CXX: C++言語のコンパイラを指定する環境変数です。通常、ホストシステム上でC++コードをコンパイルする際に使用されます。
  • CGO_ENABLED: Cgoを有効にするかどうかを制御する環境変数です。1に設定するとCgoが有効になり、0に設定すると無効になります。
  • ldflags: Goのリンカに渡されるフラグです。外部リンカ (extld) の指定など、リンク時の挙動を制御するために使用されます。
  • extld (External Linker): Goのビルドプロセスが、GoのコードとCgoでコンパイルされたC/C++コードを最終的にリンクするために使用する外部リンカ(通常はC/C++コンパイラ自身)を指します。

技術的詳細

このコミットの技術的な核心は、Goのビルドツールが、ホストとターゲットのC/C++コンパイラを適切に区別し、使用できるようにすることです。

  1. CC_FOR_TARGETCXX_FOR_TARGETの導入:

    • src/cmd/dist/a.hsrc/cmd/dist/build.cで、新しいグローバル変数defaultcctargetdefaultcxxtargetが導入されました。これらは、それぞれCC_FOR_TARGETCXX_FOR_TARGET環境変数の値を保持します。
    • src/cmd/dist/build.cinit()関数内で、これらの新しい環境変数が読み込まれます。もしCC_FOR_TARGETが設定されていなければCCの値が、CXX_FOR_TARGETが設定されていなければCXXの値(またはデフォルトのg++/clang++)がフォールバックとして使用されます。
    • これにより、Goのビルドシステムは、ホスト向けのコンパイルにはCC/CXXを、ターゲット向けのコンパイルにはCC_FOR_TARGET/CXX_FOR_TARGETを使い分けることが可能になります。
  2. cmd/go/build.goにおけるリンカの選択ロジックの変更:

    • gcToolchain.ld関数(Goのリンカを呼び出す部分)において、外部リンカ (-extld) の選択ロジックが変更されました。
    • 以前は、C++コードが含まれる場合にのみCXX環境変数(またはg++)を外部リンカとして使用していました。
    • 変更後、cxx(C++コードが含まれるか)の有無にかかわらず、ccompilerPathヘルパー関数を使用して適切なコンパイラパスを取得するようになりました。
    • ccompilerPath関数は、CXXdefaultCXX(C++の場合)、またはCCdefaultCC(Cの場合)を引数に取り、CC_FOR_TARGETCXX_FOR_TARGETの値を考慮して、ターゲット向けの適切なコンパイラを決定します。これにより、クロスコンパイル時にターゲットアーキテクチャ用のC/C++コンパイラが正しく選択されるようになります。
  3. CGO_ENABLEDのクロスコンパイル時のデフォルト無効化:

    • src/pkg/go/build/build.godefaultContext()関数内で、CGO_ENABLEDの初期化ロジックが変更されました。
    • 以前は、CGO_ENABLEDが明示的に設定されていない場合、クロスコンパイル時でもcgoEnabledマップに基づいてCgoが有効になる可能性がありました。
    • 変更後、runtime.GOARCH == c.GOARCH && runtime.GOOS == c.GOOS(つまり、ホストとターゲットが同じ場合)でない限り、CGO_ENABLEDはデフォルトで無効 (false) に設定されるようになりました。これにより、クロスコンパイル時にCgoを有効にするには、ユーザーが明示的にCGO_ENABLED=1を設定する必要があるという、より安全な挙動が強制されます。
  4. src/make.bashの更新:

    • Goのビルドスクリプトであるmake.bashが更新され、新しい環境変数CC_FOR_TARGETCXX_FOR_TARGETに関する説明が追加されました。
    • また、ホスト向けのビルドとターゲット向けのビルドで、CCCC_FOR_TARGETを適切に使い分けるように変更されました。特に、ターゲット向けのビルドではCC=$CC_FOR_TARGETが設定されるようになりました。

これらの変更により、Goのビルドシステムは、Cgoを使用するクロスコンパイルのシナリオにおいて、より予測可能で堅牢な動作をするようになりました。

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

このコミットで変更された主要なファイルと、その変更の概要は以下の通りです。

  • src/cmd/dist/a.h:
    • defaultcxxtargetdefaultcctargetという新しい外部変数が宣言されました。これらは、ターゲット向けのC/C++コンパイラを指します。
  • src/cmd/dist/build.c:
    • defaultcxxtargetdefaultcctargetの定義が追加され、CC_FOR_TARGETCXX_FOR_TARGET環境変数を読み込むロジックがinit()関数に追加されました。
    • cmdenv()関数で、CC_FOR_TARGETが環境変数として出力されるようになりました。
  • src/cmd/dist/buildgo.c:
    • mkzdefaultcc関数内で、defaultccdefaultcxxの代わりにdefaultcctargetdefaultcxxtargetが使用されるようになりました。これは、Goのビルドツールが生成する内部的なコンパイラ設定に影響します。
  • src/cmd/go/build.go:
    • gcToolchain.ld関数内の外部リンカ (-extld) の選択ロジックが大幅に変更されました。C++コードの有無にかかわらず、ccompilerPathヘルパー関数を使用して適切なコンパイラパスを取得するようになりました。
    • ccompilerPathという新しいヘルパー関数が追加されました。この関数は、指定された環境変数とデフォルトコマンドに基づいて、コンパイラのパスを決定します。
  • src/make.bash:
    • CC_FOR_TARGETCXX_FOR_TARGETに関する説明がコメントとして追加されました。
    • ホスト向けのビルドとターゲット向けのビルドで、CCCC_FOR_TARGETを適切に設定するロジックが変更されました。
  • src/pkg/go/build/build.go:
    • defaultContext()関数内で、クロスコンパイル時にCGO_ENABLEDがデフォルトで無効になるようにロジックが変更されました。

コアとなるコードの解説

src/cmd/dist/build.c の変更点

--- a/src/cmd/dist/build.c
+++ b/src/cmd/dist/build.c
@@ -166,14 +167,23 @@ init(void)
 	}
 	defaultcc = btake(&b);
 
-	xgetenv(&b, "CXX");
+	xgetenv(&b, "CC_FOR_TARGET");
 	if(b.len == 0) {
-		if(defaultclang)
-			bprintf(&b, "clang++");
-		else
-			bprintf(&b, "g++");
+		bprintf(&b, defaultcc);
 	}
-	defaultcxx = btake(&b);
+	defaultcctarget = btake(&b);
+
+	xgetenv(&b, "CXX_FOR_TARGET");
+	if(b.len == 0) {
+		xgetenv(&b, "CXX");
+		if(b.len == 0) {
+			if(defaultclang)
+				bprintf(&b, "clang++");
+			else
+				bprintf(&b, "g++");
+		}
+	}
+	defaultcxxtarget = btake(&b);
 
 	xsetenv("GOROOT", goroot);
 	xsetenv("GOARCH", goarch);
@@ -1537,6 +1547,7 @@ cmdenv(int argc, char **argv)\n 		usage();\n 
 	xprintf(format, "CC", defaultcc);\n
+	xprintf(format, "CC_FOR_TARGET", defaultcctarget);\n
 	xprintf(format, "GOROOT", goroot);\n
 	xprintf(format, "GOBIN", gobin);\n
 	xprintf(format, "GOARCH", goarch);\n

このスニペットは、src/cmd/dist/build.cinit()関数における変更を示しています。

  • 以前はCXX環境変数のみをチェックしてdefaultcxxを設定していましたが、変更後はCC_FOR_TARGETCXX_FOR_TARGETを優先的にチェックするようになりました。
  • CC_FOR_TARGETが設定されていない場合、defaultcc(ホスト向けのCコンパイラ)がdefaultcctargetのデフォルト値として使用されます。これは、ターゲット向けのCコンパイラが明示的に指定されていない場合、ホストのCコンパイラがフォールバックとして使われることを意味します。
  • CXX_FOR_TARGETが設定されていない場合、まずCXXがチェックされ、それも設定されていなければclang++またはg++がデフォルトとして使用されます。
  • cmdenv()関数では、CC_FOR_TARGETも環境変数として出力されるようになり、Goのビルドプロセス全体でこの新しい変数が利用可能になります。

src/cmd/go/build.go の変更点

--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -1708,42 +1708,42 @@ func (gcToolchain) ld(b *builder, p *Package, out string, allactions []*action,\n 	if buildContext.InstallSuffix != "" {\n 		ldflags = append(ldflags, "-installsuffix", buildContext.InstallSuffix)\n 	}\n-	if cxx {\n-		// The program includes C++ code.  If the user has not\n-		// specified the -extld option, then default to\n-		// linking with the compiler named by the CXX\n-		// environment variable, or g++ if CXX is not set.\n-		extld := false\n-		for _, f := range ldflags {\n-			if f == "-extld" || strings.HasPrefix(f, "-extld=") {\n-				extld = true\n-				break\n-			}\n-		}\n-		if !extld {\n-			compiler := strings.Fields(os.Getenv("CXX"))\n-			if len(compiler) == 0 {\n-				compiler = []string{"g++"}\n-			}\n-			ldflags = append(ldflags, "-extld="+compiler[0])\n-			if len(compiler) > 1 {\n-				extldflags := false\n-				add := strings.Join(compiler[1:], " ")\n-				for i, f := range ldflags {\n-					if f == "-extldflags" && i+1 < len(ldflags) {\n-						ldflags[i+1] = add + " " + ldflags[i+1]\n-						extldflags = true\n-						break\n-					} else if strings.HasPrefix(f, "-extldflags=") {\n-						ldflags[i] = "-extldflags=" + add + " " + ldflags[i][len("-extldflags="):]\n-						extldflags = true\n-						break\n-					}\n-				}\n-				if !extldflags {\n-					ldflags = append(ldflags, "-extldflags="+add)\n-				}\n-			}\n-		}\n-	}\n+	// If the user has not specified the -extld option, then specify the\n+	// appropriate linker. In case of C++ code, use the compiler named\n+	// by the CXX environment variable or defaultCXX if CXX is not set.\n+	// Else, use the CC environment variable and defaultCC as fallback.\n+	extld := false\n+	for _, f := range ldflags {\n+		if f == "-extld" || strings.HasPrefix(f, "-extld=") {\n+			extld = true\n+			break\n+		}\n+	}\n+	if !extld {\n+		var compiler []string\n+		if cxx {\n+			compiler = ccompilerPath("CXX", defaultCXX)\n+		} else {\n+			compiler = ccompilerPath("CC", defaultCC)\n+		}\n+		ldflags = append(ldflags, "-extld="+compiler[0])\n+		if len(compiler) > 1 {\n+			extldflags := false\n+			add := strings.Join(compiler[1:], " ")\n+			for i, f := range ldflags {\n+				if f == "-extldflags" && i+1 < len(ldflags) {\n+					ldflags[i+1] = add + " " + ldflags[i+1]\n+					extldflags = true\n+					break\n+				} else if strings.HasPrefix(f, "-extldflags=") {\n+					ldflags[i] = "-extldflags=" + add + " " + ldflags[i][len("-extldflags="):]\n+					extldflags = true\n+					break\n+				}\n+			}\n+			if !extldflags {\n+				ldflags = append(ldflags, "-extldflags="+add)\n+			}\n+		}\n+	}\n 	return b.run(".", p.ImportPath, nil, tool(archChar+"l"), "-o", out, importArgs, swigArg, ldflags, mainpkg)\n@@ -2039,6 +2036,16 @@ func envList(key string) []string {\n 	return strings.Fields(os.Getenv(key))\n }\n \n+// ccompilerCmd returns the compilerpath for the given environment\n+// variable and using the default command when the variable is empty.\n+func ccompilerPath(envvar, defcmd string) []string {\n+	compiler := envList(envvar)\n+	if len(compiler) == 0 {\n+		compiler = strings.Fields(defcmd)\n+	}\n+	return compiler\n+}\n+\n var cgoRe = regexp.MustCompile(`[/\\\\:]`)\n \n var (\n```

このスニペットは、`src/cmd/go/build.go`の`gcToolchain.ld`関数における外部リンカの選択ロジックの変更と、新しく追加された`ccompilerPath`ヘルパー関数を示しています。
*   以前はC++コードが含まれる場合にのみ`CXX`を考慮していましたが、変更後はCまたはC++のどちらのコードが含まれるかに応じて、`ccompilerPath`関数を使って適切なコンパイラ(`CC_FOR_TARGET`または`CXX_FOR_TARGET`を考慮)を選択するようになりました。
*   `ccompilerPath`関数は、指定された環境変数(例: "CC_FOR_TARGET")からコンパイラパスを取得し、もし環境変数が空であればデフォルトのコマンド(例: `defaultcctarget`)を使用します。これにより、クロスコンパイル時にターゲットアーキテクチャ向けのC/C++コンパイラが正しくリンカとして使用されるようになります。

### `src/pkg/go/build/build.go` の変更点

```diff
--- a/src/pkg/go/build/build.go
+++ b/src/pkg/go/build/build.go
@@ -303,8 +303,7 @@ func defaultContext() Context {\n 	case "0":\n 		c.CgoEnabled = false\n 	default:\n-		// golang.org/issue/5141\n-		// cgo should be disabled for cross compilation builds\n+		// cgo must be explicitly enabled for cross compilation builds\n 		if runtime.GOARCH == c.GOARCH && runtime.GOOS == c.GOOS {\n 			c.CgoEnabled = cgoEnabled[c.GOOS+"/"+c.GOARCH]\n 			break\n```

このスニペットは、`src/pkg/go/build/build.go`の`defaultContext()`関数における`CgoEnabled`のデフォルト挙動の変更を示しています。
*   以前のコメントは「cgoはクロスコンパイルビルドでは無効にすべき」と示唆していましたが、コードは必ずしもそれを強制していませんでした。
*   変更後、`runtime.GOARCH == c.GOARCH && runtime.GOOS == c.GOOS`(つまり、ホストとターゲットが同じ場合)でない限り、`CgoEnabled`はデフォルトで`false`になります。これにより、クロスコンパイル時にCgoを有効にするには、ユーザーが明示的に`CGO_ENABLED=1`を設定する必要があるという、より厳格なポリシーが適用されます。

## 関連リンク

*   Go CL 57100043: [https://golang.org/cl/57100043](https://golang.org/cl/57100043)
*   Go Issue 4714: [https://golang.org/issue/4714](https://golang.org/issue/4714)

## 参考にした情報源リンク

*   Go言語の公式ドキュメント (Cgo, Cross-compilationに関する情報)
*   Go言語のソースコード (上記コミットの変更点)
*   Go言語のIssueトラッカー (Issue 4714)
*   一般的なクロスコンパイルとC/C++コンパイラの概念に関する知識