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

[インデックス 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.gomisc/dashboard/builder/main.goの2つのファイルを変更しています。主な変更点は、gobuilderがGoのサブリポジトリを処理する方法を、カスタムのgoinstallコマンドから、より標準的なgo getgo testコマンドに移行したことです。また、ダッシュボードのパッケージ取得APIにkindパラメータを導入し、サブリポジトリを明示的にフィルタリングできるようにしています。

変更の背景

2012年当時、Go言語のエコシステムはまだ発展途上にあり、Goの公式ツールセットも進化を続けていました。このコミットが行われた背景には、以下のような理由が考えられます。

  1. ツールの標準化: goinstallのようなカスタムツールに依存するのではなく、Go言語の公式配布物に含まれる標準のgoコマンド(go get, go testなど)を使用することで、ビルドシステムの保守性を高め、将来的なGoツールの進化に対応しやすくする狙いがありました。
  2. サブリポジトリの重要性の増大: Go言語のプロジェクトが成長するにつれて、標準ライブラリとは別に、code.google.com/p/go.toolscode.google.com/p/go.netのような「サブリポジトリ」として提供されるパッケージが増えていました。これらのサブリポジトリは、Goの標準配布物には含まれないものの、Goチームによって管理され、Goエコシステムにおいて重要な役割を担っていました。これらのサブリポジトリのビルドとテストをgobuilderで適切に管理する必要がありました。
  3. ビルドプロセスの簡素化: goinstallは特定の目的のために設計されたツールでしたが、go getgo testといった汎用的なgoコマンドの機能が充実してきたことで、それらを利用する方がよりシンプルで柔軟なビルド・テストプロセスを構築できるようになったと考えられます。

前提知識の解説

  • gobuilder: Go言語の公式ビルドダッシュボード(Go Dashboard)の一部として機能するビルドシステムです。Goプロジェクトの様々なコミットやブランチに対して自動的にビルドとテストを実行し、その結果をダッシュボードに表示することで、Go言語の安定性と品質を維持する役割を担っていました。
  • Goサブリポジトリ (Go sub-repositories): 2012年頃のGo言語では、標準ライブラリに含まれないものの、Goチームによって公式に管理・提供される追加のパッケージ群を指しました。これらは通常、code.google.com/p/go.toolscode.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言語自体が提供する標準ツールへの依存へと切り替えた点にあります。

具体的には、以下の変更が行われました。

  1. dashboardPackages関数の拡張:

    • misc/dashboard/builder/http.go内のdashboardPackages関数が、kindという新しい引数を受け取るようになりました。
    • このkind引数は、ダッシュボードAPIへのリクエストURLのクエリパラメータとして渡され、取得するパッケージの種類(例: "subrepo")をフィルタリングするために使用されます。これにより、gobuilderはメインのGoリポジトリのパッケージとサブリポジトリのパッケージを区別して処理できるようになりました。
  2. buildPackagesからbuildSubreposへのリネームとロジック変更:

    • misc/dashboard/builder/main.go内のbuildPackages関数がbuildSubreposにリネームされました。これは、この関数がGoのサブリポジトリに特化した処理を行うことを明確にするためです。
    • この関数は、dashboardPackages("subrepo")を呼び出すことで、サブリポジトリとして登録されたパッケージのみを対象とするようになりました。
  3. 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のインストールパスを使用するようにしています。また、goBingoコマンドのパス)をPATH環境変数に追加することで、goコマンドが正しく実行されるようにしています。
  4. 結果記録の改善:

    • 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コマンドのパスが取得されています。
    • 環境変数PATHgoBingoコマンドがあるディレクトリ)を追加するロジックが追加されました。これにより、runLoggoコマンドを直接実行できるようになります。
    • パッケージのフェッチ: 以前の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) の当時のドキュメントやソースコード(もし公開されていれば)