[インデックス 13958] ファイルの概要
このコミットは、Go言語のビルドツール (cmd/go
) における、$GOROOT
内の標準パッケージの扱いに関する挙動を修正するものです。具体的には、$GOROOT
に存在するコードは常に最新であると仮定し、コンパイラやリンカのタイムスタンプに基づく陳腐化チェックを行わないように変更します。これにより、Goバイナリのタイムスタンプが保持されていない環境(例:一部のバイナリ配布)で、書き込み不可能な標準パッケージが不必要に再ビルドされる問題を回避します。
コミット
commit e80fccb441ac73a206bd99fb7f0dbea3eb9cc149
Author: Joel Sing <jsing@google.com>\nDate: Thu Sep 27 00:00:50 2012 +1000
cmd/go: assume that code in $GOROOT is up to date
Do not check compiler/linker timestamps for packages that are in the
$GOROOT. Avoids trying to rebuild non-writable standard packages when
timestamps have not been retained on the Go binaries.
Fixes #4106.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/6533053
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/e80fccb441ac73a206bd99fb7f0dbea3eb9cc149
元コミット内容
cmd/go
: $GOROOT
内のコードは最新であると仮定する。
$GOROOT
内のパッケージに対して、コンパイラ/リンカのタイムスタンプチェックを行わない。これにより、Goバイナリのタイムスタンプが保持されていない場合に、書き込み不可能な標準パッケージが再ビルドされようとするのを回避する。
Fixes #4106.
変更の背景
この変更は、主にGoのビルドシステムがパッケージの「陳腐化 (stale)」を判断する際の挙動に関連しています。Goのビルドツールは、ソースファイルやコンパイル済みパッケージが、それらを生成したコンパイラやリンカよりも古い場合に「陳腐化している」と判断し、再ビルドを試みます。
しかし、このヒューリスティックにはいくつかの問題がありました。
- Issue 3036: コンパイラやリンカのバイナリが「バックデート」されている(つまり、ファイルシステム上のタイムスタンプが実際のビルド日時よりも古い)場合、このヒューリスティックが正しく機能しないことが指摘されていました。これは、特にバイナリ配布版のGoを使用している場合に発生しうる問題です。
- Issue 4106:
$GOROOT
にインストールされている標準ライブラリのパッケージは、通常、ユーザーが直接変更するものではなく、Goのインストール時に提供されるものです。これらのパッケージはしばしば書き込み保護されており、ユーザーが再ビルドすることは意図されていません。しかし、コンパイラやリンカのタイムスタンプが何らかの理由で標準パッケージのタイムスタンプよりも新しくなってしまうと、go build
コマンドがこれらの書き込み不可能なパッケージを再ビルドしようとしてエラーになる、という問題が発生していました。これは、特にGoのバージョンアップグレード後や、異なる環境でビルドされたGoバイナリを使用した場合に顕著でした。
このコミットは、Issue 4106に直接対処し、$GOROOT
内のパッケージに対する不必要な再ビルド試行を防ぐことを目的としています。
前提知識の解説
$GOROOT
: Goのインストールディレクトリを指す環境変数です。Goの標準ライブラリのソースコードや、Goツールチェイン(コンパイラ、リンカなど)のバイナリが格納されています。- Goのビルドプロセス: Goのビルドツール (
go build
,go install
など) は、依存関係を解決し、ソースコードをコンパイルして実行可能なバイナリやパッケージアーカイブを生成します。この際、効率化のために、既にコンパイル済みのパッケージが最新であるかどうかを判断するメカニズムを持っています。 - 陳腐化 (Staleness) チェック: Goツールは、パッケージが再ビルドを必要とするかどうかを判断するために、いくつかのヒューリスティックを使用します。その一つが、コンパイル済みパッケージのタイムスタンプと、それらを生成したコンパイラやリンカのタイムスタンプを比較する方法です。
- コンパイラより古い場合: 通常のパッケージ(ライブラリ)は、それらをコンパイルしたコンパイラバイナリよりも古い場合、陳腐化していると見なされます。
- リンカより古い場合: コマンド(実行可能ファイル)は、それらをリンクしたリンカバイナリよりも古い場合、陳腐化していると見なされます。
- タイムスタンプの重要性: ファイルの最終更新タイムスタンプは、ビルドシステムが依存関係の変更を検出し、必要な部分だけを再ビルドするための重要な情報源です。しかし、ファイルコピーやアーカイブ展開の際にタイムスタンプが保持されない場合や、システム時刻のずれなどによって、この情報が信頼できなくなることがあります。
技術的詳細
Goのビルドツール (cmd/go
) の内部では、isStale
という関数がパッケージが陳腐化しているかどうかを判断しています。この関数は、パッケージのビルド情報と、現在のツールチェイン(コンパイラ、リンカ)のタイムスタンプを比較します。
変更前のコードでは、isStale
関数内で、パッケージがコンパイラやリンカよりも古いかどうかを無条件にチェックしていました。
if olderThan(buildToolchain.compiler()) {
return true
}
if p.build.IsCommand() && olderThan(buildToolchain.linker()) {
return true
}
ここで olderThan
は、対象のファイル(この場合はコンパイル済みパッケージ)が引数で指定されたファイル(コンパイラやリンカ)よりも古いタイムスタンプを持つ場合に true
を返します。
このコミットでは、この陳腐化チェックのロジックに条件を追加しました。具体的には、パッケージのルートディレクトリが $GOROOT
と異なる場合のみ、このタイムスタンプに基づく陳腐化チェックを行うように変更しました。
if p.Root != goroot { // goroot は $GOROOT のパス
if olderThan(buildToolchain.compiler()) {
return true
}
if p.build.IsCommand() && olderThan(buildToolchain.linker()) {
return true
}
}
この変更により、$GOROOT
内のパッケージ(p.Root == goroot
の場合)は、コンパイラやリンカのタイムスタンプに関わらず、常に最新であると見なされるようになります。これにより、書き込み不可能な標準パッケージが不必要に再ビルドされようとする問題が解決されます。
このアプローチは、$GOROOT
内のコードがGoの配布物の一部であり、ユーザーが変更することを意図していないという前提に基づいています。したがって、これらのパッケージが「陳腐化」していると判断されることは、通常、ビルド環境の問題(タイムスタンプの不整合など)に起因すると考えられます。
コアとなるコードの変更箇所
src/cmd/go/pkg.go
ファイルの isStale
関数が変更されています。
--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -521,14 +521,19 @@ func isStale(p *Package, topRoot map[string]bool) bool {
// As a courtesy to developers installing new versions of the compiler
// frequently, define that packages are stale if they are
// older than the compiler, and commands if they are older than
- // the linker. This heuristic will not work if the binaries are back-dated,\n- // as some binary distributions may do, but it does handle a very\n- // common case. See issue 3036.\n- if olderThan(buildToolchain.compiler()) {\n- \treturn true\n- }\n- if p.build.IsCommand() && olderThan(buildToolchain.linker()) {\n- \treturn true\n+ // the linker. This heuristic will not work if the binaries are
+ // back-dated, as some binary distributions may do, but it does handle
+ // a very common case.
+ // See issue 3036.
+ // Assume code in $GOROOT is up to date, since it may not be writeable.
+ // See issue 4106.
+ if p.Root != goroot {
+ if olderThan(buildToolchain.compiler()) {
+ return true
+ }
+ if p.build.IsCommand() && olderThan(buildToolchain.linker()) {
+ return true
+ }
}
// Have installed copy, probably built using current compilers,\n
コアとなるコードの解説
変更は src/cmd/go/pkg.go
の isStale
関数内で行われています。
-
変更前:
if olderThan(buildToolchain.compiler()) { return true } if p.build.IsCommand() && olderThan(buildToolchain.linker()) { return true }
この部分では、パッケージがコンパイラやリンカよりも古い場合に、無条件に
true
(陳腐化している)を返していました。これは、開発者が頻繁に新しいバージョンのコンパイラをインストールする際に、パッケージを最新の状態に保つための「おもてなし」的なヒューリスティックでした。しかし、バイナリがバックデートされている場合(Issue 3036)や、$GOROOT
内の書き込み不可能なパッケージ(Issue 4106)で問題を引き起こしていました。 -
変更後:
// Assume code in $GOROOT is up to date, since it may not be writeable. // See issue 4106. if p.Root != goroot { if olderThan(buildToolchain.compiler()) { return true } if p.build.IsCommand() && olderThan(buildToolchain.linker()) { return true } }
追加された
if p.Root != goroot { ... }
という条件ブロックが最も重要な変更点です。p.Root
は現在処理しているパッケージのルートディレクトリを示します。goroot
は$GOROOT
環境変数の値、つまりGoのインストールディレクトリのパスです。- この条件
p.Root != goroot
は、「現在のパッケージが$GOROOT
の外にある場合」を意味します。 - この条件が
true
の場合、つまりパッケージが$GOROOT
の外にあるユーザーのコードである場合にのみ、以前のコンパイラ/リンカのタイムスタンプに基づく陳腐化チェックが実行されます。 - 逆に、
p.Root == goroot
の場合、つまりパッケージが$GOROOT
内の標準ライブラリである場合は、この条件ブロック全体がスキップされ、タイムスタンプに基づく陳腐化チェックは行われません。これにより、$GOROOT
内のパッケージは常に最新であると見なされ、不必要な再ビルドが回避されます。
この変更は、Goのビルドシステムが、ユーザーが開発しているコードと、Goの配布物として提供される標準ライブラリとを区別し、それぞれに適切な陳腐化判断ロジックを適用するように改善されたことを示しています。
関連リンク
- Go CL (Change List): https://golang.org/cl/6533053
- Issue 4106: https://golang.org/issue/4106
- Issue 3036: https://golang.org/issue/3036
参考にした情報源リンク
- Go issue tracker (上記Issueへのリンク)
- Goのソースコード (特に
src/cmd/go/pkg.go
の関連部分) - Goのビルドプロセスに関する一般的なドキュメントや解説記事 (具体的なURLは検索結果によるため省略)
$GOROOT
およびGoの環境変数に関するドキュメント (具体的なURLは検索結果によるため省略)