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

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

このコミットは、Go言語の実験的な型チェッカーパッケージ exp/types において、ソースコードが利用できないパッケージでもインポートを許可するように変更を加えるものです。具体的には、go/build パッケージの Import 関数を使用する際に、バイナリファイルからのインポートを明示的に許可するフラグ build.AllowBinary を追加しています。これにより、コンパイル済みのパッケージオブジェクトのみが存在する場合でも、型情報の取得が可能になります。

コミット

exp/types: permit importing packages without available source.

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

https://github.com/golang/go/commit/ff2f851af1dba1155cf5d072054c5da98f3b2756

元コミット内容

exp/types: permit importing packages without available source.

R=gri, iant
CC=golang-dev
https://golang.org/cl/6586051

変更の背景

Go言語のツールチェインにおいて、型情報を扱う go/types パッケージは、プログラムの静的解析やIDEの機能、リンターなどに利用されます。通常、go/types がパッケージをインポートする際には、そのパッケージのソースコードが存在することを前提としていました。しかし、Goのビルドシステムでは、パッケージがコンパイルされると $GOPATH/pkg ディレクトリに .a (アーカイブ) ファイルなどのパッケージオブジェクトが生成されます。

このコミットが導入された背景には、以下のようなシナリオが考えられます。

  1. ソースコードがない環境での型チェック: 例えば、配布されたバイナリライブラリや、ソースコードが手元にない環境で、そのライブラリがエクスポートしている型や関数シグネチャを解析したい場合。
  2. ビルド済みパッケージの利用: go install などで一度ビルドされたパッケージは、そのパッケージオブジェクト(バイナリ)が $GOPATH/pkg に存在します。型チェッカーがこれらの既存のバイナリから型情報を効率的に読み取れるようにすることで、パフォーマンスの向上や、不必要なソースコードの再解析を避けることができます。
  3. ツール開発の柔軟性: go/types を利用するツール(例: go vetgocode など)が、ソースコードの有無に依存せず、より柔軟にパッケージの型情報を取得できるようにするため。

この変更は、go/types パッケージが、ソースコードの有無に関わらず、利用可能なパッケージオブジェクトから型情報をインポートできるようにするための重要なステップでした。

前提知識の解説

このコミットを理解するためには、以下のGo言語のパッケージと概念について知っておく必要があります。

  1. go/types パッケージ:

    • Go言語の公式ライブラリの一部で、Goプログラムの型システムを静的に解析するためのパッケージです。
    • コンパイラやリンター、IDE、静的解析ツールなどが、Goのソースコードやコンパイル済みバイナリから型情報を抽出し、検証するために使用します。
    • このパッケージは、Goの言語仕様に厳密に従って型チェックを行い、プログラムの正確性を保証します。
  2. go/build パッケージ:

    • Go言語のソースコードをビルド、インストール、テストする際のパッケージの検索、依存関係の解決、ビルドタグの処理など、Goのビルドシステムに関する機能を提供するパッケージです。
    • go build コマンドの内部で利用される機能の多くが、このパッケージによって提供されています。
    • build.Import 関数は、指定されたパスに基づいてGoパッケージを検索し、そのパッケージに関する情報(ソースディレクトリ、パッケージオブジェクトのパスなど)を build.Package 構造体として返します。
  3. build.Import 関数:

    • go/build パッケージの主要な関数の一つで、Goのパッケージパス(例: "fmt", "github.com/user/repo")と現在のソースディレクトリ (srcDir) を受け取り、そのパッケージに関する詳細情報 (build.Package 構造体) を返します。
    • この関数は、パッケージのソースコードの場所、依存関係、ビルドタグなどを特定するために使用されます。
  4. build.ImportMode (インポートモード):

    • build.Import 関数に渡される引数で、パッケージのインポート動作を制御するためのビットフラグのセットです。
    • build.FindOnly: このフラグが設定されている場合、build.Import はパッケージのソースコードを解析せず、単にパッケージのディレクトリとパッケージオブジェクトのパスを見つけるだけです。これは、パッケージの存在確認や、コンパイル済みバイナリの場所を特定するのに役立ちます。
    • build.AllowBinary: このフラグは、build.Import がパッケージを検索する際に、ソースコードが存在しない場合でも、コンパイル済みのパッケージオブジェクト(例: .a ファイル)を考慮に入れることを許可します。このフラグがない場合、通常はソースコードが見つからないとエラーになるか、パッケージが見つからないと判断されます。

技術的詳細

このコミットの技術的な核心は、go/build パッケージの build.Import 関数に渡す build.ImportMode の変更にあります。

変更前は、gcimporter.go 内の FindPkg 関数で、パッケージの検索に build.FindOnly フラグのみを使用していました。

bp, _ := build.Import(path, srcDir, build.FindOnly)

build.FindOnly は、パッケージのソースディレクトリとパッケージオブジェクトのパスを見つけることを目的としていますが、デフォルトではソースコードの存在を前提としています。もしソースコードが見つからない場合、build.Import はそのパッケージを見つけられないと判断するか、エラーを返す可能性があります。

変更後は、build.FindOnly に加えて build.AllowBinary フラグがビットOR演算子 (|) で追加されました。

bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)

build.AllowBinary フラグの追加により、build.Import 関数は、指定されたパッケージのソースコードが見つからない場合でも、Goの標準的なパッケージオブジェクトの場所(例: $GOPATH/pkg/$GOOS_$GOARCH/) に存在するコンパイル済みのバイナリファイル(通常は .a 拡張子を持つアーカイブファイル)を検索し、それを有効なパッケージとして認識するようになります。

これにより、go/types パッケージは、ソースコードが手元にない環境でも、既にコンパイルされてインストールされているパッケージの型情報を、そのバイナリファイルから読み取ることができるようになりました。これは、特にクロスコンパイル環境や、配布されたライブラリの型情報を利用するツールにとって非常に重要な機能改善です。

gcimporter.go は、Goコンパイラが生成するパッケージオブジェクト(GC形式のバイナリ)をインポートするためのロジックを扱うファイルです。このファイルが build.AllowBinary を使用することで、go/types がこれらのバイナリから型情報を効率的に抽出できるようになります。

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

変更は src/pkg/exp/types/gcimporter.go ファイルの1箇所のみです。

--- a/src/pkg/exp/types/gcimporter.go
+++ b/src/pkg/exp/types/gcimporter.go
@@ -42,7 +42,8 @@ func FindPkg(path, srcDir string) (filename, id string) {
 	switch {
 	default:
 		// "x" -> "$GOPATH/pkg/$GOOS_$GOARCH/x.ext", "x"
-		bp, _ := build.Import(path, srcDir, build.FindOnly)
+		// Don't require the source files to be present.
+		bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)
 		if bp.PkgObj == "" {
 			return
 		}

コアとなるコードの解説

src/pkg/exp/types/gcimporter.go 内の FindPkg 関数は、与えられたパッケージパス (path) とソースディレクトリ (srcDir) に基づいて、Goパッケージのファイル名とIDを特定する役割を担っています。この関数は、go/types パッケージが他のパッケージの型情報をインポートする際に、そのパッケージの物理的な場所(特にコンパイル済みパッケージオブジェクトの場所)を特定するために使用されます。

変更前のコードでは、build.Import 関数を build.FindOnly モードで呼び出していました。これは、パッケージのソースディレクトリとパッケージオブジェクトのパスを見つけることを意図していますが、ソースコードの存在を前提としていました。

変更後のコードでは、build.Import の呼び出しが以下のように修正されました。

// Don't require the source files to be present.
bp, _ := build.Import(path, srcDir, build.FindOnly|build.AllowBinary)

この変更のポイントは、build.ImportModebuild.AllowBinary フラグが追加されたことです。

  • build.FindOnly: これは以前から存在し、パッケージのソースディレクトリとパッケージオブジェクトのパスを特定するモードです。
  • build.AllowBinary: この新しいフラグは、build.Import 関数に対して、ソースコードが見つからない場合でも、コンパイル済みのパッケージオブジェクト(例: $GOPATH/pkg にある .a ファイル)を有効なパッケージとして考慮するように指示します。

コメント // Don't require the source files to be present. が追加されたことからもわかるように、この変更の目的は、ソースコードが利用できない状況でもパッケージのインポートを可能にすることです。これにより、go/types は、コンパイル済みのバイナリから直接型情報を読み取ることができるようになり、より幅広いシナリオで利用できるようになりました。

bp.PkgObj == "" のチェックは、build.Import がパッケージオブジェクトのパスを見つけられなかった場合に、関数を早期に終了させるためのものです。build.AllowBinary の追加により、この bp.PkgObj が空でない可能性が高まり、より多くのケースでパッケージオブジェクトが正常に特定されるようになります。

関連リンク

参考にした情報源リンク