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

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

このコミットは、Goコマンドラインツール (cmd/go) における go list コマンドの機能拡張と、gccgo コンパイラ使用時のパッケージの「古さ (stale)」判定ロジックのバグ修正を目的としています。具体的には、go list コマンドに -compiler フラグのサポートを追加し、pkg.go 内の isStale 関数が unsafe パッケージと gccgo コンパイラを正しく扱うように修正しています。これにより、gccgo を使用している環境で go buildgo install が不要な再ビルドを実行する問題を解決します。

コミット

commit 5c4d6ebb118bc541647b9b15af5a19502570d0dd
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date:   Wed Mar 7 23:15:55 2012 +0100

    cmd/go: support -compiler for go list, fix isStale for gccgo.
    
    Fixes #3228.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5784044

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

https://github.com/golang/go/commit/5c4d6ebb118bc541647b9b15af5a19502570d0dd

元コミット内容

commit 5c4d6ebb118bc541647b9b15af5a19502570d0dd
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date:   Wed Mar 7 23:15:55 2012 +0100

    cmd/go: support -compiler for go list, fix isStale for gccgo.
    
    Fixes #3228.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/5784044
---
 src/cmd/go/list.go | 1 +\
 src/cmd/go/pkg.go  | 4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/cmd/go/list.go b/src/cmd/go/list.go
index 99a8704021..446e2304be 100644
--- a/src/cmd/go/list.go
+++ b/src/cmd/go/list.go
@@ -86,6 +86,7 @@ For more about specifying packages, see 'go help packages'.
 
 func init() {
 	cmdList.Run = runList // break init cycle
+	cmdList.Flag.Var(buildCompiler{}, "compiler", "")
 }
 
 var listE = cmdList.Flag.Bool("e", false, "")
diff --git a/src/cmd/go/pkg.go b/src/cmd/go/pkg.go
index 5878d8f71e..09d84e5f27 100644
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -372,7 +372,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
 	}
 
 	// unsafe is a fake package.
-	if p.Standard && p.ImportPath == "unsafe" {\n+\tif p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") {\n \t\tp.target = ""\n \t}\n 
@@ -417,7 +417,7 @@ func computeStale(pkgs ...*Package) {\n 
 // isStale reports whether package p needs to be rebuilt.\n func isStale(p *Package, topRoot map[string]bool) bool {\n-\tif p.Standard && p.ImportPath == "unsafe" {\n+\tif p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") {\n \t\t// fake, builtin package\n \t\treturn false\n \t}\n```

## 変更の背景

このコミットは、Go Issue #3228 で報告された問題を解決するために行われました。この問題は、`gccgo` コンパイラを使用している環境で、`go build` や `go install` コマンドが不必要にパッケージを再ビルドしてしまうというものでした。

Goのビルドシステムは、パッケージが「古くなっている (stale)」かどうかを判断し、必要に応じて再ビルドを行います。しかし、`gccgo` の場合、`unsafe` パッケージの扱いが他のGoコンパイラ (gc) とは異なり、この「古さ」の判定ロジックに誤解が生じていました。

具体的には、`unsafe` パッケージはGo言語の標準ライブラリの一部ですが、特殊な性質を持つ「フェイク (fake)」パッケージであり、実際のコンパイル対象となるファイルを持たないことがほとんどです。そのため、`unsafe` パッケージは常に再ビルド不要と判断されるべきです。しかし、`gccgo` 環境下では、この `unsafe` パッケージが正しく認識されず、常に「古くなっている」と判断されてしまい、結果として不必要な再ビルドが頻繁に発生していました。これはビルド時間の増加や開発体験の悪化につながります。

この問題を解決するため、`isStale` 関数が `unsafe` パッケージだけでなく、`gccgo` コンパイラが使用されている場合も、そのパッケージが再ビルド不要であると明示的に判断するように変更されました。また、`go list` コマンドが `buildContext.Compiler` の情報を利用できるように、`-compiler` フラグのサポートが追加されました。

## 前提知識の解説

*   **`go list` コマンド**: Goのパッケージに関する情報を表示するためのコマンドです。パッケージのパス、依存関係、ビルド情報などをJSON形式などで出力できます。開発者がGoプロジェクトの構造を理解したり、スクリプトでパッケージ情報を処理したりする際に非常に役立ちます。
*   **`gccgo`**: Go言語の代替コンパイラの一つです。Goの公式コンパイラである `gc` (Go Compiler) とは異なり、GCC (GNU Compiler Collection) のフロントエンドとしてGo言語をサポートします。`gccgo` は、既存のGCCツールチェインとの統合や、特定のプラットフォームでの利用において利点を持つことがあります。
*   **`isStale` 関数**: Goのビルドシステム内部で使用される関数で、特定のパッケージが再ビルドを必要とするかどうかを判断します。これは、ソースファイルの変更日時、依存関係の変更、コンパイラのバージョンなど、様々な要因に基づいて行われます。不必要な再ビルドを避けることで、ビルド時間を短縮し、開発効率を向上させます。
*   **`unsafe` パッケージ**: Go言語の標準ライブラリに含まれる特殊なパッケージです。このパッケージは、Goの型安全性をバイパスする機能(例: ポインタ演算、任意の型への変換)を提供します。通常、Goのコードでは `unsafe` パッケージの使用は推奨されませんが、特定の低レベルな操作や、C言語との相互運用などで必要となる場合があります。`unsafe` パッケージ自体は、実際のコンパイル対象となるソースファイルを持たない「フェイク」パッケージとして扱われることが多いです。
*   **`buildContext.Compiler`**: Goのビルドコンテキストの一部で、現在使用されているGoコンパイラの種類(例: "gc", "gccgo")を示す文字列です。この情報は、ビルドプロセス中にコンパイラ固有の挙動を調整するために利用されます。

## 技術的詳細

このコミットの技術的な核心は、`pkg.go` ファイル内の `load` 関数と `isStale` 関数の修正にあります。

1.  **`load` 関数の修正**:
    `load` 関数は、パッケージのロード時にそのパッケージの特性を決定します。修正前は、`unsafe` パッケージが標準パッケージである場合にのみ `p.target = ""` (ターゲットパスなし) と設定されていました。これは、`unsafe` パッケージが「フェイク」であり、実際のビルドターゲットを持たないことを意味します。
    修正後は、この条件に `buildContext.Compiler == "gccgo"` が追加されました。これにより、`gccgo` コンパイラが使用されている場合も、`unsafe` パッケージと同様に、特定のパッケージ(特に `unsafe` パッケージ)がビルドターゲットを持たないものとして扱われるようになります。これは、`gccgo` が `unsafe` パッケージを内部的に異なる方法で処理する可能性があり、その際に不必要なビルドターゲットの生成や、それに伴う「古さ」の誤判定を防ぐためです。

2.  **`isStale` 関数の修正**:
    `isStale` 関数は、パッケージが再ビルドを必要とするかどうかを判断します。修正前は、`unsafe` パッケージが標準パッケージである場合にのみ `false` (再ビルド不要) を返していました。
    修正後は、`load` 関数と同様に、`buildContext.Compiler == "gccgo"` が条件に追加されました。これにより、`gccgo` コンパイラが使用されている場合も、`unsafe` パッケージと同様に、そのパッケージが常に再ビルド不要であると判断されるようになります。この変更は、`gccgo` が `unsafe` パッケージを常に最新であると見なすようにすることで、前述の不必要な再ビルドの問題を直接的に解決します。

3.  **`cmd/go/list.go` への `-compiler` フラグの追加**:
    `go list` コマンドに `-compiler` フラグが追加されました。これは、`go list` がパッケージ情報を取得する際に、どのコンパイラを想定しているかを指定できるようにするためです。これにより、`go list` が `buildContext.Compiler` の値を適切に設定し、`pkg.go` 内の `load` や `isStale` 関数が正しいコンパイラコンテキストで動作するようになります。これは、`go list` がビルドシステムの一部として機能し、正確なパッケージ情報を提供するために重要です。

これらの変更により、`gccgo` 環境下での `unsafe` パッケージの扱いが改善され、不必要な再ビルドが抑制されることで、ビルドプロセスの効率が向上します。

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

### `src/cmd/go/list.go`

```diff
--- a/src/cmd/go/list.go
+++ b/src/cmd/go/list.go
@@ -86,6 +86,7 @@ For more about specifying packages, see 'go help packages'.
 
 func init() {
 	cmdList.Run = runList // break init cycle
+	cmdList.Flag.Var(buildCompiler{}, "compiler", "")
 }
 
 var listE = cmdList.Flag.Bool("e", false, "")

src/cmd/go/pkg.go

--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -372,7 +372,7 @@ func (p *Package) load(stk *importStack, bp *build.Package, err error) *Package
 	}
 
 	// unsafe is a fake package.
-	if p.Standard && p.ImportPath == "unsafe" {
+	if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") {
 		p.target = ""
 	}
 
@@ -417,7 +417,7 @@ func computeStale(pkgs ...*Package) {
 
 // isStale reports whether package p needs to be rebuilt.
 func isStale(p *Package, topRoot map[string]bool) bool {
-	if p.Standard && p.ImportPath == "unsafe" {
+	if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") {
 		// fake, builtin package
 		return false
 	}

コアとなるコードの解説

src/cmd/go/list.go の変更

  • cmdList.Flag.Var(buildCompiler{}, "compiler", "")
    • go list コマンドのフラグセットに、buildCompiler{} 型の変数を -compiler という名前で登録しています。これにより、go list -compiler=gccgo のように、使用するコンパイラを指定できるようになります。
    • buildCompiler{} は、flag.Value インターフェースを実装した型であり、コマンドライン引数から値を受け取り、それを buildContext.Compiler に設定する役割を担います。この変更により、go list がビルドコンテキストをより正確にシミュレートできるようになります。

src/cmd/go/pkg.go の変更

  • func (p *Package) load(...) 内の変更:

    • 変更前: if p.Standard && p.ImportPath == "unsafe" {
      • 標準パッケージであり、かつインポートパスが "unsafe" である場合にのみ、パッケージのターゲットパスを空 ("") に設定していました。これは unsafe が特殊な「フェイク」パッケージであることを示します。
    • 変更後: if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") {
      • 条件に || buildContext.Compiler == "gccgo" が追加されました。これにより、標準パッケージであり、かつインポートパスが "unsafe" であるか、または 現在のビルドコンテキストのコンパイラが "gccgo" である場合に、ターゲットパスを空に設定するようになりました。
      • この修正は、gccgounsafe パッケージを扱う際に、他のコンパイラとは異なる内部的な挙動を持つ可能性があるため、gccgo 環境下でも unsafe パッケージが正しく「ビルドターゲットを持たない」と認識されるようにします。
  • func isStale(p *Package, topRoot map[string]bool) bool 内の変更:

    • 変更前: if p.Standard && p.ImportPath == "unsafe" {
      • 標準パッケージであり、かつインポートパスが "unsafe" である場合にのみ、false (再ビルド不要) を返していました。
    • 変更後: if p.Standard && (p.ImportPath == "unsafe" || buildContext.Compiler == "gccgo") {
      • 条件に || buildContext.Compiler == "gccgo" が追加されました。これにより、標準パッケージであり、かつインポートパスが "unsafe" であるか、または 現在のビルドコンテキストのコンパイラが "gccgo" である場合に、false (再ビルド不要) を返すようになりました。
      • この修正は、gccgo 環境下で unsafe パッケージが不必要に「古くなっている」と判断され、再ビルドが繰り返される問題を直接的に解決します。gccgounsafe パッケージを常に最新であると見なすことで、ビルドの効率が向上します。

関連リンク

参考にした情報源リンク

  • 上記の関連リンクに記載されたGo IssueとGerrit Code Reviewのページ。
  • Go言語の公式ドキュメント(go list コマンド、unsafe パッケージ、ビルドコンテキストに関する情報)。
  • GCCGoに関する一般的な情報源。
  • Go言語のビルドシステムに関する技術記事や解説。