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

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

このコミットは、Go言語のコマンドラインツール cmd/go に対する複数の改善を含んでいます。主な変更点は以下の通りです。

  1. $GOROOT/src/pkg$GOROOT/src/cmd$GOPATH/src といったトップレベルのディレクトリをパッケージディレクトリとして扱わないように修正し、これらのサブディレクトリのみをパッケージとして認識するようにしました。これにより、Goのパッケージ解決のセマンティクスがより正確になります。
  2. cgo のビルドプロセスにおいて、環境変数 $CGO_CFLAGS および $CGO_LDFLAGS から追加のコンパイラおよびリンカ引数を受け入れるようにしました。これにより、Cgoを使用する際のビルドの柔軟性が向上します。
  3. pkg-config コマンドの実行が失敗した場合に、その出力を表示するように改善しました。これにより、pkg-config 関連のエラーのデバッグが容易になります。
  4. バージョン管理システム (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のビルドシステムとツールチェーンにおけるいくつかの既存の問題と改善点を解決するために行われました。

  1. Issue 2602: トップレベルディレクトリのパッケージ認識問題

    • Goのビルドツール go buildgo install は、パッケージを識別する際に、$GOROOT/src/pkg$GOROOT/src/cmd$GOPATH/src といったディレクトリ自体をパッケージとして誤って認識してしまうことがありました。これらのディレクトリは通常、パッケージのルートではなく、パッケージを含むコンテナディレクトリであるべきです。この誤認識により、ビルドエラーや予期せぬ動作が発生する可能性がありました。例えば、go install pkg のようなコマンドが意図しない結果を招くことがありました。
  2. Cgoビルドにおけるコンパイラ/リンカ引数の不足

    • cgo はGoコードからC/C++コードを呼び出すためのメカニズムですが、従来のGoのビルドシステムでは、C/C++コンパイラやリンカに追加のフラグ(例えば、特定のインクルードパスやライブラリパス)を渡すための標準的な方法が不足していました。Makefilesを使用していた時代にはこれらの引数を渡すことができましたが、cmd/go ツールが主流になるにつれて、この機能が失われていました。開発者は、外部のC/C++ライブラリに依存するCgoプロジェクトをビルドする際に、これらの引数を指定できず、不便を強いられていました。
  3. Issue 2785: pkg-config 失敗時のデバッグ情報の不足

    • pkg-config は、C/C++ライブラリのコンパイル・リンクに必要なフラグ(インクルードパス、ライブラリパス、ライブラリ名など)を取得するためのツールです。Cgoプロジェクトでは、外部Cライブラリの依存関係を解決するために pkg-config が利用されます。しかし、pkg-config の実行が何らかの理由で失敗した場合(例えば、必要なライブラリが見つからない、設定ファイルが不正など)、cmd/go ツールはその失敗の具体的な原因を示す出力を表示しませんでした。これにより、開発者は pkg-config 関連のビルドエラーのデバッグに苦労していました。
  4. 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)などを指定するのに使われます。
  • pkg-config:

    • Unix系システムで広く使われているツールで、インストールされているライブラリのコンパイル・リンクに必要な情報を取得するために使用されます。例えば、pkg-config --cflags --libs gtk+-3.0 のように実行すると、GTK+ 3.0ライブラリを使用するために必要なコンパイラフラグとリンカフラグが出力されます。Cgoプロジェクトでは、Goプログラムが外部のCライブラリに依存する場合に、pkg-config を利用してこれらのライブラリのビルド情報を動的に取得します。
  • バージョン管理システム (VCS):

    • Git: 分散型バージョン管理システム。ソースコードの変更履歴を管理し、複数の開発者間での共同作業を容易にします。
    • git clone: リモートリポジトリの完全なコピーをローカルに作成するコマンド。
    • git fetch: リモートリポジトリから最新の変更(コミット、ブランチ、タグなど)をローカルにダウンロードするが、現在のブランチにはマージしないコマンド。
    • git checkout: ブランチやコミット、タグなど、特定の状態に作業ディレクトリを切り替えるコマンド。
    • git tag: 特定のコミットに意味のある名前(タグ)を付けるコマンド。通常、リリースバージョンを示すために使われます。
    • git branch: ブランチを管理するコマンド。master は伝統的にメインの開発ラインを指すブランチ名です。
    • go get: Goのパッケージをリモートリポジトリから取得し、ビルドしてインストールするコマンド。内部的にVCSコマンド(git, hg, svn, bzrなど)を呼び出します。

技術的詳細

このコミットは、Goのビルドツール cmd/go の内部実装に複数の重要な変更を加えています。

  1. トップレベルディレクトリのパッケージ認識の修正 (src/cmd/go/main.go):

    • 以前の cmd/go は、$GOROOT/src/pkg$GOROOT/src/cmd$GOPATH/src といったディレクトリ自体を有効なGoパッケージとして扱ってしまうバグがありました。しかし、これらのディレクトリは通常、Goパッケージのルートではなく、複数のパッケージを含むコンテナディレクトリです。
    • このコミットでは、filepath.Walk を使用してファイルシステムを走査し、パッケージディレクトリを検出するロジックに修正が加えられました。具体的には、path == cmdpath == srcpath == dir といった条件が追加され、これらのトップレベルディレクトリ自体がパッケージとして認識されないように変更されました。これにより、go install pkg のようなコマンドが意図しない動作をするのを防ぎ、Goのパッケージ解決のセマンティクスがより正確になりました。
  2. CGO_CFLAGS および CGO_LDFLAGS のサポート (src/cmd/go/build.go):

    • cgo のビルドプロセスにおいて、環境変数 CGO_CFLAGSCGO_LDFLAGS の値を読み込み、それぞれCコンパイラとリンカに渡す引数リストに追加する機能が実装されました。
    • envList という新しいヘルパー関数が導入され、環境変数の値をスペースで区切られた文字列のリストとして解析します。
    • cgoCFLAGScgoLDFLAGS の初期化時に、envList("CGO_CFLAGS")envList("CGO_LDFLAGS") の結果が p.info.CgoCFLAGSp.info.CgoLDFLAGS と結合されるようになりました。これにより、開発者は環境変数を通じて追加のコンパイラ/リンカ引数を簡単に指定できるようになり、特に外部Cライブラリへの依存がある場合にビルドの柔軟性が大幅に向上しました。
  3. 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 関連のビルドエラーが発生した際に、より詳細なデバッグ情報が提供され、問題解決が容易になります。
  4. git コマンドの改善 (src/cmd/go/vcs.go, src/cmd/go/get.go):

    • vcs.go ファイルでは、vcsCmd 構造体に tagSyncDefault という新しいフィールドが追加されました。これは、特定のタグが指定されていない場合にデフォルトで使用されるタグ同期コマンドを定義します。
    • git の定義において、tagSyncDefaultcheckout 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_CFLAGSCGO_LDFLAGS のような環境変数から複数の引数を取得する際に便利です。
  • CGO_CFLAGSCGO_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 側でデフォルトタグの処理を行う必要がなくなったためです。これにより、コードがより簡潔になりました。

関連リンク

参考にした情報源リンク