[インデックス 11458] ファイルの概要
このコミットは、Go言語のビルドシステムであるgobuilder
の内部ロジックを更新し、Goのサブリポジトリのビルドとテストに標準のgo
コマンドラインツールを使用するように変更したものです。これにより、ビルドプロセスの標準化と簡素化が図られています。
コミット
commit 43ebc6b5c64ca580b4a10430b2b620f4f5e6a84d
Author: Andrew Gerrand <adg@golang.org>
Date: Mon Jan 30 12:02:14 2012 +1100
gobuilder: use go tool to build and test sub-repositories
R=rsc
CC=golang-dev
https://golang.org/cl/5576047
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/43ebc6b5c64ca580b4a10430b2b620f4f5e6a84d
元コミット内容
このコミットは、misc/dashboard/builder/http.go
とmisc/dashboard/builder/main.go
の2つのファイルを変更しています。主な変更点は、gobuilder
がGoのサブリポジトリを処理する方法を、カスタムのgoinstall
コマンドから、より標準的なgo get
とgo test
コマンドに移行したことです。また、ダッシュボードのパッケージ取得APIにkind
パラメータを導入し、サブリポジトリを明示的にフィルタリングできるようにしています。
変更の背景
2012年当時、Go言語のエコシステムはまだ発展途上にあり、Goの公式ツールセットも進化を続けていました。このコミットが行われた背景には、以下のような理由が考えられます。
- ツールの標準化:
goinstall
のようなカスタムツールに依存するのではなく、Go言語の公式配布物に含まれる標準のgo
コマンド(go get
,go test
など)を使用することで、ビルドシステムの保守性を高め、将来的なGoツールの進化に対応しやすくする狙いがありました。 - サブリポジトリの重要性の増大: Go言語のプロジェクトが成長するにつれて、標準ライブラリとは別に、
code.google.com/p/go.tools
やcode.google.com/p/go.net
のような「サブリポジトリ」として提供されるパッケージが増えていました。これらのサブリポジトリは、Goの標準配布物には含まれないものの、Goチームによって管理され、Goエコシステムにおいて重要な役割を担っていました。これらのサブリポジトリのビルドとテストをgobuilder
で適切に管理する必要がありました。 - ビルドプロセスの簡素化:
goinstall
は特定の目的のために設計されたツールでしたが、go get
やgo test
といった汎用的なgo
コマンドの機能が充実してきたことで、それらを利用する方がよりシンプルで柔軟なビルド・テストプロセスを構築できるようになったと考えられます。
前提知識の解説
- gobuilder: Go言語の公式ビルドダッシュボード(Go Dashboard)の一部として機能するビルドシステムです。Goプロジェクトの様々なコミットやブランチに対して自動的にビルドとテストを実行し、その結果をダッシュボードに表示することで、Go言語の安定性と品質を維持する役割を担っていました。
- Goサブリポジトリ (Go sub-repositories): 2012年頃のGo言語では、標準ライブラリに含まれないものの、Goチームによって公式に管理・提供される追加のパッケージ群を指しました。これらは通常、
code.google.com/p/go.tools
やcode.google.com/p/go.net
のような独立したバージョン管理システム(当時はMercurialが主流)で管理されていました。これらは現在のgolang.org/x/...
リポジトリの前身のような位置づけです。 go
コマンド: Go言語の公式ツールチェーンの中核をなすコマンドラインツールです。ソースコードのコンパイル、パッケージの管理(go get
)、テストの実行(go test
)、ドキュメントの生成など、Go開発における多岐にわたるタスクを処理します。go get
: 指定されたパッケージとその依存関係をダウンロードし、インストールするGoコマンドです。このコミットでは-d
フラグが使われており、これは依存関係をダウンロードするだけでビルドは行わないことを意味します。go test
: 指定されたパッケージのテストを実行するGoコマンドです。- Mercurial (hg): 当時、Go言語プロジェクトの公式バージョン管理システムとしてGitと並行して使用されていた分散型バージョン管理システムです。コミットログに
.hg
ディレクトリの存在チェックがあることから、サブリポジトリの一部がMercurialで管理されていたことが伺えます。
技術的詳細
このコミットの技術的な核心は、gobuilder
がGoパッケージをビルド・テストする際の内部実装を、カスタムスクリプトや特定のバイナリへの依存から、Go言語自体が提供する標準ツールへの依存へと切り替えた点にあります。
具体的には、以下の変更が行われました。
-
dashboardPackages
関数の拡張:misc/dashboard/builder/http.go
内のdashboardPackages
関数が、kind
という新しい引数を受け取るようになりました。- この
kind
引数は、ダッシュボードAPIへのリクエストURLのクエリパラメータとして渡され、取得するパッケージの種類(例: "subrepo")をフィルタリングするために使用されます。これにより、gobuilder
はメインのGoリポジトリのパッケージとサブリポジトリのパッケージを区別して処理できるようになりました。
-
buildPackages
からbuildSubrepos
へのリネームとロジック変更:misc/dashboard/builder/main.go
内のbuildPackages
関数がbuildSubrepos
にリネームされました。これは、この関数がGoのサブリポジトリに特化した処理を行うことを明確にするためです。- この関数は、
dashboardPackages("subrepo")
を呼び出すことで、サブリポジトリとして登録されたパッケージのみを対象とするようになりました。
-
goinstall
からgo tool
への移行:- 最も重要な変更は、
goinstall
関数がbuildSubrepo
関数にリネームされ、その内部ロジックが全面的に書き換えられたことです。 - 以前は、
goRoot/bin/goinstall
というカスタムバイナリを使用してパッケージのフェッチとビルドを行っていました。 - 新しい
buildSubrepo
関数では、goRoot/bin/go
という標準のgo
コマンドバイナリを使用します。 - パッケージのフェッチ:
go get -d <pkg>
コマンドを使用して、パッケージとその依存関係をダウンロードするようになりました。-d
フラグは、ダウンロードのみを行い、ビルドは行わないことを保証します。 - Mercurialリポジトリの検出:
go get -d
がサブリポジトリのトップレベルディレクトリで失敗する可能性があるため、エラーが発生した場合でも、filepath.Join(goRoot, "src/pkg", pkg, ".hg")
の存在を確認することで、それがMercurialで管理されているサブリポジトリであるかどうかを判断し、処理を続行するロジックが追加されました。これは、当時のGoサブリポジトリがMercurialで管理されていたことの名残です。 - テストの実行: パッケージのビルドではなく、
go test <pkg>/...
コマンドを使用してテストを実行するようになりました。これは、gobuilder
がサブリポジトリの健全性を確認する上で、ビルドだけでなくテストの成功も重視していることを示しています。 - 環境変数の設定:
GOROOT
環境変数を設定し、go
コマンドが正しいGoのインストールパスを使用するようにしています。また、goBin
(go
コマンドのパス)をPATH
環境変数に追加することで、go
コマンドが正しく実行されるようにしています。
- 最も重要な変更は、
-
結果記録の改善:
recordResult
関数への呼び出しで、ビルドの成功/失敗を示すブール値の計算がerr == nil
に修正されました。これにより、エラーオブジェクトの有無に基づいてより正確に結果を記録できるようになりました。
これらの変更により、gobuilder
はGoの標準ツールチェーンとの整合性を高め、より堅牢で将来性のあるビルド・テストインフラへと進化しました。
コアとなるコードの変更箇所
misc/dashboard/builder/http.go
--- a/misc/dashboard/builder/http.go
+++ b/misc/dashboard/builder/http.go
@@ -183,11 +183,12 @@ func dashboardCommit(pkg, hash string) bool {
return err == nil
}
-func dashboardPackages() []string {
+func dashboardPackages(kind string) []string {
+ args := url.Values{"kind": []string{kind}}
var resp []struct {
Path string
}
- if err := dash("GET", "packages", nil, nil, &resp); err != nil {
+ if err := dash("GET", "packages", args, nil, &resp); err != nil {
log.Println("dashboardPackages:", err)
return nil
}
misc/dashboard/builder/main.go
--- a/misc/dashboard/builder/main.go
+++ b/misc/dashboard/builder/main.go
@@ -318,8 +318,8 @@ func (b *Builder) buildHash(hash string) (err error) {
return fmt.Errorf("recordResult: %s", err)
}
- // build goinstallable packages
- b.buildPackages(filepath.Join(workpath, "go"), hash)
+ // build Go sub-repositories
+ b.buildSubrepos(filepath.Join(workpath, "go"), hash)
// finish here if codeUsername and codePassword aren't set
if b.codeUsername == "" || b.codePassword == "" || !*buildRelease {
@@ -355,46 +355,67 @@ func (b *Builder) buildHash(hash string) (err error) {
return
}
-func (b *Builder) buildPackages(goRoot, goHash string) {
- for _, pkg := range dashboardPackages() {
+func (b *Builder) buildSubrepos(goRoot, goHash string) {
+ for _, pkg := range dashboardPackages("subrepo") {
// get the latest todo for this package
hash, err := b.todo("build-package", pkg, goHash)
if err != nil {
- log.Printf("buildPackages %s: %v", pkg, err)
+ log.Printf("buildSubrepos %s: %v", pkg, err)
continue
}
if hash == "" {
continue
}
- // goinstall the package
+ // build the package
if *verbose {
- log.Printf("buildPackages %s: installing %q", pkg, hash)
+ log.Printf("buildSubrepos %s: building %q", pkg, hash)
}
- buildLog, err := b.goinstall(goRoot, pkg, hash)
- ok := buildLog == ""
+ buildLog, err := b.buildSubrepo(goRoot, pkg, hash)
if err != nil {
- ok = false
- log.Printf("buildPackages %s: %v", pkg, err)
+ if buildLog == "" {
+ buildLog = err.Error()
+ }
+ log.Printf("buildSubrepos %s: %v", pkg, err)
}
// record the result
- err = b.recordResult(ok, pkg, hash, goHash, buildLog, 0)
+ err = b.recordResult(err == nil, pkg, hash, goHash, buildLog, 0)
if err != nil {
- log.Printf("buildPackages %s: %v", pkg, err)
+ log.Printf("buildSubrepos %s: %v", pkg, err)
}
}
}
-func (b *Builder) goinstall(goRoot, pkg, hash string) (string, error) {
- bin := filepath.Join(goRoot, "bin/goinstall")
+// buildSubrepo fetches the given package, updates it to the specified hash,
+// and runs 'go test pkg/...'. It returns the build log and any error.
+func (b *Builder) buildSubrepo(goRoot, pkg, hash string) (string, error) {
+ goBin := filepath.Join(goRoot, "bin")
+ goTool := filepath.Join(goBin, "go")
env := append(b.envv(), "GOROOT="+goRoot)
+ // add goBin to PATH
+ for i, e := range env {
+ const p = "PATH="
+ if !strings.HasPrefix(e, p) {
+ continue
+ }
+ env[i] = p + goBin + string(os.PathListSeparator) + e[len(p):]
+ }
+
// fetch package and dependencies
- log, status, err := runLog(env, "", goRoot, bin,
- "-dashboard=false", "-install=false", pkg)
- if err != nil || status != 0 {
- return log, err
+ log, status, err := runLog(env, "", goRoot, goTool, "get", "-d", pkg)
+ if err == nil && status != 0 {
+ err = fmt.Errorf("go exited with status %d", status)
+ }
+ if err != nil {
+ // 'go get -d' will fail for a subrepo because its top-level
+ // directory does not contain a go package. No matter, just
+ // check whether an hg directory exists and proceed.
+ hgDir := filepath.Join(goRoot, "src/pkg", pkg, ".hg")
+ if fi, e := os.Stat(hgDir); e != nil || !fi.IsDir() {
+ return log, err
+ }
}
// hg update to the specified hash
@@ -403,8 +447,11 @@ func (b *Builder) goinstall(goRoot, pkg, hash string) (string, error) {
return "", err
}
- // build the package
- log, _, err = runLog(env, "", goRoot, bin, "-dashboard=false", pkg)
+ // test the package
+ log, status, err = runLog(env, "", goRoot, goTool, "test", pkg+"/...")
+ if err == nil && status != 0 {
+ err = fmt.Errorf("go exited with status %d", status)
+ }
return log, err
}
@@ -491,8 +515,10 @@ func commitWatcher() {
if *verbose {
log.Printf("poll...")
}
+ // Main Go repository.
commitPoll(key, "")
- for _, pkg := range dashboardPackages() {
+ // Go sub-repositories.
+ for _, _, pkg := range dashboardPackages("subrepo") {
commitPoll(key, pkg)
}
if *verbose {
コアとなるコードの解説
misc/dashboard/builder/http.go
の変更点
dashboardPackages
関数がkind string
引数を受け取るようになりました。url.Values{"kind": []string{kind}}
を使って、HTTPリクエストのクエリパラメータにkind
を追加しています。これにより、ダッシュボードのバックエンドAPIに対して、取得したいパッケージの種類(例: サブリポジトリ)を明示的に指定できるようになりました。
misc/dashboard/builder/main.go
の変更点
buildHash
関数:b.buildPackages
の呼び出しがb.buildSubrepos
に変更され、コメントも「goinstallable packagesをビルド」から「Goサブリポジトリをビルド」に修正されました。これは、このビルドステップがサブリポジトリに特化していることを明確にしています。
buildPackages
からbuildSubrepos
へのリネームとロジック変更:- 関数名が
buildPackages
からbuildSubrepos
に変更されました。 dashboardPackages()
の呼び出しがdashboardPackages("subrepo")
に変更され、サブリポジトリのみを対象とすることが明示されました。- ログメッセージも
buildPackages
からbuildSubrepos
に更新されています。 b.goinstall
の呼び出しがb.buildSubrepo
に変更されました。recordResult
の第一引数(成功/失敗を示すブール値)がok
変数からerr == nil
に直接変更されました。これは、buildSubrepo
がエラーを返さなければ成功と見なすという、より直接的なロジックです。
- 関数名が
goinstall
からbuildSubrepo
へのリネームと大幅なロジック変更:- 関数名が
goinstall
からbuildSubrepo
に変更され、その役割がコメントで「指定されたパッケージをフェッチし、指定されたハッシュに更新し、'go test pkg/...'を実行する」と明確に記述されました。 bin := filepath.Join(goRoot, "bin/goinstall")
というカスタムバイナリのパス指定が削除され、代わりにgoTool := filepath.Join(goBin, "go")
として標準のgo
コマンドのパスが取得されています。- 環境変数
PATH
にgoBin
(go
コマンドがあるディレクトリ)を追加するロジックが追加されました。これにより、runLog
でgo
コマンドを直接実行できるようになります。 - パッケージのフェッチ: 以前の
goinstall
コマンドによるフェッチロジックが、runLog(env, "", goRoot, goTool, "get", "-d", pkg)
というgo get -d
コマンドの実行に置き換えられました。go get -d
がエラーを返した場合、それがサブリポジトリのトップレベルディレクトリにGoパッケージがないことによるものかを判断するため、.hg
ディレクトリ(Mercurialリポジトリの目印)の存在をチェックするロジックが追加されています。これは、当時のGoサブリポジトリの構造とバージョン管理システムを考慮したものです。
- テストの実行: 以前の
goinstall
コマンドによるビルドロジックが、runLog(env, "", goRoot, goTool, "test", pkg+"/...")
というgo test
コマンドの実行に置き換えられました。これにより、ビルドだけでなくテストも自動的に実行されるようになりました。
- 関数名が
commitWatcher
関数:- サブリポジトリのポーリングループで、
dashboardPackages()
の呼び出しがdashboardPackages("subrepo")
に変更されました。 - メインGoリポジトリとGoサブリポジトリのポーリングについて、より明確なコメントが追加されました。
- サブリポジトリのポーリングループで、
これらの変更は、gobuilder
がGoの標準ツールチェーンとより密接に連携し、サブリポジトリのビルドとテストをより効率的かつ標準的な方法で処理できるようにするための重要なステップでした。
関連リンク
- Go言語の公式ウェブサイト: https://golang.org/
- Go言語のバージョン管理システムに関する議論(当時の状況を理解する一助となる可能性のある情報源)
参考にした情報源リンク
- コミットメッセージ内のGo CLリンク: https://golang.org/cl/5576047 (Goのコードレビューシステム)
- Go言語の歴史に関するドキュメントやブログ記事(当時の
goinstall
やサブリポジトリの役割について言及されている可能性のあるもの) - Go Dashboard (gobuilder) の当時のドキュメントやソースコード(もし公開されていれば)