[インデックス 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言語のビルドプロセスに関する一般的な知識