[インデックス 18753] ファイルの概要
このコミットは、Go言語のビルドシステムの一部である cmd/dist
ツールが、システム環境変数 CFLAGS
および LDFLAGS
を尊重するように変更を加えるものです。これにより、Goのツールチェインをビルドする際に、ユーザーが指定したコンパイラフラグやリンカフラグを適用できるようになり、特定のビルド環境やクロスコンパイルのシナリオにおいて、より柔軟なカスタマイズが可能になります。
コミット
- コミットハッシュ:
202e6153f54e2a2b0d3a51cd0d69754f1ce60580
- Author: Jan Ziak 0xe2.0x9a.0x9b@gmail.com
- Date: Wed Mar 5 14:10:22 2014 -0500
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/202e6153f54e2a2b0d3a51cd0d69754f1ce60580
元コミット内容
cmd/dist: respect system CFLAGS/LDFLAGS
Update #6882.
LGTM=rsc
R=golang-codereviews, rsc
CC=golang-codereviews
https://golang.org/cl/69860055
変更の背景
この変更の背景には、Go言語のビルドプロセスにおける柔軟性の向上が挙げられます。Goのツールチェイン(コンパイラ、リンカなど)はC言語で書かれた部分も多く、これらをビルドする際にはCコンパイラ(通常はGCCやClang)が使用されます。
従来の cmd/dist
は、これらのC言語部分をビルドする際に、Go自身が内部的に定義するコンパイラフラグやリンカフラグのみを使用していました。しかし、特定のシステム環境や、特殊な最適化、デバッグ設定、あるいはクロスコンパイルのシナリオでは、ユーザーが独自のコンパイラフラグ(CFLAGS
)やリンカフラグ(LDFLAGS
)をCコンパイラに渡したい場合があります。
例えば、以下のようなケースが考えられます。
- セキュリティ強化: 特定のセキュリティフラグ(例:
-fstack-protector-all
,-Wl,-z,relro,-z,now
)を適用して、生成されるバイナリのセキュリティを向上させたい場合。 - パフォーマンス最適化: 特定のCPUアーキテクチャに特化した最適化フラグ(例:
-march=native
,-mtune=haswell
)を使用して、パフォーマンスを最大化したい場合。 - デバッグ: デバッグシンボルを含めるためのフラグ(例:
-g
)や、特定の警告を有効にするフラグ(例:-Wall
,-Wextra
)を追加したい場合。 - クロスコンパイル: ターゲット環境に合わせたライブラリパスやインクルードパスを指定する必要がある場合。
このコミットは、これらの要求に応えるため、cmd/dist
がビルド時にシステム環境変数 CFLAGS
と LDFLAGS
の値を読み込み、それをCコンパイラとリンカに渡すようにすることで、Goのビルドプロセスをよりカスタマイズ可能にすることを目的としています。コミットメッセージにある "Update #6882" は、この変更が特定の課題(おそらく、CFLAGS
/LDFLAGS
のサポートに関するもの)に対応していることを示唆しています。
前提知識の解説
1. CFLAGS
と LDFLAGS
CFLAGS
(C Compiler Flags): C言語のソースコードをコンパイルする際に、Cコンパイラ(例: GCC, Clang)に渡されるオプションを指定するための環境変数です。これには、最適化レベル(-O2
,-O3
)、デバッグ情報の有無(-g
)、警告レベル(-Wall
)、インクルードファイルの検索パス(-I/path/to/include
)、マクロ定義(-DDEBUG
)などが含まれます。LDFLAGS
(Linker Flags): コンパイルされたオブジェクトファイルをリンクして実行可能ファイルやライブラリを作成する際に、リンカに渡されるオプションを指定するための環境変数です。これには、ライブラリの検索パス(-L/path/to/lib
)、リンクするライブラリ(-lm
,-lpthread
)、リンカの動作に関するオプション(-Wl,--as-needed
)などが含まれます。
これらの環境変数は、Unix系システムにおけるソフトウェアのビルドにおいて広く利用されており、ビルドプロセスを柔軟に制御するための標準的なメカニズムです。
2. cmd/dist
cmd/dist
は、Go言語のソースコードリポジトリに含まれるツールであり、Goのツールチェイン自体(Goコンパイラ、Goリンカ、Goランタイムなど)をビルドするために使用されます。これは、Go言語で書かれたアプリケーションをビルドする際に使用する go build
コマンドとは異なります。cmd/dist
は、Goのブートストラッププロセス(Go自身をビルドするプロセス)において重要な役割を果たします。
cmd/dist
は、Goのソースコードをダウンロードし、必要なCコンポーネントをコンパイルし、最終的なGoツールチェインを構築する一連のタスクを自動化します。このツールは、Goのバージョンアップや、異なるプラットフォームへの移植(クロスコンパイル)の際に特に重要になります。
3. クロスコンパイル
クロスコンパイルとは、あるコンピュータシステム(ホストシステム)上で、それとは異なるアーキテクチャやオペレーティングシステムを持つ別のコンピュータシステム(ターゲットシステム)向けの実行可能ファイルやライブラリを生成するプロセスです。
Go言語はクロスコンパイルを強力にサポートしており、GOOS
や GOARCH
といった環境変数を設定することで、簡単に異なるプラットフォーム向けのバイナリを生成できます。しかし、Goのツールチェイン自体をビルドする際には、C言語で書かれた部分(例えば、ランタイムの一部やCgo関連のコード)もコンパイルする必要があり、このC言語部分のクロスコンパイルには、ターゲットシステムに対応したCコンパイラやリンカ、そして適切な CFLAGS
/LDFLAGS
の設定が必要となる場合があります。
このコミットは、cmd/dist
がこれらの環境変数を尊重することで、Goツールチェインのクロスコンパイルをより円滑に行えるようにするものです。
技術的詳細
このコミットの主要な変更は、src/cmd/dist/build.c
ファイルに集中しています。このファイルは、GoのビルドプロセスにおけるC言語コンポーネントのコンパイルとリンクを管理する役割を担っています。
変更の核心は以下の点にあります。
-
環境変数の読み込み:
init()
関数内で、CFLAGS
とLDFLAGS
環境変数の値を読み込むための新しい変数が導入されました。char *defaultcflags; char *defaultldflags;
これらの変数は、
xgetenv()
関数を使用して、それぞれCFLAGS
とLDFLAGS
環境変数の値で初期化されます。xgetenv()
は、指定された環境変数の値を読み込み、その結果をバッファに格納するユーティリティ関数です。xgetenv(&b, "CFLAGS"); defaultcflags = btake(&b); xgetenv(&b, "LDFLAGS"); defaultldflags = btake(&b);
これにより、
cmd/dist
が起動される際に、システムに設定されているCFLAGS
とLDFLAGS
の値が取得され、内部的に保持されるようになります。 -
gccargs
の初期化ロジックの変更:install()
関数内で、Cコンパイラに渡す引数を格納するgccargs
ベクタの初期化ロジックが変更されました。 以前は、defaultcc
(デフォルトのCコンパイラ)のみがgccargs
に追加されていましたが、変更後はdefaultcflags
の値も追加されるようになりました。// 変更前 // bprintf(&b, "%s", defaultcc); // splitfields(&gccargs, bstr(&b)); // 変更後 bprintf(&b, "%s %s", defaultcc, defaultcflags); splitfields(&gccargs, bstr(&b));
splitfields()
関数は、スペースで区切られた文字列を個々の引数に分割し、gccargs
ベクタに追加します。これにより、CFLAGS
に含まれる複数のフラグが正しくCコンパイラに渡されるようになります。 -
proto_gccargs2
の導入とフォールバック:CFLAGS
環境変数が設定されていない場合(defaultcflags[0] == '\0'
)、デフォルトの最適化フラグ(-O2
)を適用するための新しい静的配列proto_gccargs2
が導入されました。static char *proto_gccargs2[] = { #if defined(__NetBSD__) && defined(__arm__) #else "-O2", #endif };
これは、NetBSD ARM環境では
-O1
がデフォルトであり、それ以外の環境では-O2
がデフォルトとなるように設計されています。CFLAGS
が空の場合にのみ、このproto_gccargs2
の内容がgccargs
に追加されます。これにより、ユーザーがCFLAGS
を明示的に指定しない場合でも、適切なデフォルト最適化が適用されるようになります。 -
ldargs
の初期化と適用: リンカに渡す引数を格納する新しいベクタldargs
が導入されました。install()
関数内で、defaultldflags
が空でない場合にのみ、ldargs
がdefaultldflags
の値で初期化されます。if(ldargs.len == 0 && defaultldflags[0] != '\0') { bprintf(&b, "%s", defaultldflags); splitfields(&ldargs, bstr(&b)); }
そして、C言語のコマンド(実行可能ファイルのリンク)を実行する際に、
gccargs
に加えてldargs
の内容もリンカに渡されるようになりました。// 変更前 // vcopy(&link, gccargs.p, gccargs.len); // 変更後 vcopy(&link, gccargs.p, gccargs.len); vcopy(&link, ldargs.p, ldargs.len);
これにより、
LDFLAGS
に含まれるリンカオプションが、GoのC言語コンポーネントのリンク時に適用されるようになります。
これらの変更により、Goのビルドシステムは、外部から提供される CFLAGS
と LDFLAGS
を透過的に利用できるようになり、ビルドの柔軟性とカスタマイズ性が大幅に向上しました。
コアとなるコードの変更箇所
src/cmd/dist/build.c
ファイルにおける主要な変更箇所は以下の通りです。
-
新しい変数の宣言:
--- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -27,6 +27,8 @@ char *gochar; char *goversion; char *slash; // / for unix, \ for windows char *defaultcc; +char *defaultcflags; +char *defaultldflags; char *defaultcxxtarget; char *defaultcctarget; bool rebuildall;
-
環境変数
CFLAGS
とLDFLAGS
の読み込み:--- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -169,6 +171,12 @@ init(void)\n }\n defaultcc = btake(&b);\n \n+\txgetenv(&b, "CFLAGS"); +\tdefaultcflags = btake(&b); +\n+\txgetenv(&b, "LDFLAGS"); +\tdefaultldflags = btake(&b); +\n \txgetenv(&b, "CC_FOR_TARGET"); \tif(b.len == 0) { \t\tbprintf(&b, defaultcc);\
-
proto_gccargs2
の定義:--- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -465,12 +473,19 @@ static char *proto_gccargs[] = {\n // GCC 4.5.4 (NetBSD nb1 20120916) on ARM is known to mis-optimize gc/mparith3.c\n // Fix available at http://patchwork.ozlabs.org/patch/64562/.\n "-O1",\n+#endif\n+};\n+\n+// gccargs2 is the second part of gccargs.\n+// it is used if the environment isn't defining CFLAGS.\n+static char *proto_gccargs2[] = {\n+#if defined(__NetBSD__) && defined(__arm__)\n #else\n "-O2",\n #endif\n };\n \n-static Vec gccargs;\n+static Vec gccargs, ldargs;\n \n // deptab lists changes to the default dependencies for a given prefix.\
-
gccargs
の初期化ロジックの変更とproto_gccargs2
の適用:--- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -682,10 +697,14 @@ install(char *dir)\n \n // set up gcc command line on first run.\n if(gccargs.len == 0) {\n-\t\tbprintf(&b, "%s", defaultcc);\n+\t\tbprintf(&b, "%s %s", defaultcc, defaultcflags);\n \t\tsplitfields(&gccargs, bstr(&b)); \t\tfor(i=0; i<nelem(proto_gccargs); i++)\n \t\t\tvadd(&gccargs, proto_gccargs[i]);\n+\t\tif(defaultcflags[0] == '\0') {\n+\t\t\tfor(i=0; i<nelem(proto_gccargs2); i++)\n+\t\t\t\tvadd(&gccargs, proto_gccargs2[i]);\n+\t\t}\ \t\tif(contains(gccargs.p[0], "clang")) {\n \t\t\t// disable ASCII art in clang errors, if possible\n \t\t\tvadd(&gccargs, "-fno-caret-diagnostics");
-
ldargs
の初期化とリンカへの適用:--- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -699,6 +718,10 @@ install(char *dir)\n \t\t\tvadd(&gccargs, "-mmacosx-version-min=10.6");\n \t\t}\n \t}\n+\tif(ldargs.len == 0 && defaultldflags[0] != '\0') {\n+\t\tbprintf(&b, "%s", defaultldflags);\n+\t\tsplitfields(&ldargs, bstr(&b));\n+\t}\
\n \tislib = hasprefix(dir, "lib") || streq(dir, "cmd/cc") || streq(dir, "cmd/gc");\n \tispkg = hasprefix(dir, "pkg"); @@ -742,7 +765,7 @@ install(char *dir)\n \t\ttarg = link.len;\n \t\tvadd(&link, bpathf(&b, "%s/%s%s", tooldir, elem, exe));\n \t} else {\n-\t\t// C command. Use gccargs.\n+\t\t// C command. Use gccargs and ldargs.\n \t\tif(streq(gohostos, "plan9")) {\n \t\t\tvadd(&link, bprintf(&b, "%sl", gohostchar));\n \t\t\tvadd(&link, "-o"); @@ -750,6 +773,7 @@ install(char *dir)\n \t\t\tvadd(&link, bpathf(&b, "%s/%s", tooldir, name));\n \t\t} else {\n \t\t\tvcopy(&link, gccargs.p, gccargs.len);\n+\t\t\tvcopy(&link, ldargs.p, ldargs.len);\n \t\t\tif(sflag)\n \t\t\t\tvadd(&link, "-static");\n \t\t\tvadd(&link, "-o"); ```
コアとなるコードの解説
上記の変更箇所を順に解説します。
-
新しい変数の宣言:
char *defaultcflags;
とchar *defaultldflags;
は、それぞれ環境変数CFLAGS
とLDFLAGS
の値を保持するためのポインタ変数です。これらはbuild.c
ファイルのグローバルスコープで宣言されており、cmd/dist
プロセス全体でアクセス可能です。 -
環境変数
CFLAGS
とLDFLAGS
の読み込み:init()
関数は、cmd/dist
が起動される際に一度だけ呼び出され、初期設定を行う役割を担っています。この関数内で、xgetenv(&b, "CFLAGS");
とxgetenv(&b, "LDFLAGS");
が呼び出されます。xgetenv()
は、指定された環境変数の値を読み込み、その値を一時的なバッファb
に格納します。btake(&b)
は、バッファb
の内容を新しいメモリ領域にコピーし、そのポインタを返します。これにより、環境変数の値がdefaultcflags
とdefaultldflags
に永続的に保存されます。 このステップにより、Goのビルドプロセスは、ユーザーが設定したCFLAGS
とLDFLAGS
の値を認識できるようになります。
-
proto_gccargs2
の定義:proto_gccargs2
は、CFLAGS
環境変数が設定されていない場合に、Cコンパイラに渡されるデフォルトの最適化フラグ(-O2
)を定義する静的配列です。#if defined(__NetBSD__) && defined(__arm__)
のプリプロセッサディレクティブは、NetBSD ARM環境に特化した条件コンパイルを示しています。この環境では、特定のGCCのバグを回避するために-O1
が使用されることが知られており、それ以外の環境では一般的な最適化レベルである-O2
が適用されます。これにより、特定のプラットフォームでの互換性と安定性が確保されます。 -
gccargs
の初期化ロジックの変更とproto_gccargs2
の適用:install()
関数は、Goの各パッケージやツールをビルド・インストールする際に呼び出されます。if(gccargs.len == 0)
の条件は、gccargs
ベクタがまだ初期化されていない場合にのみ、以下の処理を実行することを示しています。bprintf(&b, "%s %s", defaultcc, defaultcflags);
は、デフォルトのCコンパイラ (defaultcc
) と、読み込んだCFLAGS
(defaultcflags
) を結合した文字列をバッファb
に書き込みます。splitfields(&gccargs, bstr(&b));
は、この結合された文字列をスペースで分割し、個々の引数をgccargs
ベクタに追加します。これにより、CFLAGS
に含まれるすべてのフラグがCコンパイラに渡されるようになります。if(defaultcflags[0] == '\0')
の条件は、CFLAGS
環境変数が空文字列(つまり、ユーザーによって設定されていない)の場合に真となります。この場合、proto_gccargs2
に定義されているデフォルトの最適化フラグ(通常は-O2
)がgccargs
に追加されます。これにより、ユーザーが明示的にCFLAGS
を指定しない場合でも、適切なデフォルト最適化が適用されることが保証されます。
-
ldargs
の初期化とリンカへの適用:if(ldargs.len == 0 && defaultldflags[0] != '\0')
の条件は、ldargs
ベクタがまだ初期化されておらず、かつLDFLAGS
環境変数が空でない場合に真となります。この場合、bprintf(&b, "%s", defaultldflags);
とsplitfields(&ldargs, bstr(&b));
によって、LDFLAGS
の内容がldargs
ベクタに格納されます。- C言語のコマンド(実行可能ファイルのリンク)を実行するブロック内で、
vcopy(&link, gccargs.p, gccargs.len);
の後にvcopy(&link, ldargs.p, ldargs.len);
が追加されました。vcopy()
は、指定されたベクタの内容を別のベクタにコピーするユーティリティ関数です。- これにより、
gccargs
に含まれるコンパイラフラグに加えて、ldargs
に含まれるリンカフラグも、最終的なリンクコマンドに渡されるようになります。
これらの変更により、Goのビルドシステムは、C言語コンポーネントのコンパイルとリンクにおいて、システム環境変数 CFLAGS
と LDFLAGS
を完全に尊重するようになり、より高度なカスタマイズと特定のビルド要件への対応が可能になりました。
関連リンク
特になし。
参考にした情報源リンク
特になし。