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

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

このコミットは、Go言語のビルドダッシュボードの一部であるmisc/dashboard/builder/main.goファイルに対する変更です。具体的には、go get -dコマンドのエラーハンドリングに関する以前の「ハック」を再導入するものです。

コミット

commit af832ac0b9609922d486f511dad04fba7a24dc44
Author: Andrew Gerrand <adg@golang.org>
Date:   Mon Sep 17 11:21:02 2012 -0700

    misc/dashboard/builder: reinstate 'go get -d' error handling hack
    
    I thought this was redundant since the behavior of 'go get -d' had
    changed. I was wrong. Should have tested more thoroughly.
    
    R=golang-dev, minux.ma
    CC=golang-dev
    https://golang.org/cl/6500136

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

https://github.com/golang/go/commit/af832ac0b9609922d486f511dad04fba7a24dc44

元コミット内容

このコミットは、misc/dashboard/builder/main.goファイルにおいて、go get -dコマンドがサブモジュール(subrepo)に対して失敗した場合の特定のエラーを無視するロジックを再導入しています。以前、この「ハック」はgo get -dの挙動変更により不要になったと考えられ削除されましたが、実際にはまだ必要であったため、元に戻されました。

変更の背景

このコミットが行われた2012年9月時点では、Go言語のビルドシステムやパッケージ管理ツールであるgo getはまだ発展途上にありました。特に、Goプロジェクト自体が複数のリポジトリ(当時はMercurialが主流)に分割されており、それらを「サブリポジトリ(subrepo)」として扱う必要がありました。

当時のgo get -dコマンドは、パッケージのソースコードをダウンロードするものの、ビルドやインストールは行わないという挙動でした。しかし、Goのサブリポジトリの中には、そのリポジトリのルートディレクトリ自体にはGoパッケージが含まれていないものがありました。例えば、golang.org/x/toolsのようなリポジトリは、そのルートにはGoパッケージがなく、そのサブディレクトリ(例: golang.org/x/tools/cmd/goimports)に実際のGoパッケージが存在する、といった構造です。

このようなサブリポジトリに対してgo get -dを実行すると、ルートディレクトリにGoパッケージがないためにコマンドがエラーを返してしまうという問題がありました。しかし、ビルドダッシュボードの文脈では、ソースコードがダウンロードされ、Mercurialリポジトリ(.hgディレクトリ)が存在していれば、その後のビルドプロセスには問題がないと判断できました。

以前のコミットで、go get -dの挙動が変更されたため、この特定のエラーを無視する「ハック」が不要になったと判断され、削除されました。しかし、実際にはその挙動変更が期待通りではなかったか、あるいは特定のサブリポジトリのケースで依然としてエラーが発生したため、この「ハック」を再導入する必要が生じました。コミットメッセージにある「I was wrong. Should have tested more thoroughly.」という記述は、この判断ミスを認めるものです。

前提知識の解説

  • go get -d: Go言語のパッケージ管理コマンドgo getのオプションの一つです。-dフラグは「ダウンロードのみ」を意味し、指定されたパッケージのソースコードをGOPATH内の適切なディレクトリにダウンロードしますが、ビルドやインストールは行いません。これは、ソースコードを取得した後、手動でビルドしたり、特定のバージョンに切り替えたりする場合に便利です。
  • Goのビルドダッシュボード (misc/dashboard/builder): Go言語プロジェクトは、継続的なインテグレーション(CI)とテストのために、独自のビルドダッシュボードを運用していました。これは、様々なプラットフォームや構成でGoのコードベースをビルド・テストし、その結果を報告するシステムです。misc/dashboard/builder/main.goはそのビルドプロセスを管理するコンポーネントの一部でした。
  • サブリポジトリ (Subrepo): Go言語の初期のモジュールシステムが導入される前は、Goプロジェクトの様々なコンポーネント(例: 標準ライブラリの拡張、ツールなど)は、独立したMercurialリポジトリとして管理され、Goのメインリポジトリから参照されることがありました。これらは「サブリポジトリ」と呼ばれ、golang.org/x/...のようなパスで参照されることが多かったです。
  • Mercurial (.hgディレクトリ): Gitが普及する以前、Go言語の公式リポジトリはMercurialという分散型バージョン管理システムで管理されていました。Mercurialリポジトリのルートには、.hgという隠しディレクトリが存在し、これがリポジトリのメタデータ(コミット履歴など)を格納しています。このコミットでは、go get -dがエラーを返しても、.hgディレクトリが存在すれば、ソースコードのダウンロード自体は成功していると判断するロジックが用いられています。

技術的詳細

このコミットの技術的な核心は、go get -dコマンドの実行結果に対するエラーハンドリングの変更です。

buildSubrepo関数は、特定のサブリポジトリをビルド環境に取得する役割を担っています。このプロセスの一部として、go get -dコマンドが実行されます。

通常、コマンドがエラーを返した場合(err != nil)、そのエラーは即座に呼び出し元に返され、処理は中断されます。しかし、このコミットでは、そのエラーが特定の条件を満たす場合に限り、エラーを無視して処理を続行するロジックが追加されています。

その条件とは、以下の通りです。

  1. go get -dがエラーを返した。
  2. エラーが発生したにもかかわらず、対象のサブリポジトリのパスにMercurialリポジトリを示す.hgディレクトリが存在し、それが実際にディレクトリである。

このロジックは、go get -dが「トップレベルディレクトリにGoパッケージがない」という理由でエラーを返すものの、実際にはソースコードのダウンロード自体は成功しており、Mercurialリポジトリが正しくクローンされている場合に適用されます。このような状況では、go get -dのエラーはビルドプロセスにとって致命的ではないため、無視して次のステップ(指定されたハッシュへのhg updateなど)に進むことが許容されます。

このアプローチは、特定の既知の、かつ無害なエラーパターンを識別し、それらをフィルタリングすることで、ビルドプロセスの堅牢性を高めることを目的としています。しかし、これはあくまで特定の状況下での「ハック」であり、go get -dの本来の意図とは異なる挙動を許容するものです。

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

変更はmisc/dashboard/builder/main.goファイルのbuildSubrepo関数内で行われています。

--- a/misc/dashboard/builder/main.go
+++ b/misc/dashboard/builder/main.go
@@ -360,7 +360,13 @@ func (b *Builder) buildSubrepo(goRoot, pkg, hash string) (string, error) {
 	// go get -d the subrepo
 	log, err := b.run(goRoot, "", "go", "get", "-d", pkg)
 	if err != nil {
-		return log, err
+		// '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

コアとなるコードの解説

変更されたコードブロックは、go get -dコマンドの実行後にエラーが発生した場合の処理を定義しています。

 	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
 		}
 	}
  1. if err != nil: go get -dコマンドの実行結果としてエラーが発生したかどうかをチェックします。エラーが発生した場合のみ、このブロック内の処理が実行されます。
  2. コメント行: 「go get -dは、そのトップレベルディレクトリにGoパッケージが含まれていないサブリポジトリに対しては失敗するだろう。問題ない、単にhgディレクトリが存在するかどうかを確認して続行する。」という説明が書かれています。これがこのハックの意図を明確に示しています。
  3. hgDir := filepath.Join(goRoot, "src/pkg", pkg, ".hg"): 対象のサブリポジトリのパス(goRoot/src/pkg/pkg)の下に、Mercurialリポジトリを示す.hgディレクトリが存在するかどうかを確認するための絶対パスを構築しています。
  4. if fi, e := os.Stat(hgDir); e != nil || !fi.IsDir():
    • os.Stat(hgDir): 指定されたパスhgDirのファイル情報を取得しようとします。
    • e != nil: os.Statがエラーを返した場合(例: ファイルやディレクトリが存在しない、アクセス権がないなど)。
    • !fi.IsDir(): os.Statが成功しても、取得したファイル情報fiがディレクトリではない場合。
    • この条件式全体は、「.hgディレクトリが存在しない、または存在してもそれがディレクトリではない」場合に真となります。
  5. return log, err: 上記の条件が真、つまり.hgディレクトリが期待通りに存在しない場合は、元のgo get -dのエラーをそのまま返して処理を中断します。これは、本当にソースコードの取得に失敗したと判断されるケースです。

このロジックにより、go get -dがエラーを返しても、そのエラーが「サブリポジトリのルートにGoパッケージがない」という無害な理由によるものであり、かつMercurialリポジトリが正しくダウンロードされている(.hgディレクトリが存在する)場合は、エラーを無視して処理を続行できるようになります。

関連リンク

参考にした情報源リンク

  • Go言語のコミット履歴と関連するコードレビュー(CL): https://golang.org/cl/6500136 (このコミットのCode Reviewリンク)
  • 当時のGo言語のビルドシステムやリポジトリ構造に関する情報(アーカイブされたメーリングリストや古いドキュメントなど)
    • Go言語の初期のバージョン管理システムに関する議論(MercurialからGitへの移行など)
    • go getコマンドの歴史的な挙動変更に関する情報
  • os.Stat関数のGo言語公式ドキュメント: https://pkg.go.dev/os#Stat
  • filepath.Join関数のGo言語公式ドキュメント: https://pkg.go.dev/path/filepath#Join# [インデックス 13839] ファイルの概要

このコミットは、Go言語のビルドダッシュボードの一部であるmisc/dashboard/builder/main.goファイルに対する変更です。具体的には、go get -dコマンドのエラーハンドリングに関する以前の「ハック」を再導入するものです。

コミット

commit af832ac0b9609922d486f511dad04fba7a24dc44
Author: Andrew Gerrand <adg@golang.org>
Date:   Mon Sep 17 11:21:02 2012 -0700

    misc/dashboard/builder: reinstate 'go get -d' error handling hack
    
    I thought this was redundant since the behavior of 'go get -d' had
    changed. I was wrong. Should have tested more thoroughly.
    
    R=golang-dev, minux.ma
    CC=golang-dev
    https://golang.org/cl/6500136

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

https://github.com/golang/go/commit/af832ac0b9609922d486f511dad04fba7a24dc44

元コミット内容

このコミットは、misc/dashboard/builder/main.goファイルにおいて、go get -dコマンドがサブモジュール(subrepo)に対して失敗した場合の特定のエラーを無視するロジックを再導入しています。以前、この「ハック」はgo get -dの挙動変更により不要になったと考えられ削除されましたが、実際にはまだ必要であったため、元に戻されました。

変更の背景

このコミットが行われた2012年9月時点では、Go言語のビルドシステムやパッケージ管理ツールであるgo getはまだ発展途上にありました。特に、Goプロジェクト自体が複数のリポジトリ(当時はMercurialが主流)に分割されており、それらを「サブリポジトリ(subrepo)」として扱う必要がありました。

当時のgo get -dコマンドは、パッケージのソースコードをダウンロードするものの、ビルドやインストールは行わないという挙動でした。しかし、Goのサブリポジトリの中には、そのリポジトリのルートディレクトリ自体にはGoパッケージが含まれていないものがありました。例えば、golang.org/x/toolsのようなリポジトリは、そのルートにはGoパッケージがなく、そのサブディレクトリ(例: golang.org/x/tools/cmd/goimports)に実際のGoパッケージが存在する、といった構造です。

このようなサブリポジトリに対してgo get -dを実行すると、ルートディレクトリにGoパッケージがないためにコマンドがエラーを返してしまうという問題がありました。しかし、ビルドダッシュボードの文脈では、ソースコードがダウンロードされ、Mercurialリポジトリ(.hgディレクトリ)が存在していれば、その後のビルドプロセスには問題がないと判断できました。

以前のコミットで、go get -dの挙動が変更されたため、この特定のエラーを無視する「ハック」が不要になったと判断され、削除されました。しかし、実際にはその挙動変更が期待通りではなかったか、あるいは特定のサブリポジトリのケースで依然としてエラーが発生したため、この「ハック」を再導入する必要が生じました。コミットメッセージにある「I was wrong. Should have tested more thoroughly.」という記述は、この判断ミスを認めるものです。

前提知識の解説

  • go get -d: Go言語のパッケージ管理コマンドgo getのオプションの一つです。-dフラグは「ダウンロードのみ」を意味し、指定されたパッケージのソースコードをGOPATH内の適切なディレクトリにダウンロードしますが、ビルドやインストールは行いません。これは、ソースコードを取得した後、手動でビルドしたり、特定のバージョンに切り替えたりする場合に便利です。
  • Goのビルドダッシュボード (misc/dashboard/builder): Go言語プロジェクトは、継続的なインテグレーション(CI)とテストのために、独自のビルドダッシュボードを運用していました。これは、様々なプラットフォームや構成でGoのコードベースをビルド・テストし、その結果を報告するシステムです。misc/dashboard/builder/main.goはそのビルドプロセスを管理するコンポーネントの一部でした。
  • サブリポジトリ (Subrepo): Go言語の初期のモジュールシステムが導入される前は、Goプロジェクトの様々なコンポーネント(例: 標準ライブラリの拡張、ツールなど)は、独立したMercurialリポジトリとして管理され、Goのメインリポジトリから参照されることがありました。これらは「サブリポジトリ」と呼ばれ、golang.org/x/...のようなパスで参照されることが多かったです。
  • Mercurial (.hgディレクトリ): Gitが普及する以前、Go言語の公式リポジトリはMercurialという分散型バージョン管理システムで管理されていました。Mercurialリポジトリのルートには、.hgという隠しディレクトリが存在し、これがリポジトリのメタデータ(コミット履歴など)を格納しています。このコミットでは、go get -dがエラーを返しても、.hgディレクトリが存在すれば、ソースコードのダウンロード自体は成功していると判断するロジックが用いられています。

技術的詳細

このコミットの技術的な核心は、go get -dコマンドの実行結果に対するエラーハンドリングの変更です。

buildSubrepo関数は、特定のサブリポジトリをビルド環境に取得する役割を担っています。このプロセスの一部として、go get -dコマンドが実行されます。

通常、コマンドがエラーを返した場合(err != nil)、そのエラーは即座に呼び出し元に返され、処理は中断されます。しかし、このコミットでは、そのエラーが特定の条件を満たす場合に限り、エラーを無視して処理を続行するロジックが追加されています。

その条件とは、以下の通りです。

  1. go get -dがエラーを返した。
  2. エラーが発生したにもかかわらず、対象のサブリポジトリのパスにMercurialリポジトリを示す.hgディレクトリが存在し、それが実際にディレクトリである。

このロジックは、go get -dが「トップレベルディレクトリにGoパッケージがない」という理由でエラーを返すものの、実際にはソースコードのダウンロード自体は成功しており、Mercurialリポジトリが正しくクローンされている場合に適用されます。このような状況では、go get -dのエラーはビルドプロセスにとって致命的ではないため、無視して次のステップ(指定されたハッシュへのhg updateなど)に進むことが許容されます。

このアプローチは、特定の既知の、かつ無害なエラーパターンを識別し、それらをフィルタリングすることで、ビルドプロセスの堅牢性を高めることを目的としています。しかし、これはあくまで特定の状況下での「ハック」であり、go get -dの本来の意図とは異なる挙動を許容するものです。

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

変更はmisc/dashboard/builder/main.goファイルのbuildSubrepo関数内で行われています。

--- a/misc/dashboard/builder/main.go
+++ b/misc/dashboard/builder/main.go
@@ -360,7 +360,13 @@ func (b *Builder) buildSubrepo(goRoot, pkg, hash string) (string, error) {
 	// go get -d the subrepo
 	log, err := b.run(goRoot, "", "go", "get", "-d", pkg)
 	if err != nil {
-		return log, err
+		// '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

コアとなるコードの解説

変更されたコードブロックは、go get -dコマンドの実行後にエラーが発生した場合の処理を定義しています。

 	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
 		}
 	}
  1. if err != nil: go get -dコマンドの実行結果としてエラーが発生したかどうかをチェックします。エラーが発生した場合のみ、このブロック内の処理が実行されます。
  2. コメント行: 「go get -dは、そのトップレベルディレクトリにGoパッケージが含まれていないサブリポジトリに対しては失敗するだろう。問題ない、単にhgディレクトリが存在するかどうかを確認して続行する。」という説明が書かれています。これがこのハックの意図を明確に示しています。
  3. hgDir := filepath.Join(goRoot, "src/pkg", pkg, ".hg"): 対象のサブリポジトリのパス(goRoot/src/pkg/pkg)の下に、Mercurialリポジトリを示す.hgディレクトリが存在するかどうかを確認するための絶対パスを構築しています。
  4. if fi, e := os.Stat(hgDir); e != nil || !fi.IsDir():
    • os.Stat(hgDir): 指定されたパスhgDirのファイル情報を取得しようとします。
    • e != nil: os.Statがエラーを返した場合(例: ファイルやディレクトリが存在しない、アクセス権がないなど)。
    • !fi.IsDir(): os.Statが成功しても、取得したファイル情報fiがディレクトリではない場合。
    • この条件式全体は、「.hgディレクトリが存在しない、または存在してもそれがディレクトリではない」場合に真となります。
  5. return log, err: 上記の条件が真、つまり.hgディレクトリが期待通りに存在しない場合は、元のgo get -dのエラーをそのまま返して処理を中断します。これは、本当にソースコードの取得に失敗したと判断されるケースです。

このロジックにより、go get -dがエラーを返しても、そのエラーが「サブリポジトリのルートにGoパッケージがない」という無害な理由によるものであり、かつMercurialリポジトリが正しくダウンロードされている(.hgディレクトリが存在する)場合は、エラーを無視して処理を続行できるようになります。

関連リンク

参考にした情報源リンク

  • Go言語のコミット履歴と関連するコードレビュー(CL): https://golang.org/cl/6500136 (このコミットのCode Reviewリンク)
  • 当時のGo言語のビルドシステムやリポジトリ構造に関する情報(アーカイブされたメーリングリストや古いドキュメントなど)
    • Go言語の初期のバージョン管理システムに関する議論(MercurialからGitへの移行など)
    • go getコマンドの歴史的な挙動変更に関する情報
  • os.Stat関数のGo言語公式ドキュメント: https://pkg.go.dev/os#Stat
  • filepath.Join関数のGo言語公式ドキュメント: https://pkg.go.dev/path/filepath#Join