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

[インデックス 11782] ファイルの概要

このコミットは、Go言語の実験的な型システムパッケージである exp/types における GcImporter の実装を改善するものです。具体的には、パッケージの検索ロジックにおいて、従来の runtime.GOROOT()filepath.Join を用いたパス構築から、より堅牢でGoのビルドシステムに統合された go/build パッケージの build.FindTree 関数を使用するように変更しています。これにより、Goのパッケージ管理のベストプラクティスに沿った形で、インポートされたパッケージの解決が行われるようになります。

コミット

commit 09f6a491947373107e1425eae1187d573e398492
Author: James Whitehead <jnwhiteh@gmail.com>
Date:   Fri Feb 10 13:35:03 2012 -0800

    exp/types: Use build.FindTree in GcImporter
    
    Fixes #2932
    
    R=gri
    CC=golang-dev
    https://golang.org/cl/5654046

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/09f6a491947373107e1425eae1187d573e398492

元コミット内容

exp/types: Use build.FindTree in GcImporter

Fixes #2932

R=gri
CC=golang-dev
https://golang.org/cl/5654046

変更の背景

このコミットの背景には、Go言語のパッケージ管理とビルドシステムの進化があります。元のコードでは、Goの標準ライブラリやサードパーティパッケージのパスを解決するために、GOROOT 環境変数と filepath.Join を直接使用していました。しかし、Goのビルドシステムは GOPATH の導入やモジュールシステムの進化に伴い、より複雑なパッケージ解決ロジックを必要とするようになりました。

Fixes #2932 とあるように、この変更は特定のバグや問題の修正を目的としています。ウェブ検索の結果から、build.FindTreego/build パッケージの初期のAPIの一部であり、後に ImportImportDir といったより新しい関数に置き換えられたことが示唆されています。このコミットが行われた2012年2月時点では、build.FindTree がパッケージ解決のより適切な方法として導入されたと考えられます。

exp/types パッケージは、Go言語の型システムに関する実験的な機能を提供しており、コンパイラやツールがパッケージの型情報をインポートする際に GcImporter を使用します。したがって、GcImporter が正確かつ効率的にパッケージを解決することは、型システム全体の機能にとって重要でした。この変更は、GcImporter がGoのビルドシステムとより密接に連携し、正しいパッケージパスを特定できるようにするための改善です。

前提知識の解説

Go言語のパッケージとインポート

Go言語では、コードは「パッケージ」という単位で整理されます。パッケージは、関連する機能の集合であり、他のパッケージからインポートして利用することができます。import "fmt" のように記述することで、fmt パッケージの関数や型を利用できるようになります。

GOROOTGOPATH

  • GOROOT: Goのインストールディレクトリを指します。Goの標準ライブラリのソースコードやコンパイル済みパッケージがここに格納されます。
  • GOPATH: Goのワークスペースディレクトリを指します。ユーザーが開発するプロジェクトのソースコード、コンパイル済みパッケージ、およびダウンロードしたサードパーティのパッケージがここに格納されます。Go 1.11以降はGo Modulesが導入され、GOPATH の役割は限定的になりましたが、このコミットが作成された当時は GOPATH がGoのパッケージ管理の中心でした。

go/build パッケージ

go/build パッケージは、Goのソースコードをビルドするための情報を提供します。これには、Goのパッケージの検索、依存関係の解決、ビルドタグの処理などが含まれます。このパッケージは、Goのツールチェイン(go build, go install など)の基盤となっています。

exp/types パッケージ

exp/types は、Go言語の型システムに関する実験的な機能を提供するパッケージです。これは、Goのコンパイラや静的解析ツールが、Goのソースコードから型情報を抽出し、操作するために使用されることがあります。GcImporter は、このパッケージ内で、コンパイル済みのGoパッケージ(.a.5.6.8 などの拡張子を持つファイル)から型情報をインポートする役割を担っています。

build.FindTree 関数 (旧API)

build.FindTree は、go/build パッケージの初期バージョンに存在した関数で、指定されたパッケージパスに対応するGoのソースツリー(GOROOT または GOPATH 内のディレクトリ構造)を見つけるために使用されました。この関数は、パッケージのルートディレクトリとパッケージ名を含む Tree 構造体を返しました。

技術的詳細

このコミットの主要な技術的変更点は、GcImporter がパッケージの物理的な場所を特定する方法を改善したことです。

変更前は、GcImporterpkgRoot というグローバル変数を使用していました。この pkgRootfilepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH) のように、GOROOT と現在のOS・アーキテクチャに基づいてハードコードされたパスを構築していました。そして、インポートされるパッケージ名(例: "fmt")をこの pkgRoot に結合して、パッケージのコンパイル済みファイル(例: $GOROOT/pkg/$GOOS_$GOARCH/fmt.a)を探していました。

このアプローチの問題点は、GOPATH のような他のパッケージ検索パスを考慮していないこと、およびGoのビルドシステムが内部的に持つパッケージ解決ロジックを再実装している点にありました。Goのビルドシステムは、GOROOT だけでなく GOPATH も考慮し、さらにベンダーディレクトリやモジュールパスなど、より複雑なルールに基づいてパッケージを解決します。GcImporter が独自のパス解決ロジックを持つことは、Goのビルドシステムとの整合性を損ない、将来的な変更への対応を困難にする可能性がありました。

このコミットでは、runtime パッケージのインポートを削除し、代わりに go/build パッケージをインポートしています。そして、findPkg 関数内で、pkgRoot を直接使用する代わりに build.FindTree(path) を呼び出すように変更しています。

build.FindTree(path) は、与えられたパッケージパス(例: "x")に基づいて、Goのビルドシステムがパッケージを検索するのと同じロジックを使用して、そのパッケージがどこにあるべきかを特定します。成功した場合、Tree 構造体とパッケージ名、エラーを返します。Tree 構造体には、パッケージのルートディレクトリを示す PkgDir() メソッドが含まれています。この PkgDir() を使用することで、GcImporter はGoのビルドシステムが認識する正しいパッケージディレクトリを取得し、そこにパッケージのコンパイル済みファイルが存在すると期待できるようになります。

これにより、GcImporter はGoのビルドシステムとより密接に連携し、GOROOTGOPATH の両方、さらには将来的なパッケージ解決の変更にも対応できるようになります。これは、Goのツールチェイン全体の一貫性と堅牢性を高める上で重要な改善です。

コアとなるコードの変更箇所

diff --git a/src/pkg/exp/types/gcimporter.go b/src/pkg/exp/types/gcimporter.go
index a573fbb246..8b28aede1e 100644
--- a/src/pkg/exp/types/gcimporter.go
+++ b/src/pkg/exp/types/gcimporter.go
@@ -11,12 +11,12 @@ import (
 	"errors"
 	"fmt"
 	"go/ast"
+"go/build"
 	"go/token"
 	"io"
 	"math/big"
 	"os"
 	"path/filepath"
-"runtime"
 	"strconv"
 	"text/scanner"
 )
@@ -24,7 +24,6 @@ import (
 const trace = false // set to true for debugging
 
 var (
-"pkgRoot = filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH)"
 	pkgExts = [...]string{".a", ".5", ".6", ".8"}
 )
 
@@ -39,8 +38,12 @@ func findPkg(path string) (filename, id string) {
 	var noext string
 	switch path[0] {
 	default:
-"// "x" -> "$GOROOT/pkg/$GOOS_$GOARCH/x.ext", "x""
-"noext = filepath.Join(pkgRoot, path)"
+"// "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x""
+"tree, pkg, err := build.FindTree(path)"
+"if err != nil {"
+"return"
+"}"
+"noext = filepath.Join(tree.PkgDir(), pkg)"
 
 	case '.':
 	// "./x" -> "/this/directory/x.ext", "/this/directory/x"
diff --git a/src/pkg/exp/types/gcimporter_test.go b/src/pkg/exp/types/gcimporter_test.go
index 912d467ea0..5411f3bcce 100644
--- a/src/pkg/exp/types/gcimporter_test.go
+++ b/src/pkg/exp/types/gcimporter_test.go
@@ -61,7 +61,7 @@ func testPath(t *testing.T, path string) bool {
 const maxTime = 3 * time.Second
 
 func testDir(t *testing.T, dir string, endTime time.Time) (nimports int) {
-"dirname := filepath.Join(pkgRoot, dir)"
+"dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir)"
 	list, err := ioutil.ReadDir(dirname)
 	if err != nil {
 		t.Errorf("testDir(%s): %s", dirname, err)

コアとなるコードの解説

src/pkg/exp/types/gcimporter.go

  1. インポートの変更:

    • - "runtime": runtime パッケージのインポートが削除されました。これは、pkgRoot の定義で runtime.GOROOT()runtime.GOOSruntime.GOARCH を直接使用する必要がなくなったためです。
    • + "go/build": go/build パッケージが新しくインポートされました。これにより、Goのビルドシステムが提供するパッケージ検索機能を利用できるようになります。
  2. pkgRoot グローバル変数の削除:

    • - pkgRoot = filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH): pkgRoot というグローバル変数の定義が削除されました。この変数は、Goのインストールディレクトリ内のパッケージパスをハードコードしていましたが、build.FindTree の導入により不要になりました。
  3. findPkg 関数の変更:

    • switch path[0]default ケース(相対パスではないパッケージ名、例: "fmt")において、パッケージの解決ロジックが大きく変更されました。
    • - noext = filepath.Join(pkgRoot, path): 以前は、ハードコードされた pkgRoot にパッケージパスを結合していました。
    • + tree, pkg, err := build.FindTree(path): 新しく build.FindTree 関数が呼び出されます。この関数は、Goのビルドシステムがパッケージを検索するのと同じロジックを使用して、指定された path に対応するパッケージツリーを検索します。
    • + if err != nil { return }: build.FindTree がエラーを返した場合(パッケージが見つからないなど)、関数はすぐに終了します。
    • + noext = filepath.Join(tree.PkgDir(), pkg): build.FindTree が成功した場合、返された tree オブジェクトの PkgDir() メソッドを使用してパッケージのルートディレクトリを取得し、それにパッケージ名 pkg を結合して、コンパイル済みパッケージのパスを構築します。これにより、GOROOTGOPATH の両方からパッケージを適切に解決できるようになります。コメントも "$GOROOT/pkg..." から "$GOPATH/pkg..." に変更され、GOPATH の考慮が明示されています。

src/pkg/exp/types/gcimporter_test.go

  1. testDir 関数の変更:
    • - dirname := filepath.Join(pkgRoot, dir): テストコードでも、以前は pkgRoot グローバル変数を使用してテスト対象のディレクトリパスを構築していました。
    • + dirname := filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH, dir): テストコードでは、pkgRoot が削除されたため、代わりに runtime.GOROOT() を直接使用して、Goの標準パッケージが格納されているディレクトリを明示的に指定するように変更されました。これは、テストの目的が特定のGoのインストールパスにあるパッケージを検証することであるため、build.FindTree のような汎用的なパッケージ検索ロジックではなく、固定パスを使用することが適切と判断されたためと考えられます。

これらの変更により、GcImporter はGoのビルドシステムとより密接に連携し、パッケージの解決がより堅牢でGoの標準的な方法に準拠するようになりました。

関連リンク

参考にした情報源リンク