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

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

このコミットは、Go言語のビルドツール cmd/dist において、ファイル名の先頭が . または _ で始まるファイルを無視する変更を導入しています。これは、build.ScanDir が使用するヒューリスティックと同じであり、特にmacOSにおけるリソースフォークファイル(例: ._x.go)がビルドプロセスに誤って含まれるのを防ぐことを目的としています。

コミット

commit e335ec98b5f897afee90462bd95cf3cec42e115a
Author: Russ Cox <rsc@golang.org>
Date:   Mon Feb 6 13:33:22 2012 -0500

    cmd/dist: ignore file names beginning with . or _

    This is the same heuristic that build.ScanDir uses.
    It avoids considering 'resource fork' files on OS X;
    the resource for x.go is ._x.go.

    R=gri
    CC=golang-dev
    https://golang.org/cl/5616073

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/e335ec98b5f897afee90462bd95cf3cec42e115a

元コミット内容

cmd/dist: ファイル名の先頭が . または _ で始まるファイルを無視する。

これは build.ScanDir が使用するヒューリスティックと同じである。 macOS上の「リソースフォーク」ファイルを考慮しないようにする。 x.go のリソースは ._x.go である。

変更の背景

この変更の背景には、Go言語のビルドシステムが、意図しないファイルを処理してしまう問題がありました。特にmacOS環境では、ファイルシステムが「リソースフォーク」と呼ばれる特殊なファイルを生成することがあります。これは、メインのデータファイル(例: x.go)に関連するメタデータやリソース(アイコン、プレビュー情報など)を格納するために使用されます。リソースフォークファイルは通常、元のファイル名に ._ をプレフィックスとして付加した形式(例: ._x.go)で作成されます。

Goのビルドツール cmd/dist は、ソースディレクトリ内のすべてのファイルをスキャンし、ビルド対象として考慮していました。この挙動により、._x.go のようなリソースフォークファイルが誤ってGoのソースファイルとして認識され、ビルドエラーや予期せぬ動作を引き起こす可能性がありました。

この問題に対処するため、Goの標準ライブラリの一部である build.ScanDir 関数が既に採用していたヒューリスティック(ファイル名の先頭が . または _ で始まるファイルを無視する)を cmd/dist にも適用することになりました。これにより、macOSのリソースフォークファイルだけでなく、エディタの一時ファイル(例: .bak~ で終わるファイル)など、ビルドプロセスに不要なファイルが除外され、ビルドの堅牢性と効率が向上します。

前提知識の解説

cmd/dist

cmd/dist は、Go言語のツールチェインをビルドするための内部ツールです。Goのソースコードからコンパイラ、リンカ、標準ライブラリなどを構築する際に使用されます。Goのビルドシステムの中核をなす部分であり、Goのバージョンアップやクロスコンパイル環境の構築において重要な役割を果たします。

build.ScanDir

go/build パッケージは、Goのソースコードを解析し、パッケージの依存関係を解決するための機能を提供します。build.ScanDir は、指定されたディレクトリ内のGoソースファイルをスキャンし、パッケージ情報を収集する関数です。この関数は、GoのビルドシステムやIDE、コード分析ツールなどで広く利用されています。この関数が既に特定のファイル(先頭が ._ のファイル)を無視するヒューリスティックを持っていたことが、今回の cmd/dist の変更の根拠となっています。

macOSのリソースフォーク

macOS(および以前のMac OS)のHFS+ファイルシステムには、「リソースフォーク」という概念があります。これは、ファイルがデータフォーク(実際のファイル内容)とリソースフォーク(メタデータ、アイコン、GUI要素など)の2つの部分を持つことができるという特徴です。WindowsやLinuxなどの他のOSでは、通常、ファイルは単一のデータストリームとして扱われます。

リソースフォークは、特に古いMacアプリケーションでGUI要素やローカライズされた文字列を格納するために広く使用されていました。現代のmacOSでは、リソースフォークの使用は減少していますが、一部のシステムプロセスや特定のアプリケーションが互換性のために引き続き使用することがあります。

ネットワークファイルシステム(NFSやSMBなど)を介してmacOSのファイルを他のOSにコピーすると、リソースフォークは通常、メインのデータファイルとは別の隠しファイルとして表現されます。この隠しファイルは、元のファイル名に ._ をプレフィックスとして付加した形式(例: ._filename.ext)で作成されます。これらのファイルは、他のOSのファイルシステムでは単なる通常のファイルとして認識されるため、Goのビルドツールのようなプログラムが誤ってこれらを処理しようとすることがありました。

ヒューリスティック

ヒューリスティックとは、厳密な証明や保証はないものの、経験則に基づいて問題解決に役立つ発見的な手法や規則のことです。このコミットでは、「ファイル名の先頭が . または _ で始まるファイルを無視する」というヒューリスティックが採用されています。これは、これらのファイルが通常、隠しファイル、一時ファイル、またはシステム関連のファイルであり、Goのソースコードとしては不適切であるという経験則に基づいています。

技術的詳細

このコミットは、src/cmd/dist/build.c ファイルの install 関数に変更を加えています。install 関数は、Goの標準ライブラリやツールチェインの各コンポーネントをビルドし、インストールする役割を担っています。

変更の核心は、xreaddir 関数によって読み込まれたファイルリストから、特定の条件に合致するファイルをフィルタリングするロジックの追加です。具体的には、以下の条件に合致するファイルがリストから除外されます。

  1. ファイル名の先頭が . で始まる場合: これは、Unix系システムにおける隠しファイル(例: .gitignore, .DS_Store)や、macOSのリソースフォークファイル(例: ._main.go)を対象とします。
  2. ファイル名の先頭が _ で始まり、かつ .go 拡張子を持つ場合: コミットメッセージにもあるように、Goのソースファイルの中には _ で始まるものも存在しうるため、この条件はGoファイルに限定されています。これにより、例えば _cgo_export.c のようなC言語のファイルが誤って除外されることを防ぎつつ、._main.go のようなリソースフォークのGoファイルを確実に除外します。

このフィルタリング処理は、files という動的配列(bstr 型)を走査し、条件に合致しないファイルのみを新しい位置にコピーしていくことで実現されています。最終的に、配列の長さ files.len が更新され、不要なファイルがリストから論理的に削除されます。

この変更により、cmd/distbuild.ScanDir と同様のファイルフィルタリングロジックを持つことになり、ビルドプロセスにおける不要なファイルの処理を回避し、特にmacOS環境でのビルドの安定性を向上させます。

コアとなるコードの変更箇所

--- a/src/cmd/dist/build.c
+++ b/src/cmd/dist/build.c
@@ -555,6 +555,22 @@ install(char *dir)
 	// Everything in that directory, and any target-specific
 	// additions.
 	xreaddir(&files, bstr(&path));
+\
+	// Remove files beginning with . or _,\
+	// which are likely to be editor temporary files.\
+	// This is the same heuristic build.ScanDir uses.\
+	// There do exist real C files beginning with _,\
+	// so limit that check to just Go files.\
+	n = 0;\
+	for(i=0; i<files.len; i++) {\
+		p = files.p[i];\
+		if(hasprefix(p, ".") || (hasprefix(p, "_") && hassuffix(p, ".go")))\
+			xfree(p);\
+		else\
+			files.p[n++] = p;\
+	}\
+	files.len = n;\
+\
 	for(i=0; i<nelem(deptab); i++) {
 		if(hasprefix(dir, deptab[i].prefix)) {
 			for(j=0; (p=deptab[i].dep[j])!=nil; j++) {
@@ -595,7 +611,7 @@ install(char *dir)
 		}
 	}\
 	vuniq(&files);\
-\
+\
 	// Convert to absolute paths.
 	for(i=0; i<files.len; i++) {
 		if(!isabs(files.p[i])) {

コアとなるコードの解説

追加されたコードブロックは、install 関数内でファイルリスト files を処理する部分です。

  1. n = 0;: 新しい有効なファイルの数を追跡するためのカウンタ n を初期化します。
  2. for(i=0; i<files.len; i++) { ... }: files 配列の各要素(ファイルパス p)をループで処理します。
  3. p = files.p[i];: 現在のファイルパスを p に代入します。
  4. if(hasprefix(p, ".") || (hasprefix(p, "_") && hassuffix(p, ".go"))):
    • hasprefix(p, "."): ファイルパス p. で始まるかどうかをチェックします。
    • hasprefix(p, "_") && hassuffix(p, ".go"): ファイルパス p_ で始まり、かつ .go で終わるかどうかをチェックします。
    • これらの条件のいずれかが真である場合、そのファイルは除外対象となります。
  5. xfree(p);: 除外対象のファイルの場合、そのファイルパスが占めていたメモリを解放します。
  6. else files.p[n++] = p;: 除外対象ではないファイルの場合、そのファイルパスを files 配列の n 番目の位置にコピーし、n をインクリメントします。これにより、有効なファイルのみが配列の先頭から詰められていきます。
  7. files.len = n;: ループが終了した後、files 配列の実際の長さを n に更新します。これにより、除外されたファイルは配列の有効な範囲外となり、以降の処理では無視されます。

このロジックは、既存の配列をインプレースで変更することで、メモリ効率良く不要なファイルをフィルタリングしています。

関連リンク

参考にした情報源リンク

  • コミットメッセージ内の https://golang.org/cl/5616073 (Gerritの変更リストへのリンク)
  • Go言語の go/build パッケージのドキュメント (特に ScanDir 関数について): https://pkg.go.dev/go/build
  • macOSのリソースフォークに関する一般的な情報源 (例: Wikipedia, Apple Developer Documentation)
  • Unix系システムにおける隠しファイルの慣習に関する情報