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

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

このコミットは、Go言語のコマンドラインツールcmd/goにおける、テスト実行時の二重コンパイル(double compilation)の問題を修正するものです。具体的には、テスト対象のパッケージが依存するパッケージのビルドに失敗した場合に、早期に処理を終了せず、不必要な再コンパイルが発生していた問題を解決します。

コミット

commit f0fef323833b9b060024507a3229fa7bf9b523a8
Author: Rob Pike <r@golang.org>
Date:   Fri Aug 16 12:49:51 2013 +1000

    cmd/go: fix at least some instances of double compilation
    
    When the packages the tested package depends on don't build,
    we weren't getting out early. Added a simple check for a successful
    build to an existing early out.
    
    There may be other ways that double compilation arises, but
    this fixes the one listed in the issue.
    Fixes #5679
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/13036043

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

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

元コミット内容

cmd/go: fix at least some instances of double compilation

このコミットは、cmd/goツールにおける二重コンパイルの少なくとも一部のインスタンスを修正します。テスト対象のパッケージが依存するパッケージのビルドに失敗した場合に、早期に処理を終了していませんでした。既存の早期終了処理に、ビルドが成功したかどうかの単純なチェックを追加しました。二重コンパイルが発生する他の方法があるかもしれませんが、これはイシューに記載されているものを修正します。

Fixes #5679

変更の背景

この変更は、Goのイシュートラッカーで報告されたIssue 5679: cmd/go: double compilation when dependencies fail to buildに対応するものです。

このイシューでは、go testコマンドを実行した際に、テスト対象のパッケージが依存するパッケージのビルドに失敗した場合でも、go testが不必要に再度コンパイルを試みるという問題が報告されていました。通常、ビルドが失敗した場合は、それ以上処理を進めるべきではありませんが、このケースでは早期終了せずに処理が続行され、結果として無駄なコンパイル処理が繰り返されていました。これは、ビルド時間の増加や、エラーメッセージの重複といった形でユーザーエクスペリエンスを損なう可能性がありました。

このコミットの目的は、このような非効率な動作を排除し、ビルド失敗時に速やかに処理を中断することで、go testコマンドの堅牢性と効率性を向上させることにあります。

前提知識の解説

このコミットを理解するためには、以下のGo言語のビルドシステムとテストに関する基本的な知識が必要です。

  • go testコマンド: Go言語の標準的なテスト実行コマンドです。指定されたパッケージのテストコードを実行し、結果を報告します。テスト実行時には、テスト対象のパッケージとその依存関係がビルドされます。
  • パッケージの依存関係: Goのプロジェクトは複数のパッケージで構成され、あるパッケージが別のパッケージの機能を利用する場合、そのパッケージに依存していると見なされます。go buildgo testのようなコマンドは、これらの依存関係を解決し、必要に応じてビルドを行います。
  • ビルドプロセス: goコマンドは、ソースコードをコンパイルして実行可能なバイナリやパッケージアーカイブを生成します。このプロセス中にエラーが発生した場合、通常はビルドが中断されます。
  • 早期終了 (Early Exit): プログラムやスクリプトの実行中に、特定の条件(例えばエラーの発生)が満たされた場合に、それ以降の処理をスキップして速やかに終了するプログラミングパターンです。これにより、無駄な処理を避け、リソースを節約し、エラーハンドリングを簡素化できます。
  • cmd/go: Go言語のツールチェインの中核をなすコマンドラインツールです。go build, go run, go testなど、Go開発者が日常的に使用する多くのサブコマンドを提供します。このコミットで変更されるtest.goファイルは、go testコマンドの内部ロジックを定義しています。
  • actiondoメソッド: Goのビルドシステム内部では、各パッケージのビルドやインストールといった操作が「アクション」として抽象化され、それらのアクションを実行するロジックがdoメソッドなどにカプセル化されています。a.failedのようなフラグは、特定のアクションが成功したか失敗したかを追跡するために使用されます。

技術的詳細

このコミットは、src/cmd/go/test.goファイル内のrunTest関数に小さな変更を加えることで、二重コンパイルの問題を解決しています。

runTest関数は、go testコマンドの主要なロジックを処理します。テスト対象のパッケージとその依存関係のビルド、テストの実行、結果の集計などを行います。

変更前のコードでは、依存パッケージのビルドが完了した後、!testCという条件で早期終了を判断していました。testCは、テストコンパイルが必要かどうかを示すフラグであると推測されます。しかし、この条件だけでは、依存パッケージのビルド自体が失敗した場合に、その失敗を適切に検知して早期終了することができませんでした。

コミットの変更は、この早期終了の条件にa.failedという新しい条件を追加しています。aは、現在のテストアクション(または関連するビルドアクション)を表すオブジェクトであり、a.failedはそのアクションが失敗したかどうかを示すブール値のフラグです。

したがって、変更後の条件!testC || a.failedは、以下のいずれかの条件が真であれば早期終了することを意味します。

  1. testCが偽である(テストコンパイルが不要である)。
  2. a.failedが真である(現在のビルドまたはテストアクションが失敗した)。

このa.failedの追加により、テスト対象のパッケージが依存するパッケージのビルドが失敗した場合、a.failedtrueとなり、runTest関数はそれ以降の不必要な処理(二重コンパイルを含む)を実行せずに早期に終了するようになります。これにより、リソースの無駄遣いを防ぎ、ユーザーに迅速にビルド失敗を通知できるようになります。

この修正は、特定のシナリオ(依存関係のビルド失敗)における二重コンパイルを直接的に解決しますが、コミットメッセージにもあるように、二重コンパイルが発生する他の原因が存在する可能性も示唆しています。しかし、この修正は報告されたイシューに特化した、効果的な解決策です。

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

--- a/src/cmd/go/test.go
+++ b/src/cmd/go/test.go
@@ -378,7 +378,7 @@ func runTest(cmd *Command, args []string) {
 		t.deps = append(t.deps, b.action(modeInstall, modeInstall, p))
 	}
 	b.do(t)
-	if !testC {
+	if !testC || t.failed {
 		return
 	}
 	b.init()

コアとなるコードの解説

変更はsrc/cmd/go/test.goファイルのrunTest関数内、具体的には379行目付近にあります。

元のコード:

	if !testC {
		return
	}

この行は、testCという変数がfalseの場合に、runTest関数から早期にreturn(終了)する条件を示していました。testCは、テストのコンパイルが必要かどうかを示すフラグであると推測されます。つまり、テストコンパイルが不要な場合は、ここで処理を終了していました。

変更後のコード:

	if !testC || t.failed {
		return
	}

この変更では、既存の条件!testCに加えて、論理OR演算子||を用いてt.failedという新しい条件が追加されました。

  • tは、このコンテキストにおける現在のテストアクション(または関連するビルドアクション)を表す変数です。
  • t.failedは、そのアクションが失敗したかどうかを示すブール値のフィールドです。

この修正により、runTest関数は以下のいずれかの条件が満たされた場合に早期終了するようになります。

  1. testCfalseである(テストコンパイルが不要な場合)。
  2. t.failedtrueである(現在のテストまたはビルドアクションが失敗した場合)。

特に2番目の条件が重要です。これにより、テスト対象のパッケージが依存するパッケージのビルドが失敗した場合(その失敗がt.failedに反映される)、runTest関数はそれ以上処理を進めることなく、すぐに終了します。これにより、ビルドが失敗しているにもかかわらず、不必要にテストの再コンパイルや実行を試みる「二重コンパイル」の問題が解消されます。

この変更は非常に小さく見えますが、ビルドシステムの効率性とエラーハンドリングの正確性を向上させる上で重要な役割を果たしています。

関連リンク

参考にした情報源リンク