[インデックス 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のリリースノートで