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

[インデックス 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のビルドシステムがパッケージの依存関係を解決する際に呼び出されます。

変更点には以下の要素が含まれます。

  1. 新しい排他マップの導入:

    • raceExclude: 競合検出が有効な場合にruntime/raceパッケージの暗黙的なインポートから除外するパッケージを定義します。これにはruntime/race自身、runtime/cgocmd/cgosyscallerrorsが含まれます。syscallerrorsが追加されたのは、これらのパッケージがruntime/raceに依存すると循環依存が発生する可能性があるためと考えられます。
    • cgoExclude: cgoプログラムがruntime/cgoを暗黙的にインポートする際に除外するパッケージを定義します。これにはruntime/cgo自身が含まれます。
    • cgoSyscallExclude: cgoプログラムがsyscallを暗黙的にインポートする際に除外するパッケージを定義します。これにはruntime/cgoruntime/raceが含まれます。これは、runtime/cgoruntime/racesyscallに依存すると循環依存が発生する可能性があるためです。
  2. syscallの暗黙的なインポートロジックの追加:

    • if len(p.CgoFiles) > 0 && (!p.Standard || !cgoSyscallExclude[p.ImportPath])という条件が追加されました。これは、パッケージがcgoファイルを含み(len(p.CgoFiles) > 0)、かつ標準パッケージでないか、またはcgoSyscallExcludeマップに含まれていない場合に、syscallパッケージをimportPathsに追加することを意味します。これにより、cgoプログラムは自動的にsyscallに依存するようになります。
  3. runtime/cgoの暗黙的なインポートロジックの修正:

    • 既存のruntime/cgoの暗黙的なインポートロジックの条件が(!p.Standard || p.ImportPath != "runtime/cgo")から(!p.Standard || !cgoExclude[p.ImportPath])に変更されました。これは、cgoExcludeマップを使用して、より柔軟に除外パッケージを管理できるようにするためです。
  4. 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や競合検出といった特殊なケースをどのように扱うかを根本的に変更しています。

  1. 排他マップの導入:

    • raceExclude, cgoExclude, cgoSyscallExcludeといったマップは、特定のパッケージが暗黙的にインポートされるべきではない状況を明確に定義します。これは、循環依存を避けるための重要なメカニズムです。例えば、runtime/cgosyscallに依存し、syscallruntime/cgoに依存するといった循環依存はGoのビルドでは許されません。これらのマップにより、ビルドシステムは安全に暗黙的な依存関係を追加できます。
    • 特にsyscallerrorsraceExcludeに追加されたことは、これらのパッケージがruntime/raceと直接的または間接的に相互依存関係を持つ可能性があり、それを回避する必要があったことを示唆しています。
  2. 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/cgoruntime/raceのような、syscallを暗黙的にインポートすると循環依存を引き起こす可能性のあるパッケージは除外されます。 この変更により、cgoプログラムは明示的なimport "syscall"がなくても、syscallパッケージの機能を利用できるようになり、errnoの適切な処理が保証されます。
  3. 既存の暗黙的インポートロジックの改善:

    • runtime/cgoruntime/raceの暗黙的なインポートロジックが、ハードコードされた除外リストから、新しく導入された排他マップを使用するように変更されました。これにより、コードの可読性と保守性が向上し、将来的に除外パッケージを追加・変更する際も容易になります。

src/cmd/go/test.bashにおける新しいテストケースは、これらの変更が意図した通りに機能することを検証します。特に、cgoプログラムがsyscallに依存し、かつ競合検出が有効な場合でも、ビルドが成功することを確認しています。これは、ビルドシステムの堅牢性を高める上で非常に重要です。

全体として、このコミットはGoのビルドシステムにおける依存関係解決のロジックを洗練させ、特にcgoプログラムの信頼性と正確性を向上させるものです。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント(cgo、syscall、runtime/raceに関する一般的な知識)
  • Goのソースコード(src/cmd/go/pkg.goおよびsrc/cmd/go/test.bashのdiff)
  • GoのIssueトラッカー(Issue #5048に関する情報、ただし直接的な検索結果は得られず、一般的な問題解決の文脈で理解)