[インデックス 12439] ファイルの概要
このコミットは、Go言語のディストリビューションツールである misc/dist/bindist.go
に、ソースアーカイブを準備する機能を追加するものです。これにより、バイナリディストリビューションだけでなく、ソースコードのパッケージングもこのツールで行えるようになります。
コミット
commit 243ac1613e625d73fac19f45edea68b603a26346
Author: Andrew Gerrand <adg@golang.org>
Date: Wed Mar 7 13:13:26 2012 +1100
misc/dist: prepare source archives
Fixes #95.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5756066
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/243ac1613e625d73fac19f45edea68b603a26346
元コミット内容
misc/dist: prepare source archives
このコミットは、Go言語の配布ツールにソースアーカイブを準備する機能を追加します。
Fixes #95.
これは、Goプロジェクトの古い課題トラッカー(code.google.com/p/go
)におけるIssue #95を修正するものです。このIssueは、GoディストリビューションのZIPファイルが簡単にダウンロードできないことに関するものでした。このコミットにより、ソースコードの配布形式が改善され、より簡単に利用できるようになることが期待されます。
変更の背景
Go言語の初期のディストリビューションプロセスでは、主にコンパイル済みのバイナリパッケージの作成に焦点が当てられていました。しかし、開発者やユーザーがGoのソースコード自体をダウンロードし、ビルドしたり、特定の環境で利用したりするニーズも存在します。特に、Goのソースコードからビルドを行う場合、ビルドによって生成される中間ファイルやバイナリがソースツリー内に残ってしまうと、クリーンなソースアーカイブを作成することが困難になります。
このコミットは、このような背景から、misc/dist
ツールにソースアーカイブを生成する機能を追加し、その際に不要なビルド成果物(bin
ディレクトリやpkg
ディレクトリなど)を適切にクリーンアップすることで、配布に適したクリーンなソースパッケージを提供できるようにすることを目的としています。これにより、Goのソースコードからのビルドや配布がより効率的かつ容易になります。また、Fixes #95
とあるように、Goの配布形式に関する既存の課題を解決する一環でもあります。
前提知識の解説
このコミットを理解するためには、以下の概念について基本的な知識があると役立ちます。
- Go言語のビルドシステム: Go言語は、
go build
コマンドやmake.bash
(Unix系) /make.bat
(Windows) スクリプトを使用して、ソースコードからバイナリをビルドします。これらのスクリプトは、Goのツールチェイン自体をビルドするためにも使用されます。 - GoのワークスペースとGOPATH: Go 1.0以降のバージョンでは、
GOPATH
という環境変数によってワークスペースが定義され、ソースコード、パッケージ、バイナリが配置される構造が推奨されていました。bin
ディレクトリには実行可能ファイル、pkg
ディレクトリにはコンパイル済みパッケージが格納されます。 - ディストリビューションツール: ソフトウェアプロジェクトでは、リリース可能なパッケージ(バイナリ、ソースコード、ドキュメントなど)を作成するための専用ツールやスクリプトがしばしば使用されます。Goプロジェクトにおける
misc/dist
は、このようなディストリビューションパッケージを作成するための内部ツールです。 tar.gz
とzip
: これらは一般的なアーカイブ形式であり、複数のファイルを一つのファイルにまとめ、圧縮するために使用されます。Unix系システムではtar.gz
が、Windowsではzip
がよく使われます。filepath.Join
: Go言語のpath/filepath
パッケージにある関数で、OS固有のパス区切り文字を使用してパス要素を結合します。これにより、クロスプラットフォームでのパス操作が可能になります。os.RemoveAll
: Go言語のos
パッケージにある関数で、指定されたパスのファイルまたはディレクトリ(およびその内容)を再帰的に削除します。ioutil.WriteFile
: Go言語のio/ioutil
パッケージにある関数で、指定されたファイルにバイトスライスを書き込みます。filepath.Glob
: Go言語のpath/filepath
パッケージにある関数で、パターンにマッチするファイルパスを検索します。- Issue Tracker: ソフトウェア開発プロジェクトでバグ報告や機能要望などを管理するためのシステム。Goプロジェクトでは、かつて
code.google.com/p/go
が使用されていました。
技術的詳細
このコミットの主要な技術的変更点は、misc/dist/bindist.go
ツールが、従来のバイナリディストリビューションの作成に加えて、ソースコードのディストリビューション(ソースアーカイブ)を作成する機能を持つようになったことです。
具体的には、以下の点が変更されています。
-
sourceCleanFiles
の導入:cleanFiles
とは別に、ソースアーカイブ作成時に削除すべきファイルやディレクトリのリストsourceCleanFiles
が追加されました。これには、ビルドによって生成されるbin
(バイナリ) とpkg
(コンパイル済みパッケージ) ディレクトリが含まれます。これにより、ソースアーカイブがクリーンな状態に保たれます。 -
Build
構造体の拡張:Build
構造体にSource bool
フィールドが追加されました。このフィールドがtrue
の場合、そのビルドがソースアーカイブ作成のためのものであることを示し、OS
とArch
フィールドは空になります。 -
コマンドライン引数の解析の変更:
main
関数内で、コマンドライン引数(ターゲット)の解析ロジックが変更されました。従来のOS-Arch
形式のターゲットに加えて、source
という新しいターゲットが認識されるようになりました。source
ターゲットが指定された場合、Build
構造体のSource
フィールドがtrue
に設定されます。 -
ビルドプロセスの分岐:
Build.Do()
メソッド内で、b.Source
の値に基づいてビルドプロセスが分岐するようになりました。b.Source
がtrue
の場合:make.bash --dist-tool
を実行して、ディストリビューションツール自体のみをビルドします。これは、ソースアーカイブには完全なGoツールチェインのバイナリを含める必要がなく、ソースからビルドするための最小限のツールがあれば十分であるためです。b.Source
がfalse
の場合: 従来のmake.bat
(Windows) またはmake.bash
(Unix系) を実行して、完全なGoツールチェインをビルドします。
-
バージョン文字列の取得方法の変更: バージョン文字列の取得ロジックがより堅牢になりました。以前は
bin/go version
の出力から直接バージョンを抽出していましたが、変更後はpkg/tool/*/dist
パターンでディストリビューションツール(dist
)のパスを検索し、そのdist
ツールにversion
コマンドを実行してフルバージョン文字列を取得するようになりました。これにより、バージョン情報の取得がより正確になります。 -
クリーンアップ処理の改善:
Build.Do()
メソッド内で、クリーンアップ処理がb.clean(files []string)
という新しいヘルパー関数に委譲されるようになりました。また、b.Source
がtrue
の場合は、cleanFiles
に加えてsourceCleanFiles
もクリーンアップ対象として実行されます。 -
パッケージ名の生成ロジックの変更: 生成されるアーカイブファイルの名前(
targ
)のフォーマットが変更されました。ソースアーカイブの場合、go.<version>.src.tar.gz
のような形式になります。 -
アップロードメタデータの調整:
Build.upload()
メソッド内で、アップロードするパッケージのメタデータ(ラベルやサマリー)が、ソースアーカイブの場合に適切に設定されるようになりました。例えば、Type-Source
ラベルが追加され、サマリーも「Go(source only)」のような形式になります。
これらの変更により、bindist.go
はバイナリとソースの両方のディストリビューションを柔軟に作成できる、より汎用的なツールへと進化しました。
コアとなるコードの変更箇所
変更の中心は misc/dist/bindist.go
ファイルです。
--- a/misc/dist/bindist.go
+++ b/misc/dist/bindist.go
@@ -43,6 +43,11 @@ var cleanFiles = []string{
"VERSION.cache",
}
+var sourceCleanFiles = []string{
+ "bin",
+ "pkg",
+}
+
func main() {
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "usage: %s [flags] targets...\\n", os.Args[0])
@@ -57,12 +62,18 @@ func main() {
log.Println("readCredentials:", err)
}
for _, targ := range flag.Args() {
- p := strings.SplitN(targ, "-", 2)
- if len(p) != 2 {
- log.Println("Ignoring unrecognized target:", targ)
- continue
+ var b Build
+ if targ == "source" {
+ b.Source = true
+ } else {
+ p := strings.SplitN(targ, "-", 2)
+ if len(p) != 2 {
+ log.Println("Ignoring unrecognized target:", targ)
+ continue
+ }
+ b.OS = p[0]
+ b.Arch = p[1]
}
- b := Build{OS: p[0], Arch: p[1]}\n if err := b.Do(); err != nil {
if err := b.Do(); err != nil {
log.Printf("%s: %v", targ, err)
}
@@ -70,9 +81,10 @@ func main() {
}
type Build struct {
- OS string
- Arch string
- root string
+ Source bool // if true, OS and Arch must be empty
+ OS string
+ Arch string
+ root string
}
func (b *Build) Do() error {
@@ -93,44 +105,66 @@ func (b *Build) Do() error {
return err
}
- // Build.
- if b.OS == "windows" {
- _, err = b.run(filepath.Join(b.root, "src"), "cmd", "/C", "make.bat")
+ src := filepath.Join(b.root, "src")
+ if b.Source {
+ // Build dist tool only.
+ _, err = b.run(src, "bash", "make.bash", "--dist-tool")
} else {
- _, err = b.run(filepath.Join(b.root, "src"), "bash", "make.bash")
+ // Build.
+ if b.OS == "windows" {
+ _, err = b.run(src, "cmd", "/C", "make.bat")
+ } else {
+ _, err = b.run(src, "bash", "make.bash")
+ }
}
if err != nil {
return err
}
- // Get version string.
- version, err := b.run("", filepath.Join(b.root, "bin/go"), "version")
+ // Get version strings.
+ var (
+ version string // "weekly.2012-03-04"
+ fullVersion []byte // "weekly.2012-03-04 9353aa1efdf3"
+ )
+ pat := b.root + "/pkg/tool/*/dist"
+ m, err := filepath.Glob(pat)
if err != nil {
return err
}
- v := bytes.SplitN(version, []byte(" "), 4)
- version = bytes.Join(v[2:], []byte(" "))
- ver := string(v[2])
+ if len(m) == 0 {
+ return fmt.Errorf("couldn't find dist in %q", pat)
+ }
+ fullVersion, err = b.run("", m[0], "version")
+ if err != nil {
+ return err
+ }
+ v := bytes.SplitN(fullVersion, []byte(" "), 2)
+ version = string(v[0])
// Write VERSION file.
- err = ioutil.WriteFile(filepath.Join(b.root, "VERSION"), version, 0644)
+ err = ioutil.WriteFile(filepath.Join(b.root, "VERSION"), fullVersion, 0644)
if err != nil {
return err
}
// Clean goroot.
- for _, name := range cleanFiles {
- err = os.RemoveAll(filepath.Join(b.root, name))
- if err != nil {
+ if err := b.clean(cleanFiles); err != nil {
+ return err
+ }
+ if b.Source {
+ if err := b.clean(sourceCleanFiles); err != nil {
return err
}
}
// Create packages.
- targ := fmt.Sprintf("go.%s.%s-%s", ver, b.OS, b.Arch)
+ targ := fmt.Sprintf("go.%s.%s-%s", version, b.OS, b.Arch)
switch b.OS {
- case "linux", "freebsd":
+ case "linux", "freebsd", "":
// build tarball
+ if b.Source {
+ targ = fmt.Sprintf("go.%s.src", version)
+ }
targ += ".tar.gz"
_, err = b.run("", "tar", "czf", targ, "-C", work, "go")
case "darwin":
@@ -187,7 +221,7 @@ func (b *Build) Do() error {
// Build package.
_, err = b.run(work, "candle",
"-nologo",
- "-dVersion="+ver,
+ "-dVersion="+version,
"-dArch="+b.Arch,
"-dSourceDir=go",
installer, appfiles)
@@ -210,7 +244,7 @@ func (b *Build) Do() error {
err = cp(targ, msi)
if err == nil && password != "" {
- err = b.upload(string(v[2]), targ)
+ err = b.upload(version, targ)
}
return err
}
@@ -265,7 +299,7 @@ func (b *Build) env() []string {
func (b *Build) upload(version string, filename string) error {
// Prepare upload metadata.
- labels := []string{"Arch-" + b.Arch}
+ var labels []string
os_, arch := b.OS, b.Arch
switch b.Arch {
case "386":
@@ -273,6 +307,9 @@ func (b *Build) upload(version string, filename string) error {
case "amd64":
arch = "64-bit"
}
+ if arch != "" {
+ labels = append(labels, "Arch-"+b.Arch)
+ }
switch b.OS {
case "linux":
os_ = "Linux"
@@ -288,6 +325,10 @@ func (b *Build) upload(version string, filename string) error {
labels = append(labels, "Type-Installer", "OpSys-Windows")
}
summary := fmt.Sprintf("Go %s %s (%s)", version, os_, arch)
+ if b.Source {
+ labels = append(labels, "Type-Source")
+ summary = fmt.Sprintf("Go %s (source only)", version)
+ }
// Open file to upload.
f, err := os.Open(filename)
@@ -341,6 +382,16 @@ func (b *Build) upload(version string, filename string) error {
return nil
}
+func (b *Build) clean(files []string) error {
+ for _, name := range files {
+ err := os.RemoveAll(filepath.Join(b.root, name))
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
func exists(path string) bool {
_, err := os.Stat(path)
return err == nil
コアとなるコードの解説
上記の差分は、misc/dist/bindist.go
がどのようにソースアーカイブの生成をサポートするように変更されたかを示しています。
-
sourceCleanFiles
の追加:var sourceCleanFiles = []string{ "bin", "pkg", }
これは、ソースアーカイブを作成する際に削除すべきディレクトリのリストです。
bin
ディレクトリにはコンパイルされた実行可能ファイルが、pkg
ディレクトリにはコンパイルされたGoパッケージが格納されます。これらを削除することで、配布されるソースアーカイブがクリーンな状態に保たれます。 -
main
関数でのターゲット解析の変更:for _, targ := range flag.Args() { var b Build if targ == "source" { b.Source = true } else { p := strings.SplitN(targ, "-", 2) if len(p) != 2 { log.Println("Ignoring unrecognized target:", targ) continue } b.OS = p[0] b.Arch = p[1] } if err := b.Do(); err != nil { log.Printf("%s: %v", targ, err) } }
main
関数はコマンドライン引数として渡されたターゲットを処理します。以前はOS-Arch
形式のみを想定していましたが、この変更によりsource
という特別なターゲットが認識されるようになりました。source
が指定された場合、Build
構造体のSource
フィールドがtrue
に設定され、ソースアーカイブ作成モードに入ります。 -
Build
構造体へのSource
フィールド追加:type Build struct { Source bool // if true, OS and Arch must be empty OS string Arch string root string }
Source
フィールドは、現在のビルドがソースアーカイブを対象としているかどうかを示すフラグです。これがtrue
の場合、OS
とArch
は無視されます。 -
Build.Do()
メソッドでのビルドロジックの分岐:src := filepath.Join(b.root, "src") if b.Source { // Build dist tool only. _, err = b.run(src, "bash", "make.bash", "--dist-tool") } else { // Build. if b.OS == "windows" { _, err = b.run(src, "cmd", "/C", "make.bat") } else { _, err = b.run(src, "bash", "make.bash") } }
Build.Do()
メソッドは、Source
フィールドの値に基づいて異なるビルドコマンドを実行します。ソースアーカイブの場合 (b.Source
がtrue
)、make.bash --dist-tool
を実行して、Goツールチェイン全体ではなく、ディストリビューションツール自体のみをビルドします。これは、ソースアーカイブには完全なビルド済みツールチェインは不要であり、ソースからビルドするための最小限のツールがあれば十分であるためです。 -
バージョン文字列取得の改善:
var ( version string // "weekly.2012-03-04" fullVersion []byte // "weekly.2012-03-04 9353aa1efdf3" ) pat := b.root + "/pkg/tool/*/dist" m, err := filepath.Glob(pat) // ... fullVersion, err = b.run("", m[0], "version") // ... v := bytes.SplitN(fullVersion, []byte(" "), 2) version = string(v[0])
以前は
bin/go version
の出力からバージョンを抽出していましたが、この変更により、pkg/tool/*/dist
パターンでdist
ツールを見つけ、そのdist
ツールにversion
コマンドを実行してフルバージョン文字列を取得するようになりました。これにより、より正確で安定した方法でバージョン情報を取得できます。 -
クリーンアップ処理の委譲とソース固有のクリーンアップ:
if err := b.clean(cleanFiles); err != nil { return err } if b.Source { if err := b.clean(sourceCleanFiles); err != nil { return err } } // ... func (b *Build) clean(files []string) error { for _, name := range files { err := os.RemoveAll(filepath.Join(b.root, name)) if err != nil { return err } } return nil }
クリーンアップロジックが
b.clean
ヘルパー関数にまとめられました。ソースアーカイブの場合 (b.Source
がtrue
)、通常のクリーンアップファイル (cleanFiles
) に加えて、sourceCleanFiles
(つまりbin
とpkg
ディレクトリ) も削除されます。 -
パッケージ名の生成とアップロードメタデータの調整:
// Create packages. targ := fmt.Sprintf("go.%s.%s-%s", version, b.OS, b.Arch) switch b.OS { case "linux", "freebsd", "": // Added "" for source builds // build tarball if b.Source { targ = fmt.Sprintf("go.%s.src", version) // Source archive name } targ += ".tar.gz" // ... } // ... func (b *Build) upload(version string, filename string) error { // ... if b.Source { labels = append(labels, "Type-Source") summary = fmt.Sprintf("Go %s (source only)", version) } // ... }
生成されるアーカイブファイルの名前が、ソースアーカイブの場合は
go.<version>.src.tar.gz
のような形式になるように調整されました。また、アップロード時のメタデータ(ラベルやサマリー)も、ソースアーカイブであることを示すように変更されています。
これらの変更により、bindist.go
はGo言語のソースコード配布を自動化し、クリーンで適切な形式のソースアーカイブを生成できるようになりました。
関連リンク
- Go言語の公式ウェブサイト: https://go.dev/
- Go言語のソースコードリポジトリ (GitHub): https://github.com/golang/go
- Go言語のIssue Tracker (現在の): https://github.com/golang/go/issues
参考にした情報源リンク
- コミットハッシュ:
243ac1613e625d73fac19f45edea68b603a26346
- GitHubコミットページ: https://github.com/golang/go/commit/243ac1613e625d73fac19f45edea68b603a26346
- Go言語の古いIssue #95に関する情報 (Web検索結果より): GoディストリビューションのZIPファイルに関する課題であったことが示唆されています。