[インデックス 12561] ファイルの概要
コミット
commit 2ae860585920e17c1d43098c476ffb11c21b35f8
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Sun Mar 11 21:02:40 2012 -0700
misc/dist: use archive/zip, seek out windows deps, add --upload flag
Use archive/zip instead of 7z on Windows.
Look for all Windows deps before starting build, and include looking
for them in their common locations instead of making users update
their PATHs.
Add an --upload flag that, if set to false, doesn't require credential
files.
R=golang-dev, alex.brainman, adg
CC=golang-dev
https://golang.org/cl/5794046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2ae860585920e17c1d43098c476ffb11c21b35f8
元コミット内容
misc/dist: use archive/zip, seek out windows deps, add --upload flag
Use archive/zip instead of 7z on Windows.
Look for all Windows deps before starting build, and include looking
for them in their common locations instead of making users update
their PATHs.
Add an --upload flag that, if set to false, doesn't require credential
files.
R=golang-dev, alex.brainman, adg
CC=golang-dev
https://golang.org/cl/5794046
変更の背景
このコミットは、Go言語のディストリビューションツール(misc/dist/bindist.go
)におけるWindows環境でのビルドおよびパッケージングプロセスの改善を目的としています。主な背景は以下の通りです。
- 外部依存の削減とポータビリティの向上: 以前はWindows環境でZIPアーカイブを作成するために外部ツールである
7z
(7-Zip)に依存していました。これは、ビルド環境に7z
がインストールされ、かつPATHが適切に設定されていることを前提としており、ユーザーにとってセットアップの負担となっていました。Go標準ライブラリのarchive/zip
パッケージを使用することで、外部ツールへの依存をなくし、Goのビルドシステム自体のポータビリティと自己完結性を高めることが可能になります。 - Windows依存関係の検出とユーザーエクスペリエンスの改善: GoのWindows向けバイナリディストリビューションをビルドするには、MinGW (GCC)、WiXツールセット、Mercurialといった特定の外部ツールが必要でした。これらのツールがユーザーのPATHに設定されていない場合、ビルドが失敗し、ユーザーは手動でPATHを設定する必要がありました。このコミットでは、これらの依存関係をビルド開始前に自動的に検出し、一般的なインストールパスも探索することで、ユーザーが手動でPATHを設定する手間を省き、ビルドプロセスの堅牢性とユーザーフレンドリーさを向上させています。
- アップロード機能の柔軟性: 生成されたバイナリパッケージをGoogle Code(当時のGoプロジェクトのホスティング先)にアップロードする機能がありましたが、これは常に認証情報を必要としました。
--upload
フラグを追加することで、アップロードが不要な場合に認証情報を求めないようにし、ツールの利用シナリオを広げています。例えば、単にパッケージを生成したいだけで、公開はしないといったケースに対応できます。
これらの変更は、Go言語のディストリビューション作成プロセスをより堅牢で、ユーザーにとって使いやすいものにすることを目指しています。
前提知識の解説
このコミットを理解するためには、以下の技術的知識が役立ちます。
- Go言語のビルドシステム: Go言語は、
go build
コマンドを通じてソースコードから実行可能ファイルを生成します。また、Goプロジェクト自体もGoで書かれており、そのビルドプロセスはmake.bash
(Unix系)やmake.bat
(Windows系)といったスクリプトによって制御されます。misc/dist
ディレクトリ内のツールは、Goの公式バイナリディストリビューション(SDK)を作成するための特別なビルドスクリプトやツール群を含んでいます。 archive/zip
パッケージ: Go言語の標準ライブラリに含まれるパッケージで、ZIPアーカイブの読み書きをサポートします。これにより、外部のZIPツールに依存することなく、Goプログラム内で直接ZIPファイルの作成や展開が可能です。- 7-Zip (7z): 高い圧縮率を誇るオープンソースのファイルアーカイバです。Windows環境ではコマンドラインツールとしても広く利用されており、以前のGoのビルドシステムではZIPファイルの作成にこの
7z
コマンドを外部プロセスとして呼び出していました。 - MinGW (Minimalist GNU for Windows): Windows上でGCC (GNU Compiler Collection) などのGNU開発ツールを使用できるようにする環境です。GoのWindows向けバイナリをビルドする際には、C言語で書かれた部分(例: ランタイムや一部の標準ライブラリ)をコンパイルするためにMinGWのGCCが必要となる場合があります。
- WiX Toolset (Windows Installer XML Toolset): Windows Installer (MSI) パッケージを作成するためのオープンソースのツールセットです。GoのWindows向けディストリビューションには、MSIインストーラが含まれることがあり、その作成にWiXツールセット(特に
heat
,candle
,light
といったコマンド)が使用されます。 - Mercurial (hg): 分散型バージョン管理システムの一つです。GoプロジェクトはかつてMercurialで管理されており、ソースコードの取得(チェックアウト)に
hg
コマンドが使用されていました。 - PATH環境変数: オペレーティングシステムが実行可能ファイルを探すディレクトリのリストです。コマンドラインでプログラム名を入力した際に、OSはこのPATHリストを順に検索して該当する実行ファイルを見つけます。
- Google Code: かつてGoogleが提供していたオープンソースプロジェクトのホスティングサービスです。Goプロジェクトもここでホストされており、生成されたバイナリディストリビューションがアップロードされていました。
exec.LookPath
: Go言語のos/exec
パッケージに含まれる関数で、指定された実行可能ファイルがシステムのPATH環境変数内でどこにあるかを検索します。
技術的詳細
このコミットは、misc/dist/bindist.go
ファイルに以下の主要な変更を加えています。
-
archive/zip
の導入:import ("archive/zip")
が追加されました。- Windows向けのZIPファイル作成処理において、以前は
b.run(work, "7z", "a", "-tzip", zip, "go")
という形で外部の7z
コマンドを呼び出していた箇所が、新しく定義されたmakeZip(zip, work)
関数に置き換えられました。 makeZip
関数は、archive/zip
パッケージを使用して、指定されたディレクトリ(work
)の内容をZIPファイル(targ
)に圧縮します。filepath.Walk
を使ってディレクトリツリーを走査し、各ファイルをzip.NewWriter
を通じてZIPアーカイブに追加しています。特に、Windowsのパス区切り文字\
をUnixスタイルの/
に変換する処理が含まれており、ZIPアーカイブ内のパスの互換性を確保しています。
-
Windows依存関係の自動検出:
checkWindowsDeps()
関数が新しく追加されました。この関数は、GoのWindowsビルドに必要な外部ツール(gcc
,heat
,candle
,light
,cmd
,hg
)がシステムに存在するかどうかをチェックします。windowsDeps
というmap[string]tool
型のグローバル変数が定義され、各ツール名とそのツールに関する情報(ヘルプURL、一般的なインストールディレクトリのリスト)が格納されています。lookPath(prog string)
関数が追加されました。これはexec.LookPath
のラッパーであり、もしexec.LookPath
でツールが見つからなかった場合でも、windowsDeps
に定義されたcommonDirs
(一般的なインストールディレクトリ)を探索し、実行可能ファイルを見つけようとします。見つかった場合は、そのディレクトリを一時的にPATH環境変数に追加します。これにより、ユーザーが手動でPATHを設定していなくても、ツールが検出される可能性が高まります。main
関数内でruntime.GOOS == "windows"
の場合にcheckWindowsDeps()
が呼び出されるようになりました。これにより、Windows環境でのビルド開始前に必要な依存関係が揃っているかどうかが確認され、不足している場合はエラーメッセージが表示されます。
-
--upload
フラグの追加:upload = flag.Bool("upload", true, "upload resulting files to Google Code")
という新しいコマンドラインフラグが追加されました。デフォルト値はtrue
です。main
関数内で、*upload
がtrue
の場合にのみreadCredentials()
(Google Codeへのアップロードに必要な認証情報を読み込む関数)が呼び出されるようになりました。- 同様に、ビルド完了後のファイルアップロード処理も
if err == nil && *upload { ... }
という条件で囲まれ、--upload=false
が指定された場合はアップロード処理がスキップされるようになりました。
-
詳細出力 (
-v
フラグ):verbose = flag.Bool("v", false, "verbose output")
というフラグが追加されました。run
関数内で、*verbose
がtrue
の場合、実行されるコマンドとその引数がログに出力されるようになりました。これにより、デバッグや問題の特定が容易になります。
-
misc/dist/windows/README.txt
の更新:7zip
の依存関係がREADMEから削除されました。これは、内部のarchive/zip
パッケージを使用するようになったためです。
これらの変更により、GoのWindows向けディストリビューションビルドプロセスは、外部依存が減り、依存関係の検出が自動化され、アップロード機能の制御がより柔軟になりました。
コアとなるコードの変更箇所
このコミットのコアとなる変更は、主にmisc/dist/bindist.go
ファイルに集中しています。
-
archive/zip
パッケージのインポート:--- a/misc/dist/bindist.go +++ b/misc/dist/bindist.go @@ -7,6 +7,7 @@ package main import ( + "archive/zip" "bufio" "bytes" "encoding/base64"
-
--upload
フラグと-v
フラグの追加:--- a/misc/dist/bindist.go +++ b/misc/dist/bindist.go @@ -26,8 +27,10 @@ import ( ) var ( - tag = flag.String("tag", "weekly", "mercurial tag to check out") - repo = flag.String("repo", "https://code.google.com/p/go", "repo URL") + 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") username, password string // for Google Code upload )
-
Windows依存関係チェックと認証情報読み込みの条件化:
--- a/misc/dist/bindist.go +++ b/misc/dist/bindist.go @@ -59,8 +62,14 @@ func main() { if flag.NArg() == 0 { flag.Usage() } - if err := readCredentials(); err != nil { - log.Println("readCredentials:", err) + if runtime.GOOS == "windows" { + checkWindowsDeps() + } + + if *upload { + if err := readCredentials(); err != nil { + log.Println("readCredentials:", err) + } } for _, targ := range flag.Args() { var b Build
-
WindowsでのZIP作成に
makeZip
関数を使用:--- a/misc/dist/bindist.go +++ b/misc/dist/bindist.go @@ -208,7 +221,7 @@ func (b *Build) Do() error { case "windows": // Create ZIP file. zip := filepath.Join(work, base+".zip") - _, err = b.run(work, "7z", "a", "-tzip", zip, "go") + err = makeZip(zip, work) // Copy zip to target file. targ := base + ".zip" err = cp(targ, zip)
-
アップロード処理の条件化:
--- a/misc/dist/bindist.go +++ b/misc/dist/bindist.go @@ -260,7 +273,7 @@ func (b *Build) Do() error { err = cp(targ, msi) targs = append(targs, targ) } - if err == nil && password != "" { + if err == nil && *upload { for _, targ := range targs { err = b.upload(version, targ) if err != nil {
-
run
関数の詳細出力とlookPath
の利用:--- a/misc/dist/bindist.go +++ b/misc/dist/bindist.go @@ -273,9 +286,18 @@ func (b *Build) Do() error { func (b *Build) run(dir, name string, args ...string) ([]byte, error) { buf := new(bytes.Buffer) - cmd := exec.Command(name, args...) - cmd.Stdout = buf - cmd.Stderr = buf + absName, err := lookPath(name) + if err != nil { + return nil, err + } + cmd := exec.Command(absName, args...) + var output io.Writer = buf + if *verbose { + log.Printf("Running %q %q", absName, args) + output = io.MultiWriter(buf, os.Stdout) + } + cmd.Stdout = output + cmd.Stderr = output cmd.Dir = dir cmd.Env = b.env() if err := cmd.Run(); err != nil {\
-
makeZip
関数の追加:--- a/misc/dist/bindist.go +++ b/misc/dist/bindist.go @@ -471,3 +493,125 @@ func cp(dst, src string) error { _, err = io.Copy(df, sf) return err }\n+\n+func makeZip(targ, workdir string) error {\n+ f, err := os.Create(targ)\n+ if err != nil {\n+ return err\n+ }\n+ zw := zip.NewWriter(f)\n+\n+ filepath.Walk(workdir, filepath.WalkFunc(func(path string, fi os.FileInfo, err error) error {\n+ if fi.IsDir() {\n+ return nil\n+ }\n+ if !strings.HasPrefix(path, workdir) {\n+ log.Panicf("walked filename %q doesn't begin with workdir %q", path, workdir)\n+ }\n+ name := path[len(workdir):]\n+\n+ // Convert to Unix-style named paths, as that's the\n+ // type of zip file that archive/zip creates.\n+ name = strings.Replace(name, "\\\\", "/", -1)\n+ // Chop of any leading / from filename, leftover from removing workdir.\n+ if strings.HasPrefix(name, "/") {\n+ name = name[1:]\n+ }\n+ // Don't include things outside of the go subdirectory (for instance,\n+ // the zip file that we're currently writing here.)\n+ if !strings.HasPrefix(name, "go/") {\n+ return nil\n+ }\n+ if *verbose {\n+ log.Printf("adding to zip: %s", name)\n+ }\n+ fh, err := zip.FileInfoHeader(fi)\n+ if err != nil {\n+ return err\n+ }\n+ fh.Name = name\n+ fh.Method = zip.Deflate\n+ w, err := zw.CreateHeader(fh)\n+ if err != nil {\n+ return err\n+ }\n+ r, err := os.Open(path)\n+ if err != nil {\n+ return err\n+ }\n+ defer r.Close()\n+ _, err = io.Copy(w, r)\n+ return err\n+ }))\n+\n+ if err := zw.Close(); err != nil {\n+ return err\n+ }\n+ return f.Close()\n+}\n+\n+type tool struct {\n+ name string\n+ commonDirs []string\n+}\n+\n+var wixTool = tool{\n+ "http://wix.sourceforge.net/, version 3.5",\n+ []string{`C:\\Program Files\\Windows Installer XML v3.5\\bin`,\n+ `C:\\Program Files (x86)\\Windows Installer XML v3.5\\bin`},\n+}\n+\n+var hgTool = tool{\n+ "http://mercurial.selenic.com/wiki/WindowsInstall",\n+ []string{`C:\\Program Files\\Mercurial`,\n+ `C:\\Program Files (x86)\\Mercurial`,\n+ },\n+}\n+\n+var gccTool = tool{\n+ "Mingw gcc; http://sourceforge.net/projects/mingw/files/Installer/mingw-get-inst/",\n+ []string{`C:\\Mingw\\bin`},\n+}\n+\n+var windowsDeps = map[string]tool{\n+ "gcc": gccTool,\n+ "heat": wixTool,\n+ "candle": wixTool,\n+ "light": wixTool,\n+ "cmd": {"Windows cmd.exe", nil},\n+ "hg": hgTool,\n+}\n+\n+func checkWindowsDeps() {\n+ for prog, help := range windowsDeps {\n+ absPath, err := lookPath(prog)\n+ if err != nil {\n+ log.Fatalf("Failed to find necessary binary %q in path or common locations; %s", prog, help)\n+ }\n+ if *verbose {\n+ log.Printf("found windows dep %s at %s", prog, absPath)\n+ }\n+ }\n+}\n+\n+func lookPath(prog string) (absPath string, err error) {\n+ absPath, err = exec.LookPath(prog)\n+ if err == nil {\n+ return\n+ }\n+ t, ok := windowsDeps[prog]\n+ if !ok {\n+ return\n+ }\n+ for _, dir := range t.commonDirs {\n+ for _, ext := range []string{"exe", "bat"} {\n+ absPath = filepath.Join(dir, prog+"."+ext)\n+ if _, err1 := os.Stat(absPath); err1 == nil {\n+ err = nil\n+ os.Setenv("PATH", os.Getenv("PATH")+";"+dir)\n+ return\n+ }\n+ }\n+ }\n+ return\n+}\n```
-
misc/dist/windows/README.txt
からの7zip
の削除:--- a/misc/dist/windows/README.txt +++ b/misc/dist/windows/README.txt @@ -4,7 +4,6 @@ Windows build dependencies - Mercurial (hg): http://mercurial.selenic.com/\n - MinGW: http://www.mingw.org/\n - Windows Installer XML (WiX) toolset: http://wix.sourceforge.net/\n-- 7zip\n \n Packaging
コアとなるコードの解説
このコミットの核となるのは、Goの標準ライブラリであるarchive/zip
パッケージを導入し、Windows環境でのZIPアーカイブ作成に外部ツール7z
への依存をなくした点、そしてWindowsビルドに必要な外部依存関係を自動的に検出・解決するメカニズムを導入した点です。
-
makeZip
関数:- この関数は、Goの
archive/zip
パッケージを使用して、指定されたworkdir
(作業ディレクトリ)の内容をtarg
(ターゲットZIPファイル)に圧縮します。 filepath.Walk
は、workdir
以下のすべてのファイルとディレクトリを再帰的に走査します。- 各ファイルについて、
zip.FileInfoHeader
を使ってファイル情報からZIPヘッダーを作成し、zw.CreateHeader
でZIPアーカイブ内にエントリを作成します。 - 特に重要なのは、
name = strings.Replace(name, "\\\\", "/", -1)
の部分です。Windowsのパス区切り文字であるバックスラッシュ\
を、ZIPアーカイブ内で一般的に使用されるUnixスタイルのスラッシュ/
に変換しています。これにより、生成されるZIPファイルのクロスプラットフォーム互換性が向上します。 if !strings.HasPrefix(name, "go/")
のチェックは、ZIPアーカイブにgo/
ディレクトリ以下のファイルのみを含めるためのものです。これは、Goのディストリビューションが通常go
というルートディレクトリを持つためです。io.Copy(w, r)
でファイルの内容をZIPエントリにコピーしています。
- この関数は、Goの
-
checkWindowsDeps
関数とlookPath
関数:checkWindowsDeps
は、windowsDeps
マップに定義された各ツール(gcc
,heat
,candle
,light
,cmd
,hg
)について、lookPath
関数を呼び出してシステムに存在するかを確認します。lookPath
関数は、まずexec.LookPath(prog)
を呼び出し、システムのPATH環境変数からツールを探します。- もし
exec.LookPath
で見つからなかった場合、windowsDeps
マップからそのツールのcommonDirs
(一般的なインストールディレクトリのリスト)を取得します。 commonDirs
内の各ディレクトリと、一般的な実行可能ファイルの拡張子(.exe
,.bat
)を組み合わせて、ツールのフルパスを構築し、os.Stat
でファイルが存在するかを確認します。- ツールが見つかった場合、そのツールのディレクトリを現在のPATH環境変数に追加します(
os.Setenv("PATH", os.Getenv("PATH")+";"+dir)
)。これにより、後続のexec.Command
呼び出しでツールが正しく見つけられるようになります。 - このメカニズムにより、ユーザーが手動でPATHを設定していなくても、GoのビルドツールがWindowsの依存関係を自動的に見つけ出し、ビルドプロセスをよりスムーズに進めることができます。
-
--upload
フラグ:- このフラグは、Goのディストリビューションツールが生成したファイルをGoogle Codeにアップロードするかどうかを制御します。
*upload
というブール値のフラグによって、認証情報の読み込みと実際のアップロード処理が条件付けられています。これにより、開発者がローカルでパッケージを生成するだけでよい場合など、アップロードが不要なシナリオで、不要な認証情報の入力をスキップできるようになりました。
これらの変更は、GoのビルドシステムがWindows環境でより堅牢に、かつユーザーフレンドリーに動作するための重要な改善です。
関連リンク
- Go言語の
archive/zip
パッケージのドキュメント: https://pkg.go.dev/archive/zip - Go言語の
os/exec
パッケージのドキュメント: https://pkg.go.dev/os/exec - Go言語の
filepath
パッケージのドキュメント: https://pkg.go.dev/path/filepath - Mercurial (hg) 公式サイト: https://www.mercurial-scm.org/
- MinGW 公式サイト: https://www.mingw-w64.org/
- WiX Toolset 公式サイト: https://wixtoolset.org/
参考にした情報源リンク
- Go Change-Id: I2ae860585920e17c1d43098c476ffb11c21b35f8 (Gerrit Change-Id): https://go-review.googlesource.com/c/go/+/5794046 (これはコミットメッセージに記載されている
https://golang.org/cl/5794046
の新しいURL形式です。GoプロジェクトはGerritというコードレビューシステムを使用しており、各変更には一意のChange-Idが割り当てられます。) - Goの
misc/dist
ディレクトリの目的に関する一般的な情報 (Goのソースコードリポジトリ): https://github.com/golang/go/tree/master/misc/dist - GoのWindowsビルドに関する一般的な情報 (Goの公式ドキュメントやWiki): https://go.dev/doc/install/source (Goのソースからのインストールに関するドキュメント。Windows固有の依存関係についても言及されることがあります。)
- 7-Zip 公式サイト: https://www.7-zip.org/
- Google Code (アーカイブ): https://code.google.com/archive/ (Google Codeはサービス終了しており、現在はアーカイブされています。)