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

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

このコミットは、Go言語のコマンドラインツール cmd/go における2つの重要なバグ修正を目的としています。具体的には、GOPATH 環境変数が GOROOT と同じ値に設定された場合に発生する問題と、ソースコードを持たないバイナリのみのパッケージの認識に関する問題に対処しています。

コミット

commit 6b0929505ba7d4ede45d587239459d3f2eb8c3d4
Author: Russ Cox <rsc@golang.org>
Date:   Tue Mar 27 10:41:44 2012 -0400

    cmd/go: fix two bugs
    
    Issue 3207 was caused by setting GOPATH=GOROOT.
    This is a common mistake, so diagnose it at command start
    and also correct the bug that it caused in get (downloading
    to GOROOT/src/foo instead of GOROOT/src/pkg/foo).
    
    Issue 3268 was caused by recognizing 'packages' that
    had installed binaries but no source.  This behavior is not
    documented and causes trouble, so remove it.  We can
    revisit the concept of binary-only packages after Go 1.
    
    Fixes #3207.
    Fixes #3268.
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/5930044

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

https://github.com/golang/go/commit/6b0929505ba7d4ede45d587239459d3f2eb8c3d4

元コミット内容

cmd/go: 2つのバグを修正

Issue 3207 は GOPATH=GOROOT の設定によって引き起こされていました。 これはよくある間違いであるため、コマンド開始時に診断し、 get コマンドで発生していたバグ(GOROOT/src/foo ではなく GOROOT/src/pkg/foo にダウンロードされるべきところを誤ってダウンロードしていた)も修正します。

Issue 3268 は、インストールされたバイナリは存在するがソースがない「パッケージ」を認識することによって引き起こされていました。この動作は文書化されておらず、問題を引き起こすため、削除します。Go 1 リリース後に、バイナリのみのパッケージの概念を再検討することができます。

Issue #3207 を修正。 Issue #3268 を修正。

変更の背景

このコミットは、Go言語の開発初期段階における cmd/go ツールの成熟度向上を反映しています。特に、ユーザーが直面する可能性のある一般的な設定ミスや、ツールの意図しない動作に対処しています。

  1. Issue 3207: GOPATH=GOROOT の問題: Go言語の初期において、GOPATHGOROOT の概念は新しいものであり、ユーザーがこれらを混同したり、誤って設定したりすることがありました。特に GOPATHGOROOT と同じディレクトリに設定してしまうと、go get コマンドがパッケージをダウンロードする際に、本来 GOPATH 内に配置されるべきユーザーのプロジェクトコードが GOROOT のシステムディレクトリにダウンロードされてしまうという問題が発生していました。これは、Goの標準ライブラリとユーザーのコードが混在し、ビルドや管理が困難になる原因となっていました。このコミットでは、この一般的な設定ミスを検出し、ユーザーに警告するとともに、go get のダウンロードパスのロジックを修正することで、この問題を根本的に解決しようとしています。

  2. Issue 3268: ソースコードなしバイナリパッケージの認識問題: Goのパッケージ管理システムは、通常、ソースコードを基盤としています。しかし、この問題は、ソースコードが存在しないにもかかわらず、インストールされたバイナリ(実行ファイルやライブラリ)のみでパッケージとして認識されてしまうという、意図しない動作を示していました。このような「バイナリのみのパッケージ」の概念は、Go 1 リリース時点では正式にサポートされておらず、ツールの動作を複雑にし、予期せぬエラーを引き起こす可能性がありました。このコミットでは、この未定義の動作を一時的に無効にすることで、ツールの安定性を向上させ、将来的にバイナリ配布の仕組みを検討する際の基盤を整えています。

前提知識の解説

  • GOROOT: Go言語のインストールディレクトリを指す環境変数です。Goの標準ライブラリやツールチェインがここに格納されています。通常、ユーザーがこのディレクトリを直接変更することはありません。
  • GOPATH: Go言語のワークスペースディレクトリを指す環境変数です。ユーザーが開発するGoのプロジェクトや、go get コマンドでダウンロードされるサードパーティのパッケージのソースコードがここに配置されます。GOPATH は複数のパスを設定でき、Goツールはこれらのパスを検索してパッケージを見つけます。GOPATH の各エントリは、src (ソースコード), pkg (コンパイル済みパッケージ), bin (コンパイル済みコマンド) のサブディレクトリを持ちます。
  • go get コマンド: リモートリポジトリからGoパッケージのソースコードをダウンロードし、GOPATH 内に配置するコマンドです。依存関係も自動的に解決してダウンロードします。
  • build.Context: Goのビルドシステムがパッケージのビルドに必要な情報(環境変数、OS、アーキテクチャなど)を保持する構造体です。
  • build.AllowBinary: build.Context.Import メソッドに渡されるフラグの一つで、バイナリのみのパッケージのインポートを許可するかどうかを制御します。

技術的詳細

このコミットは、主に cmd/go ツール内の3つのファイル (src/cmd/go/get.go, src/cmd/go/main.go, src/cmd/go/pkg.go) に変更を加えています。

  1. GOPATH=GOROOT の診断と修正 (src/cmd/go/main.go および src/cmd/go/get.go):

    • src/cmd/go/main.go では、cmd/go コマンドの起動時に GOPATH 環境変数が GOROOT と同じ値に設定されているかどうかをチェックするロジックが追加されました。もし同じであれば、標準エラー出力に警告メッセージが表示されます。これは、ユーザーが一般的な設定ミスをしていることを早期に通知し、混乱を避けるためのものです。
    • src/cmd/go/get.go では、downloadPackage 関数内でパッケージのダウンロード先を決定するロジックが修正されました。以前は GOPATH が設定されていればその最初のディレクトリを使用し、そうでなければ GOROOT を使用していました。しかし、GOPATHGOROOT と同じ場合、GOROOT のディレクトリ構造 (src/pkg など) を正しく利用するように変更されました。これにより、GOPATH=GOROOT の場合でも、パッケージが GOROOT/src/pkg/foo のような正しいパスにダウンロードされるようになります。
  2. バイナリのみのパッケージの認識の無効化 (src/cmd/go/pkg.go):

    • src/cmd/go/pkg.goloadImport 関数内で、buildContext.Import メソッドを呼び出す際に渡されるフラグが変更されました。以前は build.AllowBinary フラグが渡されていましたが、これが 0 (つまり、バイナリのみのインポートを許可しない) に変更されました。これにより、ソースコードが存在しないバイナリのみのパッケージは、Go 1 リリース時点では cmd/go ツールによって認識されなくなります。コミットメッセージにもあるように、この動作は将来的に再検討される可能性がありますが、Go 1 の安定性を優先するための措置です。

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

src/cmd/go/get.go

--- a/src/cmd/go/get.go
+++ b/src/cmd/go/get.go
@@ -252,7 +252,9 @@ func downloadPackage(p *Package) error {
 
 	if p.build.SrcRoot == "" {
 		// Package not found.  Put in first directory of $GOPATH or else $GOROOT.
-		if list := filepath.SplitList(buildContext.GOPATH); len(list) > 0 {
+		// Guard against people setting GOPATH=$GOROOT.  We have to use
+		// $GOROOT's directory hierarchy (src/pkg, not just src) in that case.
+		if list := filepath.SplitList(buildContext.GOPATH); len(list) > 0 && list[0] != goroot {
 			p.build.SrcRoot = filepath.Join(list[0], "src")
 			p.build.PkgRoot = filepath.Join(list[0], "pkg")
 		} else {

src/cmd/go/main.go

--- a/src/cmd/go/main.go
+++ b/src/cmd/go/main.go
@@ -16,6 +16,7 @@ import (
 	"path"
 	"path/filepath"
 	"regexp"
+	"runtime"
 	"strings"
 	"sync"
 	"text/template"
@@ -121,6 +122,13 @@ func main() {
 		return
 	}
 
+	// Diagnose common mistake: GOPATH==GOROOT.
+	// This setting is equivalent to not setting GOPATH at all,
+	// which is not what most people want when they do it.
+	if gopath := os.Getenv("GOPATH"); gopath == runtime.GOROOT() {
+		fmt.Fprintf(os.Stderr, "warning: GOPATH set to GOROOT (%s) has no effect\n", gopath)
+	}
+
 	for _, cmd := range commands {
 		if cmd.Name() == args[0] && cmd.Run != nil {
 			cmd.Flag.Usage = func() { cmd.Usage() }

src/cmd/go/pkg.go

--- a/src/cmd/go/pkg.go
+++ b/src/cmd/go/pkg.go
@@ -217,7 +217,10 @@ func loadImport(path string, srcDir string, stk *importStack, importPos []token.
 	// Load package.
 	// Import always returns bp != nil, even if an error occurs,
 	// in order to return partial information.
-	bp, err := buildContext.Import(path, srcDir, build.AllowBinary)
+	//
+	// TODO: After Go 1, decide when to pass build.AllowBinary here.
+	// See issue 3268 for mistakes to avoid.
+	bp, err := buildContext.Import(path, srcDir, 0)
 	bp.ImportPath = importPath
 	p.load(stk, bp, err)
 	if p.Error != nil && len(importPos) > 0 {

コアとなるコードの解説

  • src/cmd/go/get.go の変更: downloadPackage 関数は、ダウンロードするパッケージのルートディレクトリ (SrcRoot, PkgRoot) を決定します。変更前は GOPATH が設定されていればその最初のパスを無条件に使用していましたが、変更後は list[0] != goroot という条件が追加されました。これは、GOPATH の最初のパスが GOROOT と同じでない場合にのみ GOPATH を使用し、もし同じであれば GOROOT の内部構造 (src/pkg) を考慮したパス解決を行うためのガードです。これにより、GOPATH=GOROOT の誤設定時でも go get が正しく動作するようになります。

  • src/cmd/go/main.go の変更: main 関数に、GOPATHruntime.GOROOT() と等しい場合に警告メッセージを出力するコードが追加されました。os.Getenv("GOPATH") で現在の GOPATH を取得し、runtime.GOROOT() でGoのインストールパスを取得して比較しています。この警告は、ユーザーが意図せず GOPATH を無効な値に設定していることを知らせるためのものです。

  • src/cmd/go/pkg.go の変更: loadImport 関数は、パッケージのインポート処理を行います。以前は buildContext.Import を呼び出す際に build.AllowBinary フラグを渡していましたが、これが 0 に変更されました。この変更により、cmd/go ツールは、ソースコードを持たないバイナリのみのパッケージをインポートしようとしなくなります。コメントには「Go 1 の後に、ここで build.AllowBinary を渡すかどうかを決定する」とあり、この機能が将来的に再検討される可能性が示唆されています。

関連リンク

  • GitHubコミットページ: https://github.com/golang/go/commit/6b0929505ba7d4ede45d587239459d3f2eb8c3d4
  • Go CL (Code Review): https://golang.org/cl/5930044
  • Go Issue 3207 (元のIssueページは特定できませんでしたが、コミットメッセージから GOPATH=GOROOT の問題であることが示唆されています)
  • Go Issue 3268 (元のIssueページは特定できませんでしたが、コミットメッセージからソースコードなしバイナリパッケージの認識問題であることが示唆されています)

参考にした情報源リンク

  • Go言語の公式ドキュメント (GOPATH, GOROOT, go get コマンドに関する一般的な情報)
  • Go言語のソースコード (特に cmd/go ディレクトリ内のファイル)
  • コミットメッセージと差分情報