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

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

このコミットは、Go言語のコマンドラインツールであるgoコマンドのテスト関連フラグを処理するsrc/cmd/go/testflag.goファイルに対する変更です。具体的には、go testコマンドがCコンパイラに渡すフラグである-ccflagsの取り扱いを改善することを目的としています。

コミット

  • コミットハッシュ: 0a8f5177f698086b4f83defb15a6010d6ad863a5
  • 作者: Shenghou Ma minux.ma@gmail.com
  • コミット日時: Mon Apr 21 00:02:21 2014 -0400

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

https://github.com/golang/go/commit/0a8f5177f6998086b4f83defb15a6010d6ad863a5

元コミット内容

cmd/go: handle -ccflags in 'go test'
CL 89050043 only allows -ccflags for 'go test', this
CL really handles the flag like the other -??flags.
Many thanks to Dobrosław Żybort for pointing this out.
Fixes #7810 (again).

LGTM=iant, matrixik
R=golang-codereviews, iant, matrixik
CC=golang-codereviews
https://golang.org/cl/89230044

変更の背景

このコミットの背景には、go testコマンドにおける-ccflagsの不完全な処理がありました。コミットメッセージによると、以前の変更(CL 89050043)では、go testに対して-ccflagsの使用が許可されたものの、そのフラグが他の同様のフラグ(例: -gcflags)と同じように適切に処理されていませんでした。

Go言語のテスト実行環境では、Cgo(GoとC言語の相互運用機能)を使用するパッケージのテストを行う際に、Cコンパイラに特定のフラグを渡す必要が生じることがあります。-ccflagsは、このようなCコンパイラへのフラグ指定を意図したものです。しかし、以前の実装では、このフラグが正しくパースされず、Cコンパイラに渡されない、あるいは意図しない形で渡されるといった問題が発生していたと考えられます。

コミットメッセージにある「Fixes #7810 (again)」という記述は、この問題が以前にも報告され、一度修正が試みられたものの、その修正が不完全であったか、あるいは回帰してしまったことを示唆しています。Dobrosław Żybort氏からの指摘により、この問題が再認識され、今回のコミットでより堅牢な修正が施されることになりました。

前提知識の解説

go testコマンド

go testは、Go言語のパッケージに含まれるテストを実行するためのコマンドです。テスト関数はTestで始まる名前を持ち、_test.goというサフィックスを持つファイルに記述されます。go testは、これらのテストファイルをコンパイルし、実行可能なテストバイナリを生成して実行します。

go build関連のフラグ (-gcflags, -ldflags, -ccflags)

Goのビルドシステムでは、コンパイルやリンクの挙動を制御するために様々なフラグが提供されています。

  • -gcflags: Goコンパイラ(go tool compile)に渡されるフラグです。最適化レベルの指定、インライン化の制御、デバッグ情報の追加など、Goコードのコンパイルに関する設定を行います。
  • -ldflags: Goリンカ(go tool link)に渡されるフラグです。ビルドされるバイナリの挙動(例: バージョン情報の埋め込み、シンボルの削除)や、外部ライブラリへのリンクに関する設定を行います。
  • -ccflags: Cgoを使用する際に、Cコンパイラに渡されるフラグです。CgoはGoコードからCコードを呼び出すためのメカニズムであり、Cコードをコンパイルする際には通常のCコンパイラ(GCCやClangなど)が使用されます。-ccflagsは、これらのCコンパイラに対して、インクルードパスの指定(-I)、マクロ定義(-D)、警告オプション(-Wall)などを指示するために用いられます。通常、CGO_CFLAGS環境変数やGoソースコード内の// #cgo CFLAGS:ディレクティブを通じて設定されます。このコミットは、go testコマンドラインで-ccflagsが指定された場合に、それを適切に処理するためのものです。

Change List (CL)

Goプロジェクトの開発では、変更は「Change List (CL)」として提出され、レビュープロセスを経てマージされます。CLは、Gitのコミットに相当する概念ですが、GoプロジェクトではGerritというコードレビューシステムが使われており、CLはそのシステムにおける変更の単位を指します。コミットメッセージに記載されているhttps://golang.org/cl/89230044は、この変更に対応するGerritのCLへのリンクです。

fatalf関数

fatalfは、Goのツールチェイン内でエラーが発生した場合に、フォーマットされたエラーメッセージを出力してプログラムを終了させるための関数です。通常、引数のパースエラーや予期せぬ状態に陥った際に使用されます。

splitQuotedFields関数

splitQuotedFieldsは、Goの標準ライブラリには直接存在しない関数ですが、その名前から推測されるように、引用符で囲まれた部分を考慮しながら文字列をフィールドに分割するユーティリティ関数です。例えば、"arg1 \"quoted arg\" arg3"のような文字列を、["arg1", "quoted arg", "arg3"]のように分割する役割を担います。これは、コマンドライン引数のように、スペースで区切られているが引用符内のスペースは区切り文字と見なさないような文字列をパースする際に非常に重要です。

技術的詳細

go testコマンドは、実行時に様々なフラグを受け取ります。これらのフラグは、テストの実行方法、ビルドプロセス、またはCgoに関連するコンパイルオプションを制御するために使用されます。src/cmd/go/testflag.goファイルは、これらのフラグを解析し、適切な内部変数に値を割り当てる役割を担っています。

以前の実装では、-gcflags-ldflagsといったGoコンパイラやリンカ向けのフラグは適切に処理されていましたが、-ccflagsに関しては不十分でした。これは、testFlags関数内で、受け取ったフラグの名前(f.name)に基づいて処理を分岐するswitch文に、"ccflags"に対応するcaseが欠けていたか、不完全であったためと考えられます。

このコミットの目的は、-ccflagsが他のビルド関連フラグと同様に、go testコマンドによって適切にパースされ、その値がbuildCcflagsという内部変数に格納されるようにすることです。これにより、Cgoを使用するテストのビルド時に、指定されたCコンパイラフラグが正しく適用されるようになります。

splitQuotedFields関数は、-ccflagsに渡される値が複数の引数を含む場合(例: -ccflags="-I/path -DDEBUG")に、それらを正しく個々のフラグに分割するために使用されます。この関数がエラーを返した場合(例えば、引用符の閉じ忘れなど)、fatalfが呼び出され、ユーザーに無効なフラグ引数であることを通知します。

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

diff --git a/src/cmd/go/testflag.go b/src/cmd/go/testflag.go
index adfc2d9216..8c45e5c1b2 100644
--- a/src/cmd/go/testflag.go
+++ b/src/cmd/go/testflag.go
@@ -160,6 +160,11 @@ func testFlags(args []string) (packageNames, passToTest []string) {
 			if err != nil {
 				fatalf("invalid flag argument for -%s: %v", f.name, err)
 			}
+		case "ccflags":
+			buildCcflags, err = splitQuotedFields(value)
+			if err != nil {
+				fatalf("invalid flag argument for -%s: %v", f.name, err)
+			}
 		case "gcflags":
 			buildGcflags, err = splitQuotedFields(value)
 			if err != nil {

コアとなるコードの解説

変更はsrc/cmd/go/testflag.goファイルのtestFlags関数内で行われています。この関数は、go testコマンドに渡された引数を解析し、テストに関連するフラグを処理します。

追加されたコードは、switch文の新しいcaseブロックです。

		case "ccflags":
			buildCcflags, err = splitQuotedFields(value)
			if err != nil {
				fatalf("invalid flag argument for -%s: %v", f.name, err)
			}
  1. case "ccflags":: これは、現在処理しているフラグの名前が"ccflags"である場合にこのブロックのコードが実行されることを意味します。
  2. buildCcflags, err = splitQuotedFields(value):
    • valueは、-ccflagsフラグに続く引数の文字列です(例: -ccflags="-I/usr/local/include -DDEBUG"の場合、value"-I/usr/local/include -DDEBUG")。
    • splitQuotedFields関数がこのvalueを呼び出し、引用符を考慮して複数のフィールド(Cコンパイラに渡す個々のフラグ)に分割します。
    • 分割されたフィールドのリストはbuildCcflags変数に格納されます。このbuildCcflagsは、後続のビルドプロセスでCコンパイラに渡される実際のフラグのリストとして使用されます。
    • errは、splitQuotedFieldsの実行中にエラーが発生した場合に設定されます(例: 引用符の不一致)。
  3. if err != nil { fatalf("invalid flag argument for -%s: %v", f.name, err) }:
    • splitQuotedFieldsがエラーを返した場合、このifブロックが実行されます。
    • fatalf関数が呼び出され、ユーザーに対して「無効なフラグ引数」であることを示すエラーメッセージを出力し、プログラムを終了させます。これにより、不正な-ccflagsの指定が早期に検出され、ビルドの失敗を防ぎます。

この変更により、go testコマンドは-ccflagsを他のビルド関連フラグと同様に、適切にパースし、その値を内部的に保持できるようになりました。これにより、Cgoを使用するテストのビルド時に、ユーザーが指定したCコンパイラフラグが正しく適用されるようになり、以前のバグが修正されました。

関連リンク

  • Go CL (Change List): https://golang.org/cl/89230044
  • Go Issue #7810: コミットメッセージに記載されているGoのIssue #7810は、現在のGoのIssueトラッカーでは直接見つけることができませんでした。しかし、コミットメッセージの「Fixes #7810 (again)」という記述から、これはgo testにおける-ccflagsの処理に関する既存のバグであり、以前にも修正が試みられたものの、不完全であったか、あるいは回帰した問題であると推測されます。

参考にした情報源リンク

  • Go Command Documentation (go.dev): go testコマンドやCgoに関する公式ドキュメント
  • Stack Overflow: go testにおける-ccflagsの扱い、CGO_CFLAGS環境変数、// #cgoディレクティブに関する議論
  • Go言語のソースコード: src/cmd/go/testflag.goの周辺コードや、splitQuotedFieldsのような内部ユーティリティ関数の実装
  • GitHub: 関連するGoのIssueやコミット履歴 (ただし、Issue #7810は直接特定できず)