[インデックス 1224] ファイルの概要
このコミットは、Go言語の初期のビルドツールである gobuild
の機能を拡張するものです。具体的には、Go以外のファイル(例: C言語のソースファイル)内に記述されたパッケージ宣言を認識できるようにし、さらに特定のコメント gobuild: ignore
を用いてビルド対象からファイルを除外する機能を追加しています。
コミット
commit 6c4d8f830965d04854dd42294c939c48d3d61d83
Author: Russ Cox <rsc@golang.org>
Date: Mon Nov 24 11:21:56 2008 -0800
gobuild:
recognize "// package foo" in non-go files
recognize "gobuild: ignore" to exclude from build
R=r
DELTA=10 (7 added, 2 deleted, 1 changed)
OCL=19878
CL=19905
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6c4d8f830965d04854dd42294c939c48d3d61d83
元コミット内容
このコミットは、gobuild
ツールに対して以下の2つの主要な変更を導入します。
- 非Goファイルにおけるパッケージ宣言の認識:
.go
拡張子を持たないファイル(例:.c
や.s
ファイル)内でも、// package foo
の形式で記述されたパッケージ宣言をgobuild
が認識できるようになります。これにより、Go以外の言語で書かれたソースファイルがGoのパッケージ構造にどのように属するかをgobuild
に伝えることが可能になります。 - ビルドからのファイル除外機能: ファイル内に
gobuild: ignore
という文字列が含まれている場合、そのファイルをgobuild
のビルドプロセスから除外する機能が追加されます。これは、特定のファイルを一時的にビルド対象外にしたり、特定のビルド条件でのみ含めたりする際に有用です。
変更の背景
Go言語の初期段階において、gobuild
は現在の go build
コマンドの前身となるビルドツールでした。Go言語はC言語との連携(cgo)やアセンブリ言語(.sファイル)の使用を考慮して設計されており、これらの非Go言語のソースファイルもGoのビルドシステムに統合する必要がありました。
このコミットの背景には、以下の課題があったと考えられます。
- 異種言語ソースファイルの統合: Goプロジェクトでは、C言語のコードやアセンブリ言語のコードがGoのコードと共存することがあります。
gobuild
がこれらの非Goファイルに含まれるGoパッケージ情報を適切に処理できないと、ビルドプロセスが複雑になったり、エラーが発生したりする可能性がありました。特に、Goのパッケージシステムはディレクトリ構造に基づいていますが、非GoファイルがどのGoパッケージに属するかを明示的に指定するメカニズムが必要でした。 - 柔軟なビルド制御: 開発の過程で、特定のファイルを一時的にビルドから除外したい、あるいは特定の条件でのみビルドに含めたいというニーズが生じます。例えば、プラットフォーム固有のコードや、まだ開発中の機能に関連するコードなどです。このような柔軟性を提供するために、ファイル自体にビルド指示を埋め込むメカニズムが求められていました。
このコミットは、これらの課題に対処し、gobuild
の実用性と柔軟性を向上させることを目的としています。非Goファイルにおけるパッケージ宣言の認識は、Goと他の言語とのシームレスな連携を促進し、gobuild: ignore
ディレクティブは、開発者がビルドプロセスをより細かく制御できるようにします。
前提知識の解説
gobuild
と go build
gobuild
は、Go言語の初期に存在したビルドツールです。現在の go build
コマンドの機能の多くを担っていました。Go言語の進化とともに、より洗練された go
コマンド(go build
, go run
, go test
などを含む)に統合され、gobuild
は廃止されました。しかし、このコミットは gobuild
の設計思想や初期のGoビルドシステムの課題を理解する上で貴重な情報を提供します。
Goのパッケージシステム
Go言語は、コードをパッケージ(package
)という単位で整理します。各Goソースファイルは必ず package
宣言を持ち、そのファイルがどのパッケージに属するかを示します。通常、同じディレクトリ内のGoファイルは同じパッケージに属します。ビルドツールは、このパッケージ宣言を読み取り、依存関係を解決し、実行可能ファイルやライブラリを構築します。
C言語とGoの連携 (cgo)
Go言語は cgo
というメカニズムを通じてC言語のコードと連携できます。Goのソースファイル内でCの関数を呼び出したり、Cの構造体を使用したりすることが可能です。この場合、GoのビルドプロセスはCコンパイラを呼び出してCのコードをコンパイルし、Goのコードとリンクする必要があります。このコミットの変更は、cgo
を使用する際に、CのソースファイルがどのGoパッケージに属するかを gobuild
に伝えるための手段を提供します。
アセンブリ言語ファイル (.s)
Go言語は、パフォーマンスが重要な部分や、特定のハードウェア機能を利用するためにアセンブリ言語で書かれたコード(通常 .s
拡張子を持つファイル)をサポートしています。これらのファイルもGoのビルドプロセスに統合される必要があります。
suffix
関数 (C言語)
C言語のコードスニペットに見られる suffix(file, ".go")
のような関数は、ファイル名が特定の拡張子で終わるかどうかをチェックするユーティリティ関数です。このコミットでは、ファイルが .go
拡張子を持つかどうかを判断するために使用されています。
strstr
関数 (C言語)
strstr(haystack, needle)
はC標準ライブラリの関数で、haystack
文字列内で needle
文字列が最初に出現する位置へのポインタを返します。見つからない場合は NULL
を返します。このコミットでは、行内に gobuild: ignore
という文字列が含まれているかを検出するために使用されています。
技術的詳細
このコミットの技術的変更は、src/cmd/gobuild/gobuild.c
ファイル内の getpkg
関数と main
関数に集中しています。
getpkg
関数の変更
getpkg
関数は、与えられたファイルからパッケージ名を抽出する役割を担っています。
変更前:
if(!suffix(file, ".go"))
return nil;
このコードは、ファイルが .go
拡張子を持たない場合、即座に nil
を返していました。これは、gobuild
がGoソースファイル以外のファイルからパッケージ情報を読み取らないことを意味します。
変更後:
if(!suffix(file, ".go")) {
if(*p != '/' || *(p+1) != '/')
continue;
p += 2;
}
if(strstr(p, "gobuild: ignore"))
return "main";
この変更により、以下の2つの新しい動作が導入されました。
-
非Goファイルにおける
// package foo
の認識:if(!suffix(file, ".go"))
ブロックが追加されました。これにより、ファイルが.go
拡張子を持たない場合でも処理が続行されます。if(*p != '/' || *(p+1) != '/') continue; p += 2;
の行は、現在の行p
が//
で始まっているかをチェックします。もしそうでなければ、その行はスキップされ、次の行の処理に移ります (continue
)。もし//
で始まっていれば、ポインタp
を2文字進め(//
をスキップ)、その後の文字列をパッケージ宣言として解析しようとします。- このメカニズムにより、C言語のソースファイルやアセンブリ言語ファイルなどの非Goファイル内に
// package mypackage
のようなコメント形式でGoのパッケージ宣言を埋め込むことが可能になり、gobuild
がそれを認識できるようになります。
-
gobuild: ignore
ディレクティブによる除外:if(strstr(p, "gobuild: ignore")) return "main";
の行が追加されました。これは、現在の行(//
がスキップされた後)に文字列gobuild: ignore
が含まれているかをチェックします。- もしこの文字列が見つかった場合、
getpkg
関数は"main"
を返します。この"main"
は、このファイルが通常のパッケージとして扱われるべきではないことをgobuild
に伝えるための特別なシグナルであると考えられます。コミットメッセージの「exclude from build」という記述から、このファイルがビルドプロセスから除外されるか、あるいは特別な方法で扱われることを示唆しています。Goのビルドシステムでは、main
パッケージは実行可能エントリポイントを意味しますが、ここでは「無視」の意図で使われている可能性が高いです。
main
関数の変更
main
関数では、テストファイルの処理に関する微細な変更が行われています。
変更前:
if(suffix(argv[i], "_test.go") != nil)
変更後:
if(suffix(argv[i], "_test.go"))
この変更は機能的なものではなく、C言語における慣用的な記述への修正です。suffix
関数は文字列へのポインタを返すか、マッチしない場合は NULL
を返すと推測されます。C言語では、NULL
ポインタはブールコンテキストで false
と評価され、非NULL
ポインタは true
と評価されます。したがって、!= nil
を明示的に書くことは冗長であり、if(suffix(...))
と書くだけで同じ論理的な結果が得られます。これはコードの可読性と簡潔性を向上させるためのクリーンアップです。
コアとなるコードの変更箇所
変更は src/cmd/gobuild/gobuild.c
ファイルに集中しています。
--- a/src/cmd/gobuild/gobuild.c
+++ b/src/cmd/gobuild/gobuild.c
@@ -206,12 +206,17 @@ getpkg(char *file)
char *p, *q;
int i;
- if(!suffix(file, ".go"))
- return nil;
if((b = Bopen(file, OREAD)) == nil)
sysfatal("open %s: %r", file);
while((p = Brdline(b, '\n')) != nil) {
p[Blinelen(b)-1] = '\0';
+ if(!suffix(file, ".go")) {
+ if(*p != '/' || *(p+1) != '/')
+ continue;
+ p += 2;
+ }
+ if(strstr(p, "gobuild: ignore"))
+ return "main";
while(*p == ' ' || *p == '\t')
p++;
if(strncmp(p, "package", 7) == 0 && (p[7] == ' ' || p[7] == '\t')) {
@@ -487,7 +492,7 @@ main(int argc, char **argv)
njob = 0;
job = emalloc(argc*sizeof job[0]);
for(i=0; i<argc; i++) {
- if(suffix(argv[i], "_test.go") != nil)
+ if(suffix(argv[i], "_test.go"))
continue;
job[njob].name = argv[i];
job[njob].pass = -1;
コアとなるコードの解説
getpkg
関数内の変更
-
非Goファイル処理の追加:
- 元の
if(!suffix(file, ".go")) return nil;
が削除され、非Goファイルでも行の読み込みと解析が続行されるようになりました。 if(!suffix(file, ".go")) { ... }
ブロックが追加され、ファイルがGoファイルでない場合にのみ、行が//
で始まるかどうかのチェックが行われます。これにより、C言語のコメント形式でGoのパッケージ宣言を記述できるようになります。p += 2;
は、//
をスキップして、その後の文字列をパッケージ宣言として解析するためのポインタの移動です。
- 元の
-
gobuild: ignore
ディレクティブの追加:if(strstr(p, "gobuild: ignore")) return "main";
が追加されました。これは、現在の行にgobuild: ignore
という文字列が含まれている場合、getpkg
関数が"main"
を返すようにします。この"main"
は、このファイルがビルドから除外されるべきであるというシグナルとして機能します。
main
関数内の変更
- テストファイルチェックの簡略化:
if(suffix(argv[i], "_test.go") != nil)
がif(suffix(argv[i], "_test.go"))
に変更されました。これはC言語の慣用的な記述への修正であり、suffix
関数が返すポインタがNULL
でない場合に真となる条件をより簡潔に表現しています。機能的な変更はありません。
これらの変更により、gobuild
はGo以外のソースファイルもより柔軟に扱い、ビルドプロセスにおけるファイルの包含/除外をより細かく制御できるようになりました。
関連リンク
- Go言語の初期のビルドシステムに関する議論やドキュメントは、Goプロジェクトのメーリングリストや初期の設計ドキュメントに散見されます。
cgo
の現在のドキュメント: https://pkg.go.dev/cmd/cgo- Go言語のパッケージに関する公式ドキュメント: https://go.dev/doc/code
参考にした情報源リンク
- Go言語の公式ドキュメント
- Go言語のソースコードリポジトリ (GitHub)
- C言語の標準ライブラリ関数
strstr
およびポインタの扱いに関する一般的な知識。 - Go言語の初期のビルドツールに関するコミュニティの議論(Stack Overflow, Go Forumなど)。
gobuild
の具体的な動作に関する情報は、現在のgo
コマンドのドキュメントには直接記載されていないため、Goの初期のコミットログや設計ドキュメントから推測しました。suffix
関数の具体的な実装はコミットに含まれていませんが、C言語における一般的な文字列操作関数のパターンからその動作を推測しました。