[インデックス 11524] ファイルの概要
このコミットは、Go言語のコマンドラインツール cmd/go に対する複数の改善を含んでいます。主な変更点は以下の通りです。
$GOROOT/src/pkg、$GOROOT/src/cmd、$GOPATH/srcといったトップレベルのディレクトリをパッケージディレクトリとして扱わないように修正し、これらのサブディレクトリのみをパッケージとして認識するようにしました。これにより、Goのパッケージ解決のセマンティクスがより正確になります。cgoのビルドプロセスにおいて、環境変数$CGO_CFLAGSおよび$CGO_LDFLAGSから追加のコンパイラおよびリンカ引数を受け入れるようにしました。これにより、Cgoを使用する際のビルドの柔軟性が向上します。pkg-configコマンドの実行が失敗した場合に、その出力を表示するように改善しました。これにより、pkg-config関連のエラーのデバッグが容易になります。- バージョン管理システム (VCS) の操作、特に
gitコマンドの内部的な使用方法を改善しました。
コミット
commit 0f1056667fb8952046e5360ebf9f3285b6f7de33
Author: Russ Cox <rsc@golang.org>
Date: Tue Jan 31 17:40:36 2012 -0500
cmd/go: improvements
Do not treat $GOROOT/src/pkg, $GOROOT/src/cmd,
$GOPATH/src as package directories (only subdirectories
of those can be package directories). Fixes issue 2602.
Accept additional compiler and linker arguments during
cgo from $CGO_CFLAGS and $CGO_LDFLAGS, as the
Makefiles used to do.
Show failed pkg-config output. Fixes issue 2785.
Use different (perhaps better) git commands. Fixes issue 2109.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5605045
---
src/cmd/go/build.go | 17 +++++++++++++----\n src/cmd/go/get.go | 6 +-----\n src/cmd/go/main.go | 6 +++---\n src/cmd/go/vcs.go | 25 ++++++++++++++-----------\n 4 files changed, 31 insertions(+), 23 deletions(-)\n
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0f1056667fb8952046e5360ebf9f3285b6f7de33
元コミット内容
cmd/go: improvements
Do not treat $GOROOT/src/pkg, $GOROOT/src/cmd,
$GOPATH/src as package directories (only subdirectories
of those can be package directories). Fixes issue 2602.
Accept additional compiler and linker arguments during
cgo from $CGO_CFLAGS and $CGO_LDFLAGS, as the
Makefiles used to do.
Show failed pkg-config output. Fixes issue 2785.
Use different (perhaps better) git commands. Fixes issue 2109.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5605045
変更の背景
このコミットは、Goのビルドシステムとツールチェーンにおけるいくつかの既存の問題と改善点を解決するために行われました。
-
Issue 2602: トップレベルディレクトリのパッケージ認識問題
- Goのビルドツール
go buildやgo installは、パッケージを識別する際に、$GOROOT/src/pkg、$GOROOT/src/cmd、$GOPATH/srcといったディレクトリ自体をパッケージとして誤って認識してしまうことがありました。これらのディレクトリは通常、パッケージのルートではなく、パッケージを含むコンテナディレクトリであるべきです。この誤認識により、ビルドエラーや予期せぬ動作が発生する可能性がありました。例えば、go install pkgのようなコマンドが意図しない結果を招くことがありました。
- Goのビルドツール
-
Cgoビルドにおけるコンパイラ/リンカ引数の不足
cgoはGoコードからC/C++コードを呼び出すためのメカニズムですが、従来のGoのビルドシステムでは、C/C++コンパイラやリンカに追加のフラグ(例えば、特定のインクルードパスやライブラリパス)を渡すための標準的な方法が不足していました。Makefilesを使用していた時代にはこれらの引数を渡すことができましたが、cmd/goツールが主流になるにつれて、この機能が失われていました。開発者は、外部のC/C++ライブラリに依存するCgoプロジェクトをビルドする際に、これらの引数を指定できず、不便を強いられていました。
-
Issue 2785:
pkg-config失敗時のデバッグ情報の不足pkg-configは、C/C++ライブラリのコンパイル・リンクに必要なフラグ(インクルードパス、ライブラリパス、ライブラリ名など)を取得するためのツールです。Cgoプロジェクトでは、外部Cライブラリの依存関係を解決するためにpkg-configが利用されます。しかし、pkg-configの実行が何らかの理由で失敗した場合(例えば、必要なライブラリが見つからない、設定ファイルが不正など)、cmd/goツールはその失敗の具体的な原因を示す出力を表示しませんでした。これにより、開発者はpkg-config関連のビルドエラーのデバッグに苦労していました。
-
Issue 2109:
gitコマンドの非効率性または非推奨な使用cmd/goツールは、go getコマンドなどでリモートリポジトリからソースコードを取得する際に、内部的にgitなどのバージョン管理システムコマンドを呼び出します。このコミット以前は、使用されているgitコマンドの一部が非効率的であったり、特定のシナリオで問題を引き起こす可能性がありました。例えば、タグの同期やリポジトリの更新方法に関して、より堅牢で推奨されるgitの操作方法が存在していました。
これらの問題に対処し、Goのビルドシステム全体の堅牢性、使いやすさ、およびデバッグ能力を向上させることが、このコミットの主な目的でした。
前提知識の解説
このコミットの変更内容を理解するためには、以下の概念について基本的な知識が必要です。
-
Go言語のワークスペースと環境変数:
GOROOT: Goのインストールディレクトリを指す環境変数です。Goの標準ライブラリのソースコード($GOROOT/src以下)などが含まれます。GOPATH: Goのワークスペースディレクトリを指す環境変数です。ユーザーが開発するGoプロジェクトのソースコード、コンパイル済みパッケージ、実行可能ファイルなどが配置されます。通常、$GOPATH/srcにソースコード、$GOPATH/pkgにコンパイル済みパッケージ、$GOPATH/binに実行可能ファイルが置かれます。- パッケージディレクトリ: Goでは、ディレクトリがパッケージの単位となります。
import "path/to/package"のように指定された場合、Goツールは$GOROOT/src/path/to/packageや$GOPATH/src/path/to/packageを探します。
-
cgo:- Go言語の機能の一つで、C言語のコードをGoプログラムから呼び出すことを可能にします。また、GoコードをC言語から呼び出すこともできます。
cgoを使用すると、Goのビルドプロセス中にC/C++コンパイラやリンカが呼び出され、GoとC/C++の間のバインディングが生成されます。 CGO_CFLAGS/CGO_LDFLAGS:cgoのビルドプロセス中にC/C++コンパイラ(CGO_CFLAGS)やリンカ(CGO_LDFLAGS)に渡される追加のフラグを指定するための環境変数です。これらは、インクルードパス(-I)、ライブラリパス(-L)、ライブラリ名(-l)などを指定するのに使われます。
- Go言語の機能の一つで、C言語のコードをGoプログラムから呼び出すことを可能にします。また、GoコードをC言語から呼び出すこともできます。
-
pkg-config:- Unix系システムで広く使われているツールで、インストールされているライブラリのコンパイル・リンクに必要な情報を取得するために使用されます。例えば、
pkg-config --cflags --libs gtk+-3.0のように実行すると、GTK+ 3.0ライブラリを使用するために必要なコンパイラフラグとリンカフラグが出力されます。Cgoプロジェクトでは、Goプログラムが外部のCライブラリに依存する場合に、pkg-configを利用してこれらのライブラリのビルド情報を動的に取得します。
- Unix系システムで広く使われているツールで、インストールされているライブラリのコンパイル・リンクに必要な情報を取得するために使用されます。例えば、
-
バージョン管理システム (VCS):
- Git: 分散型バージョン管理システム。ソースコードの変更履歴を管理し、複数の開発者間での共同作業を容易にします。
git clone: リモートリポジトリの完全なコピーをローカルに作成するコマンド。git fetch: リモートリポジトリから最新の変更(コミット、ブランチ、タグなど)をローカルにダウンロードするが、現在のブランチにはマージしないコマンド。git checkout: ブランチやコミット、タグなど、特定の状態に作業ディレクトリを切り替えるコマンド。git tag: 特定のコミットに意味のある名前(タグ)を付けるコマンド。通常、リリースバージョンを示すために使われます。git branch: ブランチを管理するコマンド。masterは伝統的にメインの開発ラインを指すブランチ名です。go get: Goのパッケージをリモートリポジトリから取得し、ビルドしてインストールするコマンド。内部的にVCSコマンド(git, hg, svn, bzrなど)を呼び出します。
技術的詳細
このコミットは、Goのビルドツール cmd/go の内部実装に複数の重要な変更を加えています。
-
トップレベルディレクトリのパッケージ認識の修正 (
src/cmd/go/main.go):- 以前の
cmd/goは、$GOROOT/src/pkg、$GOROOT/src/cmd、$GOPATH/srcといったディレクトリ自体を有効なGoパッケージとして扱ってしまうバグがありました。しかし、これらのディレクトリは通常、Goパッケージのルートではなく、複数のパッケージを含むコンテナディレクトリです。 - このコミットでは、
filepath.Walkを使用してファイルシステムを走査し、パッケージディレクトリを検出するロジックに修正が加えられました。具体的には、path == cmdやpath == src、path == dirといった条件が追加され、これらのトップレベルディレクトリ自体がパッケージとして認識されないように変更されました。これにより、go install pkgのようなコマンドが意図しない動作をするのを防ぎ、Goのパッケージ解決のセマンティクスがより正確になりました。
- 以前の
-
CGO_CFLAGSおよびCGO_LDFLAGSのサポート (src/cmd/go/build.go):cgoのビルドプロセスにおいて、環境変数CGO_CFLAGSとCGO_LDFLAGSの値を読み込み、それぞれCコンパイラとリンカに渡す引数リストに追加する機能が実装されました。envListという新しいヘルパー関数が導入され、環境変数の値をスペースで区切られた文字列のリストとして解析します。cgoCFLAGSとcgoLDFLAGSの初期化時に、envList("CGO_CFLAGS")とenvList("CGO_LDFLAGS")の結果がp.info.CgoCFLAGSやp.info.CgoLDFLAGSと結合されるようになりました。これにより、開発者は環境変数を通じて追加のコンパイラ/リンカ引数を簡単に指定できるようになり、特に外部Cライブラリへの依存がある場合にビルドの柔軟性が大幅に向上しました。
-
pkg-config失敗時の出力表示 (src/cmd/go/build.go):pkg-configコマンド(--cflagsまたは--libs)の実行が失敗した場合、以前は単にエラーが返されるだけで、pkg-config自体の標準出力や標準エラー出力が表示されませんでした。これにより、何が問題だったのかを特定するのが困難でした。- このコミットでは、
b.showOutputメソッドが呼び出され、pkg-configコマンドの出力(たとえそれがエラーメッセージであっても)がユーザーに表示されるようになりました。さらに、b.print(err.Error() + "\n")を通じて、Go側で捕捉されたエラーメッセージも出力されます。これにより、pkg-config関連のビルドエラーが発生した際に、より詳細なデバッグ情報が提供され、問題解決が容易になります。
-
gitコマンドの改善 (src/cmd/go/vcs.go,src/cmd/go/get.go):vcs.goファイルでは、vcsCmd構造体にtagSyncDefaultという新しいフィールドが追加されました。これは、特定のタグが指定されていない場合にデフォルトで使用されるタグ同期コマンドを定義します。gitの定義において、tagSyncDefaultがcheckout origin/masterに変更されました。これは、gitリポジトリでタグが指定されない場合に、リモートのmasterブランチにチェックアウトするという、より一般的で堅牢な動作を反映しています。以前はtagDefaultフィールドがありましたが、これは削除され、より柔軟なtagSyncDefaultに置き換えられました。get.goファイルでは、selectTag関数の結果を直接vcs.tagSyncに渡すように変更されました。これにより、tagDefaultのような中間的な変数やロジックが不要になり、コードが簡素化されました。- これらの変更は、
go getが内部的にgitを使用してリポジトリを操作する際の信頼性と効率性を向上させることを目的としています。特に、タグの同期やリポジトリの初期クローン/更新の際に、より適切なgitコマンドが使用されるようになります。
コアとなるコードの変更箇所
src/cmd/go/build.go
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -1131,6 +1131,10 @@ func (b *builder) gccCmd(objdir string) []string {
return a
}
+func envList(key string) []string {
+ return strings.Fields(os.Getenv(key))
+}
+
var cgoRe = regexp.MustCompile(`[/\\\\:]`)
func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo, outObj []string, err error) {
@@ -1140,19 +1144,24 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []string) (outGo,
outObj = append(outObj, "") // for importObj, at end of function
- cgoCFLAGS := stringList(p.info.CgoCFLAGS)
- cgoLDFLAGS := stringList(p.info.CgoLDFLAGS)
+ cgoCFLAGS := stringList(envList("CGO_CFLAGS"), p.info.CgoCFLAGS)
+ cgoLDFLAGS := stringList(envList("CGO_LDFLAGS"), p.info.CgoLDFLAGS)
+
if pkgs := p.info.CgoPkgConfig; len(pkgs) > 0 {
out, err := b.runOut(p.Dir, p.ImportPath, "pkg-config", "--cflags", pkgs)
if err != nil {
- return nil, nil, err
+ b.showOutput(p.Dir, "pkg-config --cflags "+strings.Join(pkgs, " "), string(out))
+ b.print(err.Error() + "\n")
+ return nil, nil, errPrintedOutput
}
if len(out) > 0 {
cgoCFLAGS = append(cgoCFLAGS, strings.Fields(string(out))...)
}
out, err = b.runOut(p.Dir, p.ImportPath, "pkg-config", "--libs", pkgs)
if err != nil {
- return nil, nil, err
+ b.showOutput(p.Dir, "pkg-config --libs "+strings.Join(pkgs, " "), string(out))
+ b.print(err.Error() + "\n")
+ return nil, nil, errPrintedOutput
}
if len(out) > 0 {
cgoLDFLAGS = append(cgoLDFLAGS, strings.Fields(string(out))...)
src/cmd/go/main.go
--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -347,7 +347,7 @@ func allPackages(pattern string) []string {
goroot := build.Path[0].Path
cmd := filepath.Join(goroot, "src/cmd") + string(filepath.Separator)
filepath.Walk(cmd, func(path string, fi os.FileInfo, err error) error {
- if err != nil || !fi.IsDir() {
+ if err != nil || !fi.IsDir() || path == cmd {
return nil
}
name := path[len(cmd):]
@@ -378,7 +378,7 @@ func allPackages(pattern string) []string {
}
src := t.SrcDir() + string(filepath.Separator)
filepath.Walk(src, func(path string, fi os.FileInfo, err error) error {
- if err != nil || !fi.IsDir() {
+ if err != nil || !fi.IsDir() || path == src {
return nil
}
@@ -445,7 +445,7 @@ func allPackagesInFS(pattern string) []string {
var pkgs []string
filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
- if err != nil || !fi.IsDir() {
+ if err != nil || !fi.IsDir() || path == dir {
return nil
}
src/cmd/go/vcs.go
--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -23,9 +23,9 @@ type vcsCmd struct {
createCmd string // command to download a fresh copy of a repository
downloadCmd string // command to download updates into an existing repository
- tagCmd []tagCmd // commands to list tags
- tagDefault string // default tag to use
- tagSyncCmd string // command to sync to specific tag
+ tagCmd []tagCmd // commands to list tags
+ tagSyncCmd string // command to sync to specific tag
+ tagSyncDefault string // command to sync to default tag
}
// A tagCmd describes a command to list available tags
@@ -71,8 +71,8 @@ var vcsHg = &vcsCmd{
{"tags", `^(\S+)`},
{"branches", `^(\S+)`},
},
- tagDefault: "default",
- tagSyncCmd: "update -r {tag}",
+ tagSyncCmd: "update -r {tag}",
+ tagSyncDefault: "update default",
}
// vcsGit describes how to use Git.
@@ -83,9 +83,9 @@ var vcsGit = &vcsCmd{
createCmd: "clone {repo} {dir}",
downloadCmd: "fetch",
- tagCmd: []tagCmd{{"tag", `^(\S+)$`}},
- tagDefault: "master",
- tagSyncCmd: "checkout {tag}",
+ tagCmd: []tagCmd{{"tag", `^(\S+)$`}},
+ tagSyncCmd: "checkout {tag}",
+ tagSyncDefault: "checkout origin/master",
}
// vcsBzr describes how to use Bazaar.
@@ -99,9 +99,9 @@ var vcsBzr = &vcsCmd{
// Replace by --overwrite-tags after http://pad.lv/681792 goes in.
downloadCmd: "pull --overwrite",
- tagCmd: []tagCmd{{"tags", `^(\S+)`}},
- tagDefault: "revno:-1",
- tagSyncCmd: "update -r {tag}",
+ tagCmd: []tagCmd{{"tags", `^(\S+)`}},
+ tagSyncCmd: "update -r {tag}",
+ tagSyncDefault: "update -r revno:-1",
}
// vcsSvn describes how to use Subversion.
@@ -198,6 +198,9 @@ func (v *vcsCmd) tagSync(dir, tag string) error {\n if v.tagSyncCmd == "" {\n return nil\n }\n+\tif tag == "" && v.tagSyncDefault != "" {\n+\t\treturn v.run(dir, v.tagSyncDefault)\n+\t}\n return v.run(dir, v.tagSyncCmd, "tag", tag)\n }\n
src/cmd/go/get.go
--- a/src/cmd/go/get.go
+++ b/src/cmd/go/get.go
@@ -215,11 +215,7 @@ func downloadPackage(p *Package) error {
if i := strings.Index(vers, " "); i >= 0 {
vers = vers[:i]
}
-\ttag := selectTag(vers, tags)
-\tif tag == "" {\n-\t\ttag = vcs.tagDefault\n-\t}\n-\tif err := vcs.tagSync(root, tag); err != nil {\n+\tif err := vcs.tagSync(root, selectTag(vers, tags)); err != nil {\n \t\treturn err\n \t}\n
コアとなるコードの解説
src/cmd/go/build.go の変更
envList関数の追加:func envList(key string) []stringは、指定された環境変数keyの値を取得し、その値をスペースで分割して文字列スライスとして返します。これは、CGO_CFLAGSやCGO_LDFLAGSのような環境変数から複数の引数を取得する際に便利です。
CGO_CFLAGSとCGO_LDFLAGSの統合:- 変更前:
cgoCFLAGS := stringList(p.info.CgoCFLAGS) - 変更後:
cgoCFLAGS := stringList(envList("CGO_CFLAGS"), p.info.CgoCFLAGS) - これにより、
cgoのビルド時に、パッケージ情報 (p.info.CgoCFLAGS) に加えて、環境変数CGO_CFLAGSで指定されたフラグもcgoCFLAGSリストに追加されるようになりました。cgoLDFLAGSも同様です。stringList関数は、複数の文字列スライスを結合するヘルパー関数です。
- 変更前:
pkg-config失敗時の出力表示:pkg-configコマンドの実行がエラーになった場合 (if err != nil) の処理が変更されました。- 変更前は単に
return nil, nil, errでエラーを返していましたが、変更後は以下の2行が追加されました。b.showOutput(p.Dir, "pkg-config --cflags "+strings.Join(pkgs, " "), string(out)):pkg-configコマンドとその出力(エラーメッセージを含む)をユーザーに表示します。これにより、何が問題だったのかが視覚的に確認できるようになります。b.print(err.Error() + "\n"):Go側で捕捉されたエラーメッセージも出力します。return nil, nil, errPrintedOutput:エラーが既に表示されたことを示す特別なエラーerrPrintedOutputを返します。これにより、上位の呼び出し元で重複してエラーメッセージが表示されるのを防ぎます。
src/cmd/go/main.go の変更
- トップレベルディレクトリのパッケージ認識の修正:
filepath.Walkを使用してパッケージを探索する際に、if err != nil || !fi.IsDir()という条件に加えて、|| path == cmd、|| path == src、|| path == dirという条件が追加されました。- これは、
$GOROOT/src/cmd、$GOPATH/src、およびallPackagesInFSで指定されたルートディレクトリ自体が、有効なGoパッケージとして誤って認識されるのを防ぐためのものです。これらのディレクトリはパッケージのコンテナであり、それ自体がパッケージであるべきではありません。この修正により、Goのパッケージ解決のロジックがより正確になりました。
src/cmd/go/vcs.go の変更
vcsCmd構造体の変更:tagDefault stringフィールドが削除され、代わりにtagSyncDefault stringフィールドが追加されました。tagSyncDefaultは、特定のタグが指定されていない場合に、リポジトリを同期するためのデフォルトコマンドを保持します。これにより、VCSの種類ごとに異なるデフォルトの同期動作を柔軟に設定できるようになります。
gitの定義の変更:vcsGitの定義において、tagDefault: "master"が削除され、tagSyncDefault: "checkout origin/master"が追加されました。- これは、
gitにおいてタグが指定されない場合のデフォルトの同期動作を、リモートのmasterブランチにチェックアウトするという、より一般的で堅牢な方法に変更したことを意味します。
tagSyncメソッドの変更:func (v *vcsCmd) tagSync(dir, tag string) errorメソッドに以下のロジックが追加されました。if tag == "" && v.tagSyncDefault != "" { return v.run(dir, v.tagSyncDefault) }- これは、
tag引数が空文字列(つまり、特定のタグが指定されていない)であり、かつv.tagSyncDefaultが設定されている場合に、tagSyncDefaultで定義されたコマンドを実行するようにします。これにより、デフォルトの同期動作がより柔軟に制御できるようになりました。
src/cmd/go/get.go の変更
vcs.tagSyncの呼び出しの簡素化:- 変更前は、
selectTagの結果をtag変数に格納し、tagが空の場合はvcs.tagDefaultを使用するというロジックがありました。 - 変更後:
if err := vcs.tagSync(root, selectTag(vers, tags)); err != nil { selectTag(vers, tags)の結果を直接vcs.tagSyncに渡すように変更されました。これは、vcs.tagSyncメソッド自体がtagSyncDefaultロジックを内部で処理するようになったため、get.go側でデフォルトタグの処理を行う必要がなくなったためです。これにより、コードがより簡潔になりました。
- 変更前は、
関連リンク
- Go CL 5605045: https://golang.org/cl/5605045
参考にした情報源リンク
- Go Issue 2602: cmd/go: go install pkg should not install pkg itself (closed by this commit) - https://github.com/golang/go/issues/2602
- Go Issue 2785: cmd/go: show pkg-config output on failure (closed by this commit) - https://github.com/golang/go/issues/2785
- Go Issue 2109: cmd/go: use different (perhaps better) git commands (closed by this commit) - https://github.com/golang/go/issues/2109
- Go Modules (Go公式ドキュメント): https://go.dev/blog/using-go-modules (Go Modulesはコミット当時のGoのバージョンでは存在しませんでしたが、
GOPATHの概念を理解する上で参考になります) - cgo (Go公式ドキュメント): https://go.dev/blog/cgo
- pkg-config (Wikipedia): https://ja.wikipedia.org/wiki/Pkg-config
- Git Documentation: https://git-scm.com/doc
- Go Command Documentation: https://go.dev/cmd/go/ (当時のバージョンとは異なる可能性がありますが、
cmd/goの基本的な機能理解に役立ちます)