[インデックス 18579] ファイルの概要
このコミットは、Go言語のツールチェインの一部であるcmd/pack
のテストファイルsrc/cmd/pack/pack_test.go
に対する変更です。具体的には、go env
コマンドの出力からGOCHAR
環境変数の値を解析する方法を改善し、Windows環境でのビルド問題を解決することを目的としています。
コミット
cmd/pack
: go env
の出力から引用符を探さないようにする。
少なくともWindowsは引用符を出力しない。
Windowsビルドを修正するかもしれない。
LGTM=bradfitz R=bradfitz CC=golang-codereviews https://golang.org/cl/66120046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2037756fcc6962808ce45e145b386ce70b41530c
元コミット内容
commit 2037756fcc6962808ce45e145b386ce70b41530c
Author: Rob Pike <r@golang.org>
Date: Wed Feb 19 15:33:47 2014 -0800
cmd/pack: don't look for " in output from go env
Windows at least doesn't emit one.
Maybe fix Windows build.
LGTM=bradfitz
R=bradfitz
CC=golang-codereviews
https://golang.org/cl/66120046
---
src/cmd/pack/pack_test.go | 12 ++++++++----\n 1 file changed, 8 insertions(+), 4 deletions(-)\n
diff --git a/src/cmd/pack/pack_test.go b/src/cmd/pack/pack_test.go
index a073fa4521..cab236fa88 100644
--- a/src/cmd/pack/pack_test.go
+++ b/src/cmd/pack/pack_test.go
@@ -12,7 +12,7 @@ import (
"os"
"os/exec"
"path/filepath"
- "strings"
+ "regexp"
"testing"
"time"
"unicode/utf8"
@@ -193,11 +193,15 @@ func TestHello(t *testing.T) {
}
out := run("go", "env")
- i := strings.Index(out, "GOCHAR=\"")
- if i < 0 {
+ re, err := regexp.Compile(`\s*GOCHAR="?(\w)"?`)
+ if err != nil {
+ t.Fatal(err)
+ }
+ fields := re.FindStringSubmatch(out)
+ if fields == nil {
t.Fatal("cannot find GOCHAR in 'go env' output:\n", out)
}
- char := out[i+8 : i+9]
+ char := fields[1]
run("go", "build", "cmd/pack") // writes pack binary to dir
run("go", "tool", char+"g", "hello.a", "hello."+char)
変更の背景
このコミットの背景には、Go言語のビルドシステムが異なるオペレーティングシステム(特にWindows)で一貫して動作しないという問題がありました。go env
コマンドはGoの環境変数を表示するために使用されますが、その出力形式はOSによって微妙に異なる場合があります。
具体的には、GOCHAR
という環境変数の値を取得する際に、従来のコードでは値が二重引用符("
)で囲まれていることを前提としていました。しかし、Windows環境ではgo env
の出力においてGOCHAR
の値が引用符で囲まれていないことが判明しました。この差異が原因で、src/cmd/pack/pack_test.go
内のテストがWindowsで失敗し、ビルドプロセスに影響を与えていました。
このコミットは、go env
の出力形式の差異を吸収し、より堅牢な方法でGOCHAR
の値を抽出することで、Windows環境でのビルドの安定性を向上させることを目的としています。
前提知識の解説
go env
コマンド:go env
はGo言語の環境変数を表示するコマンドです。Goのツールチェインがどのように設定されているか、どのバージョンのGoが使用されているか、ビルドに関するパスなどが確認できます。例えば、GOOS
(オペレーティングシステム)、GOARCH
(アーキテクチャ)、GOPATH
(Goのワークスペース)、そしてこのコミットで問題となっているGOCHAR
などが含まれます。GOCHAR
環境変数:GOCHAR
はGoのビルドシステム内部で使用される環境変数で、ターゲットアーキテクチャを示す単一の文字です。例えば、6
はamd64、8
は386、5
はarmなどを示します。これは、go tool
コマンドが特定のアーキテクチャ向けのツール(例:6g
はamd64向けのGoコンパイラ)を呼び出す際に利用されます。src/cmd/pack
:pack
コマンドは、Goのアーカイブファイル(.a
ファイル)を操作するためのツールです。Goのビルドプロセスにおいて、コンパイルされたオブジェクトファイルをまとめるために使用されます。strings.Index
: Go言語の標準ライブラリstrings
パッケージに含まれる関数で、文字列内で指定された部分文字列が最初に出現するインデックスを返します。見つからない場合は-1を返します。regexp
パッケージ: Go言語の標準ライブラリregexp
パッケージは、正規表現を扱うための機能を提供します。正規表現は、文字列のパターンマッチングや検索、置換を行うための強力なツールです。regexp.Compile
: 正規表現の文字列をコンパイルして*regexp.Regexp
型のオブジェクトを生成します。これにより、正規表現のパターンマッチングを効率的に実行できます。regexp.FindStringSubmatch
: コンパイルされた正規表現オブジェクトのメソッドで、入力文字列に対してパターンマッチングを行い、マッチした部分文字列と、キャプチャグループ(正規表現内の括弧で囲まれた部分)にマッチした部分文字列のリストを返します。マッチしない場合はnil
を返します。
技術的詳細
このコミットの核心は、go env
コマンドの出力からGOCHAR
の値を抽出するロジックを、strings.Index
を用いた単純な文字列検索から、regexp
パッケージを用いた正規表現マッチングへと変更した点にあります。
従来のコードでは、out := run("go", "env")
で取得したgo env
の出力文字列に対して、strings.Index(out, "GOCHAR=\"")
を使用して"GOCHAR=\""
という部分文字列の開始位置を探していました。このアプローチは、GOCHAR="x"
のように値が引用符で囲まれている場合にのみ正しく機能します。しかし、Windows環境ではGOCHAR=x
のように引用符なしで出力されることがあったため、strings.Index
は-1
を返し、GOCHAR
が見つからないというエラーが発生していました。
新しいアプローチでは、regexp.Compile
を用いて\s*GOCHAR="?(\w)"?
という正規表現をコンパイルしています。この正規表現は以下のように解釈されます。
\s*
: 0個以上の空白文字にマッチします。GOCHAR=
: リテラル文字列GOCHAR=
にマッチします。"?
: 0個または1個の二重引用符にマッチします。これにより、引用符があってもなくても対応できます。(\w)
: 1個の単語文字(英数字またはアンダースコア)にマッチし、これをキャプチャグループとしてfields
スライスに格納します。これがGOCHAR
の実際の値になります。"?
: 再び0個または1個の二重引用符にマッチします。
re.FindStringSubmatch(out)
は、この正規表現がgo env
の出力文字列out
内で最初に見つかったマッチを検索します。マッチが見つかると、fields
スライスにはマッチ全体(fields[0]
)と、キャプチャグループにマッチした部分(fields[1]
)が格納されます。この場合、fields[1]
がGOCHAR
の実際の値(例: 6
や8
)となります。
この変更により、go env
の出力がGOCHAR="x"
であってもGOCHAR=x
であっても、GOCHAR
の値を正しく抽出できるようになり、Windows環境でのテストの失敗が解消されました。正規表現を使用することで、将来的に出力形式がわずかに変更された場合でも、より柔軟に対応できる堅牢なコードになっています。
コアとなるコードの変更箇所
変更はsrc/cmd/pack/pack_test.go
ファイル内で行われています。
--- a/src/cmd/pack/pack_test.go
+++ b/src/cmd/pack/pack_test.go
@@ -12,7 +12,7 @@ import (
"os"
"os/exec"
"path/filepath"
- "strings"
+ "regexp" // stringsパッケージからregexpパッケージへの変更
"testing"
"time"
"unicode/utf8"
@@ -193,11 +193,15 @@ func TestHello(t *testing.T) {
}
out := run("go", "env")
- i := strings.Index(out, "GOCHAR=\"") // 従来の文字列検索
- if i < 0 {
+ re, err := regexp.Compile(`\s*GOCHAR="?(\w)"?`) // 正規表現のコンパイル
+ if err != nil {
+ t.Fatal(err) // エラーハンドリングの追加
+ }
+ fields := re.FindStringSubmatch(out) // 正規表現によるマッチング
+ if fields == nil {
t.Fatal("cannot find GOCHAR in 'go env' output:\n", out)
}
- char := out[i+8 : i+9] // 従来のGOCHAR値の抽出
+ char := fields[1] // 正規表現のキャプチャグループからGOCHAR値の抽出
run("go", "build", "cmd/pack") // writes pack binary to dir
run("go", "tool", char+"g", "hello.a", "hello."+char)
コアとなるコードの解説
変更されたコードブロックは、TestHello
関数内にあります。
-
インポートの変更:
"strings"
パッケージのインポートが削除され、代わりに"regexp"
パッケージがインポートされています。これは、文字列操作から正規表現ベースのパターンマッチングへの移行を示しています。 -
go env
出力の解析ロジックの変更:-
旧コード:
i := strings.Index(out, "GOCHAR=\"") if i < 0 { t.Fatal("cannot find GOCHAR in 'go env' output:\n", out) } char := out[i+8 : i+9]
このコードは、
go env
の出力文字列out
の中から"GOCHAR=\""
という部分文字列を探し、その開始インデックスi
を取得していました。もし見つからなければエラーを報告し、見つかった場合はi+8
からi+9
までの1文字をGOCHAR
の値として抽出していました。この+8
というオフセットは、"GOCHAR=\""
という文字列の長さ(8文字)に依存しており、非常に脆弱な実装でした。 -
新コード:
re, err := regexp.Compile(`\s*GOCHAR="?(\w)"?`) if err != nil { t.Fatal(err) } fields := re.FindStringSubmatch(out) if fields == nil { t.Fatal("cannot find GOCHAR in 'go env' output:\n", out) } char := fields[1]
この新しいコードでは、まず
regexp.Compile
を使って正規表現パターン\s*GOCHAR="?(\w)"?
をコンパイルしています。この正規表現は、行頭の空白(\s*
)、リテラル文字列GOCHAR=
、オプションの引用符("?
)、そしてキャプチャグループ(\w)
(GOCHAR
の実際の値)とオプションの引用符("?
)にマッチします。 コンパイルに失敗した場合はt.Fatal(err)
でテストを終了します。 次に、re.FindStringSubmatch(out)
を呼び出して、go env
の出力から正規表現にマッチする部分を探します。FindStringSubmatch
は、マッチした文字列全体と、正規表現内のキャプチャグループにマッチした部分文字列のリストを返します。fields
がnil
の場合(マッチが見つからなかった場合)は、GOCHAR
が見つからないというエラーを報告します。 マッチが見つかった場合、fields[1]
にはキャプチャグループ(\w)
にマッチした文字、つまりGOCHAR
の実際の値が格納されており、これがchar
変数に代入されます。
-
この変更により、go env
の出力形式が引用符の有無に関わらず、GOCHAR
の値を正確かつ堅牢に抽出できるようになりました。
関連リンク
- Go言語の
regexp
パッケージのドキュメント: https://pkg.go.dev/regexp - Go言語の
strings
パッケージのドキュメント: https://pkg.go.dev/strings - Go言語の環境変数に関する公式ドキュメント(
go env
について言及されている可能性のある箇所): https://go.dev/doc/go1.1 (Go 1.1のリリースノートは、このコミットが作成された時期に近い情報源として関連する可能性があります。)
参考にした情報源リンク
- Go言語の公式リポジトリ: https://github.com/golang/go
- Go言語のコードレビューシステム(Gerrit)の変更リスト: https://golang.org/cl/66120046 (コミットメッセージに記載されているCLリンク)
- Go言語の
go env
コマンドに関する一般的な情報源(例:go help env
の出力や、Goの環境変数に関するブログ記事など) - 正規表現の基本的な概念に関する情報源 (例: MDN Web Docsの正規表現ガイドなど)
- Go言語のテストに関する情報源 (例:
testing
パッケージのドキュメントなど) - WindowsにおけるGoのビルドに関する一般的な情報源 (例: Goの公式ドキュメントのWindowsビルドに関するセクションなど)
GOCHAR
環境変数に関する情報源 (Goのビルドシステム内部のドキュメントや議論など)