[インデックス 17559] ファイルの概要
このコミットは、Go言語のビルドシステムにおけるCgoファイルの扱いに関するバグ修正です。具体的には、CGO_ENABLED=0
の環境下でCgoファイルのみを含むディレクトリが、ビルド可能なGoソースファイルがないにも関わらずエラーを適切に報告しない問題を解決します。
コミット
go/build
: Cgoが使用されていない場合、Cgoファイルのみを含むディレクトリを拒否する
以前の「Goファイルがない」ことのテストは p.Name == ""
であり、これはGoパッケージのステートメントが見つからなかったことを意味していました。このテストは、Cgoファイルがパースされた(そしてパッケージ名が記録された)が、Cgoが利用できないために使用しないことを選択した場合に失敗します。
代わりに実際のファイルリストをテストします。
Fixes #6078.
R=golang-dev, iant CC=golang-dev https://golang.org/cl/13661043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/611b182190e759879b3988077daf7d52eb831b5e
元コミット内容
commit 611b182190e759879b3988077daf7d52eb831b5e
Author: Russ Cox <rsc@golang.org>
Date: Wed Sep 11 13:25:30 2013 -0400
go/build: reject directory with only cgo files if cgo not in use
The old test for "no Go files" was p.Name == "", meaning we never
saw a Go package statement. That test fails if there are cgo files
that we parsed (and recorded the package name) but then chose
not to use (because cgo is not available).
Test the actual file lists instead.
Fixes #6078.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/13661043
---
src/cmd/go/test.bash | 13 +++++++++++++
src/cmd/go/testdata/src/cgotest/m.go | 5 +++++
src/pkg/go/build/build.go | 4 +++-\n 3 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/src/cmd/go/test.bash b/src/cmd/go/test.bash
index a2ba1ca95a..c5effe757e 100755
--- a/src/cmd/go/test.bash
+++ b/src/cmd/go/test.bash
@@ -167,6 +167,19 @@ elif ! grep testdata/shadow/root1/src/foo testdata/err >/dev/null; then
fi
unset GOPATH
+TEST go install fails with no buildable files
+export GOPATH=$(pwd)/testdata
+export CGO_ENABLED=0
+if ./testgo install cgotest 2>testdata/err; then
+\techo "go install cgotest succeeded unexpectedly"
+elif ! grep 'no buildable Go source files' testdata/err >/dev/null; then
+\techo "go install cgotest did not report 'no buildable Go source files'"
+\tcat testdata/err
+\tok=false
+fi
+unset CGO_ENABLED
+unset GOPATH
+
# Test that without $GOBIN set, binaries get installed
# into the GOPATH bin directory.
TEST install into GOPATH
diff --git a/src/cmd/go/testdata/src/cgotest/m.go b/src/cmd/go/testdata/src/cgotest/m.go
new file mode 100644
index 0000000000..4d68307cf0
--- /dev/null
+++ b/src/cmd/go/testdata/src/cgotest/m.go
@@ -0,0 +1,5 @@
+package cgotest
+
+import "C"
+
+var _ C.int
diff --git a/src/pkg/go/build/build.go b/src/pkg/go/build/build.go
index f259525f5e..be48df9d38 100644
--- a/src/pkg/go/build/build.go
+++ b/src/pkg/go/build/build.go
@@ -747,6 +747,8 @@ Found:\n \t\t\tallTags[\"cgo\"] = true
\t\t\tif ctxt.CgoEnabled {\n \t\t\t\tp.CgoFiles = append(p.CgoFiles, name)\n+\t\t\t} else {\n+\t\t\t\tp.IgnoredGoFiles = append(p.IgnoredGoFiles, name)\n \t\t\t}\n \t\t} else if isXTest {\n \t\t\tp.XTestGoFiles = append(p.XTestGoFiles, name)\n@@ -756,7 +758,7 @@ Found:\n \t\t\tp.GoFiles = append(p.GoFiles, name)\n \t\t}\n \t}\n-\tif p.Name == \"\" {\n+\tif len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {\n \t\treturn p, &NoGoError{p.Dir}\n \t}\n \n```
## 変更の背景
このコミットは、GoのビルドシステムがCgoファイルのみを含むディレクトリを適切に処理できないというバグ(Issue #6078)を修正するために行われました。
従来のGoビルドシステムでは、ディレクトリ内にビルド可能なGoソースファイルが存在しないかどうかを判断するために、`Package.Name` フィールドが空であるかどうかをチェックしていました。`Package.Name` は、Goソースファイルから読み取られるパッケージ名を格納します。
しかし、Cgoファイル(`.go` ファイル内にC言語のコードを埋め込んだもの)は、Goのパーサーによって解析され、パッケージ名が抽出されます。このとき、たとえ環境変数 `CGO_ENABLED` が `0` に設定されており、Cgoが事実上無効化されている場合でも、パーサーはCgoファイルからパッケージ名を読み取ります。
この状況下で、ディレクトリ内にGoソースファイルが一切なく、Cgoファイルのみが存在する場合、`p.Name` はCgoファイルから読み取られたパッケージ名で設定されてしまうため、空になりません。結果として、ビルドシステムは「ビルド可能なGoソースファイルがない」というエラーを報告せず、あたかも有効なパッケージであるかのように振る舞ってしまい、ユーザーを混乱させる原因となっていました。
このコミットは、この誤った挙動を修正し、`CGO_ENABLED=0` の場合にCgoファイルがビルド対象から除外されることを考慮に入れ、真にビルド可能なGoソースファイルが存在しない場合にのみエラーを報告するように変更します。
## 前提知識の解説
### Goのビルドシステム (`go/build` パッケージ)
`go/build` パッケージは、Goのソースコードを解析し、パッケージの依存関係を解決し、ビルド可能なファイルを特定するための機能を提供します。`go build` や `go install` といったコマンドの基盤となる重要なパッケージです。
### Cgo
Cgoは、GoプログラムからC言語のコードを呼び出すためのGoの機能です。Goのソースファイル内に `import "C"` を記述することで、C言語の関数やデータ構造をGoから利用できるようになります。Cgoを使用すると、既存のCライブラリをGoプロジェクトに統合したり、パフォーマンスが重要な部分をCで記述したりすることが可能になります。
### `CGO_ENABLED` 環境変数
`CGO_ENABLED` は、Cgoの有効/無効を制御する環境変数です。
- `CGO_ENABLED=1` (デフォルト): Cgoが有効になり、Cgoファイルがビルドプロセスに含まれます。
- `CGO_ENABLED=0`: Cgoが無効になり、Cgoファイルはビルドプロセスから除外されます。これは、クロスコンパイル時や、Cコンパイラが利用できない環境でGoプログラムをビルドする際によく使用されます。
### `NoGoError`
`go/build` パッケージが、指定されたディレクトリ内にビルド可能なGoソースファイルを見つけられなかった場合に返すエラーです。このエラーは、ユーザーに対して、ビルド対象のディレクトリが空であるか、または有効なGoパッケージではないことを通知するために使用されます。
### `Package` 構造体
`go/build` パッケージの `Package` 構造体は、Goパッケージに関する様々な情報(パッケージ名、Goソースファイルのリスト、Cgoファイルのリスト、テストファイルのリストなど)を保持します。
- `p.Name`: パッケージ名。Goソースファイルから読み取られます。
- `p.GoFiles`: 通常のGoソースファイルのリスト。
- `p.CgoFiles`: Cgoソースファイルのリスト。
- `p.TestGoFiles`: 通常のテストファイルのリスト(`_test.go`)。
- `p.XTestGoFiles`: 外部テストファイルのリスト(`_test.go` で `package main` 以外のパッケージをテストするもの)。
- `p.IgnoredGoFiles`: ビルドタグや `CGO_ENABLED` の設定により無視されたGoソースファイルのリスト。
## 技術的詳細
このコミットの核心は、`go/build` パッケージ内の `build.go` ファイルにおけるパッケージのビルド可能性の判断ロジックの変更です。
### 変更前
変更前は、パッケージがビルド可能かどうかを判断する際に、主に `p.Name == ""` という条件を使用していました。これは、Goソースファイルが一つも存在しない場合、`p.Name` が設定されず空文字列になるという前提に基づいています。
```go
// src/pkg/go/build/build.go (変更前)
if p.Name == "" {
return p, &NoGoError{p.Dir}
}
しかし、CgoファイルはGoのパーサーによって解析され、その中に記述された package
宣言から p.Name
が設定されてしまいます。CGO_ENABLED=0
の場合、これらのCgoファイルはビルド対象から除外されるにも関わらず、p.Name
は空ではないため、上記の条件では NoGoError
が返されませんでした。
変更後
このコミットでは、p.Name == ""
という曖昧なチェックを廃止し、代わりに Package
構造体内の実際のファイルリストの長さを確認するように変更されました。
// src/pkg/go/build/build.go (変更後)
if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
return p, &NoGoError{p.Dir}
}
この新しい条件は、通常のGoソースファイル (p.GoFiles
)、Cgoソースファイル (p.CgoFiles
)、通常のテストファイル (p.TestGoFiles
)、および外部テストファイル (p.XTestGoFiles
) のいずれのリストも空である場合にのみ NoGoError
を返すようにします。
さらに、CGO_ENABLED=0
の場合にCgoファイルが適切に p.IgnoredGoFiles
リストに追加されるように、ファイル解析ロジックも修正されています。これにより、Cgoが無効な環境ではCgoファイルがビルド対象としてカウントされなくなり、ビルド可能性の判断がより正確になります。
// src/pkg/go/build/build.go (変更後)
if ctxt.CgoEnabled {
p.CgoFiles = append(p.CgoFiles, name)
} else {
p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
}
この変更により、CGO_ENABLED=0
の環境でCgoファイルのみを含むディレクトリをビルドしようとした場合、NoGoError
が適切に報告されるようになり、ユーザーは「ビルド可能なGoソースファイルがない」という明確なエラーメッセージを受け取ることができます。
コアとなるコードの変更箇所
src/pkg/go/build/build.go
--- a/src/pkg/go/build/build.go
+++ b/src/pkg/go/build/build.go
@@ -747,6 +747,8 @@ Found:
allTags["cgo"] = true
if ctxt.CgoEnabled {
p.CgoFiles = append(p.CgoFiles, name)
+ } else {
+ p.IgnoredGoFiles = append(p.IgnoredGoFiles, name)
}
} else if isXTest {
p.XTestGoFiles = append(p.XTestGoFiles, name)
@@ -756,7 +758,7 @@ Found:
p.GoFiles = append(p.GoFiles, name)
}
}
- if p.Name == "" {
+ if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 {
return p, &NoGoError{p.Dir}
}
src/cmd/go/test.bash
新しいテストケースが追加され、CGO_ENABLED=0
の環境でCgoファイルのみを含むディレクトリを go install
しようとした場合に、no buildable Go source files
エラーが適切に報告されることを確認しています。
src/cmd/go/testdata/src/cgotest/m.go
新しいテストケースで使用される、Cgoの import "C"
を含むシンプルなGoファイルが追加されています。
package cgotest
import "C"
var _ C.int
コアとなるコードの解説
src/pkg/go/build/build.go
の変更点
-
Cgoファイルの処理の改善:
if ctxt.CgoEnabled { p.CgoFiles = append(p.CgoFiles, name) } else { p.IgnoredGoFiles = append(p.IgnoredGoFiles, name) }
この変更は、
go/build
パッケージがGoソースファイルを解析する際のロジックの一部です。ファイルがCgoファイルであると識別された場合、ctxt.CgoEnabled
(Cgoが有効かどうかを示すコンテキスト情報) をチェックします。ctxt.CgoEnabled
がtrue
の場合(Cgoが有効)、そのファイルはp.CgoFiles
リストに追加されます。これは、そのファイルがビルドプロセスでCgoとして扱われることを意味します。ctxt.CgoEnabled
がfalse
の場合(Cgoが無効)、そのファイルはp.IgnoredGoFiles
リストに追加されます。これは、Cgoファイルであっても、現在のビルド設定では無視されるべきファイルであることを明示します。この変更により、CGO_ENABLED=0
の場合にCgoファイルがビルド対象として誤ってカウントされることがなくなります。
-
ビルド可能性の判断ロジックの変更:
- if p.Name == "" { + if len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0 { return p, &NoGoError{p.Dir} }
これがこのコミットの最も重要な変更点です。
- 変更前 (
p.Name == ""
): 以前は、パッケージ名 (p.Name
) が空であるかどうかで、ディレクトリ内にビルド可能なGoソースファイルがないかを判断していました。しかし、前述の通り、Cgoファイルが存在するとp.Name
が設定されてしまうため、このチェックは不正確でした。 - 変更後 (
len(p.GoFiles)+len(p.CgoFiles)+len(p.TestGoFiles)+len(p.XTestGoFiles) == 0
): 新しいロジックでは、Package
構造体内の実際のファイルリストの合計がゼロであるかどうかをチェックします。p.GoFiles
: 通常のGoソースファイル。p.CgoFiles
: Cgoソースファイル。p.TestGoFiles
: 通常のテストファイル。p.XTestGoFiles
: 外部テストファイル。 これらのリストのいずれかにファイルが含まれていれば、そのディレクトリはビルド可能なGoソースファイルを持つと判断されます。逆に、これらのリストがすべて空である場合、そのディレクトリにはビルド可能なGoソースファイルが一つも存在しないと判断され、NoGoError
が返されます。 この変更により、CGO_ENABLED=0
の場合にCgoファイルがp.CgoFiles
に追加されず、p.IgnoredGoFiles
に追加されるようになったため、len(p.CgoFiles)
はゼロになり、正確なビルド可能性の判断が可能になります。
- 変更前 (
src/cmd/go/test.bash
の変更点
このシェルスクリプトはGoコマンドのテストスイートの一部です。追加されたテストケースは以下の手順を実行します。
GOPATH
を一時的に設定します。CGO_ENABLED=0
をエクスポートし、Cgoを無効にします。./testgo install cgotest
を実行します。cgotest
は、新しく追加されたsrc/cmd/go/testdata/src/cgotest/m.go
のパッケージです。このファイルはCgoファイルのみを含んでいます。go install
コマンドが予期せず成功した場合はエラーを報告します。go install
コマンドの出力(標準エラー出力)にno buildable Go source files
という文字列が含まれていることを確認します。この文字列が見つからない場合はエラーを報告します。
このテストケースは、CGO_ENABLED=0
の環境でCgoファイルのみを含むディレクトリをビルドしようとした際に、期待通りに NoGoError
が発生し、適切なエラーメッセージが表示されることを保証します。
関連リンク
- Go Issue #6078: cmd/go: go install of cgo-only directory fails if CGO_ENABLED=0
- Go Code Review: https://golang.org/cl/13661043
参考にした情報源リンク
- Go言語の公式ドキュメント (go/buildパッケージ、cgoに関する情報)
- Go言語のソースコード (特に
src/pkg/go/build/build.go
) - Go言語のIssueトラッカー (Issue #6078)
- Go言語のCode Reviewシステム (CL 13661043)