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

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

このコミットは、Go言語のビルドシステムにおいて、Cgo(C言語との連携機能)が有効になっている場合にクロスコンパイル(異なるOSやアーキテクチャ向けにコンパイルすること)を無効にする変更を導入しています。これにより、クロスコンパイル時のCgoに関連するビルドエラーや複雑さを回避し、ビルドプロセスの安定性を向上させることが目的です。

コミット

commit 949ae8cced70fdd06cdd220e1e06467bb332d91c
Author: Dave Cheney <dave@cheney.net>
Date:   Wed Apr 3 19:13:37 2013 +1100

    go/build: disable cgo when cross compiling
    
    Fixes #5141.
    
    R=golang-dev, minux.ma, ality, bradfitz
    CC=golang-dev
    https://golang.org/cl/8134043

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

https://github.com/golang/go/commit/949ae8cced70fdd06cdd220e1e06467bb332d91c

元コミット内容

go/build: disable cgo when cross compiling

Fixes #5141.

R=golang-dev, minux.ma, ality, bradfitz
CC=golang-dev
https://golang.org/cl/8134043

変更の背景

Go言語は、その強力なクロスコンパイル機能で知られています。単一の環境で異なるオペレーティングシステムやCPUアーキテクチャ向けのバイナリを生成できることは、Goの大きな利点の一つです。しかし、Cgo(GoプログラムからC言語のコードを呼び出すためのメカニズム)を使用している場合、このクロスコンパイルが複雑になるという問題がありました。

Cgoは、GoのランタイムとCのランタイム(通常はglibcなどのC標準ライブラリ)をリンクすることで機能します。クロスコンパイルを行う場合、ターゲットとなるOSとアーキテクチャに対応するCコンパイラとCライブラリが必要になります。これは、ホスト環境(コンパイルを実行する環境)に加えて、ターゲット環境のC開発ツールチェーンをセットアップする必要があることを意味し、非常に手間がかかります。

Goのビルドシステムは、GOOS(ターゲットOS)とGOARCH(ターゲットアーキテクチャ)の環境変数に基づいてビルドを調整しますが、Cgoが有効な場合は、これらの環境変数がホスト環境と異なる場合に問題が発生していました。具体的には、ターゲット環境のCコンパイラやライブラリが見つからない、あるいは互換性がないためにビルドが失敗するという事象が発生していました。

このコミットは、このようなクロスコンパイル時のCgoに関する問題を解決するために導入されました。Goのビルドシステムがクロスコンパイルを検出した場合に、自動的にCgoを無効にすることで、ユーザーが手動でCgoを無効にする手間を省き、よりスムーズなクロスコンパイル体験を提供することを目的としています。これにより、Goのビルドプロセスの堅牢性が向上し、Cgoを使用するプロジェクトでもクロスコンパイルがより予測可能になります。

前提知識の解説

このコミットの理解には、以下のGo言語およびビルドに関する基本的な知識が必要です。

  1. Go言語 (Golang): Googleによって開発されたオープンソースのプログラミング言語です。シンプルさ、効率性、並行処理のサポートが特徴で、システムプログラミング、Webサービス、CLIツールなどで広く利用されています。

  2. Cgo: Go言語の機能の一つで、GoプログラムからC言語の関数を呼び出したり、C言語のコードをGoプログラムに組み込んだりするためのメカニズムです。Cgoを使用すると、既存のCライブラリをGoプロジェクトで再利用したり、Goでは実装が難しい低レベルの操作を行ったりすることができます。Cgoを使用するGoプログラムは、Goコンパイラだけでなく、Cコンパイラ(通常はGCCやClang)も必要とします。

  3. クロスコンパイル (Cross-compilation): あるコンピュータアーキテクチャ(ホスト)上で、別のコンピュータアーキテクチャ(ターゲット)で実行可能なバイナリを生成するプロセスです。例えば、macOS上のIntelチップを搭載したコンピュータで、Linux上のARMチップ向けに実行ファイルをコンパイルするようなケースです。Go言語は、GOOSGOARCH環境変数を設定するだけで簡単にクロスコンパイルできることで知られています。

  4. GOOS 環境変数: Goのビルド時にターゲットとなるオペレーティングシステムを指定する環境変数です。例: linux, windows, darwin (macOS)。

  5. GOARCH 環境変数: Goのビルド時にターゲットとなるCPUアーキテクチャを指定する環境変数です。例: amd64, arm, arm64, 386

  6. runtime パッケージ: Goの標準ライブラリの一部で、Goランタイムに関する情報(現在のOS、アーキテクチャなど)を提供するパッケージです。

    • runtime.GOOS: 現在のGoプログラムが実行されているオペレーティングシステムの名前(例: "linux", "windows", "darwin")を文字列で返します。
    • runtime.GOARCH: 現在のGoプログラムが実行されているCPUアーキテクチャの名前(例: "amd64", "arm64")を文字列で返します。
  7. go/build パッケージ: Goの標準ライブラリの一部で、Goのビルドプロセスに関する情報やユーティリティを提供するパッケージです。このパッケージは、Goのソースコードを解析し、ビルドタグ、パッケージの依存関係、Cgoの有効/無効などを決定するために使用されます。このコミットで変更されている build.go ファイルは、このパッケージの一部です。

これらの概念を理解することで、コミットがGoのビルドシステムにどのように影響し、Cgoとクロスコンパイルの間の相互作用をどのように改善しているかを深く把握できます。

技術的詳細

このコミットは、Goのビルドシステムにおける go/build パッケージの defaultContext() 関数に修正を加えています。この関数は、Goのビルドコンテキスト(ビルドに必要な環境情報)を初期化する役割を担っています。特に、Cgoが有効であるかどうかを決定するロジックに焦点を当てています。

Goのビルドシステムは、CGO_ENABLED 環境変数によってCgoの有効/無効を制御できます。

  • CGO_ENABLED=1 (デフォルト): Cgoが有効になります。
  • CGO_ENABLED=0: Cgoが無効になります。

defaultContext() 関数内では、まず CGO_ENABLED 環境変数の値を確認します。

  • もし CGO_ENABLED が明示的に 0 に設定されていれば、Cgoは無効になります。
  • それ以外の場合、Goのビルドシステムは内部の cgoEnabled マップ(GOOS/GOARCH の組み合わせに対してCgoがデフォルトで有効かどうかを定義するマップ)を参照してCgoの有効性を決定します。

このコミットが導入する主要な変更は、この「それ以外の場合」のロジックにあります。変更前は、CGO_ENABLED0 でない限り、常に cgoEnabled[c.GOOS+"/"+c.GOARCH] の値に基づいてCgoの有効性が決定されていました。これは、クロスコンパイルの場合でも、ターゲットの GOOS/GOARCH の組み合わせでCgoがデフォルトで有効であれば、Cgoが有効になってしまうことを意味します。

変更後は、cgoEnabled マップを参照する前に、現在の実行環境(ホスト)の GOOSGOARCH が、ビルドターゲットの c.GOOSc.GOARCH と一致するかどうかを runtime.GOARCH == c.GOARCH && runtime.GOOS == c.GOOS で確認します。

  • 一致する場合 (ホストとターゲットが同じ): これは通常のビルド(クロスコンパイルではない)を意味します。この場合、以前と同様に cgoEnabled[c.GOOS+"/"+c.GOARCH] の値に基づいてCgoの有効性が決定されます。つまり、通常のビルドではCgoの動作は変わりません。
  • 一致しない場合 (ホストとターゲットが異なる): これはクロスコンパイルを意味します。この場合、c.CgoEnabled は強制的に false に設定されます。つまり、クロスコンパイル時にはCgoが自動的に無効になります。

この変更により、Goのビルドシステムはクロスコンパイルを自動的に検出し、Cgoを無効にすることで、CコンパイラやCライブラリの不一致によるビルドエラーを防ぎます。これにより、ユーザーはクロスコンパイル時に明示的に CGO_ENABLED=0 を設定する必要がなくなり、ビルドの信頼性と利便性が向上します。

この修正は、Goのビルドシステムがより賢く、ユーザーフレンドリーになるための重要なステップであり、特に組み込みシステムや異なるプラットフォーム向けのGoアプリケーション開発において、開発体験を大きく改善します。

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

--- a/src/pkg/go/build/build.go
+++ b/src/pkg/go/build/build.go
@@ -301,7 +301,13 @@ func defaultContext() Context {
 	case "0":
 		c.CgoEnabled = false
 	default:
-		c.CgoEnabled = cgoEnabled[c.GOOS+"/"+c.GOARCH]
+		// golang.org/issue/5141
+		// cgo should be disabled for cross compilation builds
+		if runtime.GOARCH == c.GOARCH && runtime.GOOS == c.GOOS {
+			c.CgoEnabled = cgoEnabled[c.GOOS+"/"+c.GOARCH]
+			break
+		}
+		c.CgoEnabled = false
 	}
 
 	return c

コアとなるコードの解説

変更は src/pkg/go/build/build.go ファイルの defaultContext() 関数内で行われています。この関数は、Goのビルドコンテキストを初期化し、特にCgoの有効/無効を決定するロジックを含んでいます。

変更前のコードは以下のようになっていました。

	case "0":
		c.CgoEnabled = false
	default:
		c.CgoEnabled = cgoEnabled[c.GOOS+"/"+c.GOARCH]

これは、CGO_ENABLED 環境変数が "0" でない場合(つまり、Cgoを明示的に無効にしていない場合)、cgoEnabled マップ(GOOS/GOARCH の組み合わせでCgoがデフォルトで有効かどうかを定義する内部マップ)の値に基づいて c.CgoEnabled を設定していました。

変更後のコードは以下のようになっています。

	case "0":
		c.CgoEnabled = false
	default:
		// golang.org/issue/5141
		// cgo should be disabled for cross compilation builds
		if runtime.GOARCH == c.GOARCH && runtime.GOOS == c.GOOS {
			c.CgoEnabled = cgoEnabled[c.GOOS+"/"+c.GOARCH]
			break
		}
		c.CgoEnabled = false

この変更のポイントは default: ブロック内の新しい if ステートメントです。

  1. // golang.org/issue/5141

    • このコメントは、この変更がGoのIssue #5141を修正するためのものであることを示しています。
  2. // cgo should be disabled for cross compilation builds

    • このコメントは、クロスコンパイルビルドではCgoを無効にすべきであるという変更の意図を明確に示しています。
  3. if runtime.GOARCH == c.GOARCH && runtime.GOOS == c.GOOS { ... }

    • これがこのコミットの核心部分です。
    • runtime.GOARCH は、現在Goプログラムが実行されている(つまり、コンパイルが行われているホスト環境の)CPUアーキテクチャを示します。
    • runtime.GOOS は、現在Goプログラムが実行されている(ホスト環境の)オペレーティングシステムを示します。
    • c.GOARCH は、ビルドターゲットのCPUアーキテクチャを示します。
    • c.GOOS は、ビルドターゲットのオペレーティングシステムを示します。
    • この if 条件は、ホスト環境のOSとアーキテクチャが、ビルドターゲットのOSとアーキテクチャと完全に一致するかどうかをチェックしています。
      • 一致する場合: これは通常のビルド(クロスコンパイルではない)を意味します。この場合、ブロック内のコードが実行されます。
        • c.CgoEnabled = cgoEnabled[c.GOOS+"/"+c.GOARCH]:以前と同様に、cgoEnabled マップの値に基づいてCgoの有効性を設定します。
        • breakswitch ステートメントを終了します。
      • 一致しない場合: これはクロスコンパイルを意味します。この場合、if ブロックはスキップされ、その後の行が実行されます。
        • c.CgoEnabled = false:Cgoを強制的に無効にします。

このロジックにより、Goのビルドシステムは、ホストとターゲットが異なる(クロスコンパイルである)場合に自動的にCgoを無効にするようになります。これにより、クロスコンパイル時のCgoに関連するビルドエラーが回避され、ビルドプロセスの信頼性が向上します。

関連リンク

  • Go Change-ID: 8134043 (GoのコードレビューシステムであるGerritの変更ID)
  • Go Issue #5141: このコミットが修正したとされるGoのIssue。ただし、現在のGoのIssueトラッカーでは直接見つからない可能性があります。これは、GoのIssueトラッカーが時間の経過とともに移行されたり、Issue番号が再割り当てされたりする可能性があるためです。

参考にした情報源リンク

(注: Go Issue #5141は、現在のGoのIssueトラッカーでは直接検索できませんでした。これは、Issueトラッカーの移行や古いIssueのアーカイブによるものと考えられます。しかし、コミットメッセージに明記されているため、当時の問題として存在していたことは確かです。)