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

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

このコミットは、Go言語のコマンドラインツール(cmd/go)におけるパッケージスキャン処理の改善を目的としています。具体的には、パッケージの検索時に、名前にアンダースコア(_)で始まるディレクトリ(例: _obj)をスキップするように変更が加えられました。これにより、ビルドアーティファクトなどが含まれる不要なディレクトリがスキャン対象から除外され、パッケージ検索の効率化と正確性の向上が図られています。

コミット

commit f47807a57f9dacab74ebf7e9d86f3dc0dcb933b0
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Wed Jan 18 19:27:16 2012 -0800

    cmd/go: skip _obj directories in package scans
    
    Fixes #2693
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5557057

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

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

元コミット内容

cmd/go: skip _obj directories in package scans

Fixes #2693

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/5557057

変更の背景

Go言語のビルドシステムでは、コンパイル済みオブジェクトファイルやその他のビルドアーティファクトを格納するために、慣習的にアンダースコアで始まるディレクトリ(例: _obj)が使用されることがあります。cmd/goツールがパッケージをスキャンする際、これらのディレクトリも対象に含まれてしまうと、以下のような問題が発生する可能性がありました。

  1. パフォーマンスの低下: 不要なディレクトリを走査することで、パッケージスキャンにかかる時間が増加します。特に大規模なプロジェクトや多数のビルドアーティファクトが存在する場合、この影響は顕著になります。
  2. 誤ったパッケージの検出: _objディレクトリ内に、意図しない形でGoのソースファイルやパッケージとして解釈されうるファイルが存在した場合、cmd/goがそれらを誤ってパッケージとして認識してしまう可能性があります。これはビルドエラーや予期せぬ動作の原因となり得ます。
  3. ビルドシステムの整合性: _objのようなディレクトリは、通常、一時的なビルド成果物であり、ユーザーが直接操作するパッケージソースとは区別されるべきものです。これらをスキップすることで、cmd/goのパッケージ検索がより「クリーン」なソースコードのみを対象とするようになり、ビルドシステムの整合性が保たれます。

コミットメッセージにある「Fixes #2693」は、この変更が特定の課題(Issue 2693)を解決したことを示しています。ただし、現在の公開されているGoのIssueトラッカーでは、この番号に直接関連する詳細な情報を見つけることは困難でした。これは、コミットが2012年と古いため、当時の内部的なIssue管理システムや、現在ではクローズされているか番号が変更されている可能性が考えられます。しかし、変更内容から推測するに、上記のような問題が実際に発生し、それを解決するためにこのコミットが作成されたと考えるのが妥当です。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびファイルシステム関連の概念を理解しておく必要があります。

  • Goパッケージスキャン: go buildgo installなどのコマンドを実行する際、Goツールは指定されたパスや現在のディレクトリからGoのソースコード(パッケージ)を検索します。この検索プロセスは、ファイルシステムを再帰的に走査して行われます。
  • filepath.Split: Go標準ライブラリのpath/filepathパッケージに含まれる関数です。与えられたパス文字列をディレクトリ部分とファイル名(または最後のディレクトリ名)部分に分割します。例えば、filepath.Split("/a/b/c.go")("/a/b/", "c.go")を返します。このコミットでは、ディレクトリのパスからそのディレクトリ自身の名前(elem)を取得するために使用されています。
  • strings.HasPrefix: Go標準ライブラリのstringsパッケージに含まれる関数です。ある文字列が特定のプレフィックス(接頭辞)で始まるかどうかを判定します。このコミットでは、ディレクトリ名が.(ドット)や_(アンダースコア)で始まるかどうかをチェックするために使用されています。
  • filepath.SkipDir: path/filepathパッケージのWalkFuncfilepath.Walk関数に渡されるコールバック関数)が返すエラー値の一つです。WalkFuncfilepath.SkipDirを返すと、filepath.Walkはそのディレクトリのサブディレクトリへの再帰的な走査をスキップします。これにより、特定の条件に合致するディレクトリツリー全体を効率的に無視することができます。
  • .fooディレクトリ: Unix系システムでは、ファイル名やディレクトリ名が.で始まるものは隠しファイル/ディレクトリとして扱われる慣習があります。Goのパッケージスキャンにおいても、これらの隠しディレクトリは通常、パッケージソースを含まないためスキップされます。
  • testdataディレクトリ: Goのテストコードにおいて、テスト用のデータファイルを格納するために慣習的に使用されるディレクトリです。このディレクトリ内のファイルはGoのソースコードとしてコンパイルされるべきではないため、パッケージスキャンから除外されます。
  • _objディレクトリ: 過去のGoのビルドシステムや一部のプロジェクトでは、コンパイル済みオブジェクトファイルや中間生成物を_objのようなアンダースコアで始まるディレクトリに格納する慣習がありました。これらはソースコードではないため、パッケージスキャンから除外されるべきです。

技術的詳細

このコミットの変更は、src/cmd/go/main.goファイル内のallPackages関数とallPackagesInFS関数の2箇所に適用されています。これらの関数は、Goのパッケージを検索し、そのパスのリストを返す役割を担っています。

両関数内では、filepath.Walk関数を使用してファイルシステムを再帰的に走査しています。filepath.Walkは、走査中にディレクトリやファイルが見つかるたびに、引数として渡されたWalkFuncコールバック関数を呼び出します。このWalkFunc内で、特定の条件に基づいてディレクトリをスキップするかどうかの判断が行われます。

変更前のコードでは、ディレクトリ名が.で始まる(隠しディレクトリ)か、またはtestdataという名前である場合に、filepath.SkipDirを返してそのディレクトリの走査をスキップしていました。

// 変更前
if strings.HasPrefix(elem, ".") || elem == "testdata" {
    return filepath.SkipDir
}

このコミットでは、この条件にstrings.HasPrefix(elem, "_")が追加されました。

// 変更後
if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
    return filepath.SkipDir
}

これにより、ディレクトリ名がアンダースコア(_)で始まる場合も、そのディレクトリとそのサブディレクトリがパッケージスキャンの対象から除外されるようになりました。この変更は、特に_objのようなビルドアーティファクトを格納するディレクトリを効率的に無視するために重要です。

allPackages関数は、GOPATH環境変数で指定されたパスやGoの標準ライブラリパスなど、Goのビルドシステムが認識するすべてのパッケージパスを検索します。一方、allPackagesInFS関数は、特定のファイルシステムパス内でのパッケージ検索に特化しています。どちらの関数も同様のディレクトリスキップロジックを共有しているため、両方に同じ変更が適用されています。

この変更は、Goツールがパッケージを検出する際の基本的な振る舞いを改善し、不要なファイルやディレクトリを無視することで、より堅牢で効率的なビルドプロセスに貢献しています。

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

diff --git a/src/cmd/go/main.go b/src/cmd/go/main.go
index 8ef6395f4f..fdea80916f 100644
--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -327,9 +327,9 @@ func allPackages(pattern string) []string {
 			return nil
 		}
 
-		// Avoid .foo and testdata directory trees.
+		// Avoid .foo, _foo, and testdata directory trees.
 		_, elem := filepath.Split(path)
-		if strings.HasPrefix(elem, ".") || elem == "testdata" {
+		if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
 			return filepath.SkipDir
 		}
 
@@ -394,9 +394,9 @@ func allPackagesInFS(pattern string) []string {
 			return nil
 		}
 
-		// Avoid .foo and testdata directory trees.
+		// Avoid .foo, _foo, and testdata directory trees.
 		_, elem := filepath.Split(path)
-		if strings.HasPrefix(elem, ".") || elem == "testdata" {
+		if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
 			return filepath.SkipDir
 		}
 

コアとなるコードの解説

上記のdiffは、src/cmd/go/main.goファイル内の2つの関数、allPackagesallPackagesInFSにおける変更を示しています。

  1. コメントの変更:

    -		// Avoid .foo and testdata directory trees.
    +		// Avoid .foo, _foo, and testdata directory trees.
    

    これはコードの振る舞いを説明するコメントの更新です。以前は.で始まるディレクトリとtestdataディレクトリを避けることを示していましたが、変更後は_で始まるディレクトリも避けるようになったことを明示しています。これはコードの意図を正確に反映するための重要な変更です。

  2. 条件式の変更:

    -		if strings.HasPrefix(elem, ".") || elem == "testdata" {
    +		if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
    

    これがこのコミットの核心的な変更です。

    • elemは、filepath.Split(path)によって取得された現在のディレクトリ名(またはファイル名)です。
    • 変更前は、elem.で始まる(strings.HasPrefix(elem, "."))か、またはelemが厳密に"testdata"である場合に、そのディレクトリをスキップしていました。
    • 変更後は、この条件に|| strings.HasPrefix(elem, "_")が追加されました。これにより、elemがアンダースコア(_)で始まる場合も、ディレクトリがスキップされるようになりました。

この変更は、allPackagesallPackagesInFSの両方の関数内のfilepath.Walkコールバック関数内で適用されています。filepath.Walkはファイルシステムを再帰的に走査する際に、各ディレクトリに対してこの条件を評価します。条件が真(true)になった場合、filepath.SkipDirが返され、filepath.Walkはそのディレクトリのサブディレクトリへの再帰的な走査を停止し、次の兄弟ディレクトリへと進みます。

これにより、_objのようなビルドアーティファクトを格納するディレクトリがGoのパッケージスキャンから除外され、スキャンプロセスの効率化と、誤ったパッケージ検出の防止に貢献しています。

関連リンク

参考にした情報源リンク