[インデックス 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ファイルに関する課題であったことが示唆されています。