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

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

このコミットは、Go言語のコマンドラインツール cmd/go において、外部テストファイル (XTestGoFiles) が gofmtgovetgofix といったコード品質向上ツールによって適切に処理されるようにするための変更です。また、XTestGoFilesgo command の公開APIに追加し、Goパッケージの構造に関する情報がより正確に反映されるようにしています。

コミット

commit 2693232f16fb91d715349b81bbb7af3c96c5459e
Author: Sanjay Menakuru <balasanjay@gmail.com>
Date:   Thu Jan 5 10:37:15 2012 +1100

    cmd/go: include external test files in the files sent to gofmt, govet, and gofix
    
    Also, add XTestGoFiles to the go command's public api.
    
    Fixes #2649.
    
    R=golang-dev, adg
    CC=golang-dev
    https://golang.org/cl/5502102

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

https://github.com/golang/go/commit/2693232f16fb91d715349b81bbb7af3c96c5459e

元コミット内容

cmd/go: gofmtgovetgofix に送られるファイルに外部テストファイルを含める。 また、XTestGoFilesgo command の公開APIに追加する。 Issue #2649 を修正。

変更の背景

このコミットが行われる以前は、Go言語のビルドシステムおよびツール群において、パッケージ内部のテストファイル (TestGoFiles) とは異なる「外部テストファイル」(XTestGoFiles) が、gofmt (コードフォーマッタ)、govet (静的解析ツール)、gofix (コード自動修正ツール) といった重要な開発ツールによって適切に処理されていませんでした。

これにより、以下のような問題が発生する可能性がありました。

  1. コードスタイルの不統一: 外部テストファイルが gofmt の対象外であったため、これらのファイルのコードスタイルがプロジェクト全体の規約から逸脱する可能性がありました。手動でのフォーマットは手間がかかり、見落としも発生しやすくなります。
  2. 潜在的なバグの見落とし: govet はGoコードの一般的なバグや疑わしい構造を検出するのに役立ちますが、外部テストファイルが解析対象外であると、テストコード内に潜むバグや非効率な記述が見過ごされるリスクがありました。
  3. API変更への追従の遅れ: gofix はGo言語の進化に伴うAPIの変更や非推奨化に対応するために、古いコードを自動的に新しい形式に修正するツールです。外部テストファイルが gofix の対象外であると、Go言語のバージョンアップ時にこれらのテストファイルがコンパイルエラーになったり、意図しない動作を引き起こしたりする可能性がありました。

このコミットは、これらの問題を解決し、Goプロジェクト全体のコード品質、一貫性、およびメンテナンス性を向上させることを目的としています。特に、テストコードも本番コードと同様に高品質であることが、ソフトウェア全体の信頼性を保証する上で不可欠であるという認識に基づいています。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびGoツールチェーンに関する知識が必要です。

  1. Go言語のパッケージとテスト:

    • パッケージ: Go言語のコードはパッケージにまとめられます。パッケージはディレクトリ構造に対応し、関連する機能を提供します。
    • 内部テスト (TestGoFiles): 通常、テストファイルはテスト対象のパッケージと同じディレクトリに配置され、ファイル名が _test.go で終わります。これらのテストファイルは、テスト対象のパッケージと同じ名前空間に属し、パッケージの内部関数や変数にアクセスできます。これらは Package.TestGoFiles として扱われます。
    • 外部テスト (XTestGoFiles): 大規模なパッケージや、パッケージの公開APIのみをテストしたい場合、テストファイルを別のパッケージとして定義することがあります。この場合、テストファイルは _test.go で終わるファイル名を持ちますが、_test サフィックスを持つ別のパッケージ(例: package foo_test)に属します。これにより、テストコードはテスト対象のパッケージの公開された要素のみにアクセスでき、より厳密な結合テストが可能になります。これらは Package.XTestGoFiles として扱われます。
  2. Goツールチェーンの主要コマンド:

    • go test: Go言語のテストを実行するためのコマンドです。内部テストと外部テストの両方を自動的に検出して実行します。
    • gofmt: Goソースコードを標準的なスタイルに自動的にフォーマットするツールです。Goコミュニティでは、gofmt によってフォーマットされたコードが標準とされています。
    • govet: Goソースコードの静的解析を行うツールです。潜在的なバグや疑わしいコード構造(例: unreachable code, unkeyed struct literals)を検出します。
    • gofix: Go言語の進化に伴い、APIの変更や非推奨化が行われた際に、古いコードを新しいAPIに自動的に修正するためのツールです。Go 1リリース前の移行期間に特に重要でした。
  3. cmd/go パッケージ:

    • Go言語の公式コマンドラインツール (go コマンド) の実装が含まれるパッケージです。このパッケージは、Goのビルド、テスト、フォーマット、静的解析など、Go開発における様々なタスクを管理します。
    • Package 構造体: cmd/go パッケージ内で、Goのパッケージに関するメタデータ(ソースファイル、依存関係など)を表現するために使用される構造体です。この構造体には、GoFiles (通常のGoソースファイル)、TestGoFiles (内部テストファイル)、CFiles (Cソースファイル) などのフィールドが含まれます。
    • scanPackage 関数: ファイルシステムをスキャンし、Goのパッケージ情報を解析して Package 構造体を生成する役割を担う関数です。

技術的詳細

このコミットの技術的な核心は、cmd/go パッケージ内の Package 構造体と、パッケージをスキャンするロジックの変更にあります。

  1. Package 構造体への XTestGoFiles フィールドの追加:

    • src/cmd/go/list.go および src/cmd/go/pkg.go 内の Package 構造体に、XTestGoFiles []string という新しいフィールドが追加されました。このフィールドは、パッケージの外部テストファイル (_test.go で終わるが、別の _test パッケージに属するファイル) のリストを保持します。
    • これにより、go command の内部で、外部テストファイルが正式に認識され、その情報が Package 構造体を通じて公開されるようになりました。
  2. scanPackage 関数における XTestGoFiles の読み込み:

    • src/cmd/go/pkg.go 内の scanPackage 関数が修正され、build.DirInfo から XTestGoFiles の情報を読み込み、新しく追加された Package.XTestGoFiles フィールドに割り当てるようになりました。
    • build.DirInfo は、Goのビルドシステムがディレクトリをスキャンして収集するファイル情報を含んでいます。この変更により、scanPackage がパッケージのすべての関連ファイルを正確に識別できるようになります。
  3. gofiles スライスへの XTestGoFiles の追加:

    • Package 構造体には、gofiles という内部フィールドがあります。これは、gofmtgovetgofix などのツールが処理すべきすべてのGoソースファイルの絶対パスを保持するスライスです。
    • このコミットでは、scanPackage 関数内で、info.XTestGoFiles の内容が p.gofiles スライスに追加されるようになりました。
    • この変更が最も重要です。これにより、gofmtgovetgofix といったツールが Package.gofiles を参照して処理対象ファイルを決定する際に、外部テストファイルも自動的に含まれるようになります。結果として、これらのツールは外部テストファイルに対してもフォーマット、静的解析、自動修正を適用するようになります。

これらの変更により、Goツールチェーンは外部テストファイルを「ファーストクラスの市民」として扱い、本番コードと同様に品質管理の対象とすることで、Goプロジェクト全体の健全性を高めています。

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

このコミットは主に src/cmd/go/list.gosrc/cmd/go/pkg.go の2つのファイルに影響を与えています。

src/cmd/go/list.go の変更

Package 構造体の定義に XTestGoFiles フィールドが追加され、既存の GoFiles および TestGoFiles のコメントが更新されました。

--- a/src/cmd/go/list.go
+++ b/src/cmd/go/list.go
@@ -36,12 +36,13 @@ being passed to the template is:
         Stale      bool   // would 'go install' do anything for this package?
 
         // Source files
-        GoFiles     []string // .go source files (excluding CgoFiles and TestGoFiles)
-        TestGoFiles []string // _test.go source files
-        CFiles      []string // .c source files
-        HFiles      []string // .h source files
-        SFiles      []string // .s source files
-        CgoFiles    []string // .go sources files that import "C"
+        GoFiles      []string // .go source files (excluding CgoFiles, TestGoFiles, and XTestGoFiles)
+        TestGoFiles  []string // _test.go source files internal to the package they are testing
+        XTestGoFiles []string // _test.go source files external to the package they are testing
+        CFiles       []string // .c source files
+        HFiles       []string // .h source files
+        SFiles       []string // .s source files
+        CgoFiles     []string // .go sources files that import "C"
 
         // Dependency information
         Imports []string // import paths used by this package

src/cmd/go/pkg.go の変更

  1. Package 構造体の定義に XTestGoFiles フィールドが追加され、JSONタグが設定されました。
  2. Package 構造体の内部フィールド gofiles のコメントが更新され、XTestGoFiles も含まれることが明記されました。
  3. scanPackage 関数内で、Package 構造体の初期化時に info.XTestGoFiles が新しい XTestGoFiles フィールドに割り当てられるようになりました。
  4. scanPackage 関数内で、info.XTestGoFiles の各ファイルパスが p.gofiles スライスに追加されるようになりました。
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -29,14 +29,15 @@ type Package struct {
 	Stale      bool   `json:",omitempty"` // would 'go install' do anything for this package?
 
 	// Source files
-\tGoFiles     []string // .go source files (excluding CgoFiles and TestGoFiles)
-\tTestGoFiles []string `json:",omitempty"` // _test.go source files
-\tCFiles      []string `json:",omitempty"` // .c source files
-\tHFiles      []string `json:",omitempty"` // .h source files
-\tSFiles      []string `json:",omitempty"` // .s source files
-\tCgoFiles    []string `json:",omitempty"` // .go sources files that import "C"
-\tCgoCFLAGS   []string `json:",omitempty"` // cgo: flags for C compiler
-\tCgoLDFLAGS  []string `json:",omitempty"` // cgo: flags for linker
+\tGoFiles      []string // .go source files (excluding CgoFiles, TestGoFiles and XTestGoFiles)
+\tTestGoFiles  []string `json:",omitempty"` // _test.go source files internal to the package they are testing
+\tXTestGoFiles []string `json:",omitempty"` //_test.go source files external to the package they are testing
+\tCFiles       []string `json:",omitempty"` // .c source files
+\tHFiles       []string `json:",omitempty"` // .h source files
+\tSFiles       []string `json:",omitempty"` // .s source files
+\tCgoFiles     []string `json:",omitempty"` // .go sources files that import "C"
+\tCgoCFLAGS    []string `json:",omitempty"` // cgo: flags for C compiler
+\tCgoLDFLAGS   []string `json:",omitempty"` // cgo: flags for linker
 
 	// Dependency information
 	Imports []string `json:",omitempty"` // import paths used by this package
@@ -47,7 +48,7 @@ type Package struct {
 \tpkgdir  string
 \tinfo    *build.DirInfo
 \timports []*Package
-\tgofiles []string // GoFiles+CgoFiles+TestGoFiles files, absolute paths
+\tgofiles []string // GoFiles+CgoFiles+TestGoFiles+XTestGoFiles files, absolute paths
 \ttarget  string   // installed file for this package (may be executable)
 \tfake    bool     // synthesized package
 }\n
@@ -127,23 +128,24 @@ func scanPackage(ctxt *build.Context, t *build.Tree, arg, importPath, dir string\n \t}\n \n \tp := &Package{\n-\t\tName:        info.Package,\n-\t\tDoc:         doc.CommentText(info.PackageComment),\n-\t\tImportPath:  importPath,\n-\t\tDir:         dir,\n-\t\tImports:     info.Imports,\n-\t\tGoFiles:     info.GoFiles,\n-\t\tTestGoFiles: info.TestGoFiles,\n-\t\tCFiles:      info.CFiles,\n-\t\tHFiles:      info.HFiles,\n-\t\tSFiles:      info.SFiles,\n-\t\tCgoFiles:    info.CgoFiles,\n-\t\tCgoCFLAGS:   info.CgoCFLAGS,\n-\t\tCgoLDFLAGS:  info.CgoLDFLAGS,\n-\t\tStandard:    t.Goroot && !strings.Contains(importPath, \".\"),\n-\t\ttarget:      target,\n-\t\tt:           t,\n-\t\tinfo:        info,\n+\t\tName:         info.Package,\n+\t\tDoc:          doc.CommentText(info.PackageComment),\n+\t\tImportPath:   importPath,\n+\t\tDir:          dir,\n+\t\tImports:      info.Imports,\n+\t\tGoFiles:      info.GoFiles,\n+\t\tTestGoFiles:  info.TestGoFiles,\n+\t\tXTestGoFiles: info.XTestGoFiles,\n+\t\tCFiles:       info.CFiles,\n+\t\tHFiles:       info.HFiles,\n+\t\tSFiles:       info.SFiles,\n+\t\tCgoFiles:     info.CgoFiles,\n+\t\tCgoCFLAGS:    info.CgoCFLAGS,\n+\t\tCgoLDFLAGS:   info.CgoLDFLAGS,\n+\t\tStandard:     t.Goroot && !strings.Contains(importPath, \".\"),\n+\t\ttarget:       target,\n+\t\tt:            t,\n+\t\tinfo:         info,\n \t}\n \n \tvar built time.Time\n@@ -162,6 +164,9 @@ func scanPackage(ctxt *build.Context, t *build.Tree, arg, importPath, dir string\n \tfor _, f := range info.TestGoFiles {\n \t\tp.gofiles = append(p.gofiles, filepath.Join(dir, f))\n \t}\n+\tfor _, f := range info.XTestGoFiles {\n+\t\tp.gofiles = append(p.gofiles, filepath.Join(dir, f))\n+\t}\n \n \tsort.Strings(p.gofiles)\n \n```

## コアとなるコードの解説

### `src/cmd/go/list.go` の変更

*   **`Package` 構造体への `XTestGoFiles` の追加**:
    *   この変更は、`go list` コマンドがパッケージ情報を表示する際に、外部テストファイルに関する情報も提供できるようにするためのものです。`go list -json` のようなコマンドでパッケージの詳細情報を取得する際に、`XTestGoFiles` のリストが含まれるようになります。
    *   コメントの更新は、`GoFiles` が `CgoFiles`, `TestGoFiles`, `XTestGoFiles` を除外すること、`TestGoFiles` がパッケージ内部のテストファイルであること、そして `XTestGoFiles` がパッケージ外部のテストファイルであることを明確にしています。これにより、Goのソースファイルの種類と役割がより正確に定義されます。

### `src/cmd/go/pkg.go` の変更

*   **`Package` 構造体への `XTestGoFiles` の追加とJSONタグ**:
    *   `list.go` と同様に、`Package` 構造体に `XTestGoFiles` が追加されます。`json:",omitempty"` タグは、このフィールドが空の場合にJSON出力から省略されることを意味します。これは、`go list -json` の出力の簡潔さを保つための一般的なGoの慣習です。
*   **`gofiles` コメントの更新**:
    *   `gofiles` フィールドのコメントに `+XTestGoFiles` が追加されたことは、このスライスが `gofmt`、`govet`、`gofix` などのツールが処理すべきすべてのGoソースファイル(通常のGoファイル、Cgoファイル、内部テストファイル、そして今回の変更で追加された外部テストファイル)の絶対パスを含むことを明示しています。これは、ツールの動作を理解する上で非常に重要な情報です。
*   **`scanPackage` 関数における `Package` 構造体の初期化**:
    *   `scanPackage` 関数は、Goのビルドシステムがパッケージのソースファイルをスキャンして情報を収集する中心的な役割を担います。
    *   `p.XTestGoFiles: info.XTestGoFiles,` の行が追加されたことで、`build.DirInfo` から取得した外部テストファイルのリストが、新しく追加された `Package.XTestGoFiles` フィールドに正確にマッピングされるようになりました。これにより、`go command` は外部テストファイルの存在を認識し、その情報を内部で管理できるようになります。
*   **`gofiles` スライスへの `XTestGoFiles` の追加**:
    *   この部分が、`gofmt`、`govet`、`gofix` が外部テストファイルを処理するようになるための直接的な変更です。
    *   `for _, f := range info.XTestGoFiles { p.gofiles = append(p.gofiles, filepath.Join(dir, f)) }` のループが追加されたことで、`scanPackage` がパッケージのすべてのGoソースファイル(通常のGoファイル、Cgoファイル、内部テストファイル)に加えて、外部テストファイルも `p.gofiles` スライスに含めるようになりました。
    *   `p.gofiles` は、前述の通り、各種ツールが処理対象とするファイルのリストです。したがって、この変更により、これらのツールは外部テストファイルも自動的に処理するようになり、コード品質のチェックと維持の範囲が拡張されました。

これらの変更は、Goツールチェーンの堅牢性と完全性を高め、開発者がより一貫性のある方法でGoコードベース全体(テストコードを含む)を管理できるようにするために不可欠でした。

## 関連リンク

*   **GitHubコミットページ**: [https://github.com/golang/go/commit/2693232f16fb91d715349b81bbb7af3c96c5459e](https://github.com/golang/go/commit/2693232f16fb91d715349b81bbb7af3c96c5459e)
*   **Go CL (Change List)**: `https://golang.org/cl/5502102` (このリンクは古いGoのコードレビューシステムのものであり、現在は直接アクセスできない可能性がありますが、コミットメッセージに記載されているため参考として残します。)
*   **Go Issue #2649**: コミットメッセージに `Fixes #2649` と記載されていますが、GoのIssueトラッカーがGoogle CodeからGitHubへ移行したため、古いIssue番号は直接参照できない場合があります。このIssueは、外部テストファイルがツールによって処理されないという問題に関連していたと推測されます。

## 参考にした情報源リンク

*   Go言語の公式ドキュメント (特にパッケージ、テスト、`go` コマンドに関するセクション)
*   `gofmt`, `govet`, `gofix` に関するGoブログやドキュメント
*   Go言語のソースコード (特に `cmd/go` パッケージ)
*   Go言語のテストに関する一般的な慣習とベストプラクティス
*   Go言語のIssueトラッカーの歴史と移行に関する情報 (Google CodeからGitHubへ)