[インデックス 13008] ファイルの概要
このコミットは、Go言語のビルドシステムにおいて、Plan 9オペレーティングシステム向けのdistベースのビルドプロセスを導入するものです。具体的には、Plan 9環境でGoのツールチェインと標準ライブラリをビルドするための新しいスクリプトとC言語のユーティリティ関数が追加・変更されています。これにより、Plan 9上でのGoの自己ホスト型ビルドがより堅牢かつ効率的に行えるようになります。
コミット
commit 38590329807beaea154f427549302f71a7a0444e
Author: Anthony Martin <ality@pbrane.org>
Date: Tue May 1 22:32:46 2012 -0700
build: dist-based build for Plan 9
R=rsc, iant, iant, seed
CC=golang-dev
https://golang.org/cl/5608059
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/38590329807beaea154f427549302f71a7a0444e
元コミット内容
このコミットは、GoのビルドシステムにPlan 9向けのdistベースのビルド機能を追加します。
主な変更点:
src/all.rc,src/clean.rc,src/make.rc,src/run.rcという新しいrcシェルスクリプトが追加され、Plan 9環境でのビルド、クリーン、実行のワークフローを定義します。src/cmd/dist/plan9.cが追加され、Plan 9固有のシステムコールやユーティリティ関数の実装を提供します。これらはdistツールがOSに依存しない形で動作するために必要です。src/cmd/dist/build.cが修正され、Plan 9向けのコンパイル・リンクオプション(例:rcリンカの使用、Plan 9コンパイラ%scの使用、外部プリプロセッサの利用)が追加されました。また、libbio.aやlib9といったPlan 9固有のライブラリの扱いが調整されています。src/cmd/dist/a.h,src/cmd/dist/arg.h,src/cmd/dist/buildgc.c,src/cmd/dist/buildruntime.c,src/cmd/dist/goc2c.cなど、既存のビルド関連ファイルもPlan 9対応のために微調整されています。
変更の背景
Go言語は、その設計思想の一部として、様々なプラットフォームへの移植性を重視しています。Plan 9は、ベル研究所で開発された分散オペレーティングシステムであり、Go言語の開発者の一部がその影響を受けていることでも知られています。
このコミット以前は、Plan 9上でのGoのビルドプロセスが十分に統合されていなかったか、あるいは特定のスクリプトや手動の手順に依存していた可能性があります。distツールはGoの公式ビルドシステムの中核であり、クロスコンパイルやブートストラップビルドなど、複雑なビルドシナリオを管理するために設計されています。
この変更の背景には、Plan 9をGoの公式サポートプラットフォームとしてより完全に統合し、他のOSと同様にdistツールを通じて一貫したビルド体験を提供することを目指す意図があります。これにより、Plan 9ユーザーがGoをより簡単に利用できるようになり、Goの移植性とプラットフォームサポートが強化されます。特に、Plan 9の独特なシステム環境(例えば、rcシェルや独自のCコンパイラ、ファイルシステム構造など)に対応するための具体的な実装が必要とされていました。
前提知識の解説
このコミットを理解するためには、以下の知識が役立ちます。
-
Plan 9オペレーティングシステム:
- ベル研究所で開発された分散オペレーティングシステム。
- 「すべてがファイルである」という哲学を持ち、リソースはファイルシステムを通じてアクセスされる。
- 標準シェルは
rc(Bourneシェル系とは異なる構文を持つ)。 - 独自のCコンパイラ(
5c,6c,8cなど、アーキテクチャごとに異なる)とリンカ(5l,6l,8lなど)を使用する。 - 標準CライブラリはUnix系とは異なる部分が多い。
u.h,libc.hなどのヘッダファイルが特徴的。fork(),exec(),pipe()などのシステムコールもUnix系とは異なる挙動や引数を持つ場合がある。
-
Go言語のビルドシステム (
cmd/dist):- GoのソースコードからGoコンパイラ、リンカ、標準ライブラリ、その他のツールをビルドするための公式ツール。
- ブートストラップビルド(古いバージョンのGoコンパイラで新しいバージョンのGoコンパイラをビルドするプロセス)を管理する。
- クロスコンパイル(異なるOS/アーキテクチャ向けのバイナリをビルドする)をサポートする。
- C言語で書かれており、OS固有の処理は抽象化されたインターフェースを通じて行われる。
GOROOT,GOHOSTOS,GOHOSTARCH,GOOS,GOARCHなどの環境変数を使用してビルド環境を制御する。
-
rcシェル:- Plan 9の標準シェル。
- 構文が独特で、例えば変数代入に
=ではなく=を使用したり、コマンド置換に`ではなく`{}`を使用したりする。 - パイプラインやリダイレクトの概念はUnixシェルと似ているが、詳細が異なる。
-
C言語のプリプロセッサとコンパイル:
- C言語のソースコードは、プリプロセッサ(マクロ展開など)、コンパイラ、アセンブラ、リンカの段階を経て実行可能ファイルになる。
#ifdef,#ifndefなどのプリプロセッサディレクティブは、特定の条件に基づいてコードのコンパイルを制御するために使用される。y.tab.cやy.tab.hは、yaccやbisonといったパーサジェネレータによって生成されるファイルで、複雑なマクロや構造体定義を含むことが多い。
技術的詳細
このコミットは、GoのビルドシステムがPlan 9上で動作するために必要な、低レベルのOS抽象化とビルドスクリプトの変更に焦点を当てています。
-
Plan 9固有のユーティリティ関数 (
src/cmd/dist/plan9.c):- このファイルは、
distツールが内部で使用するファイル操作、プロセス管理、メモリ管理、文字列操作などの基本的なユーティリティ関数をPlan 9のシステムコール(read,write,open,create,remove,chdir,fork,exec,wait,getenv,putenvなど)を使用して再実装しています。 - 例えば、
bprintf,bpathf,bwritefは、可変引数リスト(va_list)とvsnprintfを使用してバッファにフォーマットされた文字列を書き込む関数で、Goのビルドログ出力やパス生成に使用されます。 run,runv,bgrunv,bgwaitは、Plan 9のfork/execメカニズムを利用して外部コマンドを実行し、その出力をキャプチャしたり、バックグラウンドで実行したりするためのラッパーです。特に、MAXBG(最大4つのバックグラウンドジョブ)の制限とbgwait1によるジョブ管理は、並列ビルドの制御を示唆しています。xmkdir,xmkdirall,xremove,xremoveall,xreaddirは、Plan 9のファイルシステム操作(ディレクトリ作成、ファイル削除、ディレクトリ内容読み取りなど)を抽象化しています。fatal関数は、エラーメッセージを出力してプログラムを終了させるための共通のエラーハンドリングルーチンです。xmalloc,xstrdup,xrealloc,xfreeは、メモリ割り当てと解放のためのラッパーで、メモリ不足の場合にfatalを呼び出します。main関数は、Plan 9環境でのdistツールのエントリポイントとして機能し、環境変数の設定(objtypeからgohostarchの決定、GOBINの設定)、標準出力のバッファリング、乱数シードの設定など、初期化処理を行います。
- このファイルは、
-
ビルドロジックの変更 (
src/cmd/dist/build.c):install関数は、Goのパッケージやコマンドをビルド・インストールする主要なロジックを含んでいます。このコミットでは、Plan 9固有のコンパイル・リンクオプションが条件付きで適用されるようになりました。- リンカの選択: Cライブラリのビルドにおいて、
gohostosが"plan9"の場合にはrcリンカ(Plan 9のアーカイブツール)を使用し、それ以外の場合はrscを使用するように変更されました。Cコマンドのビルドでも同様に、Plan 9では%sl(5l,6l,8lなど)リンカが使用されます。 - コンパイラの選択とオプション: Cソースファイルのコンパイルにおいて、Plan 9では
%sc(5c,6c,8cなど)コンパイラが使用され、-FTVwや-Bp+(外部プリプロセッサを使用するオプション)などのPlan 9固有のコンパイラフラグが渡されます。特に、yacc/bisonによって生成されたファイル(y.tab.c,y.tab.h)は、Plan 9コンパイラがサポートしないマクロを使用する可能性があるため、外部プリプロセッサ(-Bp+)の使用が強制されます。 - インクルードパス: Plan 9では、
$GOROOT/include/plan9と$GOROOT/include/plan9/$GOHOSTARCHがインクルードパスに追加され、Plan 9固有のヘッダファイルが正しく見つかるようにします。 - 生成されるCファイル名:
.gocファイル(GoのCバインディングファイル)から生成されるCファイルの名前が、_goarch.cから_goos_goarch.cに変更され、OS情報も含まれるようになりました。 - ライブラリの扱い: Plan 9では、多くのライブラリがシステムに既に存在するため、
shouldbuild関数でlib9(ただしlib9/goos.cを除く)やlibbioのビルドがスキップされるようになりました。また、libbio.aはリンク時に明示的に除外されます。Cバイナリのリンク時に-lm(数学ライブラリ)がPlan 9では不要なため、条件付きで除外されます。
-
rcシェルスクリプト (src/*.rc):src/all.rc: Goのビルドプロセス全体を起動するエントリポイント。make.rcを呼び出し、run.rcでテストを実行し、最後にdist bannerでビルド情報を表示します。src/clean.rc: ビルド成果物をクリーンアップするためのスクリプト。go clean -i stdとdist cleanを呼び出します。src/make.rc: Goのツールチェインと標準ライブラリをビルドする主要なスクリプト。cmd/distのブートストラップビルド(Plan 9のCコンパイラでdistツール自体をビルド)を行います。./cmd/dist/dist env -9を実行して、Plan 9環境向けのビルド環境変数を設定します。dist bootstrapコマンドでGoのブートストラップコンパイラをビルドし、その後go_bootstrap installでホスト環境およびターゲット環境向けのパッケージとコマンドをビルドします。GOROOT_FINAL環境変数による最終的なGoルートパスの指定をサポートします。
src/run.rc: ビルドされたGoのパッケージとコマンドのテストを実行するスクリプト。go installで再ビルドし、go test stdで標準パッケージのテストを実行します。GOMAXPROCSや-cpuオプションを使ったランタイムテストも含まれます。
これらの変更により、GoのビルドシステムはPlan 9の独特な環境に完全に適応し、自動化されたビルド、テスト、クリーンアップのワークフローが確立されました。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、主に以下のファイルに集中しています。
-
src/cmd/dist/plan9.c:- このファイル全体が新規追加されており、Plan 9固有のシステムコールやユーティリティ関数の実装が含まれています。これは、
distツールがOSに依存しない形で動作するための抽象化レイヤーを提供します。
- このファイル全体が新規追加されており、Plan 9固有のシステムコールやユーティリティ関数の実装が含まれています。これは、
-
src/cmd/dist/build.c:install関数内のPlan 9固有のコンパイル・リンクロジックの追加。- Cライブラリのリンクで
rscの代わりにrcを使用する部分:--- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -605,7 +606,10 @@ install(char *dir) if(islib) { // C library. vadd(&link, "ar"); - vadd(&link, "rsc"); + if(streq(gohostos, "plan9")) + vadd(&link, "rc"); + else + vadd(&link, "rsc"); prefix = ""; if(!hasprefix(name, "lib")) prefix = "lib"; - CコマンドのリンクでPlan 9固有のリンカとパスを使用する部分:
--- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -631,14 +635,21 @@ install(char *dir) } else { // C command. Use gccargs. - vcopy(&link, gccargs.p, gccargs.len); - vadd(&link, "-o"); - targ = link.len; - vadd(&link, bpathf(&b, "%s/%s%s", tooldir, name, exe)); - if(streq(gohostarch, "amd64")) - vadd(&link, "-m64"); - else if(streq(gohostarch, "386")) - vadd(&link, "-m32"); + if(streq(gohostos, "plan9")) { + vadd(&link, bprintf(&b, "%sl", gohostchar)); + vadd(&link, "-o"); + targ = link.len; + vadd(&link, bpathf(&b, "%s/%s", tooldir, name)); + } else { + vcopy(&link, gccargs.p, gccargs.len); + vadd(&link, "-o"); + targ = link.len; + vadd(&link, bpathf(&b, "%s/%s%s", tooldir, name, exe)); + if(streq(gohostarch, "amd64")) + vadd(&link, "-m64"); + else if(streq(gohostarch, "386")) + vadd(&link, "-m32"); + } } ttarg = mtime(link.p[targ]); - Plan 9での
libbio.aのスキップ:--- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -672,6 +683,8 @@ install(char *dir) \tp = bstr(&b1); \tif(hassuffix(p, ".a")) { + if(streq(gohostos, "plan9") && hassuffix(p, "libbio.a")) + continue; vadd(&lib, bpathf(&b, "%s", p)); continue; } .gocファイルから生成されるCファイル名にgoosを追加:--- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -799,10 +816,10 @@ install(char *dir) \tp = files.p[i]; \tif(!hassuffix(p, ".goc")) continue; - // b = path/zp but with _goarch.c instead of .goc + // b = path/zp but with _goos_goarch.c instead of .goc bprintf(&b, "%s%sz%s", bstr(&path), slash, lastelem(p)); b.len -= 4; - bwritef(&b, "_%s.c", goarch); + bwritef(&b, "_%s_%s.c", goos, goarch); goc2c(p, bstr(&b)); vadd(&files, bstr(&b)); }- Plan 9での外部プリプロセッサの使用 (
usecpp):--- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -816,6 +833,20 @@ install(char *dir) goto nobuild; } + // The files generated by GNU Bison use macros that aren't + // supported by the Plan 9 compilers so we have to use the + // external preprocessor when compiling. + usecpp = 0; + if(streq(gohostos, "plan9")) { + for(i=0; i<files.len; i++) { + p = files.p[i]; + if(hassuffix(p, "y.tab.c") || hassuffix(p, "y.tab.h")){ + usecpp = 1; + break; + } + } + } + // Compile the files. for(i=0; i<files.len; i++) { if(!hassuffix(files.p[i], ".c") && !hassuffix(files.p[i], ".s")) @@ -825,17 +856,26 @@ install(char *dir) vreset(&compile); if(!isgo) { // C library or tool. - vcopy(&compile, gccargs.p, gccargs.len); - vadd(&compile, "-c"); - if(streq(gohostarch, "amd64")) - vadd(&compile, "-m64"); - else if(streq(gohostarch, "386")) - vadd(&compile, "-m32"); - if(streq(dir, "lib9")) - vadd(&compile, "-DPLAN9PORT"); - - vadd(&compile, "-I"); - vadd(&compile, bpathf(&b, "%s/include", goroot)); + if(streq(gohostos, "plan9")) { + vadd(&compile, bprintf(&b, "%sc", gohostchar)); + vadd(&compile, "-FTVw"); + if(usecpp) + vadd(&compile, "-Bp+"); + vadd(&compile, bpathf(&b, "-I%s/include/plan9", goroot)); + vadd(&compile, bpathf(&b, "-I%s/include/plan9/%s", goroot, gohostarch)); + } else { + vcopy(&compile, gccargs.p, gccargs.len); + vadd(&compile, "-c"); + if(streq(gohostarch, "amd64")) + vadd(&compile, "-m64"); + else if(streq(gohostarch, "386")) + vadd(&compile, "-m32"); + if(streq(dir, "lib9")) + vadd(&compile, "-DPLAN9PORT"); + + vadd(&compile, "-I"); + vadd(&compile, bpathf(&b, "%s/include", goroot)); + } vadd(&compile, "-I"); vadd(&compile, bstr(&path)); - Plan 9での出力ファイル名の変更と
-lmの条件付き追加:--- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -882,7 +922,11 @@ install(char *dir) doclean = 0; } - b.p[b.len-1] = 'o'; // was c or s + // Change the last character of the output file (which was c or s). + if(streq(gohostos, "plan9")) + b.p[b.len-1] = gohostchar[0]; + else + b.p[b.len-1] = 'o'; vadd(&compile, "-o"); vadd(&compile, bstr(&b)); vadd(&compile, files.p[i]); @@ -923,7 +967,8 @@ install(char *dir) if(!islib && !isgo) { // C binaries need the libraries explicitly, and -lm. vcopy(&link, lib.p, lib.len); - vadd(&link, "-lm"); + if(!streq(gohostos, "plan9")) + vadd(&link, "-lm"); } // Remove target before writing it. - Plan 9でのライブラリビルドのスキップ:
--- a/src/cmd/dist/build.c +++ b/src/cmd/dist/build.c @@ -981,6 +1026,16 @@ shouldbuild(char *file, char *dir) Buf b; Vec lines, fields; + // On Plan 9, most of the libraries are already present. + // The main exception is libmach which has been modified + // in various places to support Go object files. + if(streq(gohostos, "plan9")) { + if(streq(dir, "lib9") && !hassuffix(file, "lib9/goos.c")) + return 0; + if(streq(dir, "libbio")) + return 0; + } + // Check file name for GOOS or GOARCH. name = lastelem(file); for(i=0; i<nelem(okgoos); i++)
- Cライブラリのリンクで
-
src/make.rc:- Plan 9向けの
distツールのブートストラップビルドと、その後のGoツールチェインのビルドロジック。
- Plan 9向けの
これらのファイルは、Plan 9環境でのGoのビルドプロセスを定義し、OS固有の差異を吸収するための中心的な役割を担っています。
コアとなるコードの解説
src/cmd/dist/plan9.c
このファイルは、GoのビルドツールdistがPlan 9上で動作するために必要な、低レベルのOS抽象化レイヤーを提供します。GoのビルドツールはC言語で書かれており、ファイルシステム操作、プロセス実行、環境変数アクセスなど、OSに依存する多くの処理を行います。plan9.cは、これらの一般的な操作をPlan 9のシステムコールやライブラリ関数(u.h, libc.hなど)を使って実装しています。
例えば、run関数群は、fork()とexec()を使って外部コマンドを実行し、その標準出力やエラー出力をキャプチャする機能を提供します。これは、Goコンパイラやリンカなどの外部ツールをビルドプロセスから呼び出す際に不可欠です。また、xmkdir, xremove, readfile, writefileなどの関数は、ディレクトリの作成、ファイルの削除、ファイルの読み書きといった基本的なファイルシステム操作をPlan 9のcreate, remove, openなどのシステムコールを使って実現しています。
main関数は、Plan 9環境でのdistツールのエントリポイントであり、環境変数の初期化や、gohostos("plan9"に設定)とgohostarch(objtype環境変数から取得)の設定を行います。これにより、distツールは自身がPlan 9上で動作していることを認識し、適切なビルドパスやツールを選択できるようになります。
src/cmd/dist/build.c
build.cのinstall関数は、Goのビルドプロセスにおいて、C言語で書かれたGoのツール(例えば、cmd/dist自身や、Goのランタイムの一部)をコンパイル・リンクする役割を担っています。このコミットでは、Plan 9のビルド環境の特殊性に対応するために、多くの条件分岐が追加されました。
- コンパイラとリンカの選択: Plan 9では、Unix系の
gccやarとは異なる独自のコンパイラ(5c,6c,8cなど)とリンカ(5l,6l,8lなど)、アーカイブツール(rc)を使用します。コードではstreq(gohostos, "plan9")という条件でこれらのPlan 9固有のツールが選択されるようになっています。例えば、Cライブラリのアーカイブにはrcが、Cコマンドのリンクには%slが使われます。 - コンパイラフラグ: Plan 9のCコンパイラは、Unix系のコンパイラとは異なるフラグを必要とします。
-FTVwはPlan 9コンパイラの一般的なフラグであり、-Bp+は外部プリプロセッサを使用するためのフラグです。後者は、yacc/bisonによって生成されたCファイルが、Plan 9コンパイラが直接処理できない複雑なマクロを含む場合に特に重要です。 - インクルードパス: Plan 9固有のヘッダファイル(例:
u.h,libc.h)を見つけるために、$GOROOT/include/plan9や$GOROOT/include/plan9/$GOHOSTARCHがインクルードパスに追加されます。 - ライブラリの扱い: Plan 9では、多くのシステムライブラリが既に存在するため、Goのビルドプロセスでそれらを再ビルドする必要がありません。
shouldbuild関数内の条件分岐は、lib9(ただしgoos.cを除く)やlibbioといった特定のライブラリのビルドをスキップするようにしています。また、libbio.aはリンク時に明示的に除外されます。これは、Plan 9のシステムに存在するライブラリとの重複や競合を避けるためです。 - 生成ファイル名の変更:
.gocファイルから生成されるCファイルの名前が_goos_goarch.cに変更されたのは、異なるOS/アーキテクチャ向けのGo Cバインディングが共存する場合に、ファイル名の衝突を避けるためと考えられます。
src/make.rc
make.rcは、Plan 9のrcシェルで書かれたGoのビルドスクリプトです。これは、Goのビルドプロセス全体をオーケストレーションする役割を担っています。
- ブートストラップビルド: まず、Plan 9のCコンパイラ(
$CC)とリンカ($LD)を使って、cmd/distツール自体をビルドします。これは「ブートストラップ」と呼ばれ、Goのビルドシステムが自身をビルドするために必要です。 - 環境変数の設定: ビルドされた
distツールを使ってdist env -9を実行し、Plan 9環境に特化したGoのビルド環境変数(GOHOSTOS,GOHOSTARCH,GOOS,GOARCHなど)を設定します。 - Goツールチェインのビルド: その後、ブートストラップされた
distツールとgo_bootstrapコマンドを使って、Goのコンパイラ、リンカ、標準ライブラリ、その他のツールをビルドします。go_bootstrap installコマンドは、指定されたgcflags(Goコンパイラへのフラグ)とldflags(Goリンカへのフラグ)を適用して、パッケージとコマンドをインストールします。 GOROOT_FINAL:GOROOT_FINAL環境変数は、ビルドされたGoバイナリに埋め込まれる最終的なGoルートパスを指定するために使用されます。これは、ビルド時と実行時でGoのインストールパスが異なる場合に重要です。
これらのコアなコード変更とスクリプトは連携して、Plan 9という独特な環境でGo言語の完全なビルドと実行を可能にしています。
関連リンク
- Go言語公式サイト: https://golang.org/
- Plan 9 from Bell Labs: https://9p.io/plan9/
- Goのビルドプロセスに関するドキュメント (Goのバージョンによって異なる場合があります): https://go.dev/doc/install/source
参考にした情報源リンク
- Go言語のソースコード (GitHub): https://github.com/golang/go
- Plan 9の
rcシェルに関する情報: https://9p.io/sys/doc/rc.html - Plan 9のCプログラミング環境に関する情報: https://9p.io/sys/doc/compiler.html
- Goの
cmd/distツールの役割に関する一般的な情報 (Goのソースコード内のコメントや関連ドキュメント): https://github.com/golang/go/tree/master/src/cmd/dist - Gerrit Code Review (Goプロジェクトのコードレビューシステム): https://go-review.googlesource.com/
- GoのIssue Tracker: https://go.dev/issue
- Goのメーリングリスト (golang-dev): https://groups.google.com/g/golang-dev
- Web検索: "Plan 9 Go build" (一般的な背景知識の確認のため)I have generated the detailed commit explanation in Markdown format, following all the specified instructions and chapter structure. The output is printed to standard output only, as requested.