[インデックス 10757] ファイルの概要
このコミットは、Go言語のツールチェインにおけるGoogle Codeリポジトリのインポートパスの取り扱いに関する重要な変更を導入しています。具体的には、gofix
ツールにGoogle Codeの古いインポートパス形式を新しい形式に書き換える機能を追加し、同時にgoinstall
ツールが古い形式のインポートパスを拒否するように変更しています。これにより、GoプロジェクトがGoogle Code上の依存関係をより新しい、推奨される形式で参照するよう促し、将来的な互換性の問題を回避することを目的としています。
コミット
commit 0b0a6ec7ec27f711304f86fcfd749173967b91d9
Author: Andrew Gerrand <adg@golang.org>
Date: Wed Dec 14 08:46:26 2011 +1100
gofix: add googlecode module for rewriting Google Code imports
goinstall: disallow googlecode.com import paths
R=rsc, bradfitz
CC=golang-dev
https://golang.org/cl/5421049
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/0b0a6ec7ec27f711304f86fcfd749173967b91d9
元コミット内容
このコミットは以下の2つの主要な変更を含んでいます。
gofix
へのgooglecode
モジュールの追加:gofix
はGoのコードを自動的に修正するツールです。この変更により、gofix
はfoo.googlecode.com/vcs/path
のような非推奨のGoogle Codeインポートパスをcode.google.com/p/foo/path
という新しい形式に書き換えることができるようになりました。goinstall
におけるgooglecode.com
インポートパスの禁止:goinstall
はGoパッケージをダウンロードしてインストールするツールです。この変更により、goinstall
は古い形式のGoogle Codeインポートパスを直接処理せず、エラーを返すようになりました。ユーザーにはgofix
を使用してコードを修正するよう促すメッセージが表示されます。
変更の背景
この変更の背景には、Google Codeのプロジェクトホスティングサービスにおけるインポートパスの標準化と、Go言語のエコシステムにおける依存関係管理の改善があります。
かつて、Google CodeではリポジトリのURLとして[projectname].googlecode.com/[vcs]/[path]
のような形式が使われていました。しかし、Go言語のパッケージ管理システム(当時はgoinstall
が中心)は、より統一されたcode.google.com/p/[projectname]/[path]
形式を推奨するようになりました。この新しい形式は、VCS(バージョン管理システム)の種類(svn, git, hgなど)をURLに含まず、goinstall
が自動的に適切なVCSを検出してダウンロードすることを可能にします。
このコミットは、古い形式のインポートパスを使用している既存のGoコードベースを新しい推奨形式に移行させるための支援策として導入されました。gofix
による自動修正機能を提供することで、開発者が手動で大量のインポートパスを修正する手間を省き、スムーズな移行を促進します。同時に、goinstall
で古いパスを禁止することで、新しいプロジェクトが最初から推奨される形式を使用するように誘導し、エコシステム全体の整合性を高める狙いがあります。
前提知識の解説
このコミットを理解するためには、以下のGo言語関連のツールと概念についての知識が必要です。
- Go言語のパッケージ管理: Go言語では、ソースコードはパッケージとして組織されます。パッケージはインポートパスによって識別され、通常はリポジトリのURLに対応します。
goinstall
(後のgo get
)のようなツールは、このインポートパスを使用してリモートリポジトリからソースコードをダウンロードし、ビルドします。 goinstall
: Go 1.0以前のGo言語で、リモートリポジトリからGoパッケージをダウンロードし、インストールするための主要なコマンドラインツールでした。現在のgo get
コマンドの前身にあたります。goinstall
は、インポートパスを解析し、GitHubやGoogle Codeなどのホスティングサービスから適切なVCS(Git, Mercurial, Subversionなど)を使用してコードを取得する機能を持っていました。gofix
: Go言語のコードを、新しいAPIや言語仕様の変更に合わせて自動的に修正するためのコマンドラインツールです。Go言語は後方互換性を重視していますが、大規模な変更や非推奨化が行われた際には、gofix
が開発者の移行作業を支援します。例えば、APIの名称変更やパッケージの移動などに対応するために使用されます。gofix
は、GoのAST(抽象構文木)を解析し、パターンマッチングと置換によってコードを変換します。- Google Code: かつてGoogleが提供していたオープンソースプロジェクトのホスティングサービスです。Subversion, Git, MercurialなどのVCSをサポートしていました。Go言語の初期には、多くのGoプロジェクトがGoogle Codeでホストされていました。2016年にサービスを終了しています。
- 正規表現 (Regular Expressions): テキスト内のパターンを記述するための強力なツールです。このコミットでは、インポートパスの形式を識別し、その構成要素を抽出するために正規表現が extensively に使用されています。
技術的詳細
このコミットの技術的詳細は、主にgofix
とgoinstall
の内部動作、特にインポートパスの解析と変換ロジックに焦点を当てています。
gofix
におけるGoogle Codeインポートパスの書き換え
gofix
は、Goのソースファイルを解析してAST(抽象構文木)を構築し、そのASTを走査して特定のパターンにマッチするコードを見つけ、修正を適用します。
このコミットで追加されたgooglecode.go
ファイルは、gofix
の新しい「fix」モジュールとして機能します。
-
正規表現によるパターンマッチング:
googlecodeRe
という正規表現が定義されています。^([a-z0-9\\-]+)\\.googlecode\\.com/(svn|git|hg)(/[a-z0-9A-Z_.\\-/]+)?$
この正規表現は、foo.googlecode.com/hg/bar
のような古い形式のGoogle Codeインポートパスにマッチします。([a-z0-9\\-]+)
: プロジェクト名(例:foo
,go-qux-23
,zap
)をキャプチャします。\\.googlecode\\.com/
: 固定文字列.googlecode.com/
にマッチします。(svn|git|hg)
: VCSの種類(svn
,git
,hg
のいずれか)をキャプチャします。(/[a-z0-9A-Z_.\\-/]+)?
: オプションのパス部分(例:/bar
,/some/path
)をキャプチャします。
-
ASTの走査とインポートパスの抽出:
googlecode
関数は、Goのソースファイルを表す*ast.File
を受け取ります。 ファイル内のすべてのインポート宣言(f.Imports
)をイテレートし、各インポートパスの文字列を取得します。 -
パスの変換: 取得したインポートパスが
googlecodeRe
にマッチした場合、正規表現のキャプチャグループを利用して新しい形式のパスを構築します。new := "code.google.com/p/" + m[1] + m[3]
ここで、m[1]
はプロジェクト名、m[3]
は元のパス部分(もしあれば)です。VCSの種類(m[2]
)は新しいパスには含まれません。 -
コードの書き換え:
rewriteImport
ヘルパー関数(このコミットでは変更されていないが、gofix
の既存機能)を使用して、元のインポートパスを新しいパスに置き換えます。この関数は、ソースコードの文字列を直接操作するのではなく、ASTを修正し、その後ASTから新しいソースコードを生成します。
goinstall
における古いインポートパスの禁止
goinstall
は、インポートパスに基づいてリモートリポジトリを識別し、ダウンロードするためのロジックを持っています。このコミットでは、src/cmd/goinstall/download.go
が変更され、古いGoogle Codeのインポートパス形式が明示的に拒否されるようになりました。
-
knownHosts
の変更:goinstall
は、knownHosts
というhost
構造体のスライスを使用して、様々なコードホスティングサービス(GitHub, Launchpadなど)のインポートパスを識別するための正規表現と、それに対応する処理関数を定義しています。 このコミットでは、古い([a-z0-9\\-]+\\.googlecode\\.com/(svn|git|hg))(/[a-z0-9A-Z_.\\-/]+)?$
にマッチするエントリが削除され、代わりに^code\\.google\\.com/p/([a-z0-9\\-]+(\\.[a-z0-9\\-]+)?)(/[a-z0-9A-Z_.\\-/]+)?$
にマッチする新しい正規表現が追加されました。これにより、goinstall
はもはや古い形式のパスを「既知のホスト」として認識しなくなります。 -
matchGoogleRepo
関数の変更: 以前はmatchGoogleRepo
がrepo.googlecode.com/vcs/path
形式を処理していましたが、このコミットでそのロジックが削除され、code.google.com/p/repo.subrepo/path
形式を処理するように変更されました。また、googleSubrepo
という型がgoogleRepo
にリネームされ、より汎用的なGoogle Codeリポジトリの処理を担うようになりました。 -
古いパスの明示的な拒否:
download
関数(パッケージのダウンロード処理のメインロジック)の冒頭に、新しい正規表現oldGoogleRepo
が追加されました。var oldGoogleRepo = regexp.MustCompile(
^([a-z0-9\-]+)\.googlecode\.com/(svn|git|hg)(/[a-z0-9A-Z_.\-/]+)?$)
download
関数が呼び出された際に、もしインポートパスがこのoldGoogleRepo
にマッチした場合、goinstall
はエラーを返します。 エラーメッセージは、正しいインポートパスの形式(code.google.com/p/...
)を示し、ユーザーにgofix
を実行してコードを修正するよう促します。fmt.Errorf("unsupported import path; should be %q\\nRun goinstall with -fix to gofix the code.", fixedPath)
これらの変更により、GoのツールチェインはGoogle Codeのインポートパスに関して、新しい標準形式への移行を強力に推進するようになりました。
コアとなるコードの変更箇所
このコミットにおける主要なコード変更は以下のファイルに集中しています。
-
src/cmd/gofix/Makefile
:googlecode.go
がGOFILES
リストに追加され、gofix
ビルドプロセスに新しいモジュールが組み込まれます。
-
src/cmd/gofix/googlecode.go
(新規ファイル):googlecodeFix
というfix
構造体が定義され、gofix
に新しい修正ルール「googlecode」を登録します。googlecodeRe
正規表現が定義され、古いGoogle Codeインポートパスのパターンを識別します。googlecode
関数が実装され、ASTを走査して古いインポートパスを検出し、新しい形式に書き換えるロジックを含みます。
-
src/cmd/gofix/googlecode_test.go
(新規ファイル):googlecode
修正のテストケースが定義されています。これにより、foo.googlecode.com/hg/bar
のようなパスがcode.google.com/p/foo/bar
に正しく変換されることを確認します。
-
src/cmd/goinstall/download.go
:knownHosts
スライス内の正規表現が更新され、古いgooglecode.com
形式のエントリが削除され、新しいcode.google.com/p/
形式のエントリが追加されます。matchGoogleRepo
関数のロジックが変更され、googleSubrepo
型がgoogleRepo
にリネームされます。download
関数内にoldGoogleRepo
正規表現が追加され、古い形式のインポートパスが検出された場合にエラーを返すロジックが追加されます。
-
src/cmd/goinstall/download_test.go
:FindPublicRepoTests
内のテストケースが更新され、code.google.com/p/repo/path/foo
のような新しい形式のパスが正しく処理されることを検証します。古い形式のテストケースは削除または変更されています。
コアとなるコードの解説
src/cmd/gofix/googlecode.go
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"go/ast"
"regexp"
)
func init() {
register(googlecodeFix)
}
var googlecodeFix = fix{
"googlecode",
"2011-11-21",
googlecode,
`Rewrite Google Code imports from the deprecated form
"foo.googlecode.com/vcs/path" to "code.google.com/p/foo/path".
`,
}
var googlecodeRe = regexp.MustCompile(`^([a-z0-9\\-]+)\\.googlecode\\.com/(svn|git|hg)(/[a-z0-9A-Z_.\\-/]+)?$`)
func googlecode(f *ast.File) bool {
fixed := false
for _, s := range f.Imports {
old := importPath(s) // インポートパス文字列を取得
if m := googlecodeRe.FindStringSubmatch(old); m != nil {
// 正規表現にマッチした場合、新しいパスを構築
new := "code.google.com/p/" + m[1] + m[3]
if rewriteImport(f, old, new) { // ASTを書き換え
fixed = true
}
}
}
return fixed
}
このファイルは、gofix
ツールがGoogle Codeのインポートパスを修正するためのロジックを定義しています。
googlecodeFix
は、この修正のメタデータ(名前、日付、説明)と、実際に修正を行う関数googlecode
を登録します。googlecodeRe
は、foo.googlecode.com/svn/bar
のような古い形式のインポートパスを識別するための正規表現です。プロジェクト名(m[1]
)とオプションのパス(m[3]
)をキャプチャします。VCSの種類(m[2]
)は新しいパスには使用されません。googlecode
関数は、GoのソースファイルのASTを受け取り、その中のすべてのインポート宣言を調べます。もしインポートパスがgooglecodeRe
にマッチした場合、新しい形式のパス(code.google.com/p/プロジェクト名/パス
)を構築し、rewriteImport
関数を使ってASTを修正します。
src/cmd/goinstall/download.go
// ... (既存のコード) ...
var knownHosts = []host{
{
// 古いgooglecode.com形式のエントリが削除され、
// 新しいcode.google.com/p/形式のエントリが追加された
regexp.MustCompile(`^code\\.google\\.com/p/([a-z0-9\\-]+(\\.[a-z0-9\\-]+)?)(/[a-z0-9A-Z_.\\-/]+)?$`),
matchGoogleRepo,
},
// ... (github.comなどの他のホスト) ...
}
// ... (既存のコード) ...
// matchGoogleRepo matches repos like "code.google.com/p/repo.subrepo/path".
func matchGoogleRepo(id string) (RemoteRepo, error) {
root := "code.google.com/p/" + id
return &googleRepo{baseRepo{"https://" + root, root, nil}}, nil
}
// googleRepo implements a RemoteRepo that discovers a Google Code
// repository's VCS type by scraping the code.google.com source checkout page.
type googleRepo struct{ baseRepo }
var googleRepoRe = regexp.MustCompile(`id="checkoutcmd">(hg|git|svn)`)
func (r *googleRepo) Repo(client *http.Client) (url, root string, vcs *vcs, err error) {
// ... (VCSタイプを検出するためのロジック) ...
}
// ... (既存のコード) ...
var oldGoogleRepo = regexp.MustCompile(`^([a-z0-9\\-]+)\\.googlecode\\.com/(svn|git|hg)(/[a-z0-9A-Z_.\\-/]+)?$`)
// download checks out or updates the specified package from the remote server.
func download(importPath, srcDir string) (public bool, err error) {
// ... (既存のコード) ...
// 古いGoogle Codeパスが検出された場合にエラーを返す
if m := oldGoogleRepo.FindStringSubmatch(importPath); m != nil {
fixedPath := "code.google.com/p/" + m[1] + m[3]
err = fmt.Errorf(
"unsupported import path; should be %q\\n"+
"Run goinstall with -fix to gofix the code.",
fixedPath,
)
return
}
// ... (既存のダウンロードロジック) ...
}
このファイルは、goinstall
がパッケージをダウンロードする際のロジックを定義しています。
knownHosts
の変更により、goinstall
はもはや古いfoo.googlecode.com
形式のパスを自動的に解決しようとしなくなります。matchGoogleRepo
関数と関連するgoogleRepo
型は、新しいcode.google.com/p/
形式のパスを処理し、Google CodeのウェブページをスクレイピングしてVCSタイプを動的に検出する役割を担います。- 最も重要な変更は
download
関数内のoldGoogleRepo
のチェックです。もしユーザーが古い形式のインポートパスを指定した場合、goinstall
はダウンロードを試みずに、エラーメッセージと共に正しい形式とgofix
の使用を促すメッセージを表示します。これにより、古いパスの使用を積極的に阻止し、新しい形式への移行を強制します。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/doc/
gofix
ツールの概要 (Go 1.0リリースノートより): https://golang.org/doc/go1.html#gofix- Go言語のパッケージ管理に関する歴史 (Go Modules以前): https://blog.golang.org/go-modules-part0 (Go Modulesに関する記事ですが、Goのパッケージ管理の進化を理解する上で役立ちます)
- Google Codeの閉鎖に関する情報: https://developers.google.com/open-source/google-code/closure
参考にした情報源リンク
- Go言語のソースコード (特に
src/cmd/gofix
とsrc/cmd/goinstall
ディレクトリ) - Go言語のコミット履歴とコードレビューコメント (Go CL 5421049)
- Go言語の公式ブログやリリースノート (当時のGo言語の状況を理解するため)
- 正規表現に関する一般的な知識
- 抽象構文木 (AST) に関する一般的な知識