[インデックス 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.FindTree
は go/build
パッケージの初期のAPIの一部であり、後に Import
や ImportDir
といったより新しい関数に置き換えられたことが示唆されています。このコミットが行われた2012年2月時点では、build.FindTree
がパッケージ解決のより適切な方法として導入されたと考えられます。
exp/types
パッケージは、Go言語の型システムに関する実験的な機能を提供しており、コンパイラやツールがパッケージの型情報をインポートする際に GcImporter
を使用します。したがって、GcImporter
が正確かつ効率的にパッケージを解決することは、型システム全体の機能にとって重要でした。この変更は、GcImporter
がGoのビルドシステムとより密接に連携し、正しいパッケージパスを特定できるようにするための改善です。
前提知識の解説
Go言語のパッケージとインポート
Go言語では、コードは「パッケージ」という単位で整理されます。パッケージは、関連する機能の集合であり、他のパッケージからインポートして利用することができます。import "fmt"
のように記述することで、fmt
パッケージの関数や型を利用できるようになります。
GOROOT
と GOPATH
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
がパッケージの物理的な場所を特定する方法を改善したことです。
変更前は、GcImporter
は pkgRoot
というグローバル変数を使用していました。この pkgRoot
は filepath.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のビルドシステムとより密接に連携し、GOROOT
と GOPATH
の両方、さらには将来的なパッケージ解決の変更にも対応できるようになります。これは、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
-
インポートの変更:
- "runtime"
:runtime
パッケージのインポートが削除されました。これは、pkgRoot
の定義でruntime.GOROOT()
やruntime.GOOS
、runtime.GOARCH
を直接使用する必要がなくなったためです。+ "go/build"
:go/build
パッケージが新しくインポートされました。これにより、Goのビルドシステムが提供するパッケージ検索機能を利用できるようになります。
-
pkgRoot
グローバル変数の削除:- pkgRoot = filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_"+runtime.GOARCH)
:pkgRoot
というグローバル変数の定義が削除されました。この変数は、Goのインストールディレクトリ内のパッケージパスをハードコードしていましたが、build.FindTree
の導入により不要になりました。
-
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
を結合して、コンパイル済みパッケージのパスを構築します。これにより、GOROOT
とGOPATH
の両方からパッケージを適切に解決できるようになります。コメントも"$GOROOT/pkg..."
から"$GOPATH/pkg..."
に変更され、GOPATH
の考慮が明示されています。
src/pkg/exp/types/gcimporter_test.go
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の標準的な方法に準拠するようになりました。
関連リンク
- Go言語の
go/build
パッケージに関するドキュメント: https://pkg.go.dev/go/build - Go言語の
exp/types
パッケージに関するドキュメント: https://pkg.go.dev/exp/types - Go言語の
runtime
パッケージに関するドキュメント: https://pkg.go.dev/runtime - Go言語の
filepath
パッケージに関するドキュメント: https://pkg.go.dev/path/filepath - Go言語の
GOPATH
について (Go Modules導入前の情報も含む): https://go.dev/doc/code
参考にした情報源リンク
- GitHub: golang/go commit 09f6a491947373107e1425eae1187d573e398492: https://github.com/golang/go/commit/09f6a491947373107e1425eae1187d573e398492
- Web検索結果 (golang issue 2932 exp/types build.FindTree):
- https://go.dev/doc/go1.0#build (Go 1.0のリリースノートで
go/build
パッケージの変更について言及されている可能性) - https://go.dev/doc/go1.0.html (Go 1.0のリリースノート)
- https://go.dev/blog/go1.0 (Go 1.0のブログ記事)
- https://go.dev/doc/effective_go#gopath (Effective GoのGOPATHに関する記述)
- https://go.dev/doc/modules/managing-dependencies (Go Modulesに関するドキュメント)
- https://go.dev/doc/go1.0#build (Go 1.0のリリースノートで