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

[インデックス 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.ImportNoGoError という特定のエラーを返すようになりました。これは、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.ImportNoGoError という明確なエラーを返すようになりました。これは、そのディレクトリがGoパッケージとして認識できないことを意味します。

cmd/api ツールは、Goの標準ライブラリのAPIを抽出するために使用されるツールであり、各パッケージをインポートしてその公開APIを解析します。このツールの内部では、build.Import を使用してパッケージ情報を取得していました。go/build の挙動変更により、cmd/apiruntime/cgo パッケージのインポート時に予期せぬエラーを受け取るようになり、その結果 all.bash (Goのテストおよびビルドスクリプト) がローカル環境で失敗する問題が発生しました。

このコミットは、この go/build の挙動変更に cmd/api ツールを適応させることを目的としています。

前提知識の解説

このコミットを理解するためには、以下の概念について理解しておく必要があります。

  1. 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の有効/無効など)を保持する構造体です。
  2. 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ファイルを含まないため、実質的に存在しないものとして扱われます。
  3. cmd/api ツール:

    • cmd/api は、Goの標準ライブラリの公開API(エクスポートされた型、関数、変数など)を抽出するための内部ツールです。このツールは、GoのAPIが意図せず変更されていないかを確認するために使用されます。
    • このツールは、Goの各パッケージをインポートし、そのパッケージのAST(抽象構文木)を解析して、公開されているシンボルを特定します。
  4. NoGoError:

    • NoGoError は、go/build パッケージが返す特定のエラータイプです。このエラーは、指定されたディレクトリ内にビルド可能なGoソースファイルが全く見つからない場合に発生します。これは、そのディレクトリが有効なGoパッケージではないことを示します。
  5. 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.gomain 関数内のパッケージインポートループに条件分岐を追加することで、この問題に対処しています。

				if name == "runtime/cgo" && !context.CgoEnabled {
					// w.Import(name) will return nil
					continue
				}

このコードは、以下のロジックを実装しています。

  • インポートしようとしているパッケージの名前が "runtime/cgo" であり、かつ
  • 現在のビルドコンテキスト (context.CgoEnabled) でCgoが無効になっている場合

この両方の条件が真であれば、runtime/cgo パッケージのインポートをスキップし、次のパッケージの処理に進みます。

この修正のポイントは、runtime/cgoCgoEnabled=false の場合に build.ImportNoGoError を返すようになったことを認識し、そのエラーを回避するために明示的に 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.CgoEnabledgo/build.Context のフィールドで、Cgoが有効な場合は true、無効な場合は false です。! は論理否定なので、!context.CgoEnabled はCgoが無効な場合に true となります。

この2つの条件が両方とも true の場合、つまり「runtime/cgo パッケージを処理しようとしており、かつCgoが無効になっている環境である」という場合に、continue ステートメントが実行されます。continue は、現在のループの残りの処理をスキップし、次のイテレーション(次のパッケージの処理)に進むことを意味します。

これにより、CgoEnabled=false の環境で runtime/cgo パッケージをインポートしようとした際に go/build.ImportNoGoError を返す問題が回避され、cmd/api ツールが正常に動作するようになりました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント: go/build パッケージ
  • Go言語の公式ドキュメント: Cgo
  • Go言語のソースコード (特に go/build パッケージの変更履歴)
  • Go言語のコミット履歴と関連するIssueトラッカー
  • Go言語のビルドプロセスに関する一般的な知識