[インデックス 15764] ファイルの概要
このコミットは、Go言語のビルドツールである cmd/dist
において、goos
(オペレーティングシステム) および goarch
(アーキテクチャ) に対応するビルドタグのサポートを追加するものです。これにより、特定のOSとアーキテクチャの組み合わせに特化したコードを条件付きでビルドできるようになります。
コミット
cmd/dist
: goos
, goarch
ビルドタグをサポート
これは、netpoll
を linux/386
および linux/amd64
向けにサブミットするために必要です。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4dd1b8999a8a10cc3a2f226e187ac6a973e606c1
元コミット内容
commit 4dd1b8999a8a10cc3a2f226e187ac6a973e606c1
Author: Dmitriy Vyukov <dvyukov@google.com>
Date: Thu Mar 14 19:04:47 2013 +0400
cmd/dist: support goos,goarch build tags
This is necessary to submit netpoll for linux,386 linux,amd64
R=golang-dev, bradfitz, rsc
CC=golang-dev
https://golang.org/cl/7470050
---
src/cmd/dist/build.c | 11 ++++++++++-\n 1 file changed, 10 insertions(+), 1 deletion(-)\n
変更の背景
この変更の主な背景は、Go言語のネットワークポーリングメカニズムである netpoll
を、特定のオペレーティングシステムとアーキテクチャの組み合わせ(具体的には linux/386
と linux/amd64
)向けにビルドできるようにすることでした。
Go言語では、異なるOSやアーキテクチャに特化したコードを記述するために「ビルドタグ (build tags)」という仕組みが提供されています。例えば、// +build linux,amd64
のようなコメントをファイルの先頭に記述することで、そのファイルが linux
かつ amd64
の環境でのみコンパイルされるように制御できます。
しかし、このコミット以前の cmd/dist
ツールは、goos
(OS) と goarch
(アーキテクチャ) の組み合わせを単一のビルドタグとして適切に解釈する機能が不足していました。netpoll
のような低レベルのシステムコールを扱うコンポーネントは、OSやアーキテクチャに強く依存するため、このようなきめ細かいビルド制御が不可欠です。この機能がなければ、linux/386
と linux/amd64
の両方に対応する netpoll
のコードをGoの標準ライブラリに組み込むことが困難でした。
このコミットは、cmd/dist
が goos,goarch
のようなカンマ区切りのビルドタグを正しく処理できるようにすることで、この問題を解決し、netpoll
のようなプラットフォーム固有のコードの統合を可能にしました。
前提知識の解説
Go言語のビルドタグ (Build Tags)
Go言語のビルドタグは、特定のビルド条件に基づいてソースファイルを含めたり除外したりするためのメカニズムです。ソースファイルの先頭に // +build tag1,tag2 !tag3
のような形式でコメントを記述することで、そのファイルがコンパイルされる条件を指定します。
// +build
ディレクティブ: この行は、Goコンパイラやビルドツールに対して、そのファイルがビルドされるべき条件を指示します。- タグの組み合わせ: タグはスペースまたはカンマで区切られます。
- スペース区切り (
tag1 tag2
): 論理OR (tag1
またはtag2
のいずれかが真であればビルド)。 - カンマ区切り (
tag1,tag2
): 論理AND (tag1
かつtag2
の両方が真であればビルド)。
- スペース区切り (
- 否定 (
!
): タグの前に!
を付けると、そのタグが偽の場合にビルドされます (例:!windows
はWindows以外のOSでビルド)。 - 特殊なタグ:
goos
: ビルドターゲットのオペレーティングシステム (例:linux
,windows
,darwin
)。goarch
: ビルドターゲットのアーキテクチャ (例:amd64
,386
,arm
)。goversion
: Goのバージョン (例:go1.1
,go1.12
)。cgo
: Cgoが有効な場合に真。ignore
: そのファイルを常に無視します。
例えば、// +build linux,amd64
と記述されたファイルは、ターゲットOSがLinuxで、かつターゲットアーキテクチャがAMD64の場合にのみコンパイルされます。
goos
と goarch
これらはGoの環境変数であり、ビルドターゲットのオペレーティングシステムとCPUアーキテクチャを指定するために使用されます。
GOOS
:linux
,windows
,darwin
(macOS),freebsd
など。GOARCH
:amd64
,386
,arm
,arm64
,ppc64
,s390x
など。
Goのクロスコンパイルでは、これらの環境変数を設定することで、現在の環境とは異なるOSやアーキテクチャ向けのバイナリを生成できます。
cmd/dist
cmd/dist
は、GoのソースコードからGoツールチェイン自体をビルドするために使用される内部ツールです。Goの標準ライブラリやコンパイラ、リンカなどのツールは、この dist
ツールによってビルドされます。これはGoのブートストラッププロセスにおいて重要な役割を果たします。通常のGoアプリケーション開発者が直接使用することは稀ですが、Goの内部構造を理解する上で重要です。
netpoll
netpoll
は、GoのネットワークI/Oの基盤となるパッケージです。これは、OSが提供する効率的なI/O多重化メカニズム(Linuxのepoll、macOS/FreeBSDのkqueue、WindowsのIOCPなど)を抽象化し、Goのランタイムが多数のネットワーク接続を効率的に処理できるようにします。netpoll
は低レベルのシステムコールを直接利用するため、OSやアーキテクチャに強く依存するコードを含んでいます。そのため、異なるプラットフォーム向けに個別の実装が必要となる場合があります。
技術的詳細
このコミットは、src/cmd/dist/build.c
ファイル内の matchfield
関数を変更しています。この関数は、Goのビルドタグの条件を評価し、特定のファイルが現在のビルド環境でコンパイルされるべきかどうかを判断する役割を担っています。
変更前は、matchfield
関数は単一のタグ(goos
、goarch
、cmd_go_bootstrap
、go1.1
など)との一致を単純にチェックしていました。しかし、Goのビルドタグはカンマ区切りで複数の条件をAND結合できるため、例えば linux,amd64
のようなタグを正しく処理できませんでした。
変更後の matchfield
関数は、以下のロジックを導入しています。
- カンマの検出: 入力されたタグ文字列
f
の中にカンマ (,
) が含まれているかをxstrrchr(f, ',')
でチェックします。xstrrchr
は文字列の最後から指定された文字を検索する関数です。 - カンマがない場合: カンマが見つからない場合(
p == nil
)、従来のロジックに従い、f
がgoos
、goarch
、cmd_go_bootstrap
、go1.1
のいずれかと一致するかをチェックします。これは単一のタグの評価です。 - カンマがある場合: カンマが見つかった場合(
p != nil
)、これは複数のタグがカンマで区切られていることを意味します。- カンマの位置
p
で文字列を一時的に分割します(*p = 0;
)。これにより、f
は最初のタグ、p+1
は2番目以降のタグの開始点となります。 - 再帰的に
matchfield(f)
とmatchfield(p+1)
を呼び出します。これにより、カンマで区切られた各タグが個別に評価されます。 - 両方の再帰呼び出しの結果が
true
であれば、res
をtrue
に設定します。これは論理ANDの動作を模倣しています。 - 文字列を元に戻します(
*p = ',';
)。これは、元の文字列を変更しないようにするための重要なステップです。 - 最終的な結果
res
を返します。
- カンマの位置
この再帰的なアプローチにより、matchfield
関数は tag1,tag2,tag3
のような任意の数のカンマ区切りタグを正しく評価できるようになりました。例えば、linux,amd64
というタグが与えられた場合、まず linux
と amd64
に分割され、それぞれが goos
または goarch
と一致するかどうかがチェックされます。両方が一致すれば、全体として true
が返されます。
コアとなるコードの変更箇所
--- a/src/cmd/dist/build.c
+++ b/src/cmd/dist/build.c
@@ -1046,7 +1046,16 @@ out:
static bool
matchfield(char *f)
{
- return streq(f, goos) || streq(f, goarch) || streq(f, "cmd_go_bootstrap") || streq(f, "go1.1");
+ char *p;
+ bool res;
+
+ p = xstrrchr(f, ',');
+ if(p == nil)
+ return streq(f, goos) || streq(f, goarch) || streq(f, "cmd_go_bootstrap") || streq(f, "go1.1");
+ *p = 0;
+ res = matchfield(f) && matchfield(p+1);
+ *p = ',';
+ return res;
}
// shouldbuild reports whether we should build this file.
コアとなるコードの解説
変更された matchfield
関数は、C言語で記述されており、Goのビルドタグの評価ロジックを実装しています。
static bool
matchfield(char *f)
{
char *p; // カンマの位置を指すポインタ
bool res; // 評価結果を格納する変数
// 文字列の最後からカンマを探す
p = xstrrchr(f, ',');
// カンマが見つからない場合(単一のタグの場合)
if(p == nil)
// f が goos, goarch, cmd_go_bootstrap, go1.1 のいずれかと一致するかをチェックして返す
return streq(f, goos) || streq(f, goarch) || streq(f, "cmd_go_bootstrap") || streq(f, "go1.1");
// カンマが見つかった場合(複数のタグがカンマで区切られている場合)
*p = 0; // カンマの位置をヌル終端文字で上書きし、文字列を一時的に分割する
// 再帰的に matchfield を呼び出し、分割された両方の部分が真であるかをチェック(論理AND)
res = matchfield(f) && matchfield(p+1);
*p = ','; // ヌル終端文字を元のカンマに戻し、文字列を復元する
return res; // 評価結果を返す
}
このコードは、Goのビルドタグの「カンマ区切りは論理AND」というセマンティクスをC言語で効率的に実装しています。再帰呼び出しと文字列の一時的な変更(ヌル終端文字の挿入と復元)を組み合わせることで、任意の深さのカンマ区切りタグを処理できるようになっています。これにより、linux,amd64
のようなタグが cmd/dist
によって正しく解釈され、対応するファイルがビルドに含まれるようになります。
関連リンク
- Go CL 7470050: https://golang.org/cl/7470050
参考にした情報源リンク
- Go Build Constraints: https://pkg.go.dev/cmd/go#hdr-Build_constraints
- Go Environment Variables (GOOS, GOARCH): https://go.dev/doc/install/source#environment
- Go
netpoll
package: https://pkg.go.dev/runtime/internal/netpoll (Go 1.18以降はruntime/internal/netpoll
に移動) cmd/dist
source code (for context): https://github.com/golang/go/tree/master/src/cmd/distxstrrchr
function (likely a utility function within the Go project, similar tostrrchr
): https://man7.org/linux/man-pages/man3/strrchr.3.html (Standard C librarystrrchr
for reference)