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

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

このコミットは、Go言語のバイナリ配布ツールである misc/dist/bindist.go に関連する変更です。bindist.go は、Goのリリースバイナリをパッケージ化し、Google Code(当時のGoプロジェクトのホスティングプラットフォーム)にアップロードするためのユーティリティです。このファイルは、Goのソースコードから様々なプラットフォーム(FreeBSD, Linux, OS X, そしてこのコミットでWindowsもコメントで追加)向けのバイナリをビルドし、それらを配布可能な形式で準備する役割を担っていました。

コミット

commit 70a8948a39eb2bcca635e2a3beb39812bb7122db
Author: Andrew Gerrand <adg@golang.org>
Date:   Wed Mar 28 12:24:43 2012 +1100

    misc/dist: support upload only (no build)
    
    R=golang-dev, dsymonds
    CC=golang-dev
    https://golang.org/cl/5940050

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

https://github.com/golang/go/commit/70a8948a39eb2bcca635e2a3beb39812bb7122db

元コミット内容

misc/dist: support upload only (no build)

変更の背景

このコミットの主な背景は、Go言語のバイナリ配布プロセスにおける柔軟性の向上です。以前の bindist.go ツールは、バイナリをビルドしてからアップロードするという一連のワークフローを前提としていました。しかし、Goのリリースプロセスやテスト、あるいは特定のシナリオにおいては、既にビルド済みのバイナリファイルが存在し、それを単にアップロードしたいというニーズが生じます。

例えば、以下のような状況が考えられます。

  • 手動ビルドとアップロードの分離: CI/CDパイプラインがまだ成熟していない時期や、特定の環境で手動でビルドを行った後、その成果物を公式の配布チャネルにアップロードしたい場合。
  • 再アップロード: 過去にアップロードしたファイルに問題が見つかった場合や、メタデータを変更して再アップロードしたいが、再ビルドは不要な場合。
  • 異なるビルドシステムとの連携: bindist.go 以外のツールやスクリプトでビルドされたGoバイナリを、Goプロジェクトの公式アップロードメカニズムを通じて配布したい場合。

このようなニーズに対応するため、このコミットでは bindist.go に「ビルドなしでアップロードのみを行う」機能が追加されました。これにより、ツールの利用範囲が広がり、配布ワークフローの柔軟性が向上しました。また、アップロード時に追加のラベルを付与できる機能も追加され、ファイルの分類や管理がより細かく行えるようになっています。

前提知識の解説

  • Go言語の配布ツール (misc/dist): Goプロジェクトの misc/dist ディレクトリには、Goの公式バイナリ配布物を生成するための様々なスクリプトやツールが含まれています。これらは、Goのソースコードから特定のプラットフォーム向けの実行可能ファイルをビルドし、アーカイブ(zip, tar.gzなど)にまとめ、必要に応じて署名やアップロードを行うためのものです。
  • bindist.go: misc/dist 内にあるGoプログラムで、Goのバイナリ配布物を生成する主要なツールの一つです。クロスコンパイル機能を利用して、異なるOSやアーキテクチャ向けのGoバイナリを生成し、パッケージ化します。
  • Google Code: 2012年当時、GoプロジェクトはGoogle Codeを主要なコードホスティングおよびファイル配布プラットフォームとして利用していました。Goの公式バイナリリリースは、Google Codeのダウンロードセクションを通じて提供されていました。このツールは、Google CodeのAPIを利用してファイルをアップロードする機能を持っていました。
  • Mercurial (Hg): コミットメッセージやコード内の tagrepo フラグに「mercurial tag」とあるように、Goプロジェクトはかつてバージョン管理システムとしてMercurialを使用していました。Gitへの移行は後年に行われました。このツールは、Mercurialリポジトリから特定のタグ(バージョン)のソースコードをチェックアウトする機能を前提としていました。
  • Goのビルドとクロスコンパイル: Goは、GOOSGOARCH 環境変数を設定することで、現在の実行環境とは異なるOSやアーキテクチャ向けのバイナリを簡単にビルドできるクロスコンパイル機能を標準でサポートしています。bindist.go はこの機能を利用して、複数のプラットフォーム向けのバイナリを生成します。
  • 正規表現 (Regular Expressions): 特定の文字列パターンを検索、置換、抽出するために使用される強力なツールです。このコミットでは、アップロード対象のファイル名からバージョン、OS、アーキテクチャなどの情報を解析するために正規表現が導入されています。

技術的詳細

このコミットの技術的な核心は、bindist.go ツールがコマンドライン引数としてファイル名を直接受け取り、それをビルドプロセスを経ずにGoogle Codeにアップロードできるようにした点にあります。

  1. ファイル名解析のための正規表現の導入: fileRe = regexp.MustCompile(^go\.([a-z0-9-.]+)\.(src|([a-z0-9]+)-([a-z0-9]+))\.)\ この正規表現は、Goの公式バイナリ配布物の命名規則(例: go.1.0.3.linux-amd64.tar.gz, go.1.0.3.src.tar.gz)にマッチするように設計されています。

    • ^go\\.: 文字列が "go." で始まることを意味します。
    • ([a-z0-9-.]+): 最初のキャプチャグループで、バージョン番号(例: "1.0.3")を捕捉します。
    • \\.(src|([a-z0-9]+)-([a-z0-9]+))\\.
      • src: ソース配布物の場合。
      • ([a-z0-9]+)-([a-z0-9]+): バイナリ配布物の場合、OS(例: "linux")とアーキテクチャ(例: "amd64")をそれぞれ別のキャプチャグループで捕捉します。 この正規表現により、ツールは与えられたファイル名から必要なメタデータ(バージョン、OS、アーキテクチャ、ソースかバイナリか)を自動的に抽出できるようになります。
  2. アップロード専用ロジックの追加: main 関数内で、コマンドライン引数として渡された各ターゲットが fileRe にマッチするかどうかをチェックする新しいロジックが追加されました。 if m := fileRe.FindStringSubmatch(targ); m != nil { ... } もしマッチした場合、それはビルド済みのファイル名であると判断され、ビルドプロセスをスキップして直接 b.Upload メソッドが呼び出されます。これにより、ユーザーは go.1.0.3.linux-amd64.tar.gz のようなファイル名を直接引数として渡すだけで、そのファイルをアップロードできるようになりました。

  3. upload メソッドの公開と利用: 既存の upload メソッドが Upload (先頭が大文字) にリネームされ、パッケージ外から呼び出し可能になりました。これにより、新しいアップロード専用ロジックからこのメソッドを直接利用できるようになりました。

  4. アップロードラベルの追加機能: addLabel という新しいコマンドラインフラグ (-label) が追加されました。これにより、アップロード時にファイルに追加のカスタムラベルを付与できるようになり、Google Code上でのファイルの分類や検索性が向上しました。

  5. サマリー生成ロジックの調整: アップロードされるファイルのサマリー(説明文)を生成するロジックが微調整されました。以前は常に "Go" で始まっていましたが、新しいロジックでは、サマリーが既に "go" で始まっていない場合にのみ "Go " プレフィックスを追加するようになりました。これにより、カスタムラベルやファイル名から生成されるサマリーの柔軟性が増しています。

これらの変更により、bindist.go は単なるビルドツールから、既存のバイナリ配布物を管理・アップロードするためのより汎用的なツールへと進化しました。

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

misc/dist/bindist.go ファイルにおいて、以下の主要な変更が行われました。

  1. addLabel フラグの追加:

    --- a/misc/dist/bindist.go
    +++ b/misc/dist/bindist.go
    @@ -24,16 +24,18 @@ import (
      	"os"
      	"os/exec"
      	"path/filepath"
    +	"regexp"
      	"runtime"
      	"strings"
      )
      
      var (
    -	tag     = flag.String("tag", "weekly", "mercurial tag to check out")
    -	repo    = flag.String("repo", "https://code.google.com/p/go", "repo URL")
    -	verbose = flag.Bool("v", false, "verbose output")
    -	upload  = flag.Bool("upload", true, "upload resulting files to Google Code")
    -	wxsFile = flag.String("wxs", "", "path to custom installer.wxs")
    +	tag      = flag.String("tag", "weekly", "mercurial tag to check out")
    +	repo     = flag.String("repo", "https://code.google.com/p/go", "repo URL")
    +	verbose  = flag.Bool("v", false, "verbose output")
    +	upload   = flag.Bool("upload", true, "upload resulting files to Google Code")
    +	wxsFile  = flag.String("wxs", "", "path to custom installer.wxs")
    +	addLabel = flag.String("label", "", "additional label to apply to file hwhen uploading")
      
      	username, password string // for Google Code upload
      )
    

    -label コマンドライン引数を受け取る addLabel 変数が追加されました。

  2. ファイル名解析用正規表現の追加:

    --- a/misc/dist/bindist.go
    +++ b/misc/dist/bindist.go
    @@ -64,6 +66,8 @@ var sourceCleanFiles = []string{
      	"pkg",
      }
      
    +var fileRe = regexp.MustCompile(`^go\\.([a-z0-9-.]+)\\.(src|([a-z0-9]+)-([a-z0-9]+))\\.`)\n
    +
      func main() {
      	flag.Usage = func() {
      		fmt.Fprintf(os.Stderr, "usage: %s [flags] targets...\\n", os.Args[0])
    

    fileRe という名前の正規表現が定義されました。

  3. アップロード専用ロジックの追加: main 関数内のターゲット処理ループに、新しいロジックが挿入されました。

    --- a/misc/dist/bindist.go
    +++ b/misc/dist/bindist.go
    @@ -85,6 +89,24 @@ func main() {
      	}
      	for _, targ := range flag.Args() {
      		var b Build
    +		if m := fileRe.FindStringSubmatch(targ); m != nil {
    +			// targ is a file name; upload it to googlecode.
    +			version := m[1]
    +			if m[2] == "src" {
    +				b.Source = true
    +			} else {
    +				b.OS = m[3]
    +				b.Arch = m[4]
    +			}
    +			if !*upload {
    +				log.Printf("%s: -upload=false, skipping", targ)
    +				continue
    +			}
    +			if err := b.Upload(version, targ); err != nil {
    +				log.Printf("%s: %v", targ, err)
    +			}
    +			continue
    +		}
      		if targ == "source" {
      			b.Source = true
      		} else {
    

    これにより、引数がファイル名パターンにマッチした場合、ビルドをスキップして直接アップロード処理に進むようになりました。

  4. upload メソッドの公開 (Upload へのリネーム):

    --- a/misc/dist/bindist.go
    +++ b/misc/dist/bindist.go
    @@ -296,7 +318,7 @@ func (b *Build) Do() error {
      	}\n
      	if err == nil && *upload {
      		for _, targ := range targs {
    -			err = b.upload(version, targ)
    +			err = b.Upload(version, targ)
      			if err != nil {
      				return err
      			}
    @@ -362,7 +384,7 @@ func (b *Build) env() []string {
      	return env
      }
      
    -func (b *Build) upload(version string, filename string) error {
    +func (b *Build) Upload(version string, filename string) error {
      	// Prepare upload metadata.
      	var labels []string
      	os_, arch := b.OS, b.Arch
    

    upload 関数が Upload に変更され、外部からアクセス可能になりました。

  5. サマリー生成とラベル追加ロジックの調整:

    --- a/misc/dist/bindist.go
    +++ b/misc/dist/bindist.go
    @@ -389,7 +411,7 @@ func (b *Build) upload(version string, filename string) error {
      	\tos_ = "Windows"
      	\tlabels = append(labels, "OpSys-Windows")
      	}\n
    -\tsummary := fmt.Sprintf("Go %s %s (%s)", version, os_, arch)
    +\tsummary := fmt.Sprintf("%s %s (%s)", version, os_, arch)
      	if b.OS == "windows" {
      		switch {
      		case strings.HasSuffix(filename, ".msi"):\n
    @@ -402,7 +424,14 @@ func (b *Build) upload(version string, filename string) error {
      	}\n
      	if b.Source {
      		labels = append(labels, "Type-Source")
    -\t\tsummary = fmt.Sprintf("Go %s (source only)", version)
    +\t\tsummary = fmt.Sprintf("%s (source only)", version)
    +\t}\n
    +\tif *addLabel != "" {
    +\t\tlabels = append(labels, *addLabel)
    +\t}\n
    +\t// Put "Go" prefix on summary when it doesn't already begin with "go".
    +\tif !strings.HasPrefix(strings.ToLower(summary), "go") {
    +\t\tsummary = "Go " + summary
      	}
      
      	// Open file to upload.
    

    サマリーの生成方法が変更され、addLabel フラグで指定されたラベルが追加されるようになりました。また、サマリーが "go" で始まっていない場合にのみ "Go " プレフィックスを追加するロジックが追加されました。

コアとなるコードの解説

このコミットのコアとなる変更は、main 関数内でコマンドライン引数を処理するループに導入された新しい条件分岐です。

		if m := fileRe.FindStringSubmatch(targ); m != nil {
			// targ is a file name; upload it to googlecode.
			version := m[1]
			if m[2] == "src" {
				b.Source = true
			} else {
				b.OS = m[3]
				b.Arch = m[4]
			}
			if !*upload {
				log.Printf("%s: -upload=false, skipping", targ)
				continue
			}
			if err := b.Upload(version, targ); err != nil {
				log.Printf("%s: %v", targ, err)
			}
			continue
		}

このコードブロックは、以下の手順で「ビルドなしでアップロードのみ」の機能を実現しています。

  1. ファイル名のパターンマッチング: if m := fileRe.FindStringSubmatch(targ); m != nil の部分で、コマンドライン引数 targ が、新しく定義された正規表現 fileRe にマッチするかどうかをチェックします。fileRe は、go.VERSION.OS-ARCH.EXTENSIONgo.VERSION.src.EXTENSION のようなGoの配布物ファイル名のパターンを認識します。

    • m は、マッチした部分文字列の配列です。m[1] にはバージョン、m[2] には "src" または "OS-ARCH" の部分、m[3] にはOS、m[4] にはアーキテクチャが格納されます。
  2. メタデータの抽出: マッチした場合、m から versionOSArch、そしてそれがソース配布物 (src) なのかバイナリ配布物なのかを抽出します。これらの情報は、Google Codeにアップロードする際のメタデータ(ラベルやサマリー)として使用されます。

  3. アップロードの条件チェック: if !*upload は、-upload=false フラグが指定されている場合にアップロードをスキップするためのチェックです。これにより、アップロードを明示的に無効にできます。

  4. Upload メソッドの呼び出し: 抽出したメタデータと元のファイル名 targ を引数として、b.Upload(version, targ) を呼び出します。この Upload メソッドは、Google Codeへの実際のファイルアップロード処理を担当します。以前は upload という非公開メソッドでしたが、このコミットで Upload という公開メソッドにリネームされました。

  5. エラーハンドリングと継続: アップロード中にエラーが発生した場合、log.Printf でエラーメッセージを出力し、次のターゲットの処理に進みます (continue)。

このロジックにより、bindist.go は、ビルドターゲット(例: linux-amd64)だけでなく、既に存在する配布ファイル名(例: go.1.0.3.linux-amd64.tar.gz)も直接受け付けて処理できるようになり、ツールの柔軟性と利便性が大幅に向上しました。

関連リンク

参考にした情報源リンク

  • コミットメッセージとdiffの内容
  • Go言語の一般的な開発プロセスとツールの知識
  • 正規表現の一般的な知識