Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 15764] ファイルの概要

このコミットは、Go言語のビルドツールである cmd/dist において、goos (オペレーティングシステム) および goarch (アーキテクチャ) に対応するビルドタグのサポートを追加するものです。これにより、特定のOSとアーキテクチャの組み合わせに特化したコードを条件付きでビルドできるようになります。

コミット

cmd/dist: goos, goarch ビルドタグをサポート これは、netpolllinux/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/386linux/amd64)向けにビルドできるようにすることでした。

Go言語では、異なるOSやアーキテクチャに特化したコードを記述するために「ビルドタグ (build tags)」という仕組みが提供されています。例えば、// +build linux,amd64 のようなコメントをファイルの先頭に記述することで、そのファイルが linux かつ amd64 の環境でのみコンパイルされるように制御できます。

しかし、このコミット以前の cmd/dist ツールは、goos (OS) と goarch (アーキテクチャ) の組み合わせを単一のビルドタグとして適切に解釈する機能が不足していました。netpoll のような低レベルのシステムコールを扱うコンポーネントは、OSやアーキテクチャに強く依存するため、このようなきめ細かいビルド制御が不可欠です。この機能がなければ、linux/386linux/amd64 の両方に対応する netpoll のコードをGoの標準ライブラリに組み込むことが困難でした。

このコミットは、cmd/distgoos,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の場合にのみコンパイルされます。

goosgoarch

これらは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 関数は単一のタグ(goosgoarchcmd_go_bootstrapgo1.1 など)との一致を単純にチェックしていました。しかし、Goのビルドタグはカンマ区切りで複数の条件をAND結合できるため、例えば linux,amd64 のようなタグを正しく処理できませんでした。

変更後の matchfield 関数は、以下のロジックを導入しています。

  1. カンマの検出: 入力されたタグ文字列 f の中にカンマ (,) が含まれているかを xstrrchr(f, ',') でチェックします。xstrrchr は文字列の最後から指定された文字を検索する関数です。
  2. カンマがない場合: カンマが見つからない場合(p == nil)、従来のロジックに従い、fgoosgoarchcmd_go_bootstrapgo1.1 のいずれかと一致するかをチェックします。これは単一のタグの評価です。
  3. カンマがある場合: カンマが見つかった場合(p != nil)、これは複数のタグがカンマで区切られていることを意味します。
    • カンマの位置 p で文字列を一時的に分割します(*p = 0;)。これにより、f は最初のタグ、p+1 は2番目以降のタグの開始点となります。
    • 再帰的に matchfield(f)matchfield(p+1) を呼び出します。これにより、カンマで区切られた各タグが個別に評価されます。
    • 両方の再帰呼び出しの結果が true であれば、restrue に設定します。これは論理ANDの動作を模倣しています。
    • 文字列を元に戻します(*p = ',';)。これは、元の文字列を変更しないようにするための重要なステップです。
    • 最終的な結果 res を返します。

この再帰的なアプローチにより、matchfield 関数は tag1,tag2,tag3 のような任意の数のカンマ区切りタグを正しく評価できるようになりました。例えば、linux,amd64 というタグが与えられた場合、まず linuxamd64 に分割され、それぞれが 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 によって正しく解釈され、対応するファイルがビルドに含まれるようになります。

関連リンク

参考にした情報源リンク