[インデックス 19446] ファイルの概要
src/cmd/go/vcs.go
は、Go言語のビルドツールである go
コマンドの一部であり、Goパッケージのインポートパスからバージョン管理システム (VCS) のリポジトリルートを特定するロジックを扱っています。これは、go get
コマンドがリモートリポジトリからソースコードを取得し、ローカル環境にインストールする際に不可欠な役割を担っています。具体的には、インポートパスのパターンに基づいて、Git、Mercurial、SubversionなどのどのVCSを使用すべきかを判断し、対応するリポジトリのURLを構築する機能を提供します。
コミット
Rob Pikeによる2014年5月27日のコミットで、go get
コマンドが http://
または https://
を含むインポートパスに対して、より分かりやすいエラーメッセージを出すように改善されました。
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/7f638e90231e56c523ae51ba18dbbf084db8b86e
元コミット内容
cmd/go: improve error message when import path contains http://
Common mistake (at least for me) because hg etc. require the prefix
while the go command forbids it.
Before:
% go get http://code.google.com/p/go.text/unicode/norm
package http:/code.google.com/p/go.text/unicode/norm: unrecognized import path "http:/code.google.com/p/go.text/unicode/norm"
After:
% go get http://code.google.com/p/go.text/unicode/norm
package http:/code.google.com/p/go.text/unicode/norm: "http://" not allowed in import path
LGTM=ruiu, rsc
R=rsc, ruiu
CC=golang-codereviews
https://golang.org/cl/97630046
変更の背景
Go言語の go get
コマンドは、パッケージのインポートパスを直接リポジトリのURLとして解釈するのではなく、特定のルールに基づいてリポジトリを特定し、ソースコードを取得します。例えば、code.google.com/p/go.text/unicode/norm
のようなパスが与えられると、Goツールは自動的に適切なプロトコル(Git、Mercurial、Subversionなど)とリポジトリの場所を推測してソースコードを取得します。Goのインポートパスには、通常 http://
や https://
といったプロトコルスキームを含めないのが慣習です。
しかし、Mercurial (hg
) や Git のような一部のバージョン管理システムでは、リポジトリをクローンする際に http://
や https://
といったスキームを明示的に指定するのが一般的です。このため、Go開発者がこれらのVCSの習慣に慣れていると、go get
コマンドを使用する際にも誤って http://
や https://
をインポートパスに含めてしまうという共通の誤りがありました。
このコミット以前は、http://
が含まれたインポートパスに対して go get
を実行すると、「unrecognized import path」という一般的なエラーメッセージが表示されていました。このメッセージは、なぜインポートパスが認識されないのかを具体的に示しておらず、ユーザーが問題の原因を特定するのを困難にしていました。
この変更の目的は、この一般的な誤用に対して、より具体的で分かりやすいエラーメッセージを提供することです。これにより、ユーザーはインポートパスにスキームを含めるべきではないというGoの慣習をすぐに理解し、問題を解決できるようになります。
前提知識の解説
go get
コマンド: Go言語のパッケージ管理ツールの一部であり、リモートリポジトリからGoパッケージのソースコードを取得し、ローカルのGOPATH
(Go 1.11以降はGo Modulesのキャッシュ) にインストールするために使用されます。go get
は、インポートパスに基づいて自動的に適切なバージョン管理システム(Git, Mercurial, Subversionなど)を検出し、リポジトリをクローンします。- Goのインポートパス: Go言語では、パッケージはファイルシステムのパスと対応するインポートパスによって識別されます。例えば、
"github.com/user/repo/package"
のような形式です。Goツールは、このインポートパスからリポジトリのホスト(github.com
)、ユーザー名(user
)、リポジトリ名(repo
)、およびパッケージのサブパス(package
)を解析し、ソースコードを取得します。重要なのは、Goのインポートパスには通常、http://
やhttps://
といったプロトコルスキームを含めないという慣習がある点です。 - バージョン管理システム (VCS) のURL: GitやMercurialなどのVCSでは、リポジトリのクローンやフェッチを行う際に、
https://github.com/user/repo.git
のようにプロトコルスキームを含む完全なURLを指定するのが一般的です。このVCSの習慣とGoのインポートパスの慣習との間にギャップがあり、今回のエラーメッセージ改善の背景となりました。
技術的詳細
このコミットは、src/cmd/go/vcs.go
ファイル内の repoRootForImportPathStatic
関数に修正を加えています。この関数は、与えられたインポートパスからリポジトリのルートを静的に決定しようとします。
変更の核心は、インポートパスが http://
または https://
で始まるかどうかを検出するための正規表現の導入と、それに続くエラーメッセージの生成ロジックの変更です。
-
正規表現の導入:
var httpPrefixRE = regexp.MustCompile(`^https?:`)
httpPrefixRE
という新しいグローバル変数が導入され、regexp.MustCompile
を使用して正規表現^https?:
がコンパイルされます。^
: 文字列の先頭にマッチします。http
: リテラル文字列 "http" にマッチします。s?
: "s" が0回または1回出現することにマッチします。これにより "http" と "https" の両方に対応します。:
: リテラル文字 ":" にマッチします。 この正規表現は、インポートパスがhttp:
またはhttps:
で始まるかどうかを効率的にチェックするために使用されます。
-
エラー検出ロジックの変更: 以前は
strings.Contains(importPath, "://")
を使用して://
が含まれているかをチェックし、一般的なinvalid import path
エラーを返していました。 新しいロジックでは、httpPrefixRE.FindStringIndex(importPath)
を使用して、インポートパスの先頭にhttp:
またはhttps:
が存在するかどうかを検出します。FindStringIndex
メソッドは、正規表現にマッチする最初の部分文字列の開始インデックスと終了インデックスを含む2要素の[]int
スライスを返します。マッチしない場合はnil
を返します。
-
エラーメッセージの生成:
loc := httpPrefixRE.FindStringIndex(importPath); loc != nil
の条件が真(つまり、http:
またはhttps:
が検出された)の場合、新しいエラーメッセージが生成されます。return nil, fmt.Errorf("%q not allowed in import path", importPath[loc[0]:loc[1]]+"//")
importPath[loc[0]:loc[1]]
は、マッチしたhttp:
またはhttps:
の部分文字列を抽出します。- これに
//
を連結することで、ユーザーが入力したであろうhttp://
またはhttps://
の形式を再現します。 fmt.Errorf("%q not allowed in import path", ...)
は、この再現されたスキームを含む文字列を引用符で囲み、「"http://" not allowed in import path」のような、より具体的で分かりやすいエラーメッセージを生成します。
この変更により、go get
はインポートパスの誤用をより正確に診断し、ユーザーに直接的なフィードバックを提供できるようになりました。
コアとなるコードの変更箇所
--- a/src/cmd/go/vcs.go
+++ b/src/cmd/go/vcs.go
@@ -354,6 +354,8 @@ type repoRoot struct {
root string
}
+var httpPrefixRE = regexp.MustCompile(`^https?:`)
+
// repoRootForImportPath analyzes importPath to determine the
// version control system, and code repository to use.
func repoRootForImportPath(importPath string) (*repoRoot, error) {
@@ -390,8 +392,12 @@ var errUnknownSite = errors.New("dynamic lookup required to find mapping")
//
// If scheme is non-empty, that scheme is forced.
func repoRootForImportPathStatic(importPath, scheme string) (*repoRoot, error) {
- if strings.Contains(importPath, "://") {
- return nil, fmt.Errorf("invalid import path %q", importPath)
+ // A common error is to use https://packagepath because that's what
+ // hg and git require. Diagnose this helpfully.
+ if loc := httpPrefixRE.FindStringIndex(importPath); loc != nil {
+ // The importPath has been cleaned, so has only one slash. The pattern
+ // ignores the slashes; the error message puts them back on the RHS at least.
+ return nil, fmt.Errorf("%q not allowed in import path", importPath[loc[0]:loc[1]]+"//")
}
for _, srv := range vcsPaths {
\tif !strings.HasPrefix(importPath, srv.prefix) {
コアとなるコードの解説
-
var httpPrefixRE = regexp.MustCompile(
^https?:)
: この行は、http:
またはhttps:
で始まる文字列を検出するための正規表現をコンパイルし、httpPrefixRE
という変数に格納しています。regexp.MustCompile
は、正規表現のコンパイルに失敗した場合にパニックを発生させるため、プログラムの初期化時など、正規表現が常に有効であることが保証される場所で使用されます。 -
if loc := httpPrefixRE.FindStringIndex(importPath); loc != nil { ... }
:repoRootForImportPathStatic
関数内で、この条件文が追加されました。httpPrefixRE.FindStringIndex(importPath)
は、importPath
の先頭がhttp:
またはhttps:
にマッチするかどうかをチェックします。- もしマッチした場合、
loc
にはマッチした部分文字列の開始インデックスと終了インデックスが格納されたスライスが代入され、条件は真となります。 - マッチしなかった場合、
loc
はnil
となり、条件は偽となります。
- もしマッチした場合、
-
return nil, fmt.Errorf("%q not allowed in import path", importPath[loc[0]:loc[1]]+"//")
:http:
またはhttps:
が検出された場合、この行が実行されます。importPath[loc[0]:loc[1]]
は、インポートパスから正規表現がマッチした部分(例: "http:" または "https:")を抽出します。- これに
+"//"
を連結することで、ユーザーが誤って入力したであろうhttp://
またはhttps://
の形式を再構築します。 fmt.Errorf
を使用して、この再構築された文字列を引用符で囲み、「"http://" not allowed in import path」のような、より具体的でユーザーフレンドリーなエラーメッセージを生成して返します。これにより、ユーザーはインポートパスにプロトコルスキームを含めるべきではないという明確な指示を受け取ることができます。
関連リンク
- Go CL 97630046: https://golang.org/cl/97630046
go get
command documentation (Go 1.18): https://pkg.go.dev/cmd/go#hdr-Download_and_install_packages_and_dependencies
参考にした情報源リンク
- Go言語の公式ドキュメント (特に
go get
コマンドとパッケージのインポートパスに関するセクション) - Go言語の正規表現パッケージ
regexp
のドキュメント