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

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

このコミットは、Go言語のツールチェインの一部である cmd/api コマンドのビルドプロセスにおける問題を修正するものです。具体的には、GOARCH 環境変数が設定されている場合に cmd/api のビルドが失敗する問題を解決します。

コミット

commit cb79c57dfa25b917c4ce1c08e2a9a6f9405a5998
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Sat Aug 24 09:51:42 2013 -0500

    cmd/api: ignore GOARCH when building cmd/api.
    
    This was breaking people setting GOARCH=386 before running
    all.bash on amd64 machines.
    
    cmd/go puts different architecture binaries where "go tool"
    can't find them.
    
    R=golang-dev, r, khr
    CC=golang-dev
    https://golang.org/cl/13139044

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

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

元コミット内容

cmd/api: cmd/api をビルドする際に GOARCH を無視する。

これは、amd64 マシンで all.bash を実行する前に GOARCH=386 を設定している人々にとって問題を引き起こしていた。

cmd/go は異なるアーキテクチャのバイナリを、"go tool" が見つけられない場所に配置する。

変更の背景

このコミットの背景には、Go言語のビルドシステムとクロスコンパイルの挙動に関する特定の課題がありました。

Go言語では、GOARCH 環境変数を使用してターゲットアーキテクチャを指定し、クロスコンパイルを行うことができます。例えば、amd64 (64ビット) マシン上で GOARCH=386 (32ビット) を設定してビルドを実行すると、32ビットアーキテクチャ向けのバイナリが生成されます。

問題は、cmd/api というツールをビルドする際に発生していました。cmd/api はGoの標準ライブラリのAPIサーフェスを検査するためのツールであり、Goのビルドプロセスの一部としてビルドされます。ユーザーが all.bash スクリプト(GoのソースからGo自体をビルドするためのスクリプト)を実行する前に GOARCH を設定している場合、例えば amd64 マシンで GOARCH=386 と設定すると、cmd/api も32ビットアーキテクチャ向けにビルドされてしまいます。

しかし、cmd/go (Goのビルドコマンド) は、異なるアーキテクチャ向けにビルドされたバイナリを、通常の go tool コマンドが期待する場所とは異なるディレクトリに配置します。このため、cmd/api が32ビットアーキテクチャ向けにビルドされてしまうと、後続のビルドプロセスやテストプロセスで go tool apicmd/api バイナリを見つけられなくなり、ビルド全体が失敗するという問題が発生していました。

このコミットは、cmd/api のビルド時のみ GOARCH 環境変数を無視することで、この問題を回避し、all.bash の実行をより堅牢にすることを目的としています。

前提知識の解説

このコミットを理解するためには、以下のGo言語およびシステムに関する前提知識が必要です。

  1. Go言語の環境変数 (GOARCH, GOPATH, GOROOT):

    • GOARCH: Goプログラムをビルドする際のターゲットCPUアーキテクチャを指定します。例えば、amd64 (Intel/AMD 64ビット), 386 (Intel/AMD 32ビット), arm, arm64 などがあります。クロスコンパイルを行う際に非常に重要です。
    • GOPATH: Goのワークスペースのルートディレクトリを指定します。Goのソースコード、パッケージ、ビルドされたバイナリなどがこのディレクトリ構造内に配置されます。
    • GOROOT: Goのインストールディレクトリのルートを指定します。Goの標準ライブラリのソースコードや、Goツールチェインのバイナリなどが含まれます。
    • これらの環境変数は、Goのビルド、テスト、実行の挙動に大きな影響を与えます。
  2. cmd/api:

    • Goの標準ライブラリの公開APIサーフェスを検査するためのツールです。Goのバージョンアップに伴うAPIの互換性チェックなどに利用されます。GoのソースコードからGo自体をビルドする際に、このツールもビルドされ、内部的に使用されます。
  3. cmd/go:

    • Go言語の主要なコマンドラインツールであり、Goプログラムのビルド、テスト、実行、パッケージ管理など、Go開発のほとんどの操作を担います。go build, go install, go test などのサブコマンドを提供します。
  4. all.bash:

    • Go言語の公式リポジトリに含まれるシェルスクリプトで、GoのソースコードからGoコンパイラ、標準ライブラリ、および関連ツールチェイン全体をビルドするために使用されます。Goの開発者がGoの最新バージョンをビルドしたり、変更をテストしたりする際によく利用します。
  5. 環境変数とプロセスの継承:

    • Unix/Linuxシステムでは、子プロセスは親プロセスの環境変数を継承します。シェルで export GOARCH=386 と設定すると、そのシェルから起動されるすべてのコマンド(all.bash やその中で実行される go コマンドなど)がこの GOARCH の設定を継承します。
  6. os.Environ()exec.Command().Env:

    • Go言語の os.Environ() 関数は、現在のプロセスの環境変数を []string の形式(例: ["KEY=VALUE", ...])で返します。
    • os/exec パッケージの exec.Command で外部コマンドを実行する際、cmd.Env フィールドを設定することで、そのコマンドに渡す環境変数を明示的に制御できます。nil の場合は親プロセスの環境変数を継承します。

技術的詳細

このコミットの技術的な解決策は、cmd/api をビルドする際に GOARCH 環境変数が go install コマンドに渡されないようにすることです。

変更は src/cmd/api/run.go ファイルで行われています。このファイルは cmd/api ツール自体をビルドし、実行するためのロジックを含んでいます。

元のコードでは、cmd.Env = append([]string{"GOPATH=" + gopath}, os.Environ()...) のように、現在のプロセスのすべての環境変数を go install コマンドに渡していました。これには GOARCH も含まれます。

新しいコードでは、filterOut という新しいヘルパー関数が導入されています。この関数は、指定された環境変数(この場合は GOARCH)を既存の環境変数リストから除外します。

// filterOut returns a copy of the src environment without environment
// variables from remove.
// TODO: delete when issue 6201 is fixed.
func filterOut(src []string, remove ...string) (out []string) {
S:
	for _, s := range src {
		for _, r := range remove {
			if strings.HasPrefix(s, r) && strings.HasPrefix(s, r+"=") {
				continue S
			}
		}
		out = append(out, s)
	}
	return
}

この filterOut 関数は、src スライス(元の環境変数リスト)をイテレートし、各環境変数文字列 sremove リスト内のいずれかの文字列(例: "GOARCH")で始まり、かつその後に = が続く場合(例: "GOARCH=386")、その環境変数をスキップします。それ以外の場合は、out スライスに追加します。

そして、go install コマンドの環境変数を設定する際に、この filterOut 関数を使用して GOARCH を除外しています。

cmd.Env = append([]string{"GOPATH=" + gopath}, filterOut(os.Environ(), "GOARCH")...)

これにより、cmd/api をビルドする go install コマンドは、親プロセスで GOARCH が設定されていても、その影響を受けずにデフォルトの(現在のシステムアーキテクチャの)GOARCH でビルドされるようになります。結果として、cmd/api は常にホストアーキテクチャ向けにビルドされ、go tool が正しく見つけられるようになります。

コメントにある // TODO: delete when issue 6201 is fixed. は、この filterOut 関数が一時的な回避策であることを示唆しています。issue 6201 は、Goのツールチェインがクロスコンパイルされたバイナリの配置をより適切に管理するようになるか、あるいは go install が特定のツールに対して GOARCH を無視するようなメカニズムを提供するようになることを期待している可能性があります。

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

src/cmd/api/run.go ファイルが変更されています。

--- a/src/cmd/api/run.go
+++ b/src/cmd/api/run.go
@@ -21,6 +21,7 @@ import (
 	"os/exec"
 	"path/filepath"
 	"strconv"
+	"strings"
 )
 
 // goToolsVersion is the hg revision of the go.tools subrepo we need
@@ -45,7 +46,7 @@ func main() {
  	gopath := prepGoPath()
 
  	cmd := exec.Command("go", "install", "--tags=api_tool", "cmd/api")
-	cmd.Env = append([]string{"GOPATH=" + gopath}, os.Environ()...)\n+	cmd.Env = append([]string{"GOPATH=" + gopath}, filterOut(os.Environ(), "GOARCH")...)\n \tout, err := cmd.CombinedOutput()\n  	if err != nil {\n  		log.Fatalf("Error installing cmd/api: %v\\n%s", err, out)\n@@ -61,6 +62,22 @@ func main() {\n  	fmt.Print(string(out))\n  }\n  \n+// filterOut returns a copy of the src environment without environment\n+// variables from remove.\n+// TODO: delete when issue 6201 is fixed.\n+func filterOut(src []string, remove ...string) (out []string) {\n+S:\n+\tfor _, s := range src {\n+\t\tfor _, r := range remove {\n+\t\t\tif strings.HasPrefix(s, r) && strings.HasPrefix(s, r+"=") {\n+\t\t\t\tcontinue S\n+\t\t\t}\n+\t\t}\n+\t\tout = append(out, s)\n+\t}\n+\treturn\n+}\n+\n // file expands s to $GOROOT/api/s.txt.\n // If there are more than 1, they're comma-separated.\n func file(s ...string) string {\n```

## コアとなるコードの解説

このコミットの核心は、`src/cmd/api/run.go` に追加された `filterOut` 関数と、その関数が `go install` コマンドの環境変数を設定する際に使用される点です。

1.  **`import "strings"` の追加**:
    *   `filterOut` 関数内で文字列操作を行うために `strings` パッケージがインポートされています。

2.  **`filterOut` 関数の定義**:
    *   この関数は、`src` (元の環境変数文字列のスライス) と `remove` (削除したい環境変数名の可変長引数) を受け取ります。
    *   `S:` というラベルは、ネストされたループから外側のループを `continue` するために使用されます。
    *   外側のループは `src` の各環境変数文字列 `s` を処理します。
    *   内側のループは `remove` の各環境変数名 `r` を処理します。
    *   `strings.HasPrefix(s, r) && strings.HasPrefix(s, r+"=")` の条件は、環境変数文字列 `s` が `r` で始まり、かつ `r` の直後に `=` が続く場合に `true` となります。これは、`GOARCH=386` のような形式の環境変数を正確に識別するためのものです。
    *   条件が `true` の場合、`continue S` によって現在の環境変数 `s` はスキップされ、次の `src` の要素の処理に移ります。
    *   条件が `false` の場合(つまり、`s` が削除対象の環境変数ではない場合)、`s` は `out` スライスに追加されます。
    *   最終的に、`out` スライス(指定された環境変数が除外された新しい環境変数リスト)が返されます。
    *   `// TODO: delete when issue 6201 is fixed.` というコメントは、この関数が一時的な回避策であり、将来的にGoツールチェインのより根本的な解決策が導入された際には削除されるべきであることを示しています。

3.  **`cmd.Env` の設定変更**:
    *   元のコード: `cmd.Env = append([]string{"GOPATH=" + gopath}, os.Environ()...)`
        *   これは、`GOPATH` を明示的に設定し、その後に現在のプロセスのすべての環境変数を追加していました。
    *   変更後のコード: `cmd.Env = append([]string{"GOPATH=" + gopath}, filterOut(os.Environ(), "GOARCH")...)`
        *   `os.Environ()` で取得した現在の環境変数リストから、`filterOut` 関数を使って `GOARCH` 環境変数を除外しています。
        *   これにより、`go install` コマンドは `GOPATH` は受け取りますが、親プロセスで設定された `GOARCH` は受け取らなくなります。結果として、`cmd/api` はホストのデフォルトアーキテクチャでビルドされることになります。

この変更により、ユーザーが `all.bash` を実行する前に `GOARCH` を設定していても、`cmd/api` のビルドが意図せずクロスコンパイルされることがなくなり、ビルドの安定性が向上しました。

## 関連リンク

*   **Go Issue 6201**: このコミットの `TODO` コメントで言及されているIssue。Goの公式IssueトラッカーでこのIssueを検索すると、より長期的な解決策や関連する議論が見つかる可能性があります。
    *   [https://github.com/golang/go/issues/6201](https://github.com/golang/go/issues/6201)
*   **Go言語の環境変数に関する公式ドキュメント**: `GOARCH`, `GOPATH` などの詳細な説明。
    *   [https://go.dev/doc/install/source#environment](https://go.dev/doc/install/source#environment) (Goのソースからのインストールと環境変数に関するセクション)
*   **Go言語のクロスコンパイルに関する情報**:
    *   [https://go.dev/doc/install/source#go_build](https://go.dev/doc/install/source#go_build) (Goのビルドコマンドに関するセクション)

## 参考にした情報源リンク

*   Go言語の公式ドキュメント
*   Go言語のGitHubリポジトリ (コミット履歴、Issueトラッカー)
*   Go言語のメーリングリスト (golang-dev)
*   Stack Overflow や技術ブログ (Goの環境変数、クロスコンパイルに関する一般的な情報)
*   [https://github.com/golang/go/issues/6201](https://github.com/golang/go/issues/6201) (Go Issue 6201)
*   [https://golang.org/cl/13139044](https://golang.org/cl/13139044) (Gerrit Code Review for this change)