[インデックス 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 api
が cmd/api
バイナリを見つけられなくなり、ビルド全体が失敗するという問題が発生していました。
このコミットは、cmd/api
のビルド時のみ GOARCH
環境変数を無視することで、この問題を回避し、all.bash
の実行をより堅牢にすることを目的としています。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびシステムに関する前提知識が必要です。
-
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のビルド、テスト、実行の挙動に大きな影響を与えます。
-
cmd/api
:- Goの標準ライブラリの公開APIサーフェスを検査するためのツールです。Goのバージョンアップに伴うAPIの互換性チェックなどに利用されます。GoのソースコードからGo自体をビルドする際に、このツールもビルドされ、内部的に使用されます。
-
cmd/go
:- Go言語の主要なコマンドラインツールであり、Goプログラムのビルド、テスト、実行、パッケージ管理など、Go開発のほとんどの操作を担います。
go build
,go install
,go test
などのサブコマンドを提供します。
- Go言語の主要なコマンドラインツールであり、Goプログラムのビルド、テスト、実行、パッケージ管理など、Go開発のほとんどの操作を担います。
-
all.bash
:- Go言語の公式リポジトリに含まれるシェルスクリプトで、GoのソースコードからGoコンパイラ、標準ライブラリ、および関連ツールチェイン全体をビルドするために使用されます。Goの開発者がGoの最新バージョンをビルドしたり、変更をテストしたりする際によく利用します。
-
環境変数とプロセスの継承:
- Unix/Linuxシステムでは、子プロセスは親プロセスの環境変数を継承します。シェルで
export GOARCH=386
と設定すると、そのシェルから起動されるすべてのコマンド(all.bash
やその中で実行されるgo
コマンドなど)がこのGOARCH
の設定を継承します。
- Unix/Linuxシステムでは、子プロセスは親プロセスの環境変数を継承します。シェルで
-
os.Environ()
とexec.Command().Env
:- Go言語の
os.Environ()
関数は、現在のプロセスの環境変数を[]string
の形式(例:["KEY=VALUE", ...]
)で返します。 os/exec
パッケージのexec.Command
で外部コマンドを実行する際、cmd.Env
フィールドを設定することで、そのコマンドに渡す環境変数を明示的に制御できます。nil
の場合は親プロセスの環境変数を継承します。
- Go言語の
技術的詳細
このコミットの技術的な解決策は、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
スライス(元の環境変数リスト)をイテレートし、各環境変数文字列 s
が remove
リスト内のいずれかの文字列(例: "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)