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

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

このコミットは、Goプロジェクトのダッシュボードビルダ(misc/dashboard/builder/main.go)における変更です。主な目的は、Goのサブリポジトリをビルドする際に、GOPATH環境変数を適切に設定するように修正することです。これにより、将来的に廃止される予定のgo getコマンドがGOROOTに直接ダウンロードする機能に依存しないようにします。

コミット

commit 47e5266a225476961dfc3e0b26a2e3e620d89114
Author: Dave Cheney <dave@cheney.net>
Date:   Mon Jan 7 11:24:01 2013 +1100

    misc/dashboard/builder: set GOPATH before building subrepos
    
    This proposal updates the dashboard builder to avoid relying on the (soon to be removed) support for using go get to download to $GOROOT. The result is
    
    WORKSPACE=$(the value of the -buildRoot flag / $BUILDER_NAME + hg revision)
    GOROOT=$WORKSPACE/go
    GOPATH=$WORKSPACE
    
    Required for CL 6941058.
    
    R=minux.ma, adg
    CC=golang-dev
    https://golang.org/cl/7034049

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

https://github.com/golang/go/commit/47e5266a225476961dfc3e0b26a2e3e620d89114

元コミット内容

misc/dashboard/builder: set GOPATH before building subrepos

この提案は、ダッシュボードビルダを更新し、go get$GOROOTにダウンロードする(間もなく削除される)サポートに依存しないようにするものです。その結果、以下のようになります。

WORKSPACE=$(the value of the -buildRoot flag / $BUILDER_NAME + hg revision) GOROOT=$WORKSPACE/go GOPATH=$WORKSPACE

CL 6941058のために必要です。

変更の背景

このコミットの背景には、Goのビルドシステムと環境変数の管理に関する重要な変更があります。

  1. go getの動作変更: 以前のGoのバージョンでは、go getコマンドはパッケージを$GOROOT内に直接ダウンロードする動作をサポートしていました。しかし、この動作はGoの設計思想と整合性が取れないため、将来的に廃止されることが決定されていました。go getは主に依存関係の管理に使用され、実行可能ファイルのインストールにはgo installが推奨されるようになりました。このコミットは、この廃止予定の動作に依存しないように、ビルドプロセスを適応させるためのものです。

  2. GOPATHの重要性: GOPATHは、Goのソースコード、コンパイル済みパッケージ、実行可能バイナリが配置されるワークスペースの場所を定義する環境変数です。Go Modulesの導入により、プロジェクトのソースコードが必ずしもGOPATH/src内にある必要はなくなりましたが、GOPATHは依然としてモジュールのキャッシュやグローバルにインストールされるバイナリの場所として機能します。このコミットでは、ビルド環境においてGOPATHを明示的に設定することで、go getが正しい場所にパッケージをダウンロードし、ビルドが期待通りに動作するようにしています。

  3. CL 6941058との関連: コミットメッセージに「Required for CL 6941058」と明記されているように、この変更は別の変更リスト(CL 6941058: cmd/go: add -C flag to 'go run')と密接に関連しています。これは、Goのツールチェイン全体で一貫した動作を保証し、将来の変更に備えるための調整の一環です。

Goのダッシュボードビルダは、Goプロジェクト自体の様々なサブリポジトリをビルドし、テストする役割を担っています。そのため、Goのビルドシステムや環境変数の変更に迅速に対応し、ビルドの安定性と正確性を維持することが不可欠です。このコミットは、そのための重要なステップとなります。

前提知識の解説

Goの環境変数: GOROOTGOPATH

Go言語の開発において、GOROOTGOPATHは非常に重要な環境変数でした。Go Modulesの導入によりその役割は変化しましたが、このコミットが作成された時点(2013年)では、これらはGoプロジェクトのビルドと依存関係管理の根幹をなしていました。

  • GOROOT:

    • Go SDK(Software Development Kit)のインストールディレクトリを指します。
    • Goコンパイラ、標準ライブラリ、その他のGoツールがここに配置されます。
    • 通常、Goをインストールした際に自動的に設定されるため、手動で設定する必要はほとんどありません。
    • go env GOROOTコマンドで現在の値を確認できます。
  • GOPATH:

    • Goのワークスペースの場所を定義します。
    • 伝統的に、Goのソースコード、コンパイル済みパッケージ、実行可能バイナリがここに格納されていました。
    • srcpkgbinの3つのサブディレクトリを持ちます。
      • src: Goのソースコード(自身のプロジェクトやgo getで取得した外部パッケージ)が置かれます。
      • pkg: コンパイルされたパッケージオブジェクトが置かれます。
      • bin: go installコマンドでビルドされた実行可能プログラムが置かれます。
    • Go Modules以前は、プロジェクトのコードは必ず$GOPATH/src以下に配置する必要がありました。
    • Go Modules導入後も、GOPATHはモジュールキャッシュ(pkg/mod)やグローバルにインストールされるバイナリ(bin)の場所として利用されます。

go getコマンド

go getは、Goのパッケージや依存関係をダウンロードし、インストールするためのコマンドです。

  • Go Modules以前: go getは指定されたパッケージのソースコードを$GOPATH/srcにダウンロードし、必要に応じて依存関係も解決・ダウンロードしました。また、実行可能パッケージの場合はビルドして$GOPATH/binにインストールする機能も持っていました。このコミットが修正しようとしているのは、この時期に存在した「go get$GOROOTにダウンロードする」という特殊な動作です。
  • Go Modules以後: go getの役割は主にgo.modファイル内の依存関係を調整することに限定されました。パッケージはローカルのモジュールキャッシュにダウンロードされ、実行可能ファイルのインストールにはgo installコマンドが推奨されるようになりました。

Goのサブリポジトリ (Sub-repositories)

Goプロジェクトにおける「サブリポジトリ」という用語は、主に以下の2つの意味で使われます。

  1. 公式Goプロジェクトのサブリポジトリ: golang.org/x/以下に存在する、Goチームによって管理されている独立したGitリポジトリ群を指します(例: golang.org/x/tools, golang.org/x/crypto)。これらはGoのコア言語や標準ライブラリとは別に開発されており、より緩やかな互換性要件を持っています。このコミットで言及されている「サブリポジトリ」は、この文脈でのGoプロジェクトの公式サブリポジトリを指している可能性が高いです。
  2. モノレポ内の複数Goモジュール: ユーザーのプロジェクトにおいて、単一のリポジトリ内に複数の独立したGoモジュールが存在する構成を指すこともあります。

このコミットは、Goの公式サブリポジトリをビルドする際の環境設定に関するものです。

Goダッシュボードビルダ

Goプロジェクトには、継続的インテグレーション(CI)システムの一部として「ダッシュボードビルダ」が存在します。これは、Goの様々なブランチやサブリポジトリのビルド、テスト、ベンチマークを自動的に実行し、その結果をダッシュボードに表示する役割を担っています。このビルダは、Goのソースコードや依存関係を適切に管理し、ビルド環境を正確に設定する必要があります。

技術的詳細

このコミットの技術的な核心は、Goのビルド環境におけるGOPATHGOROOTの管理方法の変更にあります。

変更前は、ダッシュボードビルダがサブリポジトリをビルドする際に、go get$GOROOTに直接ダウンロードする機能に依存していました。これは、go getがパッケージを$GOROOT/src/pkgのようなパスに配置することを期待していたことを示唆しています。しかし、この動作はGoの設計思想から逸脱しており、将来的に廃止されることが決まっていました。

このコミットでは、この依存関係を解消するために、ビルドプロセス中に明示的にGOPATHを設定するアプローチが導入されました。

  1. ワークスペースの定義: コミットメッセージに示されているように、ビルドのルートディレクトリを基点として、以下のように環境変数を設定します。

    • WORKSPACE=$(the value of the -buildRoot flag / $BUILDER_NAME + hg revision): ビルドの作業ディレクトリを定義します。これは、ビルダのルートフラグ、ビルダ名、およびMercurial (hg) のリビジョンハッシュに基づいて一意に決定されます。
    • GOROOT=$WORKSPACE/go: Goのインストールパスを、定義されたWORKSPACE内のgoディレクトリに設定します。これは、ビルド対象のGoのバージョンがこのパスに配置されることを意味します。
    • GOPATH=$WORKSPACE: 最も重要な変更点です。GOPATHWORKSPACE自体に設定します。これにより、go getやその他のGoツールがパッケージをダウンロードしたり、ビルド成果物を配置したりする際に、$WORKSPACE/src$WORKSPACE/binといったパスが使用されるようになります。これは、go get$GOROOTに直接ダウンロードするのではなく、標準的なGOPATHの構造に従うことを強制します。
  2. buildSubrepos関数のシグネチャ変更: buildSubrepos関数は、Goのサブリポジトリをビルドするロジックをカプセル化しています。変更前はgoRootgoHashのみを受け取っていましたが、変更後は新たにgoPath引数が追加されました。これにより、サブリポジトリのビルド時に明示的にGOPATHの情報を渡せるようになります。

  3. buildSubrepo関数のシグネチャ変更と環境変数設定: buildSubrepo関数は個々のサブリポジトリのフェッチ、更新、テストを行います。

    • 変更前はgoRootpkghashを受け取っていました。
    • 変更後はgoRootgoPathpkghashを受け取るようになりました。
    • この関数内で、envスライスに"GOPATH="+goPathが追加され、GOROOTGOPATHの両方が環境変数として設定されるようになりました。
    • PATH環境変数の設定も変更され、$GOROOT/binだけでなく$GOPATH/binも追加されるようになりました。これにより、goコマンドやその他のツールが正しく解決されるようになります。
  4. go getの動作変更への対応:

    • 変更前は、go get -dがサブリポジトリに対して失敗する可能性を考慮し、.hgディレクトリの存在を確認するロジックがありました。これは、go getが期待通りに動作しない場合のフォールバックとして機能していました。
    • 変更後は、go get -d pkg+"/..."の実行パスがgoRootからgoPathに変更されました。これにより、go getGOPATHの構造に従ってパッケージをダウンロードするようになります。また、go get -dが失敗した場合の特殊なハンドリングが削除されました。これは、GOPATHが正しく設定されていればgo getが期待通りに動作するという前提に基づいています。
    • pkgPathの計算もfilepath.Join(goRoot, "src/pkg", pkg)からfilepath.Join(goPath, "src", pkg)に変更され、GOPATHの構造に準拠するようになりました。

この変更により、ダッシュボードビルダはGoの標準的な環境変数設定に従うようになり、将来のgo getの動作変更にも対応できるようになりました。これにより、ビルドの堅牢性と将来性が向上します。

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

--- a/misc/dashboard/builder/main.go
+++ b/misc/dashboard/builder/main.go
@@ -281,7 +281,9 @@ func (b *Builder) buildHash(hash string) error {
 	}
 
 	// build Go sub-repositories
-	b.buildSubrepos(filepath.Join(workpath, "go"), hash)
+	goRoot := filepath.Join(workpath, "go")
+	goPath := workpath
+	b.buildSubrepos(goRoot, goPath, hash)
 
 	return nil
 }
@@ -307,7 +309,7 @@ func (b *Builder) failBuild() bool {
 	return true
 }
 
-func (b *Builder) buildSubrepos(goRoot, goHash string) {
+func (b *Builder) buildSubrepos(goRoot, goPath, goHash string) {
 	for _, pkg := range dashboardPackages("subrepo") {
 		// get the latest todo for this package
 		hash, err := b.todo("build-package", pkg, goHash)
@@ -323,7 +325,7 @@ func (b *Builder) buildSubrepos(goRoot, goHash string) {
 		if *verbose {
 			log.Printf("buildSubrepos %s: building %q", pkg, hash)
 		}
-		buildLog, err := b.buildSubrepo(goRoot, pkg, hash)
+		buildLog, err := b.buildSubrepo(goRoot, goPath, pkg, hash)
 		if err != nil {
 			if buildLog == "" {
 				buildLog = err.Error()
@@ -341,43 +343,37 @@ func (b *Builder) buildSubrepos(goRoot, goHash string) {
 
 // buildSubrepo fetches the given package, updates it to the specified hash,
 // and runs 'go test -short 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)
+func (b *Builder) buildSubrepo(goRoot, goPath, pkg, hash string) (string, error) {
+	goTool := filepath.Join(goRoot, "bin", "go")
+	env := append(b.envv(), "GOROOT="+goRoot, "GOPATH="+goPath)
 
 	// 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):]
+		sep := string(os.PathListSeparator)
+		env[i] = p + filepath.Join(goRoot, "bin") + sep + filepath.Join(goPath, "bin") + sep + e[len(p):]
 	}
 
 	// fetch package and dependencies
-	log, status, err := runLog(*cmdTimeout, env, "", goRoot, goTool, "get", "-d", pkg)
+	log, status, err := runLog(*cmdTimeout, env, "", goPath, 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
-		}
+		return log, err
 	}
 
 	// hg update to the specified hash
-	pkgPath := filepath.Join(goRoot, "src/pkg", pkg)
+	pkgPath := filepath.Join(goPath, "src", pkg)
 	if err := run(*cmdTimeout, nil, pkgPath, hgCmd("update", hash)...); err != nil {
 		return "", err
 	}
 
 	// test the package
-	log, status, err = runLog(*buildTimeout, env, "", goRoot, goTool, "test", "-short", pkg+"/...")
+	log, status, err = runLog(*buildTimeout, env, "", goPath, goTool, "test", "-short", pkg+"/...")
 	if err == nil && status != 0 {
 		err = fmt.Errorf("go exited with status %d", status)
 	}

コアとなるコードの解説

このコミットは、misc/dashboard/builder/main.goファイル内のbuildHashbuildSubreposbuildSubrepoの3つの主要な関数に変更を加えています。

  1. func (b *Builder) buildHash(hash string) error

    • この関数は、特定のハッシュ(リビジョン)に基づいてGoのビルドを実行するエントリポイントです。
    • 変更前は、b.buildSubrepos(filepath.Join(workpath, "go"), hash)のように、GOROOTに相当するパスとハッシュを直接渡していました。
    • 変更後、goRootgoPathという2つの新しい変数が導入されました。
      • goRoot := filepath.Join(workpath, "go"): GOROOTworkpath/goに設定します。
      • goPath := workpath: GOPATHworkpath自体に設定します。
    • そして、b.buildSubrepos(goRoot, goPath, hash)と、goPathを新しい引数としてbuildSubrepos関数に渡すように変更されました。これにより、サブリポジトリのビルド時にGOPATHのコンテキストが明示的に伝達されるようになります。
  2. func (b *Builder) buildSubrepos(goRoot, goHash string) から func (b *Builder) buildSubrepos(goRoot, goPath, goHash string)

    • この関数は、複数のGoサブリポジトリをループ処理してビルドします。
    • 関数のシグネチャが変更され、新たにgoPath引数を受け取るようになりました。
    • 内部のb.buildSubrepoの呼び出しも、b.buildSubrepo(goRoot, goPath, pkg, hash)のように、新しいgoPath引数を渡すように更新されました。
  3. func (b *Builder) buildSubrepo(goRoot, pkg, hash string) (string, error) から func (b *Builder) buildSubrepo(goRoot, goPath, pkg, hash string) (string, error)

    • この関数は、個々のGoサブリポジトリをフェッチ、更新、テストする具体的なロジックを含んでいます。
    • 関数のシグネチャ変更: goPath引数が追加されました。
    • 環境変数の設定:
      • 変更前はenv := append(b.envv(), "GOROOT="+goRoot)のようにGOROOTのみを設定していました。
      • 変更後、env := append(b.envv(), "GOROOT="+goRoot, "GOPATH="+goPath)と変更され、GOPATHも明示的に環境変数に追加されるようになりました。
      • PATH環境変数の設定も更新され、$GOROOT/binだけでなく、$GOPATH/binPATHに追加されるようになりました。これにより、goコマンドやその他のツールがGOPATH内で正しく見つけられるようになります。
    • go getコマンドの実行パスと引数:
      • 変更前はrunLogの第4引数(作業ディレクトリ)がgoRootでした。
      • 変更後、goPathに変更されました。これにより、go getコマンドはGOPATHのルートディレクトリで実行されることになります。
      • go get -d pkgからgo get -d pkg+"/..."に変更されました。これは、パッケージとそのサブパッケージを再帰的にダウンロードすることを意味します。
    • go get失敗時の特殊ハンドリングの削除:
      • 変更前は、go get -dがサブリポジトリに対して失敗する可能性を考慮し、.hgディレクトリの存在を確認してエラーを無視するロジックがありました。これは、go getGOROOTに直接ダウンロードする際の挙動に起因するものでした。
      • 変更後、GOPATHが適切に設定されることでgo getが標準的な動作をするため、この特殊なエラーハンドリングは不要となり削除されました。
    • pkgPathの計算:
      • pkgPath := filepath.Join(goRoot, "src/pkg", pkg)からpkgPath := filepath.Join(goPath, "src", pkg)に変更されました。これは、パッケージのソースコードがGOPATH/src以下に配置されるというGoの標準的な慣習に合わせるための変更です。
    • go testコマンドの実行パス:
      • go getと同様に、go testコマンドの実行パスもgoRootからgoPathに変更されました。

これらの変更により、ダッシュボードビルダはGoの標準的なGOPATHの概念を尊重し、go getGOROOTに直接ダウンロードする非推奨の動作に依存しない、より堅牢で将来性のあるビルドプロセスを実現しています。

関連リンク

  • Go Change-list 7034049: https://golang.org/cl/7034049
  • Go Change-list 6941058: (言及のみ、直接のリンクはコミットメッセージにないため、検索結果から推測される情報) cmd/go: add -C flag to 'go run'

参考にした情報源リンク