[インデックス 15782] ファイルの概要
このコミットは、Go言語のビルドシステムにおけるGOPATH
のパス解釈に関する挙動を修正するものです。具体的には、GOPATH
に設定されたパス文字列中にチルダ(~
)が含まれる場合の処理を緩和し、パスの先頭にチルダがある場合のみを不正なケースとして扱うように変更しています。これにより、パスの途中にチルダが含まれる正当なGOPATH
設定が誤って拒否される問題を解決します。
コミット
commit 392d5feb8b359dce5120e4b9ee3743433c1cca6b
Author: Jonathan Nieder <jrn@google.com>
Date: Thu Mar 14 23:59:49 2013 -0400
go/build: allow ~ in middle of path, just not at beginning
CL 7799045 relaxed the restriction in cmd/go on ~ in GOPATH
to allow paths with ~ in the middle while continuing to
protect against the common mistake of using GOPATH='~/home'
instead of GOPATH=~/home. Unfortunately go/build still
filters these paths out:
$ GOPATH=/tmp/test~ing go build
test.go:22:2: cannot find package "test" in any of:
/usr/lib/go/test (from $GOROOT)
($GOPATH not set)
So relax the requirement in go/build, too.
R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7826043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/392d5feb8b359dce5120e4b9ee3743433c1cca6b
元コミット内容
このコミットは、Goのビルドシステム(go/build
パッケージ)がGOPATH
環境変数に設定されたパスを処理する方法に関するものです。以前の変更(CL 7799045
)では、cmd/go
コマンドにおいてGOPATH
内のチルダ(~
)に関する制限が緩和されました。この緩和は、パスの途中にチルダが含まれることを許可しつつも、GOPATH='~/home'
のようにチルダが展開されずにパスの先頭に来てしまうというよくある間違いを防ぐことを目的としていました。
しかし、cmd/go
側での緩和にもかかわらず、go/build
パッケージは依然としてこれらのパスをフィルタリングしてしまっていました。その結果、例えばGOPATH=/tmp/test~ing
のような有効なパスを設定しても、go build
コマンドがパッケージを見つけられないという問題が発生していました。これは、go/build
がGOPATH
が設定されていないと誤認するためです。
このコミットは、この不整合を解消し、go/build
パッケージにおいてもcmd/go
と同様に、パスの途中にチルダが含まれることを許可するように要件を緩和することを目的としています。
変更の背景
Go言語のビルドシステムでは、GOPATH
という環境変数が非常に重要な役割を果たします。これはGoのソースコード、パッケージ、実行可能ファイルが配置されるワークスペースのルートディレクトリを指定するものです。ユーザーがGOPATH
を設定する際、特にUnix系のシステムではホームディレクトリを表すチルダ(~
)を使用することがあります。
しかし、シェルがチルダを展開する前に引用符で囲んでしまうなど、誤ったGOPATH
の設定方法が原因で、~
が$HOME
に展開されずにリテラルなチルダとしてパスに含まれてしまうという一般的な間違いがありました。例えば、GOPATH='~/go'
と設定すると、シェルは~
を展開せず、GOPATH
の値はリテラルな~/go
となります。Goのツールは通常、このようなパスを正しく解釈できません。
この問題に対処するため、以前のCL 7799045
ではcmd/go
コマンドが、パスの先頭にチルダがあるGOPATH
を不正なものとして扱うことで、この一般的な間違いからユーザーを保護するようになりました。しかし、この変更はcmd/go
に限定されており、Goのビルドロジックの中核を担うgo/build
パッケージには適用されていませんでした。
その結果、GOPATH=/tmp/test~ing
のように、パスの途中にチルダが含まれるが、先頭にはチルダがないという正当なパスが、go/build
パッケージによって依然として不正なパスとしてフィルタリングされてしまうという新たな問題が発生しました。これは、go/build
がパス全体にチルダが含まれているかどうかをチェックしていたためです。このコミットは、この不整合を解消し、go/build
がcmd/go
と同じロジックでGOPATH
を検証するようにすることで、より柔軟で直感的なGOPATH
の利用を可能にすることを背景としています。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびUnix系のシステムに関する基本的な知識が必要です。
-
GOPATH:
- Go言語のワークスペースのルートディレクトリを指定する環境変数です。Goのソースコード、コンパイルされたパッケージ、実行可能ファイルがこのパス以下に配置されます。
- 複数のパスをコロン(Unix系)またはセミコロン(Windows)で区切って設定できます。例:
export GOPATH=$HOME/go:/usr/local/go_projects
- Goのツール(
go build
,go install
,go get
など)は、このGOPATH
に基づいてパッケージの探索やビルドを行います。
-
チルダ(
~
)のパス解釈:- Unix系オペレーティングシステム(Linux, macOSなど)において、チルダ(
~
)は通常、現在のユーザーのホームディレクトリのショートカットとして機能します。 - 例えば、
/home/user/
がホームディレクトリの場合、~/documents
は/home/user/documents
と解釈されます。 - シェルの展開: シェル(bash, zshなど)はコマンドを実行する前に、チルダをホームディレクトリのパスに展開します。
- 引用符の影響: チルダがシングルクォート(
'
)やダブルクォート("
)で囲まれている場合、シェルはチルダを展開しません。例えば、echo '~/foo'
は~/foo
と出力されますが、echo ~/foo
は/home/user/foo
と出力されます。 GOPATH='~/home'
のような設定は、ユーザーがチルダの展開を意図しているにもかかわらず、引用符によって展開が阻害され、リテラルな~
がパスの先頭に残ってしまうという一般的な間違いです。Goのツールはこのようなパスを正しく扱えないため、問題が発生します。
- Unix系オペレーティングシステム(Linux, macOSなど)において、チルダ(
-
go/build
パッケージ:- Goの標準ライブラリの一部であり、Goのソースコードのビルドプロセスを管理するための低レベルな機能を提供します。
- Goのパッケージの解決、依存関係の解析、ビルドタグの処理など、Goのビルドシステムの中核を担うロジックが含まれています。
cmd/go
コマンドは、内部的にこのgo/build
パッケージの機能を利用してビルドを実行します。
-
strings.Contains
とstrings.HasPrefix
:- Go言語の標準ライブラリ
strings
パッケージに含まれる関数です。 strings.Contains(s, substr)
: 文字列s
が部分文字列substr
を含んでいる場合にtrue
を返します。strings.HasPrefix(s, prefix)
: 文字列s
が接頭辞prefix
で始まっている場合にtrue
を返します。- このコミットでは、パス文字列の検証ロジックが
strings.Contains
からstrings.HasPrefix
に変更されており、これが挙動の核心となります。
- Go言語の標準ライブラリ
技術的詳細
このコミットの技術的な核心は、src/pkg/go/build/build.go
ファイル内のContext.gopath()
メソッドにおけるGOPATH
のパスセグメント検証ロジックの変更です。
変更前は、以下の条件でGOPATH
のパスセグメントがフィルタリングされていました(Windows以外の場合):
if strings.Contains(p, "~") && runtime.GOOS != "windows" {
// Path segments containing ~ on Unix are almost always
// users who have incorrectly quoted ~ while setting GOPATH,
// preventing it from expanding to $HOME.
// The situation is made more confusing by the fact that
// ...
continue
}
このコードは、GOPATH
の各パスセグメントp
がチルダ(~
)を含んでいる場合に、そのパスセグメントをスキップ(continue
)していました。コメントにもあるように、これは「Unix上でチルダを含むパスセグメントは、ほとんどの場合、GOPATHを設定する際にチルダを誤って引用符で囲んでしまい、$HOMEに展開されなかったユーザーによるもの」という仮定に基づいています。この仮定は、~/home
のようなパスの先頭にチルダがある場合に有効ですが、test~ing
のようにパスの途中にチルダがある正当なケースも誤って除外してしまっていました。
このコミットによる変更は、この条件を以下のように修正します。
if strings.HasPrefix(p, "~") {
// Path segments starting with ~ on Unix are almost always
// users who have incorrectly quoted ~ while setting GOPATH,
// preventing it from expanding to $HOME.
// The situation is made more confusing by the fact that
// ...
continue
}
変更後のコードでは、strings.Contains(p, "~")
がstrings.HasPrefix(p, "~")
に置き換えられています。これにより、GOPATH
のパスセグメントp
がチルダ(~
)で始まっている場合にのみ、そのパスセグメントがスキップされるようになります。
この変更の技術的な意味合いは以下の通りです。
- 誤った
GOPATH
設定のより正確な検出:GOPATH='~/home'
のように、ユーザーが意図せずチルダを展開しなかったケースは、パスが~
で始まるため、引き続きこのフィルタリングによって検出され、無視されます。これにより、Goツールが不正なパスで動作しようとするのを防ぎ、ユーザーに正しい設定を促すことができます。 - 正当なパスの許可:
GOPATH=/tmp/test~ing
のように、パスの途中にチルダが含まれるが、パスの先頭にはチルダがないケースは、strings.HasPrefix(p, "~")
の条件に合致しないため、フィルタリングされなくなります。これにより、このような正当なパスがGOPATH
として認識され、Goツールが正しく動作するようになります。 cmd/go
との整合性: この変更により、go/build
パッケージのGOPATH
検証ロジックが、以前のCL 7799045
でcmd/go
に導入されたロジックと整合性が取れるようになります。これにより、Goツールチェーン全体でGOPATH
の解釈が一貫したものになります。
この修正は、GoのビルドシステムがGOPATH
をより柔軟に、かつユーザーの意図をより正確に反映して解釈できるようにするための重要な改善です。
コアとなるコードの変更箇所
diff --git a/src/pkg/go/build/build.go b/src/pkg/go/build/build.go
index d0e420f433..d5f181d7c1 100644
--- a/src/pkg/go/build/build.go
+++ b/src/pkg/go/build/build.go
@@ -212,8 +212,8 @@ func (ctxt *Context) gopath() []string {
// Do not get confused by this common mistake.
continue
}
- if strings.Contains(p, "~") && runtime.GOOS != "windows" {
- // Path segments containing ~ on Unix are almost always
+ if strings.HasPrefix(p, "~") {
+ // Path segments starting with ~ on Unix are almost always
// users who have incorrectly quoted ~ while setting GOPATH,
// preventing it from expanding to $HOME.
// The situation is made more confusing by the fact that
コアとなるコードの解説
変更はsrc/pkg/go/build/build.go
ファイルのgopath()
メソッド内で行われています。このメソッドは、GOPATH
環境変数から取得したパス文字列を解析し、有効なパスのリストを返す役割を担っています。
変更前のコードでは、以下の行がありました。
if strings.Contains(p, "~") && runtime.GOOS != "windows" {
この行は、GOPATH
の各パスセグメントp
に対して、以下の2つの条件をチェックしていました。
strings.Contains(p, "~")
: パスセグメントp
がチルダ(~
)を含んでいるか。runtime.GOOS != "windows"
: 現在のオペレーティングシステムがWindowsではないか(チルダのパス解釈はUnix系システム特有のため)。
この両方の条件が真の場合、そのパスセグメントは不正なものと見なされ、continue
ステートメントによって処理がスキップされていました。これにより、GOPATH=/tmp/test~ing
のような、パスの途中にチルダが含まれる正当なパスも誤って除外されていました。
変更後のコードでは、この行が以下のように修正されました。
if strings.HasPrefix(p, "~") {
この修正により、チェックされる条件がstrings.HasPrefix(p, "~")
のみになりました。これは、パスセグメントp
がチルダ(~
)で始まっているか、という条件を意味します。runtime.GOOS != "windows"
の条件は、strings.HasPrefix
のチェックがUnix系システムでのチルダの挙動に特化しているため、暗黙的に含まれる形になっています(Windowsではパスの先頭にチルダがあっても特別な意味を持たないため、このチェックは実質的にUnix系システムでのみ意味を持ちます)。
この変更のポイントは、strings.Contains
からstrings.HasPrefix
への変更です。
strings.Contains
は、パス文字列のどこかにチルダがあれば真を返します。strings.HasPrefix
は、パス文字列がチルダで始まっていれば真を返します。
この違いにより、GOPATH
のパスセグメントが「チルダで始まる」場合にのみ不正と判断されるようになり、「パスの途中にチルダが含まれる」正当なケースは許可されるようになりました。これにより、Goのビルドシステムは、ユーザーが意図せずチルダを展開しなかった一般的な間違い(例: GOPATH='~/go'
)から保護しつつ、パスの途中にチルダを含む有効なパス(例: GOPATH=/path/to/my~project
)を正しく処理できるようになります。
関連リンク
- Go Gerrit Change: https://golang.org/cl/7826043
参考にした情報源リンク
- 特になし(コミットメッセージと差分から十分に情報を得られました)