[インデックス 17121] ファイルの概要
このコミットは、Goのcmd/go
ツールにおいて、cgoプログラムがsyscall
パッケージに暗黙的に依存するように変更を加えるものです。これにより、cgoを利用するプログラムがerrno
をラップするためにsyscall
パッケージを正しくインポートするようになり、関連する問題(Issue #5048)を修正します。また、競合検出(race detection)が有効な場合の依存関係の処理も改善されています。
コミット
commit b0a1b82ec133d08505d69a064b0dac0d807817e2
Author: Russ Cox <rsc@golang.org>
Date: Fri Aug 9 09:03:25 2013 -0400
cmd/go: cgo programs depend on syscall
Fixes #5048.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/12651044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b0a1b82ec133d08505d69a064b0dac0d807817e2
元コミット内容
cmd/go
: cgoプログラムはsyscall
に依存する。
Issue #5048を修正。
変更の背景
Goのcgo機能は、GoプログラムからC言語のコードを呼び出すことを可能にします。C言語の関数は、エラーが発生した場合にerrno
というグローバル変数にエラーコードを設定することが一般的です。GoプログラムがこれらのC関数を呼び出し、そのエラー情報を適切に処理するためには、errno
の値をGoのerror
型に変換する必要があります。この変換は通常、syscall
パッケージが提供する機能によって行われます。
しかし、以前のcmd/go
のビルドシステムでは、cgoを使用するプログラムがsyscall
パッケージに暗黙的に依存していることが考慮されていませんでした。このため、cgoプログラムがsyscall
パッケージを明示的にインポートしていない場合、errno
の処理に関連する問題が発生する可能性がありました。コミットメッセージにある「Fixes #5048」は、この問題がGoのIssueトラッカーで報告されていたことを示唆しています。
このコミットは、cgoプログラムがsyscall
パッケージに暗黙的に依存することをビルドシステムに認識させることで、この問題を解決することを目的としています。これにより、cgoプログラムは常にsyscall
パッケージの機能を利用できるようになり、errno
の適切なラップとエラーハンドリングが保証されます。
前提知識の解説
- cgo: Go言語の機能の一つで、C言語のコードをGoプログラムから呼び出すためのメカニズムを提供します。
import "C"
という特殊なインポート宣言を使用し、Cの関数やデータ構造をGoのコードから利用できるようにします。 syscall
パッケージ: Goの標準ライブラリの一部で、オペレーティングシステムの低レベルなシステムコールへのアクセスを提供します。これには、ファイル操作、ネットワーク通信、プロセス管理、そしてC言語のエラーコードerrno
のGoのエラーへの変換などが含まれます。cgoを使用する際に、Cのライブラリが設定するerrno
の値をGo側で取得・処理するために重要な役割を果たします。runtime/cgo
パッケージ: cgoの内部実装に関連するGoのランタイムパッケージです。cgoの呼び出し規約や、CとGoの間のデータ変換などを担当します。cgoを使用するGoプログラムは、このパッケージに暗黙的に依存します。runtime/race
パッケージ: Goの競合検出器(Race Detector)に関連するランタイムパッケージです。競合検出器は、並行処理におけるデータ競合(複数のゴルーチンが同時に共有データにアクセスし、少なくとも一方が書き込みを行う場合に発生するバグ)を検出するためのツールです。go build -race
オプションを使用してビルドすると、このパッケージがプログラムに組み込まれ、実行時に競合を監視します。- 暗黙的な依存関係: Goのビルドシステムにおいて、特定の条件(例: cgoの使用、競合検出の有効化)が満たされた場合に、ユーザーが明示的に
import
文を記述していなくても、特定のパッケージが自動的にプログラムの依存関係に追加されることを指します。 - 循環依存: パッケージAがパッケージBに依存し、パッケージBがパッケージAに依存するといったように、依存関係がループを形成することです。Goのビルドシステムでは循環依存は許可されておらず、ビルドエラーの原因となります。このコミットでは、暗黙的な依存関係を追加する際に、既存の循環依存を避けるための考慮がなされています。
build.Package
構造体: Goのビルドシステムがパッケージの情報を表現するために使用する内部構造体です。パッケージのインポートパス、ソースファイル、Cgoファイル、依存関係などの情報を含みます。
技術的詳細
このコミットの主要な変更は、src/cmd/go/pkg.go
ファイル内のPackage.load
関数にあります。この関数は、Goのビルドシステムがパッケージの依存関係を解決する際に呼び出されます。
変更点には以下の要素が含まれます。
-
新しい排他マップの導入:
raceExclude
: 競合検出が有効な場合にruntime/race
パッケージの暗黙的なインポートから除外するパッケージを定義します。これにはruntime/race
自身、runtime/cgo
、cmd/cgo
、syscall
、errors
が含まれます。syscall
とerrors
が追加されたのは、これらのパッケージがruntime/race
に依存すると循環依存が発生する可能性があるためと考えられます。cgoExclude
: cgoプログラムがruntime/cgo
を暗黙的にインポートする際に除外するパッケージを定義します。これにはruntime/cgo
自身が含まれます。cgoSyscallExclude
: cgoプログラムがsyscall
を暗黙的にインポートする際に除外するパッケージを定義します。これにはruntime/cgo
とruntime/race
が含まれます。これは、runtime/cgo
やruntime/race
がsyscall
に依存すると循環依存が発生する可能性があるためです。
-
syscall
の暗黙的なインポートロジックの追加:if len(p.CgoFiles) > 0 && (!p.Standard || !cgoSyscallExclude[p.ImportPath])
という条件が追加されました。これは、パッケージがcgoファイルを含み(len(p.CgoFiles) > 0
)、かつ標準パッケージでないか、またはcgoSyscallExclude
マップに含まれていない場合に、syscall
パッケージをimportPaths
に追加することを意味します。これにより、cgoプログラムは自動的にsyscall
に依存するようになります。
-
runtime/cgo
の暗黙的なインポートロジックの修正:- 既存の
runtime/cgo
の暗黙的なインポートロジックの条件が(!p.Standard || p.ImportPath != "runtime/cgo")
から(!p.Standard || !cgoExclude[p.ImportPath])
に変更されました。これは、cgoExclude
マップを使用して、より柔軟に除外パッケージを管理できるようにするためです。
- 既存の
-
runtime/race
の暗黙的なインポートロジックの修正:- 競合検出が有効な場合の
runtime/race
の暗黙的なインポートロジックの条件が(!p.Standard || (p.ImportPath != "runtime/race" && p.ImportPath != "runtime/cgo" && p.ImportPath != "cmd/cgo"))
から(!p.Standard || !raceExclude[p.ImportPath])
に変更されました。これも、raceExclude
マップを使用して、より柔軟に除外パッケージを管理できるようにするためです。
- 競合検出が有効な場合の
src/cmd/go/test.bash
ファイルでは、これらの変更が正しく機能することを確認するための新しいテストケースが追加されています。
TEST cgo depends on syscall
という新しいテストブロックが追加されました。- このテストは、一時ディレクトリにcgoを含むGoプログラム(
foo.go
)を作成し、go build -race foo
コマンドでビルドを試みます。 - ビルドが成功すれば、cgoプログラムが
syscall
に正しく依存し、競合検出が有効な場合でも問題なくビルドできることが検証されます。
これらの変更により、Goのビルドシステムはcgoプログラムの依存関係をより正確に管理し、特にerrno
の処理に関連する潜在的な問題を解決します。
コアとなるコードの変更箇所
src/cmd/go/pkg.go
の変更点:
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -323,6 +323,23 @@ func expandScanner(err error) error {
return err
}
+var raceExclude = map[string]bool{
+ "runtime/race": true,
+ "runtime/cgo": true,
+ "cmd/cgo": true,
+ "syscall": true,
+ "errors": true,
+}
+
+var cgoExclude = map[string]bool{
+ "runtime/cgo": true,
+}
+
+var cgoSyscallExclude = map[string]bool{
+ "runtime/cgo": true,
+ "runtime/race": true,
+}
+
// load populates p using information from bp, err, which should
// be the result of calling build.Context.Import.
func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package {
@@ -375,17 +392,22 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
}
importPaths := p.Imports
- // Packages that use cgo import runtime/cgo implicitly,
- // except runtime/cgo itself.
- if len(p.CgoFiles) > 0 && (!p.Standard || p.ImportPath != "runtime/cgo") {
+ // Packages that use cgo import runtime/cgo implicitly.
+ // Packages that use cgo also import syscall implicitly,
+ // to wrap errno.
+ // Exclude certain packages to avoid circular dependencies.
+ if len(p.CgoFiles) > 0 && (!p.Standard || !cgoExclude[p.ImportPath]) {
importPaths = append(importPaths, "runtime/cgo")
}
+ if len(p.CgoFiles) > 0 && (!p.Standard || !cgoSyscallExclude[p.ImportPath]) {
+ importPaths = append(importPaths, "syscall")
+ }
// Everything depends on runtime, except runtime and unsafe.
if !p.Standard || (p.ImportPath != "runtime" && p.ImportPath != "unsafe") {
importPaths = append(importPaths, "runtime")
// When race detection enabled everything depends on runtime/race.
- // Exclude runtime/cgo and cmd/cgo to avoid circular dependencies.
- if buildRace && (!p.Standard || (p.ImportPath != "runtime/race" && p.ImportPath != "runtime/cgo" && p.ImportPath != "cmd/cgo")) {
+ // Exclude certain packages to avoid circular dependencies.
+ if buildRace && (!p.Standard || !raceExclude[p.ImportPath]) {
importPaths = append(importPaths, "runtime/race")
}
}
src/cmd/go/test.bash
の変更点:
--- a/src/cmd/go/test.bash
+++ b/src/cmd/go/test.bash
@@ -434,20 +434,34 @@ elif ! grep "case-insensitive file name collision" $d/out >/dev/null; then
fi
TEST go get cover
-./testgo get code.google.com/p/go.tools/cmd/cover
+./testgo get code.google.com/p/go.tools/cmd/cover || ok=false
unset GOPATH
rm -rf $d
# Only succeeds if source order is preserved.
TEST source file name order preserved
-./testgo test testdata/example[12]_test.go
+./testgo test testdata/example[12]_test.go || ok=false
# Check that coverage analysis works at all.
# Don't worry about the exact numbers
TEST coverage runs
-./testgo test -short -coverpkg=strings strings regexp
-./testgo test -short -cover strings math regexp
+./testgo test -short -coverpkg=strings strings regexp || ok=false
+./testgo test -short -cover strings math regexp || ok=false
+
+TEST cgo depends on syscall
+rm -rf $GOROOT/pkg/*_race
+d=$(TMPDIR=/var/tmp mktemp -d -t testgoXXX)
+export GOPATH=$d
+mkdir -p $d/src/foo
+echo '
+package foo
+//#include <stdio.h>
+import "C"
+' >$d/src/foo/foo.go
+./testgo build -race foo || ok=false
+rm -rf $d
+unset GOPATH
# clean up
if $started; then stop; fi
コアとなるコードの解説
src/cmd/go/pkg.go
における変更は、Goのビルドシステムがパッケージの依存関係をどのように解決し、特にcgoや競合検出といった特殊なケースをどのように扱うかを根本的に変更しています。
-
排他マップの導入:
raceExclude
,cgoExclude
,cgoSyscallExclude
といったマップは、特定のパッケージが暗黙的にインポートされるべきではない状況を明確に定義します。これは、循環依存を避けるための重要なメカニズムです。例えば、runtime/cgo
がsyscall
に依存し、syscall
がruntime/cgo
に依存するといった循環依存はGoのビルドでは許されません。これらのマップにより、ビルドシステムは安全に暗黙的な依存関係を追加できます。- 特に
syscall
とerrors
がraceExclude
に追加されたことは、これらのパッケージがruntime/race
と直接的または間接的に相互依存関係を持つ可能性があり、それを回避する必要があったことを示唆しています。
-
syscall
の暗黙的なインポート:if len(p.CgoFiles) > 0 && (!p.Standard || !cgoSyscallExclude[p.ImportPath]) { importPaths = append(importPaths, "syscall") }
この行がこのコミットの核心です。len(p.CgoFiles) > 0
は、現在のパッケージがCgoソースファイルを含んでいることを意味します。つまり、このパッケージはC言語のコードと連携しているため、errno
の処理のためにsyscall
パッケージが必要になる可能性が高いと判断されます。(!p.Standard || !cgoSyscallExclude[p.ImportPath])
は、標準パッケージでない場合、またはcgoSyscallExclude
マップで明示的に除外されていない場合に、syscall
をインポートするという条件です。これにより、runtime/cgo
やruntime/race
のような、syscall
を暗黙的にインポートすると循環依存を引き起こす可能性のあるパッケージは除外されます。 この変更により、cgoプログラムは明示的なimport "syscall"
がなくても、syscall
パッケージの機能を利用できるようになり、errno
の適切な処理が保証されます。
-
既存の暗黙的インポートロジックの改善:
runtime/cgo
とruntime/race
の暗黙的なインポートロジックが、ハードコードされた除外リストから、新しく導入された排他マップを使用するように変更されました。これにより、コードの可読性と保守性が向上し、将来的に除外パッケージを追加・変更する際も容易になります。
src/cmd/go/test.bash
における新しいテストケースは、これらの変更が意図した通りに機能することを検証します。特に、cgoプログラムがsyscall
に依存し、かつ競合検出が有効な場合でも、ビルドが成功することを確認しています。これは、ビルドシステムの堅牢性を高める上で非常に重要です。
全体として、このコミットはGoのビルドシステムにおける依存関係解決のロジックを洗練させ、特にcgoプログラムの信頼性と正確性を向上させるものです。
関連リンク
- Go CL 12651044: https://golang.org/cl/12651044
参考にした情報源リンク
- Go言語の公式ドキュメント(cgo、syscall、runtime/raceに関する一般的な知識)
- Goのソースコード(
src/cmd/go/pkg.go
およびsrc/cmd/go/test.bash
のdiff) - GoのIssueトラッカー(Issue #5048に関する情報、ただし直接的な検索結果は得られず、一般的な問題解決の文脈で理解)