[インデックス 11346] ファイルの概要
このコミットは、Go言語のコマンドラインツール cmd/go
において、go get
コマンドの実装と、既存のバグ修正および機能改善を包括的に行ったものです。特に、パッケージ情報の取り扱い、ビルドプロセスの堅牢化、そして新しい go get
コマンドの導入が主要な変更点となっています。
コミット
commit ed936a3f22bcc8165390545471633141088eca26
Author: Russ Cox <rsc@golang.org>
Date: Mon Jan 23 15:16:51 2012 -0500
cmd/go: implement go get + bug fixes
Move error information into Package struct, so that
a package can be returned even if a dependency failed
to load or did not exist. This makes it possible to run
'go fix' or 'go fmt' on packages with broken dependencies
or missing imports. It also enables go get -fix.
The new go list -e flag lets go list process those package
errors as normal data.
Change p.Doc to be first sentence of package doc, not
entire package doc. Makes go list -json or
go list -f '{{.ImportPath}} {{.Doc}}' much more reasonable.
The go tool now depends on http, which means also
net and crypto/tls, both of which use cgo. Trying to
make the build scripts that build the go tool understand
and handle cgo is too much work. Instead, we build
a stripped down version of the go tool, compiled as go_bootstrap,
that substitutes an error stub for the usual HTTP code.
The buildscript builds go_bootstrap, go_bootstrap builds
the standard packages and commands, including the full
including-HTTP-support go tool, and then go_bootstrap
gets deleted.
Also handle the case where the buildscript needs updating
during all.bash: if it fails but a go command can be found on
the current $PATH, try to regenerate it. This gracefully
handles situations like adding a new file to a package
used by the go tool.
R=r, adg
CC=golang-dev
https://golang.org/cl/5553059
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/ed936a3f22bcc8165390545471633141088eca26
元コミット内容
上記の「コミット」セクションに記載されている内容が元コミット内容です。
変更の背景
このコミットが行われた背景には、Go言語のツールチェインにおけるいくつかの課題と、開発者の利便性向上の必要性がありました。
-
依存関係の管理と取得の自動化: 当時、Goプロジェクトの依存関係を手動で管理し、取得するのは煩雑な作業でした。特に、外部ライブラリを利用する際に、そのライブラリのソースコードを適切な場所に配置する必要がありました。
go get
コマンドの導入は、このプロセスを自動化し、開発者がより簡単に外部パッケージを利用できるようにすることを目的としています。 -
壊れた依存関係を持つパッケージのツール利用: 以前のGoツールでは、パッケージの依存関係に問題がある場合(例: 存在しないインポートパス、ロードに失敗するパッケージ)、そのパッケージに対して
go fix
やgo fmt
といったツールを実行することが困難でした。これは、ツールがパッケージ情報を完全にロードできないと判断し、処理を中断してしまうためです。このコミットでは、エラー情報をPackage
構造体内に保持することで、部分的にでもパッケージ情報を利用できるようにし、壊れた依存関係を持つパッケージに対してもツールが実行できるように改善されました。 -
Goツール自身のビルドプロセスの複雑化:
go tool
(Goコマンド自体) がhttp
パッケージに依存するようになったことで、net
やcrypto/tls
といったパッケージも間接的に必要となりました。これらのパッケージはcgo
(C言語との連携機能) を使用しているため、Goツール自身のビルドプロセスが複雑化するという問題が生じました。特に、ブートストラップビルド(Goコンパイラ自身をGoでビルドするプロセス)において、cgo
のサポートを最初から組み込むのは困難でした。 -
go list
コマンドの出力改善:go list
コマンドでパッケージのドキュメント (p.Doc
) を表示する際、パッケージ全体のドキュメントが表示されてしまい、特にgo list -json
やgo list -f '{{.ImportPath}} {{.Doc}}'
のような形式で利用する際に冗長でした。これをパッケージドキュメントの最初の1文に限定することで、より簡潔で有用な出力となるよう改善が求められました。 -
ビルドスクリプトの自己修復能力:
all.bash
(Goのフルビルドスクリプト) の実行中にビルドスクリプト自体が更新を必要とするような状況が発生した場合、ビルドが失敗することがありました。このような状況でも、既存のgo
コマンドが$PATH
上にあれば、それを利用してビルドスクリプトを再生成し、ビルドを継続できるようにすることで、ビルドプロセスの堅牢性を高める必要がありました。
これらの課題に対処するため、このコミットでは go get
の導入、パッケージ情報の扱い方の改善、ブートストラップビルドの仕組みの導入、そして go list
の出力調整など、多岐にわたる変更が加えられました。
前提知識の解説
このコミットを理解するためには、以下のGo言語および関連技術の概念を理解しておく必要があります。
- Go言語のパッケージシステム: Go言語はパッケージによってコードを整理します。各パッケージは独自のインポートパスを持ち、他のパッケージからインポートして利用できます。
go
コマンド: Go言語の主要なコマンドラインツールで、コードのビルド、テスト、フォーマット、依存関係の管理など、様々な開発タスクを実行します。go build
: Goのソースコードをコンパイルして実行可能ファイルを生成します。go install
: パッケージをコンパイルし、生成されたバイナリやアーカイブを$GOBIN
や$GOPATH/pkg
にインストールします。go fix
: 古いGoのAPIを使用しているコードを、新しいAPIに自動的に書き換えます。go fmt
: Goのソースコードを標準的なスタイルにフォーマットします。go list
: 指定されたパッケージの情報を表示します。パッケージのインポートパス、ソースファイルのリスト、依存関係などの詳細を取得できます。
GOROOT
とGOPATH
:GOROOT
: Goのインストールディレクトリを指します。標準ライブラリのソースコードなどが含まれます。GOPATH
: Goのワークスペースディレクトリを指します。ユーザーが開発するプロジェクトのソースコード、コンパイル済みパッケージ、実行可能ファイルなどが配置されます。go get
コマンドでダウンロードされる外部パッケージも通常$GOPATH/src
以下に配置されます。
Package
構造体 (go/build
パッケージ): Goのビルドシステムがパッケージに関する情報を保持するために使用する構造体です。パッケージのインポートパス、ディレクトリ、ソースファイル、依存関係などのメタデータが含まれます。cgo
: Go言語からC言語のコードを呼び出すためのメカニズムです。cgo
を使用するパッケージは、GoコンパイラだけでなくCコンパイラ(GCCなど)も必要とします。- ブートストラップビルド (Bootstrap Build): Go言語のコンパイラやツールチェイン自体がGo言語で書かれているため、Goの新しいバージョンをビルドする際には、まず既存の(古い)Goコンパイラを使って新しいGoコンパイラをビルドするという「ブートストラップ」プロセスが必要です。このプロセスは、Goツールチェインの自己ホスト型特性を維持するために不可欠です。
- バージョン管理システム (VCS): Git, Mercurial (Hg), Subversion (SVN), Bazaar (Bzr) など、ソースコードの変更履歴を管理するシステムです。
go get
はこれらのVCSを利用してリモートリポジトリからソースコードを取得します。 buildscript.sh
: Goのビルドプロセスで使用されるシェルスクリプトの一つで、Goツールチェインのコンポーネントをビルドする役割を担います。all.bash
: Goのソースツリー全体をビルドし、テストを実行するためのトップレベルのスクリプトです。
技術的詳細
このコミットは、Goツールチェインの複数の側面に対して重要な技術的変更を導入しています。
1. Package
構造体におけるエラー情報の統合
- 変更点:
src/cmd/go/pkg.go
に定義されているPackage
構造体に、Incomplete bool
とError *PackageError
フィールドが追加されました。また、PackageError
構造体とimportStack
型が新しく定義されました。 - 目的: 以前は、パッケージのロード中にエラーが発生すると、そのパッケージに関する情報が全く返されないか、処理が中断されていました。この変更により、エラーが発生した場合でも
Package
構造体自体は返され、エラーの詳細はError
フィールドに格納されるようになりました。これにより、部分的にロードされたパッケージ情報でもgo fix
やgo fmt
のようなツールが動作できるようになります。 PackageError
: エラーが発生したパッケージのインポートスタック(どのパッケージからインポートされてエラーに至ったかの経路)とエラーメッセージを保持します。これにより、エラーの原因を追跡しやすくなります。Incomplete
フィールド: このパッケージまたはその依存関係のロード中にエラーが発生したことを示します。
2. go list -e
フラグの導入
- 変更点:
src/cmd/go/list.go
に-e
フラグが追加されました。 - 目的: 通常、
go list
はエラーのあるパッケージをスキップし、標準エラーに出力します。-e
フラグを使用すると、エラーのあるパッケージも通常の出力フローで処理されるようになります。これにより、エラー情報を含むパッケージリストをプログラム的に処理することが可能になり、診断ツールなどの開発に役立ちます。エラーのあるパッケージはImportPath
が非空でError
フィールドが非nilになります。
3. p.Doc
の変更
- 変更点:
Package
構造体のDoc
フィールドが、パッケージドキュメント全体の文字列ではなく、パッケージドキュメントの最初の1文のみを保持するように変更されました。 - 目的:
go list -json
やgo list -f '{{.ImportPath}} {{.Doc}}'
のようなコマンドでパッケージ情報を表示する際に、ドキュメントが長すぎると出力が読みにくくなる問題がありました。最初の1文に限定することで、より簡潔で概要を把握しやすい出力が提供されるようになりました。
4. go_bootstrap
メカニズムの導入
- 変更点: Goツール (
cmd/go
) がhttp
パッケージ(およびnet
,crypto/tls
)に依存するようになったため、これらのパッケージがcgo
を使用していることが問題となりました。Goツールチェインのブートストラップビルドプロセスでは、初期段階でcgo
を完全にサポートすることが困難でした。この問題を解決するため、go_bootstrap
という中間的なGoツールが導入されました。src/buildscript.sh
および各プラットフォームのsrc/buildscript/*.sh
スクリプトが変更され、最初にgo_bootstrap
という名前でGoツールをビルドするようになりました。このgo_bootstrap
は、http
関連の機能がエラーを返すスタブに置き換えられた、cgo
に依存しない(または依存が最小限の)軽量版です。go_bootstrap
がビルドされた後、このgo_bootstrap
を使って、標準パッケージと、http
サポートを含む完全なgo
ツールがビルドされます。- 最終的に、
go_bootstrap
は削除され、完全なgo
ツールが残ります。
- 目的: この多段階ビルドプロセスにより、
cgo
の複雑さをブートストラップビルドの初期段階から切り離し、Goツールチェイン全体のビルドを簡素化し、堅牢性を高めることができました。
5. go get
コマンドの実装
- 変更点:
src/cmd/go/get.go
が新しく追加され、go get
コマンドのロジックが実装されました。 - 機能:
- ダウンロードと更新: 指定されたインポートパスのパッケージとその依存関係をダウンロードし、必要に応じて更新します。
-d
フラグ: パッケージをダウンロードするだけで、インストールは行いません。-fix
フラグ: ダウンロードしたパッケージに対してgofix
を実行してから、依存関係の解決やビルドを行います。これにより、古いAPIを使用しているパッケージでもgo get
で取得し、自動的に修正することができます。-u
フラグ: 既存のパッケージもネットワーク経由で更新します。デフォルトでは、不足しているパッケージのみをダウンロードし、既存のパッケージの更新は行いません。- バージョン選択:
selectTag
関数により、Goのバージョン(例:release.rN
,weekly.YYYY-MM-DD
)に基づいて、VCSリポジトリ内の適切なタグ(例:go.rN
,go.weekly.YYYY-MM-DD
)を選択して同期します。これにより、特定のGoバージョンと互換性のあるパッケージバージョンを取得できます。 - キャッシュ:
downloadCache
とdownloadRootCache
を使用して、重複するダウンロード作業を避けます。 - VCS統合:
vcs.go
(このコミットで追加) と連携し、Git, Mercurial, Subversion, Bazaar などの様々なバージョン管理システムを介してリポジトリを操作します。
6. ビルドスクリプトの自己修復機能
- 変更点:
src/make.bash
に、buildscript.sh
の更新が必要な場合に、既存のgo
コマンドを利用してbuildscript.sh
を再生成するロジックが追加されました。 - 目的:
all.bash
実行中に、Goツールチェインの変更によってbuildscript.sh
自体が古くなることがあります。このような場合でも、ビルドが中断することなく、自動的にbuildscript.sh
を更新し、ビルドを続行できるようになりました。これは、Goツールチェインの継続的な開発と、ビルドプロセスの安定性向上に貢献します。
7. build.DefaultContext
から buildContext
への移行
- 変更点:
src/cmd/go/build.go
において、build.DefaultContext
の直接使用から、buildContext
という変数(build.DefaultContext
のコピー)を使用するように変更されました。また、buildContext.BuildTags
を設定するための-t
フラグが追加されました。 - 目的:
build.DefaultContext
はGoのビルド環境のデフォルト設定を提供しますが、go
コマンド内で特定のビルド設定(例: ビルドタグ)を動的に変更する必要がある場合、DefaultContext
を直接変更するとグローバルな影響が出てしまいます。buildContext
のようなコピーを使用することで、go
コマンドの実行中に特定のビルド設定を安全にカスタマイズできるようになります。-t
フラグは、条件付きコンパイルに使用されるビルドタグを指定するために利用されます。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は多岐にわたりますが、特に以下のファイルとセクションが重要です。
-
src/cmd/go/pkg.go
:Package
構造体へのIncomplete
およびError
フィールドの追加。PackageError
構造体とimportStack
型の新規定義。loadPackage
関数の変更: エラー発生時でもPackage
構造体を返し、エラー情報を格納するように修正。reloadPackage
関数の追加。
-
src/cmd/go/get.go
(新規ファイル):cmdGet
コマンドの定義とrunGet
関数の実装。download
関数: パッケージのダウンロードと依存関係の処理。downloadPackage
関数: VCS を利用したリポジトリのクローン/更新ロジック。selectTag
関数: Goバージョンに応じたVCSタグの選択ロジック。getD
,getU
,getFix
フラグの定義。
-
src/cmd/go/list.go
:cmdList
コマンドのUsageLine
に-e
フラグの追加。listE
フラグの定義。runList
関数におけるpackagesAndErrors
の利用(-e
フラグが指定された場合)。Package
構造体のドキュメントにIncomplete
,Error
,DepsErrors
フィールドの記述を追加。
-
src/cmd/go/build.go
:buildContext
変数の導入とbuild.DefaultContext
からの初期化。addBuildFlags
関数における-t
フラグの追加とbuildContext.BuildTags
への設定。goFilesPackage
およびaction
関数におけるbuildContext
の利用。
-
src/cmd/go/vcs.go
(新規ファイル):vcs
構造体と、Git, Mercurial, Subversion, Bazaar などのVCS操作(create
,download
,tags
,tagSync
)を抽象化するメソッドの定義。vcsForImportPath
関数: インポートパスから適切なVCSとリポジトリ情報を特定するロジック。
-
src/buildscript.sh
およびsrc/buildscript/*.sh
:go install -a -n cmd/go
コマンドに-t cmd_go_bootstrap
オプションが追加され、go_bootstrap
という名前でビルドされるように変更。- 最終的な
go
バイナリのコピー先がgo_bootstrap
に変更。
-
src/make.bash
:buildscript.sh
の再生成ロジックの追加。
コアとなるコードの解説
src/cmd/go/pkg.go
の変更
Package
構造体はGoのパッケージ情報を表現する中心的なデータ構造です。このコミットでは、エラー処理の柔軟性を高めるために、この構造体に以下のフィールドが追加されました。
type Package struct {
// ... 既存のフィールド ...
Incomplete bool `json:",omitempty"` // was there an error loading this package or dependencies?
Error *PackageError `json:",omitempty"` // error loading this package (not dependencies)
// ... 既存のフィールド ...
DepsErrors []*PackageError `json:",omitempty"` // errors loading dependencies
}
// A PackageError describes an error loading information about a package.
type PackageError struct {
ImportStack []string // shortest path from package named on command line to this one
Err string // the error itself
}
// An importStack is a stack of import paths.
type importStack []string
loadPackage
関数は、パッケージのロード中にエラーが発生した場合でも、nil
を返すのではなく、エラー情報を含む Package
構造体を返すように変更されました。
func loadPackage(arg string, stk *importStack) *Package {
stk.push(arg)
defer stk.pop()
// ... (キャッシュチェックなど) ...
if err != nil {
p := &Package{
ImportPath: arg,
Error: &PackageError{
ImportStack: stk.copy(),
Err: err.Error(),
},
Incomplete: true,
}
packageCache[arg] = p
return p
}
// ... (パッケージのスキャンと情報の格納) ...
}
この変更により、go fix
や go fmt
のようなツールが、依存関係が壊れているパッケージに対しても、少なくともそのパッケージ自身のソースファイルを処理できるようになりました。
src/cmd/go/get.go
の主要ロジック
go get
コマンドの核となる runGet
関数は、ダウンロードとインストールの2つのフェーズに分かれています。
func runGet(cmd *Command, args []string) {
// Phase 1. Download/update.
args = importPaths(args)
var stk importStack
for _, arg := range args {
download(arg, &stk)
}
exitIfErrors()
if *getD { // -d フラグが指定された場合、ダウンロードのみで終了
return
}
// Phase 2. Install.
// ... (パッケージキャッシュのクリア) ...
runInstall(cmd, args) // ダウンロードしたパッケージをインストール
}
download
関数は、個々のパッケージのダウンロードと依存関係の再帰的な処理を担当します。
func download(arg string, stk *importStack) {
p := loadPackage(arg, stk) // パッケージ情報をロード
if p.Standard { // 標準ライブラリのパッケージはスキップ
return
}
if downloadCache[arg] { // 重複ダウンロード防止
return
}
downloadCache[arg] = true
if p.Dir == "" || *getU { // パッケージが存在しないか、-u フラグで更新が必要な場合
stk.push(p.ImportPath)
defer stk.pop()
if err := downloadPackage(p); err != nil { // 実際のダウンロード処理
errorf("%s", &PackageError{stk.copy(), err.Error()})
return
}
p = reloadPackage(arg, stk) // 更新されたファイルからパッケージ情報を再ロード
if p.Error != nil {
errorf("%s", p.Error)
return
}
}
if *getFix { // -fix フラグが指定された場合、gofix を実行
run(stringList("gofix", relPaths(p.gofiles)))
p = reloadPackage(arg, stk) // gofix 実行でインポートパスが変わる可能性があるので再ロード
if p.Error != nil {
errorf("%s", p.Error)
return
}
}
// 依存関係を再帰的に処理
for _, dep := range p.deps {
download(dep.ImportPath, stk)
}
}
downloadPackage
関数は、VCS を利用してリポジトリをクローンまたは更新します。
func downloadPackage(p *Package) error {
vcs, repo, rootPath, err := vcsForImportPath(p.ImportPath) // インポートパスからVCS情報を取得
if err != nil {
return err
}
// ... (VCSリポジトリのルートディレクトリの決定とキャッシュチェック) ...
meta := filepath.Join(root, "."+vcs.cmd) // VCSメタデータディレクトリのパス
st, err := os.Stat(meta)
if err == nil && !st.IsDir() {
return fmt.Errorf("%s exists but is not a directory", meta)
}
if err != nil { // メタデータディレクトリが存在しない場合(新規クローン)
// ... (親ディレクトリの作成、VCS.create を呼び出してリポジトリをクローン) ...
} else { // メタデータディレクトリが存在する場合(既存リポジトリの更新)
if err = vcs.download(root); err != nil { // VCS.download を呼び出して更新
return err
}
}
tags, err := vcs.tags(root) // リポジトリのタグを取得
if err != nil {
return err
}
vers := runtime.Version()
tag := selectTag(vers, tags) // Goバージョンに合ったタグを選択
if tag == "" {
tag = vcs.tagDefault
}
if err := vcs.tagSync(root, tag); err != nil { // 選択したタグに同期
return err
}
return nil
}
selectTag
関数は、Goのバージョン文字列と利用可能なタグのリストに基づいて、最適なタグを選択します。
func selectTag(goVersion string, tags []string) (match string) {
// ... (release.rN および weekly.YYYY-MM-DD 形式のバージョンとタグのマッチングロジック) ...
return match
}
src/buildscript.sh
の変更
Goツールチェインのビルドスクリプトは、go
コマンド自体をビルドする際に、go_bootstrap
という中間バイナリを生成するように変更されました。
# ... 既存のコード ...
go install -a -n -t cmd_go_bootstrap cmd/go | sed '
s/\$GOBIN/"$GOBIN"/g
s/\$GOROOT/"$GOROOT"/g
s/\$WORK/"$WORK"/g
s;"\$GOBIN"/go;&_bootstrap;g # ここで go_bootstrap という名前でビルドされる
s;\\;/;g
'
# ... 既存のコード ...
そして、最終的な go
バイナリのコピー先も go_bootstrap
に変更されています。
--- a/src/buildscript/darwin_386.sh
+++ b/src/buildscript/darwin_386.sh
@@ -491,8 +491,8 @@ cp "$WORK"/text/template.a "$GOROOT"/pkg/darwin_386/text/template.a
mkdir -p "$WORK"/cmd/go/_obj/
cd "$GOROOT"/src/cmd/go
-8g -o "$WORK"/cmd/go/_obj/_go_.8 -p cmd/go -I "$WORK" ./build.go ./fix.go ./fmt.go ./get.go ./help.go ./list.go ./main.go ./pkg.go ./run.go ./test.go ./testflag.go ./version.go ./vet.go
+8g -o "$WORK"/cmd/go/_obj/_go_.8 -p cmd/go -I "$WORK" ./bootstrap.go ./build.go ./fix.go ./fmt.go ./get.go ./help.go ./list.go ./main.go ./pkg.go ./run.go ./test.go ./testflag.go ./vcs.go ./version.go ./vet.go
gopack grc "$WORK"/cmd/go.a "$WORK"/cmd/go/_obj/_go_.8
8l -o "$WORK"/cmd/go/_obj/a.out -L "$WORK" "$WORK"/cmd/go.a
mkdir -p "$GOBIN"/
-cp "$WORK"/cmd/go/_obj/a.out "$GOBIN"/go
+cp "$WORK"/cmd/go/_obj/a.out "$GOBIN"/go_bootstrap
この go_bootstrap
は、bootstrap.go
という新しいファイル(このコミットでは追加されていないが、このコミットの意図を反映して後続のコミットで追加される)によって、http
関連の機能がスタブ化されたバージョンとしてビルドされます。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/doc/
go get
コマンドのドキュメント (現在のバージョン): https://golang.org/cmd/go/#hdr-Download_and_install_packages_and_dependenciesgo list
コマンドのドキュメント (現在のバージョン): https://golang.org/cmd/go/#hdr-List_packages- Goのブートストラップビルドに関する情報: https://golang.org/doc/install/source
参考にした情報源リンク
- コミットメッセージ自体 (
./commit_data/11346.txt
) - Go言語のソースコード (特に
src/cmd/go/
ディレクトリ内のファイル) - Go言語の公式ドキュメント (現在のバージョンを参照し、当時の状況を推測)
- Go言語のメーリングリストやIssueトラッカー (当時の議論を検索)
- Goのビルドプロセスに関する一般的な情報源