[インデックス 12493] ファイルの概要
このコミットは、Goコマンドラインツール (cmd/go
) における go list
コマンドの機能拡張と、gccgo
コンパイラ使用時のパッケージの「古さ (stale)」判定ロジックのバグ修正を目的としています。具体的には、go list
コマンドに -compiler
フラグのサポートを追加し、pkg.go
内の isStale
関数が unsafe
パッケージと gccgo
コンパイラを正しく扱うように修正しています。これにより、gccgo
を使用している環境で go build
や go 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"
である場合に、ターゲットパスを空に設定するようになりました。 - この修正は、
gccgo
がunsafe
パッケージを扱う際に、他のコンパイラとは異なる内部的な挙動を持つ可能性があるため、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
パッケージが不必要に「古くなっている」と判断され、再ビルドが繰り返される問題を直接的に解決します。gccgo
がunsafe
パッケージを常に最新であると見なすことで、ビルドの効率が向上します。
- 条件に
- 変更前:
関連リンク
- Go Issue #3228: https://github.com/golang/go/issues/3228
- Gerrit Code Review: https://golang.org/cl/5784044
参考にした情報源リンク
- 上記の関連リンクに記載されたGo IssueとGerrit Code Reviewのページ。
- Go言語の公式ドキュメント(
go list
コマンド、unsafe
パッケージ、ビルドコンテキストに関する情報)。 - GCCGoに関する一般的な情報源。
- Go言語のビルドシステムに関する技術記事や解説。