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

[インデックス 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つの主要な変更を含んでいます。

  1. gofixへのgooglecodeモジュールの追加: gofixはGoのコードを自動的に修正するツールです。この変更により、gofixfoo.googlecode.com/vcs/pathのような非推奨のGoogle Codeインポートパスをcode.google.com/p/foo/pathという新しい形式に書き換えることができるようになりました。
  2. 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 に使用されています。

技術的詳細

このコミットの技術的詳細は、主にgofixgoinstallの内部動作、特にインポートパスの解析と変換ロジックに焦点を当てています。

gofixにおけるGoogle Codeインポートパスの書き換え

gofixは、Goのソースファイルを解析してAST(抽象構文木)を構築し、そのASTを走査して特定のパターンにマッチするコードを見つけ、修正を適用します。 このコミットで追加されたgooglecode.goファイルは、gofixの新しい「fix」モジュールとして機能します。

  1. 正規表現によるパターンマッチング: 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)をキャプチャします。
  2. ASTの走査とインポートパスの抽出: googlecode関数は、Goのソースファイルを表す*ast.Fileを受け取ります。 ファイル内のすべてのインポート宣言(f.Imports)をイテレートし、各インポートパスの文字列を取得します。

  3. パスの変換: 取得したインポートパスがgooglecodeReにマッチした場合、正規表現のキャプチャグループを利用して新しい形式のパスを構築します。 new := "code.google.com/p/" + m[1] + m[3] ここで、m[1]はプロジェクト名、m[3]は元のパス部分(もしあれば)です。VCSの種類(m[2])は新しいパスには含まれません。

  4. コードの書き換え: rewriteImportヘルパー関数(このコミットでは変更されていないが、gofixの既存機能)を使用して、元のインポートパスを新しいパスに置き換えます。この関数は、ソースコードの文字列を直接操作するのではなく、ASTを修正し、その後ASTから新しいソースコードを生成します。

goinstallにおける古いインポートパスの禁止

goinstallは、インポートパスに基づいてリモートリポジトリを識別し、ダウンロードするためのロジックを持っています。このコミットでは、src/cmd/goinstall/download.goが変更され、古いGoogle Codeのインポートパス形式が明示的に拒否されるようになりました。

  1. 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はもはや古い形式のパスを「既知のホスト」として認識しなくなります。

  2. matchGoogleRepo関数の変更: 以前はmatchGoogleReporepo.googlecode.com/vcs/path形式を処理していましたが、このコミットでそのロジックが削除され、code.google.com/p/repo.subrepo/path形式を処理するように変更されました。また、googleSubrepoという型がgoogleRepoにリネームされ、より汎用的なGoogle Codeリポジトリの処理を担うようになりました。

  3. 古いパスの明示的な拒否: 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のインポートパスに関して、新しい標準形式への移行を強力に推進するようになりました。

コアとなるコードの変更箇所

このコミットにおける主要なコード変更は以下のファイルに集中しています。

  1. src/cmd/gofix/Makefile:

    • googlecode.goGOFILESリストに追加され、gofixビルドプロセスに新しいモジュールが組み込まれます。
  2. src/cmd/gofix/googlecode.go (新規ファイル):

    • googlecodeFixというfix構造体が定義され、gofixに新しい修正ルール「googlecode」を登録します。
    • googlecodeRe正規表現が定義され、古いGoogle Codeインポートパスのパターンを識別します。
    • googlecode関数が実装され、ASTを走査して古いインポートパスを検出し、新しい形式に書き換えるロジックを含みます。
  3. src/cmd/gofix/googlecode_test.go (新規ファイル):

    • googlecode修正のテストケースが定義されています。これにより、foo.googlecode.com/hg/barのようなパスがcode.google.com/p/foo/barに正しく変換されることを確認します。
  4. src/cmd/goinstall/download.go:

    • knownHostsスライス内の正規表現が更新され、古いgooglecode.com形式のエントリが削除され、新しいcode.google.com/p/形式のエントリが追加されます。
    • matchGoogleRepo関数のロジックが変更され、googleSubrepo型がgoogleRepoにリネームされます。
    • download関数内にoldGoogleRepo正規表現が追加され、古い形式のインポートパスが検出された場合にエラーを返すロジックが追加されます。
  5. 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言語のソースコード (特にsrc/cmd/gofixsrc/cmd/goinstallディレクトリ)
  • Go言語のコミット履歴とコードレビューコメント (Go CL 5421049)
  • Go言語の公式ブログやリリースノート (当時のGo言語の状況を理解するため)
  • 正規表現に関する一般的な知識
  • 抽象構文木 (AST) に関する一般的な知識