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

[インデックス 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/buildGOPATHが設定されていないと誤認するためです。

このコミットは、この不整合を解消し、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/buildcmd/goと同じロジックでGOPATHを検証するようにすることで、より柔軟で直感的なGOPATHの利用を可能にすることを背景としています。

前提知識の解説

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

  1. GOPATH:

    • Go言語のワークスペースのルートディレクトリを指定する環境変数です。Goのソースコード、コンパイルされたパッケージ、実行可能ファイルがこのパス以下に配置されます。
    • 複数のパスをコロン(Unix系)またはセミコロン(Windows)で区切って設定できます。例: export GOPATH=$HOME/go:/usr/local/go_projects
    • Goのツール(go build, go install, go getなど)は、このGOPATHに基づいてパッケージの探索やビルドを行います。
  2. チルダ(~)のパス解釈:

    • Unix系オペレーティングシステム(Linux, macOSなど)において、チルダ(~)は通常、現在のユーザーのホームディレクトリのショートカットとして機能します。
    • 例えば、/home/user/がホームディレクトリの場合、~/documents/home/user/documentsと解釈されます。
    • シェルの展開: シェル(bash, zshなど)はコマンドを実行する前に、チルダをホームディレクトリのパスに展開します。
    • 引用符の影響: チルダがシングルクォート(')やダブルクォート(")で囲まれている場合、シェルはチルダを展開しません。例えば、echo '~/foo'~/fooと出力されますが、echo ~/foo/home/user/fooと出力されます。
    • GOPATH='~/home'のような設定は、ユーザーがチルダの展開を意図しているにもかかわらず、引用符によって展開が阻害され、リテラルな~がパスの先頭に残ってしまうという一般的な間違いです。Goのツールはこのようなパスを正しく扱えないため、問題が発生します。
  3. go/buildパッケージ:

    • Goの標準ライブラリの一部であり、Goのソースコードのビルドプロセスを管理するための低レベルな機能を提供します。
    • Goのパッケージの解決、依存関係の解析、ビルドタグの処理など、Goのビルドシステムの中核を担うロジックが含まれています。
    • cmd/goコマンドは、内部的にこのgo/buildパッケージの機能を利用してビルドを実行します。
  4. strings.Containsstrings.HasPrefix:

    • Go言語の標準ライブラリstringsパッケージに含まれる関数です。
    • strings.Contains(s, substr): 文字列sが部分文字列substrを含んでいる場合にtrueを返します。
    • strings.HasPrefix(s, prefix): 文字列sが接頭辞prefixで始まっている場合にtrueを返します。
    • このコミットでは、パス文字列の検証ロジックがstrings.Containsからstrings.HasPrefixに変更されており、これが挙動の核心となります。

技術的詳細

このコミットの技術的な核心は、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がチルダ(~)で始まっている場合にのみ、そのパスセグメントがスキップされるようになります。

この変更の技術的な意味合いは以下の通りです。

  1. 誤ったGOPATH設定のより正確な検出: GOPATH='~/home'のように、ユーザーが意図せずチルダを展開しなかったケースは、パスが~で始まるため、引き続きこのフィルタリングによって検出され、無視されます。これにより、Goツールが不正なパスで動作しようとするのを防ぎ、ユーザーに正しい設定を促すことができます。
  2. 正当なパスの許可: GOPATH=/tmp/test~ingのように、パスの途中にチルダが含まれるが、パスの先頭にはチルダがないケースは、strings.HasPrefix(p, "~")の条件に合致しないため、フィルタリングされなくなります。これにより、このような正当なパスがGOPATHとして認識され、Goツールが正しく動作するようになります。
  3. cmd/goとの整合性: この変更により、go/buildパッケージのGOPATH検証ロジックが、以前のCL 7799045cmd/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つの条件をチェックしていました。

  1. strings.Contains(p, "~"): パスセグメントpがチルダ(~)を含んでいるか。
  2. 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)を正しく処理できるようになります。

関連リンク

参考にした情報源リンク

  • 特になし(コミットメッセージと差分から十分に情報を得られました)