[インデックス 17563] ファイルの概要
このコミットは、Go言語のツールチェインの一部である cmd/api ツールに関する修正です。具体的には、go/build パッケージの最近の変更に対応し、CgoEnabled=false の場合に runtime/cgo パッケージのインポートが正しく処理されるようにするための変更が含まれています。
コミット
commit cdc5356c938446fa6b237c54c2c6d5e5d5f267c0
Author: Russ Cox <rsc@golang.org>
Date: Wed Sep 11 14:42:34 2013 -0400
cmd/api: fix tool for recent go/build change
Asking about runtime/cgo when CgoEnabled=false now correctly
returns an error from build.Import (specifically, NoGoError), because
there are no buildable Go files in that directory.
The API tool was depending on it returning a package with no Go
files instead. Correct that assumption.
Fixes all.bash on local machines.
(Dashboard appears not to be running the api tool at all.)
Update #6124
TBR=golang-dev
CC=golang-dev
https://golang.org/cl/13385046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/cdc5356c938446fa6b237c54c2c6d5e5d5f267c0
元コミット内容
cmd/api ツールは、go/build パッケージの最近の変更に対応するために修正されました。以前は、CgoEnabled=false の設定で runtime/cgo パッケージをインポートしようとした場合、go/build.Import はGoファイルを含まないパッケージを返していました。しかし、この変更により、build.Import は NoGoError という特定のエラーを返すようになりました。これは、runtime/cgo ディレクトリ内にビルド可能なGoファイルが存在しないためです。cmd/api ツールはこの新しい挙動に対応していなかったため、このコミットでその前提を修正し、NoGoError が返された場合に runtime/cgo の処理をスキップするように変更されました。これにより、ローカル環境での all.bash スクリプトの実行が修正されました。
変更の背景
この変更の背景には、Go言語のビルドシステムにおける go/build パッケージの進化があります。go/build パッケージは、Goのソースコードを解析し、パッケージの依存関係を解決し、ビルド可能なGoファイルを見つけるための重要な機能を提供します。
以前の go/build の挙動では、CgoEnabled=false (Cgoが無効な状態) の環境で runtime/cgo のようなCgoに依存するパッケージをインポートしようとすると、そのパッケージ内にGoファイルが見つからない場合でも、エラーではなく「Goファイルを含まないパッケージ」として扱われることがありました。これは、cmd/api のようなツールが、パッケージの存在を確認する際に、Goファイルがないパッケージでも何らかのオブジェクトが返されることを期待していたため、問題を引き起こしていました。
しかし、go/build パッケージの最近の変更により、この挙動がより厳密になりました。具体的には、ビルド可能なGoファイルが全く存在しないディレクトリをインポートしようとした場合、build.Import は NoGoError という明確なエラーを返すようになりました。これは、そのディレクトリがGoパッケージとして認識できないことを意味します。
cmd/api ツールは、Goの標準ライブラリのAPIを抽出するために使用されるツールであり、各パッケージをインポートしてその公開APIを解析します。このツールの内部では、build.Import を使用してパッケージ情報を取得していました。go/build の挙動変更により、cmd/api は runtime/cgo パッケージのインポート時に予期せぬエラーを受け取るようになり、その結果 all.bash (Goのテストおよびビルドスクリプト) がローカル環境で失敗する問題が発生しました。
このコミットは、この go/build の挙動変更に cmd/api ツールを適応させることを目的としています。
前提知識の解説
このコミットを理解するためには、以下の概念について理解しておく必要があります。
-
Go言語のビルドシステムと
go/buildパッケージ:- Go言語のビルドシステムは、ソースコードから実行可能ファイルを生成するプロセスを管理します。これには、依存関係の解決、パッケージのコンパイル、リンクなどが含まれます。
go/buildパッケージは、Goのソースコードを解析し、パッケージのビルド情報を取得するためのGo標準ライブラリの一部です。このパッケージは、Goのツール(go build,go install,go docなど)が内部的に使用しています。build.Import(path string)関数は、指定されたパスのGoパッケージをインポートし、そのパッケージに関する情報(含まれるファイル、依存関係など)を*build.Package構造体として返します。build.Contextは、ビルド環境に関する情報(Goのバージョン、OS、アーキテクチャ、Cgoの有効/無効など)を保持する構造体です。
-
Cgo:
- Cgoは、GoプログラムからC言語のコードを呼び出すためのGoの機能です。Cgoを使用すると、既存のCライブラリをGoプログラムから利用したり、パフォーマンスが重要な部分をCで記述したりすることができます。
CgoEnabledは、go/build.Contextのフィールドの一つで、Cgoが有効になっているかどうかを示すブール値です。この値がfalseの場合、Cgoのコードはビルドされません。runtime/cgoパッケージは、GoランタイムとCgoの間のインターフェースを提供するGo標準ライブラリの内部パッケージです。このパッケージは、Cgoが有効な場合にのみビルドされます。CgoEnabled=falseの環境では、このパッケージはビルド可能なGoファイルを含まないため、実質的に存在しないものとして扱われます。
-
cmd/apiツール:cmd/apiは、Goの標準ライブラリの公開API(エクスポートされた型、関数、変数など)を抽出するための内部ツールです。このツールは、GoのAPIが意図せず変更されていないかを確認するために使用されます。- このツールは、Goの各パッケージをインポートし、そのパッケージのAST(抽象構文木)を解析して、公開されているシンボルを特定します。
-
NoGoError:NoGoErrorは、go/buildパッケージが返す特定のエラータイプです。このエラーは、指定されたディレクトリ内にビルド可能なGoソースファイルが全く見つからない場合に発生します。これは、そのディレクトリが有効なGoパッケージではないことを示します。
-
all.bash:all.bashは、Goプロジェクトのルートディレクトリにあるシェルスクリプトで、Goのすべてのテストを実行し、すべてのツールをビルドし、Goのリリースに必要なすべてのチェックを実行するためのスクリプトです。これは、Goの開発者が変更をコミットする前に、すべてのものが正しく動作することを確認するために使用されます。
技術的詳細
このコミットの技術的な核心は、go/build パッケージの build.Import 関数の挙動変更と、それに対する cmd/api ツールの適応です。
以前の build.Import の挙動では、CgoEnabled=false の環境で runtime/cgo パッケージをインポートしようとした場合、runtime/cgo ディレクトリ内にGoファイルが存在しないため、build.Import は *build.Package オブジェクトを返しますが、その GoFiles フィールドは空(Goファイルがない)でした。cmd/api ツールは、この「Goファイルがないパッケージ」という結果を許容し、そのまま処理を続行していました。
しかし、go/build の変更により、build.Import は、ビルド可能なGoファイルが全く存在しないディレクトリに対しては、もはや空の *build.Package を返すのではなく、NoGoError を返すようになりました。これは、より厳密なエラーハンドリングと、無効なパッケージのインポートを早期に検出するための改善です。
この変更により、cmd/api ツールが runtime/cgo をインポートしようとした際に NoGoError が発生し、ツールがクラッシュするか、予期せぬ動作をするようになりました。特に、all.bash スクリプトの一部として cmd/api が実行されるため、このエラーが all.bash の失敗につながっていました。
このコミットでは、cmd/api/goapi.go の main 関数内のパッケージインポートループに条件分岐を追加することで、この問題に対処しています。
if name == "runtime/cgo" && !context.CgoEnabled {
// w.Import(name) will return nil
continue
}
このコードは、以下のロジックを実装しています。
- インポートしようとしているパッケージの名前が
"runtime/cgo"であり、かつ - 現在のビルドコンテキスト (
context.CgoEnabled) でCgoが無効になっている場合
この両方の条件が真であれば、runtime/cgo パッケージのインポートをスキップし、次のパッケージの処理に進みます。
この修正のポイントは、runtime/cgo が CgoEnabled=false の場合に build.Import が NoGoError を返すようになったことを認識し、そのエラーを回避するために明示的に runtime/cgo のインポートをスキップすることです。これにより、cmd/api ツールは go/build の新しい挙動と互換性を持つようになり、all.bash の実行が再び成功するようになりました。
コメント // w.Import(name) will return nil は、この変更が行われた時点での build.Import の挙動に関する開発者の理解を示しています。実際には NoGoError が返されるようになったため、このコメントは厳密には正確ではありませんが、意図としては「この条件下では有効なパッケージは返されない」ということを示しています。
コアとなるコードの変更箇所
変更は src/cmd/api/goapi.go ファイルの main 関数内で行われています。
--- a/src/cmd/api/goapi.go
+++ b/src/cmd/api/goapi.go
@@ -145,6 +145,10 @@ func main() {
// going to change w/o a language change.
// - We don't care about the API of commands.
if name != "unsafe" && !strings.HasPrefix(name, "cmd/") {
+ if name == "runtime/cgo" && !context.CgoEnabled {
+ // w.Import(name) will return nil
+ continue
+ }
w.export(w.Import(name))
}
}
コアとなるコードの解説
変更されたコードは、cmd/api ツールの main 関数内のパッケージをループ処理する部分にあります。
元のコードでは、unsafe パッケージと cmd/ で始まるパッケージを除外した後、残りのパッケージに対して w.export(w.Import(name)) を呼び出していました。ここで w.Import(name) は go/build.Import をラップしたもので、指定されたパッケージをインポートし、そのAPI情報を抽出します。
追加された4行のコードは、この w.Import(name) の呼び出しの前にガード条件を設けています。
if name == "runtime/cgo" && !context.CgoEnabled {
// w.Import(name) will return nil
continue
}
name == "runtime/cgo": 現在処理しているパッケージの名前が"runtime/cgo"であるかどうかをチェックします。!context.CgoEnabled: 現在のビルドコンテキスト (context) でCgoが無効になっているかどうかをチェックします。context.CgoEnabledはgo/build.Contextのフィールドで、Cgoが有効な場合はtrue、無効な場合はfalseです。!は論理否定なので、!context.CgoEnabledはCgoが無効な場合にtrueとなります。
この2つの条件が両方とも true の場合、つまり「runtime/cgo パッケージを処理しようとしており、かつCgoが無効になっている環境である」という場合に、continue ステートメントが実行されます。continue は、現在のループの残りの処理をスキップし、次のイテレーション(次のパッケージの処理)に進むことを意味します。
これにより、CgoEnabled=false の環境で runtime/cgo パッケージをインポートしようとした際に go/build.Import が NoGoError を返す問題が回避され、cmd/api ツールが正常に動作するようになりました。
関連リンク
- Go issue #6124: https://github.com/golang/go/issues/6124 (このコミットが解決した問題のトラッキング)
- Go CL 13385046: https://golang.org/cl/13385046 (このコミットのGerritレビューページ)
参考にした情報源リンク
- Go言語の公式ドキュメント:
go/buildパッケージ - Go言語の公式ドキュメント: Cgo
- Go言語のソースコード (特に
go/buildパッケージの変更履歴) - Go言語のコミット履歴と関連するIssueトラッカー
- Go言語のビルドプロセスに関する一般的な知識