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

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

このコミットは、Go言語のテストスイートにおいて、ディレクトリ内の隠しファイル(ファイル名の先頭が . で始まるファイル)をスキップするように変更を加えるものです。特に、Xcodeが生成する ._foo.go のようなファイルがテストプロセスに誤って含まれることを防ぐ目的があります。

コミット

commit c978a5a3a94fbd03cfe012fbf1ac556728d7fb41
Author: Russ Cox <rsc@golang.org>
Date:   Thu Mar 8 14:03:40 2012 -0500

    test: skip . files in directory
    
    Xcode generates ._foo.go files.
    
    R=golang-dev, gri
    CC=golang-dev
    https://golang.org/cl/5786055

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

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

元コミット内容

test: skip . files in directory

Xcode generates ._foo.go files.

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

変更の背景

この変更の背景には、macOS環境で開発を行う際にXcodeなどのツールが特定のファイルを扱う方法があります。macOSのファイルシステム(HFS+やAPFS)では、リソースフォークと呼ばれるメタデータがファイルに付随することがあります。これは、ファイルの作成日、変更日、アイコン、カスタム属性などの情報を格納するために使用されます。

Xcodeのような開発ツールや、Finderなどのシステムユーティリティがファイルをコピーしたり操作したりする際、これらのリソースフォーク情報を保持するために、元のファイル名に ._ をプレフィックスとして付けた隠しファイル(例: ._main.go)を生成することがあります。これらのファイルは、通常、ユーザーからは見えないように設定されていますが、ファイルシステム上には存在します。

Go言語のテストスイートがディレクトリ内の .go ファイルを列挙する際、これらの ._foo.go のような隠しファイルも誤って .go ファイルとして認識し、テスト対象に含めてしまう可能性がありました。しかし、これらのファイルは有効なGoのソースコードではないため、コンパイルエラーや予期せぬテストの失敗を引き起こす原因となります。

この問題を解決し、テストの安定性と信頼性を向上させるために、ファイル名の先頭が . で始まるファイルを明示的にスキップする変更が導入されました。

前提知識の解説

隠しファイル(Dotfiles)

Unix系OS(macOS、Linuxなど)では、ファイル名やディレクトリ名の先頭に . (ドット) を付けることで、そのファイルやディレクトリを「隠しファイル(dotfile)」として扱う慣習があります。これらのファイルは、通常、ファイルマネージャーやコマンドラインツール(例: ls コマンド)のデフォルト設定では表示されません。

隠しファイルは、主に設定ファイル(例: .bashrc, .gitconfig)や、システムが内部的に使用する一時ファイル、メタデータファイルなどに利用されます。今回のケースでは、Xcodeが生成する ._foo.go ファイルがこれに該当します。

Xcodeとリソースフォーク

XcodeはAppleが提供する統合開発環境(IDE)であり、macOS、iOS、watchOS、tvOS向けのアプリケーション開発に広く使用されます。Xcodeがファイルを操作する際に、macOSのファイルシステム特性であるリソースフォークを扱うことがあります。

リソースフォークは、ファイルのデータフォーク(実際のファイル内容)とは別に、ファイルに関する追加のメタデータを格納する仕組みです。古いMac OSの時代から存在し、ファイルのアイコンやカスタムプロパティなどを保存するために使われてきました。

現代のmacOSでは、リソースフォークはあまり直接的に使われることは少なくなりましたが、ファイルシステムレベルでの互換性や、特定のツールがメタデータを扱う際に、._ プレフィックスを持つ隠しファイルとしてリソースフォークの内容を保存することがあります。これは、AppleDoubleフォーマットと呼ばれる形式で、非HFS+ファイルシステム(例: FAT32、NTFS、ネットワーク共有)にファイルをコピーする際に、リソースフォーク情報を別途ファイルとして保存するために用いられます。

Go言語のテスト

Go言語には、標準でテストフレームワークが組み込まれており、go test コマンドを使用してテストを実行します。Goのテストファイルは、通常、テスト対象のソースファイルと同じディレクトリに配置され、ファイル名が _test.go で終わる必要があります。go test コマンドは、指定されたパッケージ内の _test.go ファイルを自動的に探し、テスト関数(TestXxxBenchmarkXxxExampleXxx)を実行します。

このコミットで変更された test/run.go は、Go言語の標準ライブラリやツールのテストスイートの一部であり、go test コマンドが内部的にどのようにファイルを処理するか、あるいはテスト実行環境をどのように準備するかに関連するロジックを含んでいると考えられます。

技術的詳細

このコミットの技術的な変更は非常にシンプルですが、効果的です。test/run.go ファイル内の goFiles 関数が変更されています。この関数は、指定されたディレクトリからGoのソースファイル(.go で終わるファイル)を収集する役割を担っています。

変更前は、ファイル名が .go で終わるかどうかのみをチェックしていました。

// 変更前
if strings.HasSuffix(name, ".go") {
    names = append(names, name)
}

変更後は、この条件に加えて、ファイル名が . で始まらないこともチェックするようになりました。

// 変更後
if !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") {
    names = append(names, name)
}

ここで使用されているGoの標準ライブラリ関数は以下の通りです。

  • strings.HasPrefix(s, prefix string) bool: 文字列 sprefix で始まる場合に true を返します。
  • strings.HasSuffix(s, suffix string) bool: 文字列 ssuffix で終わる場合に true を返します。

! 演算子は論理否定を表すため、!strings.HasPrefix(name, ".") は「ファイル名が . で始まらない」という条件を意味します。

この変更により、goFiles 関数は、ファイル名が .go で終わり、かつファイル名が . で始まらないファイルのみを有効なGoソースファイルとして認識し、リストに追加するようになります。これにより、._foo.go のような隠しファイルがテストプロセスに誤って含まれることがなくなります。

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

--- a/test/run.go
+++ b/test/run.go
@@ -147,7 +147,7 @@ func goFiles(dir string) []string {
 	check(err)
 	names := []string{}
 	for _, name := range dirnames {
-		if strings.HasSuffix(name, ".go") {
+		if !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") {
 			names = append(names, name)
 		}
 	}

コアとなるコードの解説

上記のコードスニペットは、test/run.go ファイル内の goFiles 関数の一部を示しています。

goFiles 関数は、引数 dir で指定されたディレクトリ内のGoソースファイル(.go ファイル)のリストを返すことを目的としています。

  1. dirnames は、指定されたディレクトリ内のファイルおよびディレクトリの名前のリストを保持しています。
  2. for _, name := range dirnames ループは、dirnames の各エントリ(ファイル名またはディレクトリ名)を反復処理します。
  3. 変更前の if strings.HasSuffix(name, ".go") は、ファイル名が .go で終わるかどうかだけをチェックしていました。
  4. 変更後の if !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") は、以下の2つの条件を論理AND (&&) で結合しています。
    • !strings.HasPrefix(name, "."): ファイル名が . で始まらないこと。これにより、._foo.go のような隠しファイルが除外されます。
    • strings.HasSuffix(name, ".go"): ファイル名が .go で終わること。これにより、Goソースファイルのみが対象となります。
  5. 両方の条件が真である場合のみ、その name は有効なGoソースファイルと見なされ、names スライスに追加されます。

この修正により、Goのテストスイートが、Xcodeなどのツールによって生成された無効な隠しファイルを誤って処理しようとすることを防ぎ、テストの実行がより堅牢になります。

関連リンク

参考にした情報源リンク