[インデックス 17000] ファイルの概要
このコミットは、Go言語のビルドシステムにおいて、OS X 10.8以降の環境でCコンパイラとしてGCCの代わりにClangを使用するように変更するものです。これは、AppleがOS XにおけるGCCのサポートを非推奨とし、Clangへの移行を推進している状況に対応するための重要な変更です。
コミット
commit 2ddb672ddcbb050521a2c031630c6dc884a78314
Author: Russ Cox <rsc@golang.org>
Date: Fri Aug 2 14:58:27 2013 -0400
build: on OS X 10.8 and later, use clang instead of gcc
Fixes #5822.
Will no doubt cause other problems, but Apple has forced our hand.
R=golang-dev, bradfitz, khr
CC=golang-dev
https://golang.org/cl/12350044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2ddb672ddcbb050521a2c031630c6dc884a78314
元コミット内容
build: on OS X 10.8 and later, use clang instead of gcc
Fixes #5822.
Will no doubt cause other problems, but Apple has forced our hand.
変更の背景
この変更の主な背景は、AppleがOS X(現在のmacOS)において、開発ツールチェーンのデフォルトC/C++/Objective-CコンパイラをGCCからClangへと移行したことにあります。特にOS X 10.8 (Mountain Lion) 以降では、Xcodeに同梱されるgcc
コマンドが実質的にClangへのシンボリックリンクまたはラッパーとなっており、従来のGCCとは異なる振る舞いをすることがありました。
Go言語のビルドプロセス、特にCgo(GoとCの相互運用機能)を使用する際には、システムにインストールされているCコンパイラに依存します。Appleのこの変更により、Goのビルドシステムが従来のGCCを前提とした動作を続けると、OS X 10.8以降の環境でビルドエラーや予期せぬ問題が発生する可能性がありました。
コミットメッセージにある「Apple has forced our hand.」という表現は、Goチームがこの変更を避けられない状況に追い込まれたことを示唆しています。GoのビルドシステムがOS Xの新しい開発環境に適合し、ユーザーがスムーズにGoをビルド・実行できるようにするために、Clangへの対応が必須となりました。また、関連するIssue #5822は、この問題がGoコミュニティ内で認識され、解決が求められていたことを示しています。
前提知識の解説
Cgo
Cgoは、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGoの機能です。Goの標準ライブラリの一部や、外部のCライブラリを利用するGoアプリケーションで広く使われています。Cgoを使用するGoプログラムをビルドする際には、Goコンパイラだけでなく、C/C++コンパイラ(通常はGCCやClang)も必要となります。Cgoは、GoコードとCコードをリンクするために、Cコンパイラを呼び出してCコードをコンパイルし、その結果をGoのビルドプロセスに統合します。
GCC (GNU Compiler Collection)
GCCは、GNUプロジェクトによって開発された、様々なプログラミング言語に対応するコンパイラ群です。C、C++、Objective-C、Fortran、Ada、Goなどの言語をサポートしています。長らくUnix系システムやLinuxディストリビューションの標準的なC/C++コンパイラとして広く利用されてきました。
Clang
Clangは、LLVMプロジェクトの一部として開発されているC、C++、Objective-C、Objective-C++コンパイラのフロントエンドです。Clangは、GCCと比較して、より高速なコンパイル、より優れたエラーメッセージ、よりモジュール化されたアーキテクチャなどの利点を持つとされています。Appleは、Xcode 4.2以降でClangをデフォルトのコンパイラとして採用し、OS Xの開発環境における主要なコンパイラとして推進してきました。
cmd/dist
cmd/dist
は、GoのソースコードからGoツールチェーン全体をビルドするためのプログラムです。Goのコンパイラ、リンカ、アセンブラ、その他のツール(go
コマンド自体を含む)を構築する役割を担っています。Goのビルドプロセスにおいて、どのCコンパイラを使用するか、どのようなビルドフラグを渡すかといった低レベルな設定を管理する重要な部分です。
uname
コマンドとOSバージョン
Unix系システムでは、uname
コマンドを使用してシステム情報を取得できます。特にuname -r
はカーネルのリリースバージョンを出力します。OS Xの場合、このリリースバージョンはOS Xのバージョン番号と関連付けられています。例えば、OS X 10.6はuname -r
で10.x.x
、OS X 10.8は12.x.x
といった形式で出力されます。このコミットでは、uname -r
の出力からOS Xのバージョンを判断し、それに基づいて使用するCコンパイラを切り替えるロジックが導入されています。
技術的詳細
このコミットは、GoのビルドシステムがOS X環境でCコンパイラを選択するロジックを根本的に変更しています。
-
Cコンパイラの動的な選択:
src/cmd/dist/unix.c
において、uname -r
の出力からOS Xのバージョンを解析し、osx
変数にOS Xのメジャーバージョン(例: 10.8なら8)を格納するようになりました。osx >= 8
(OS X 10.8以降)の場合、defaultclang
という新しいグローバル変数をtrue
に設定します。- この
defaultclang
の値は、src/cmd/dist/build.c
のinit
関数内で参照され、defaultcc
という新しいグローバル変数の初期値に影響を与えます。defaultclang
がtrue
であればclang
が、そうでなければgcc
がdefaultcc
に設定されます。 defaultcc
は、Goツールチェーンのビルド時に使用されるデフォルトのCコンパイラ名として、zdefaultcc.go
というファイルに書き込まれます。
-
Cgoにおけるコンパイラ選択の変更:
src/cmd/cgo/gcc.go
内のgccName
関数がgccBaseCmd
関数にリファクタリングされました。gccBaseCmd
は、環境変数CC
、GCC
の順にチェックし、設定されていなければ、cmd/dist
によって生成されたdefaultCC
(zdefaultcc.go
に定義されている)を使用するように変更されました。これにより、CgoがGoツールチェーン全体のビルド設定と一貫したコンパイラを使用するようになります。gccCmd
やgccDefines
といったCgoの内部関数も、p.gccName()
の代わりにp.gccBaseCmd()
を使用するように更新され、コンパイラ名の取得方法が統一されました。
-
__gcc_struct__
属性の扱い:src/cmd/cgo/out.go
では、__gcc_struct__
属性の使用に関するロジックが変更されました。これは、特定のGCCのバグ(http://gcc.gnu.org/PR52991)やGoのIssue #5603に対応するためのもので、clang
を使用している場合はこの属性を付与しないように調整されています。p.gccName()
の代わりにp.gccBaseCmd()[0]
を参照することで、新しいコンパイラ選択ロジックに対応しています。
-
go build
コマンドの変更:src/cmd/go/build.go
では、go build
コマンドがCコンパイラを決定する際に、ハードコードされた"gcc"
ではなく、defaultCC
(zdefaultcc.go
に定義されている)を使用するように変更されました。これにより、go build
もOS Xのバージョンに応じた適切なコンパイラを選択するようになります。
-
テストスクリプトの調整:
src/run.bash
では、cgo -godefs
のテスト実行条件に[ "$GOOS" == darwin ]
が追加されました。これは、OS X上でClangを使用した場合にcgo -godefs
がうまく動作しない可能性があるため、OS Xではこのテストをスキップする、または異なる振る舞いを許容するための調整と考えられます。
これらの変更により、GoのビルドシステムはOS Xのバージョンを検出し、それに応じて自動的に適切なCコンパイラ(OS X 10.8以降ではClang)を選択するようになり、ビルドの互換性と安定性が向上しました。
コアとなるコードの変更箇所
src/cmd/cgo/gcc.go
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -646,19 +646,20 @@ func (p *Package) rewriteRef(f *File) {
}
}
-// gccName returns the name of the compiler to run. Use $CC if set in
-// the environment, otherwise just "gcc".
-
-func (p *Package) gccName() string {
+// gccBaseCmd returns the start of the compiler command line.
+// It uses $CC if set, or else $GCC, or else the compiler recorded
+// during the initial build as defaultCC.
+// defaultCC is defined in zdefaultcc.go, written by cmd/dist.
+func (p *Package) gccBaseCmd() []string {
// Use $CC if set, since that's what the build uses.
- if ret := os.Getenv("CC"); ret != "" {
+ if ret := strings.Fields(os.Getenv("CC")); len(ret) > 0 {
return ret
}
- // Fall back to $GCC if set, since that's what we used to use.
- if ret := os.Getenv("GCC"); ret != "" {
+ // Try $GCC if set, since that's what we used to use.
+ if ret := strings.Fields(os.Getenv("GCC")); len(ret) > 0 {
return ret
}
- return "gcc"
+ return strings.Fields(defaultCC)
}
// gccMachine returns the gcc -m flag to use, either "-m32", "-m64" or "-marm".
@@ -681,17 +682,16 @@ func gccTmp() string {
// gccCmd returns the gcc command line to use for compiling
// the input.
func (p *Package) gccCmd() []string {
- c := []string{
- p.gccName(),
+ c := append(p.gccBaseCmd(),
"-Wall", // many warnings
"-Werror", // warnings are errors
- "-o" + gccTmp(), // write object to tmp
+ "-o"+gccTmp(), // write object to tmp
"-gdwarf-2", // generate DWARF v2 debugging symbols
"-fno-eliminate-unused-debug-types", // gets rid of e.g. untyped enum otherwise
"-c", // do not link
"-xc", // input language is C
- }
- if strings.Contains(p.gccName(), "clang") {
+ )
+ if strings.Contains(c[0], "clang") {
c = append(c,
"-ferror-limit=0",
// Apple clang version 1.7 (tags/Apple/clang-77) (based on LLVM 2.9svn)
@@ -800,7 +800,7 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)
// #defines that gcc encountered while processing the input
// and its included files.
func (p *Package) gccDefines(stdin []byte) string {
- base := []string{p.gccName(), "-E", "-dM", "-xc"}
+ base := append(p.gccBaseCmd(), "-E", "-dM", "-xc")
base = append(base, p.gccMachine()...)\n stdout, _ := runGcc(stdin, append(append(base, p.GccOptions...), "-"))
return stdout
src/cmd/dist/build.c
--- a/src/cmd/dist/build.c
+++ b/src/cmd/dist/build.c
@@ -26,8 +26,9 @@ char *tooldir;
char *gochar;
char *goversion;
char *slash; // / for unix, \ for windows
-\n-bool rebuildall = 0;
+char *defaultcc;
+bool rebuildall;
+bool defaultclang;
static bool shouldbuild(char*, char*);
static void copy(char*, char*, int);
@@ -146,6 +147,20 @@ init(void)
if(!streq(goextlinkenabled, "0") && !streq(goextlinkenabled, "1"))
fatal("unknown $GO_EXTLINK_ENABLED %s", goextlinkenabled);
}
+
+ xgetenv(&b, "CC");
+ if(b.len == 0) {
+ // Use clang on OS X, because gcc is deprecated there.
+ // Xcode for OS X 10.9 Mavericks will ship a fake "gcc" binary that
+ // actually runs clang. We prepare different command
+ // lines for the two binaries, so it matters what we call it.
+ // See golang.org/issue/5822.
+ if(defaultclang)
+ bprintf(&b, "clang");
+ else
+ bprintf(&b, "gcc");
+ }
+ defaultcc = btake(&b);
xsetenv("GOROOT", goroot);
xsetenv("GOARCH", goarch);
@@ -525,6 +540,9 @@ static struct {
"../ld/*",
"enam.c",
}},
+ {"cmd/go", {
+ "zdefaultcc.go",
+ }},
{"cmd/", {
"$GOROOT/pkg/obj/$GOOS_$GOARCH/libmach.a",
"$GOROOT/pkg/obj/$GOOS_$GOARCH/libbio.a",
@@ -557,6 +575,7 @@ static struct {
{"opnames.h", gcopnames},
{"enam.c", mkenam},
{"zasm_", mkzasm},
+ {"zdefaultcc.go", mkzdefaultcc},
{"zsys_", mkzsys},
{"zgoarch_", mkzgoarch},
{"zgoos_", mkzgoos},
@@ -616,10 +635,7 @@ install(char *dir)\n
// set up gcc command line on first run.\n if(gccargs.len == 0) {\n-\t\txgetenv(&b, "CC");\n-\t\tif(b.len == 0)\n-\t\t\tbprintf(&b, "gcc");\n-\t\tclang = contains(bstr(&b), "clang");\n+\t\tbprintf(&b, "%s", defaultcc);\n \tsplitfields(&gccargs, bstr(&b));\n \tfor(i=0; i<nelem(proto_gccargs); i++)\n \t\tvadd(&gccargs, proto_gccargs[i]);\n@@ -1443,6 +1459,7 @@ cmdenv(int argc, char **argv)\n if(argc > 0)\n \tusage();\n \n+\txprintf(format, "CC", defaultcc);\n xprintf(format, "GOROOT", goroot);\n xprintf(format, "GOBIN", gobin);\n xprintf(format, "GOARCH", goarch);
src/cmd/dist/unix.c
--- a/src/cmd/dist/unix.c
+++ b/src/cmd/dist/unix.c
@@ -651,6 +651,7 @@ int
main(int argc, char **argv)
{
Buf b;
+ int osx;
struct utsname u;
setvbuf(stdout, nil, _IOLBF, 0);
@@ -700,17 +701,23 @@ main(int argc, char **argv)
if(strcmp(gohostarch, "arm") == 0)
maxnbg = 1;
-\t// The OS X 10.6 linker does not support external
-\t// linking mode; see
-\t// https://code.google.com/p/go/issues/detail?id=5130 .
-\t// The mapping from the uname release field to the OS X
-\t// version number is complicated, but basically 10 or under is
-\t// OS X 10.6 or earlier.
+\t// The OS X 10.6 linker does not support external linking mode.
+\t// See golang.org/issue/5130.
+\t//
+\t// OS X 10.6 does not work with clang either, but OS X 10.9 requires it.
+\t// It seems to work with OS X 10.8, so we default to clang for 10.8 and later.
+\t// See golang.org/issue/5822.
+\t//
+\t// Roughly, OS X 10.N shows up as uname release (N+4),
+\t// so OS X 10.6 is uname version 10 and OS X 10.8 is uname version 12.
if(strcmp(gohostos, "darwin") == 0) {
if(uname(&u) < 0)
fatal("uname: %s", strerror(errno));
-\t\tif(u.release[1] == '.' || hasprefix(u.release, "10"))
+\t\tosx = atoi(u.release) - 4;
+\t\tif(osx <= 6)
goextlinkenabled = "0";
+\t\tif(osx >= 8)
+\t\t\tdefaultclang = 1;
}
init();
コアとなるコードの解説
src/cmd/cgo/gcc.go
の変更
gccName
からgccBaseCmd
へのリファクタリング: 以前はgccName
という関数が単一の文字列(コンパイラ名)を返していましたが、gccBaseCmd
という関数に変わり、コンパイラコマンドとその引数のスライス([]string
)を返すようになりました。これにより、CC
やGCC
環境変数に複数の単語(例:clang -arch x86_64
)が設定されている場合にも対応できるようになりました。defaultCC
の導入: 環境変数CC
やGCC
が設定されていない場合、以前はハードコードされた"gcc"
を使用していましたが、この変更によりdefaultCC
という新しい変数を使用するようになりました。このdefaultCC
は、cmd/dist
によってビルド時に決定され、zdefaultcc.go
というファイルに書き込まれます。これにより、Cgoのコンパイラ選択がビルドシステム全体の設定と同期されるようになります。clang
検出ロジックの変更:strings.Contains(p.gccName(), "clang")
からstrings.Contains(c[0], "clang")
に変更されました。これは、c
スライスの最初の要素(コンパイラ名)をチェックすることで、clang
固有のフラグ(-ferror-limit=0
など)を追加するかどうかを判断します。
src/cmd/dist/build.c
の変更
defaultcc
とdefaultclang
の導入:char *defaultcc;
とbool defaultclang;
という新しいグローバル変数が追加されました。defaultcc
はGoツールチェーンが使用するデフォルトのCコンパイラ名を保持し、defaultclang
はOS XでClangをデフォルトにするかどうかを示すフラグです。init
関数でのdefaultcc
の決定:init
関数内で、環境変数CC
が設定されていない場合、defaultclang
の値に基づいてdefaultcc
が"clang"
または"gcc"
に設定されるようになりました。これは、OS X 10.9 (Mavericks) でgcc
が実質的にClangのラッパーとなることを見越した対応です。zdefaultcc.go
の生成:cmd/go
のビルドターゲットにzdefaultcc.go
が追加され、mkzdefaultcc
関数が呼び出されるようになりました。この関数は、defaultcc
の値をGoのソースコードとして書き出し、Goのビルドプロセス全体で利用できるようにします。install
関数でのgccargs
初期化の変更:gccargs
(GCCコマンドライン引数)の初期化時に、以前は環境変数CC
を直接読み込んでいましたが、defaultcc
を使用するように変更されました。これにより、ビルドシステム全体で一貫したコンパイラ設定が適用されます。
src/cmd/dist/unix.c
の変更
- OS Xバージョンの検出:
main
関数内で、uname(&u)
で取得したシステム情報からu.release
(カーネルリリースバージョン)を解析し、atoi(u.release) - 4
という計算でOS Xのメジャーバージョン(例: 10.8なら8)をosx
変数に格納するロジックが追加されました。 defaultclang
の設定:osx >= 8
(OS X 10.8以降)の場合にdefaultclang = 1;
が設定されるようになりました。これにより、OS X 10.8以降の環境では、Goのビルドシステムが自動的にClangをデフォルトのCコンパイラとして選択するようになります。- 外部リンキングの無効化: OS X 10.6以前(
osx <= 6
)では、外部リンキングモードがサポートされていないため、goextlinkenabled = "0";
が設定されます。これは既存のロジックですが、OS Xバージョンの検出ロジックと統合されました。
これらの変更により、GoのビルドシステムはOS Xのバージョンを正確に判断し、それに基づいて最適なCコンパイラ(特にClang)を自動的に選択・設定できるようになり、OS X環境でのGoのビルドの信頼性と互換性が大幅に向上しました。
関連リンク
- Go Issue #5822: https://github.com/golang/go/issues/5822
- Go CL 12350044: https://golang.org/cl/12350044
参考にした情報源リンク
- Go Issue #5822 (詳細な議論): https://github.com/golang/go/issues/5822
- Go CL 12350044 (コードレビュー): https://golang.org/cl/12350044
- Apple Developer Documentation (Xcode, Clang, GCCに関する情報): https://developer.apple.com/documentation/ (具体的なURLは変更される可能性があるため、一般的なドキュメントルートを記載)
- LLVM Clang Documentation: https://clang.llvm.org/
- GNU Compiler Collection (GCC) Documentation: https://gcc.gnu.org/
- Go Cgo Documentation: https://go.dev/blog/c-go-is-not-c (Cgoの一般的な情報)
uname
man page (Unix系システム):man uname
(コマンドラインで参照可能)- Go Issue #5130 (OS X 10.6 linker issue): https://github.com/golang/go/issues/5130
- Go Issue #5603 (Cgo
__gcc_struct__
issue): https://github.com/golang/go/issues/5603 - GCC Bug 52991: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991