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

[インデックス 17651] ファイルの概要

このコミットは、Go言語の公式バイナリディストリビューションを生成するためのツールである misc/dist/bindist.go に関連する変更です。bindist.go は、Goのソースコードから様々なプラットフォーム向けのバイナリパッケージ(インストーラやアーカイブ)をビルドし、配布可能な形式にまとめる役割を担っています。具体的には、Goのコンパイラ、ツール、標準ライブラリ、そしてGo TourやGo Blogといった関連コンテンツをパッケージングするプロセスを自動化します。

コミット

このコミットは、Goのバイナリディストリビューションの命名規則に「ラベル」の概念を導入し、さらにGo公式ブログのコンテンツをディストリビューションに含めるように変更しました。これにより、例えば go1.2rc1.darwin-amd64-osx10.6.pkg のように、OSの特定のバージョン(例: osx10.6)を示すラベルをバイナリ名に含めることが可能になります。

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/5ec86988625908d82453f2fcc6597234bfb29b30

元コミット内容

misc/dist: add 'label' part of distro name, include blog content

This will allow us to cut binaries with names like:
        go1.2rc1.darwin-amd64-osx10.6.pkg
        go1.2rc1.darwin-amd64-osx10.8.pkg

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/13629045

変更の背景

この変更には主に二つの背景があります。

  1. より詳細なバイナリ識別子の必要性: 従来のGoバイナリの命名規則は、go<version>.<os>-<arch> の形式でした(例: go1.2rc1.darwin-amd64)。しかし、特定のOS(特にmacOSのようにバージョンによってシステムライブラリやAPIの挙動が異なる場合)では、同じOS/アーキテクチャであっても、さらに細かいOSバージョン(例: osx10.6osx10.8)によってバイナリの互換性が異なることがありました。このような場合、ユーザーが適切なバイナリを選択できるように、ディストリビューション名にOSのサブバージョンや特定の環境を示す「ラベル」を含める必要が生じました。これにより、ユーザーは自身の環境に最適なGoバイナリを容易に識別し、ダウンロードできるようになります。

  2. Go公式ブログコンテンツの統合: Goプロジェクトは、Go言語に関する重要な情報、チュートリアル、開発の進捗などを公式ブログで公開しています。これらのコンテンツをGoの公式ディストリビューションに含めることで、ユーザーはGoをインストールするだけで、オフライン環境でも最新のブログ記事や重要な情報を参照できるようになります。これは、Goの学習体験や情報アクセシビリティを向上させるための取り組みの一環と考えられます。

前提知識の解説

このコミットを理解するためには、以下の概念について知っておく必要があります。

  • Go言語のディストリビューション: Go言語は、コンパイラ、標準ライブラリ、各種ツール(go build, go run, go get など)を含む形で配布されます。これらは通常、特定のOSとアーキテクチャ(例: linux-amd64, windows-386, darwin-amd64)向けにパッケージ化されています。
  • misc/dist/bindist.go: Goプロジェクトのソースツリー内にあるこのGoプログラムは、Goの公式バイナリディストリビューションを自動的にビルドするためのスクリプトです。様々なプラットフォーム向けのパッケージングロジックや、Go Tour、Go Toolsといった関連プロジェクトの取り込みロジックが含まれています。
  • Goのバージョン命名規則: Goのリリースバイナリは、通常 go<メジャーバージョン>.<マイナーバージョン>[<リリース候補>] の形式でバージョンが付けられます(例: go1.2, go1.2rc1)。これにOSとアーキテクチャが結合され、go1.2rc1.darwin-amd64 のようなファイル名になります。
  • 正規表現 (Regular Expression): テキストの中から特定のパターンを検索、置換、抽出するための強力なツールです。このコミットでは、バイナリのファイル名からバージョン、OS、アーキテクチャ、そして新しい「ラベル」を解析するために正規表現が使用されています。Go言語の regexp パッケージで扱われます。
  • go get コマンド: Goのパッケージ管理ツールの一つで、指定されたGoパッケージのソースコードをダウンロードし、必要に応じてビルド・インストールします。このコミットでは、Goブログのソースコードをダウンロードするために利用されています。
  • GOPATH 環境変数: Goのワークスペースのルートディレクトリを指定する環境変数です。Goのソースコード、コンパイル済みパッケージ、実行可能バイナリがこのディレクトリ構造内に配置されます。go get コマンドは、通常 $GOPATH/src 以下にソースコードをダウンロードします。
  • defer キーワード: Go言語の機能で、defer に続く関数呼び出しを、その関数がリターンする直前に実行するようにスケジュールします。主にリソースの解放(ファイルのクローズ、ロックの解除など)やクリーンアップ処理に使用され、コードの可読性と堅牢性を高めます。

技術的詳細

このコミットの技術的な変更点は多岐にわたりますが、中心となるのは「ラベル」の導入とGoブログコンテンツの統合です。

  1. Label フィールドの導入:

    • Build 構造体に Label string フィールドが追加されました。このフィールドは、OSやアーキテクチャに加えて、特定の環境(例: osx10.6)を示すための文字列を保持します。
    • バイナリのファイル名解析 (fileRe 正規表現) およびターゲット文字列の解析 (strings.SplitN) の両方で、この新しい Label を抽出するロジックが追加されました。これにより、go1.2rc1.darwin-amd64-osx10.6.pkg のようなファイル名から osx10.6 を正しく識別できるようになります。
    • 最終的なパッケージファイル名を生成する際にも、Build.Label が空でなければ、base += "-" + b.Label の形式でファイル名に追加されます。
    • Goのバイナリをアップロードする際のメタデータ(ラベル)にも、この Label が追加されるようになりました。
  2. 正規表現 fileRe の変更:

    • 既存のファイル名解析用正規表現が ^(go[a-z0-9-.]+)\\.(src|([a-z0-9]+)-([a-z0-9]+)(?:-([a-z0-9.]))?)\\. に変更されました。
    • 変更の核心は (?:-([a-z0-9.]))? の追加です。
      • (?:...) は非キャプチャグループで、パターンをグループ化しますが、その内容を個別のマッチとしてキャプチャしません。
      • - はリテラルのハイフンにマッチします。
      • ([a-z0-9.]) はキャプチャグループで、英数字またはドットの任意の文字にマッチします。これが新しい「ラベル」部分(例: osx10.6)をキャプチャします。
      • ? は直前のグループが0回または1回出現することを示し、ラベル部分がオプションであることを意味します。
    • この変更により、go1.2rc1.darwin-amd64.pkg のような従来の形式と、go1.2rc1.darwin-amd64-osx10.6.pkg のような新しいラベル付き形式の両方に対応できるようになりました。
  3. Goブログコンテンツの統合:

    • blogPath 定数 (code.google.com/p/go.blog) と blogContent 変数 ([]string{"content", "template"}) が追加されました。これらはGoブログのリポジトリパスと、そのリポジトリ内でディストリビューションに含めるべきディレクトリ(contenttemplate)を指定します。
    • Build 構造体の Do() メソッド内で、新しい b.blog() メソッドが呼び出されるようになりました。これは、Go Tourコンテンツの取り込み (b.tour()) と同様のプロセスで、Goブログコンテンツをビルドプロセスに組み込むことを意味します。
    • b.blog() メソッドは、go get -d code.google.com/p/go.blog/blog を実行してGoブログのソースコードを $GOPATH/src にダウンロードし、その後 cpAllDir 関数を使って指定されたコンテンツ(contenttemplate ディレクトリ)を $GOROOT/blog ディレクトリにコピーします。これにより、Goのインストールディレクトリ内にブログコンテンツが配置されます。
  4. cleanGopath() メソッドの抽出と再利用:

    • tools() メソッドと tour() メソッドの冒頭にあった、GOPATH ディレクトリ内の bin, pkg, src ディレクトリをクリーンアップする共通のロジックが、cleanGopath() という新しいプライベートメソッドとして抽出されました。
    • この cleanGopath() メソッドは、tools(), tour(), そして新しく追加された blog() メソッドのそれぞれで defer b.cleanGopath() として呼び出されるようになりました。これにより、コードの重複が排除され、GOPATHのクリーンアップ処理が一貫して行われることが保証されます。

これらの変更は、Goのディストリビューションシステムをより柔軟にし、特定の環境への対応を強化するとともに、Goエコシステム内の重要なコンテンツへのアクセスを容易にすることを目的としています。

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

このコミットにおけるコアとなるコードの変更箇所は以下の通りです。

  1. misc/dist/bindist.gofileRe 正規表現の変更:

    --- a/misc/dist/bindist.go
    +++ b/misc/dist/bindist.go
    @@ -100,7 +106,8 @@ var raceAvailable = []string{
     	"windows-amd64",
     }
    
    -var fileRe = regexp.MustCompile(`^(go[a-z0-9-.]+)\\.(src|([a-z0-9]+)-([a-z0-9]+))\\.`)\
    +var fileRe = regexp.MustCompile(\
    +	`^(go[a-z0-9-.]+)\\.(src|([a-z0-9]+)-([a-z0-9]+)(?:-([a-z0-9.]))?)\\.`)\
    
  2. Build 構造体への Label フィールド追加:

    --- a/misc/dist/bindist.go
    +++ b/misc/dist/bindist.go
    @@ -167,6 +178,7 @@ type Build struct {
     	Race   bool // build race toolchain
     	OS     string
     	Arch   string
    +	Label  string
     	root   string
     	gopath string
     }
    
  3. Build.Do() メソッドでの b.blog() 呼び出し追加:

    --- a/misc/dist/bindist.go
    +++ b/misc/dist/bindist.go
    @@ -241,6 +253,10 @@ func (b *Build) Do() error {
     	if err != nil {
     		return err
     	}
    +	err = b.blog()
    +	if err != nil {
    +		return err
    +	}
     	err = b.tour()
     }
     if err != nil {
    
  4. 新しい blog() メソッドの追加:

    --- a/misc/dist/bindist.go
    +++ b/misc/dist/bindist.go
    @@ -451,13 +465,23 @@ func (b *Build) tools() error {
     	return err
     }
    
    +func (b *Build) blog() error {
    +	defer b.cleanGopath()
    +
    +	// Fetch the blog repository.
    +	_, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"), "get", "-d", blogPath+"/blog")
    +	if err != nil {
    +		return err
    +	}
    +
    +	// Copy blog content to $GOROOT/blog.
    +	blogSrc := filepath.Join(b.gopath, "src", filepath.FromSlash(blogPath))
    +	contentDir := filepath.Join(b.root, "blog")
    +	return cpAllDir(contentDir, blogSrc, blogContent...)
    +}
    +
     func (b *Build) tour() error {
    -	defer func() {
    -		// Clean work files from GOPATH directory.
    -		for _, d := range []string{"bin", "pkg", "src"} {
    -			os.RemoveAll(filepath.Join(b.gopath, d))
    -		}
    -	}()
    +	defer b.cleanGopath()
    
  5. cleanGopath() メソッドの抽出と既存メソッドからの呼び出し:

    --- a/misc/dist/bindist.go
    +++ b/misc/dist/bindist.go
    @@ -485,6 +509,12 @@ func (b *Build) tour() error {
     	)
     }
    
    +func (b *Build) cleanGopath() {
    +	for _, d := range []string{"bin", "pkg", "src"} {
    +		os.RemoveAll(filepath.Join(b.gopath, d))
    +	}
    +}
    +
     func ext() string {
     	if runtime.GOOS == "windows" {
     		return ".exe"
    

コアとなるコードの解説

  1. fileRe 正規表現の変更: この変更は、Goバイナリのファイル名から情報を解析する際の柔軟性を大幅に向上させます。特に (?:-([a-z0-9.]))? の追加により、go1.2rc1.darwin-amd64-osx10.6.pkg のようなファイル名から osx10.6 のような追加の「ラベル」を抽出できるようになりました。これは、特定のOSバージョンや環境に特化したバイナリを配布する際に不可欠な機能です。例えば、macOSの異なるバージョン(10.6と10.8)向けに最適化されたGoバイナリを区別して提供できるようになります。

  2. Build 構造体への Label フィールド追加: Build 構造体は、Goバイナリをビルドする際の様々な設定や情報をカプセル化しています。ここに Label フィールドが追加されたことで、ビルドプロセス全体を通じて、この追加の識別子を管理・利用できるようになりました。ファイル名の解析結果を保持するだけでなく、最終的なパッケージファイル名の生成や、アップロード時のメタデータとして利用されることで、ディストリビューションの識別能力が向上します。

  3. Build.Do() メソッドでの b.blog() 呼び出し追加: Build.Do() メソッドは、Goバイナリのビルドとパッケージングの主要なオーケストレーションを行います。この中に b.blog() の呼び出しが追加されたことで、Go公式ブログのコンテンツがGoの公式ディストリビューションの一部として自動的に含まれるようになりました。これは、Goのインストールパッケージが単なる開発ツールだけでなく、関連する重要な情報源も提供する「オールインワン」のパッケージへと進化していることを示しています。

  4. 新しい blog() メソッドの追加: このメソッドは、GoブログのコンテンツをGoディストリビューションに組み込むための具体的なロジックを実装しています。

    • defer b.cleanGopath(): この行は、blog() メソッドの実行が完了する直前に cleanGopath() を呼び出すことを保証します。これにより、Goブログのソースコードをダウンロードするために一時的に使用されたGOPATH内のディレクトリが確実にクリーンアップされ、ビルド環境が整然と保たれます。
    • b.run(...) "go", "get", "-d", blogPath+"/blog": go get -d コマンドを使用して、Goブログのリポジトリ(code.google.com/p/go.blog/blog)のソースコードを $GOPATH/src 以下にダウンロードします。-d フラグは、ダウンロードのみを行い、ビルドやインストールは行わないことを意味します。
    • cpAllDir(contentDir, blogSrc, blogContent...): ダウンロードしたGoブログのソースディレクトリから、blogContent で指定されたサブディレクトリ(contenttemplate)を、Goのインストールディレクトリ内の $GOROOT/blog にコピーします。これにより、Goのインストール後に $GOROOT/blog ディレクトリにアクセスすることで、Goブログの静的コンテンツを参照できるようになります。
  5. cleanGopath() メソッドの抽出と既存メソッドからの呼び出し: cleanGopath() メソッドの抽出は、コードの重複を排除し、保守性を向上させるための典型的なリファクタリングです。以前は tools()tour() の中にそれぞれGOPATHのクリーンアップロジックが重複して記述されていましたが、これを共通のメソッドとして切り出すことで、コードがよりDRY (Don't Repeat Yourself) になりました。また、新しい blog() メソッドでもこの共通のクリーンアップロジックを再利用できるため、一貫したリソース管理が保証されます。defer と組み合わせることで、エラーが発生した場合でも確実にクリーンアップが実行される堅牢な設計になっています。

これらの変更は、Goのディストリビューションプロセスをより洗練させ、ユーザーエクスペリエンスを向上させるための重要なステップでした。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント: 特に go get コマンドや GOPATH に関する説明。
  • Go言語のソースコード: misc/dist/bindist.go および関連ファイルの実際のコード。
  • 正規表現に関する一般的な資料: (?:...) やキャプチャグループに関する情報。
  • Go言語の defer ステートメントに関する解説。
  • 当時のGoプロジェクトのGoogle Codeリポジトリの構造。
  • Go言語のリリースプロセスに関する情報。