[インデックス 12394] ファイルの概要
このコミットは、Go言語のバイナリ配布ツールである misc/dist/bindist.go にWindows向けのパッケージングサポートを追加するものです。具体的には、Windows Installer (MSI) パッケージの生成ロジックをGoプログラム内に統合し、これまでWindowsパッケージングに使用されていたバッチスクリプト misc/dist/windows/dist.bat を削除しています。これにより、Goの配布プロセスがより一元化され、クロスプラットフォームでのビルドとパッケージングの管理が改善されました。
コミット
commit 5e46a8c9f9e7588b862b5b7882200ad912768680
Author: Andrew Gerrand <adg@golang.org>
Date: Tue Mar 6 08:55:53 2012 +1100
misc/dist: add windows packaging support
R=golang-dev, bsiegert, jdpoirier
CC=golang-dev
https://golang.org/cl/5727059
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5e46a8c9f9e7588b862b5b7882200ad912768680
元コミット内容
misc/dist: add windows packaging support
R=golang-dev, bsiegert, jdpoirier
CC=golang-dev
https://golang.org/cl/5727059
変更の背景
この変更の主な背景は、Go言語のWindows向けバイナリ配布プロセスを合理化し、自動化することにあります。以前は、Windows向けのインストーラー(MSI)パッケージの作成には misc/dist/windows/dist.bat というバッチスクリプトが使用されていました。しかし、このアプローチにはいくつかの課題がありました。
- プラットフォーム依存性: バッチスクリプトはWindows環境に特化しており、クロスプラットフォームなビルドシステムとの統合が困難でした。
- メンテナンス性: バッチスクリプトはGo言語のコードベースとは異なる言語で書かれており、Go開発者にとってメンテナンスが煩雑になる可能性がありました。
- 一貫性: 他のOS(Linux, FreeBSD, Darwin)向けのパッケージングは
bindist.goというGoプログラムによって処理されていたため、Windowsだけが異なる方法であることは、プロセスの一貫性を欠いていました。
このコミットは、Windows向けのパッケージングロジックを bindist.go に組み込むことで、これらの課題を解決しようとしています。これにより、すべての主要なOS向けのバイナリ配布プロセスがGo言語で統一され、より堅牢でメンテナンスしやすいシステムが構築されます。
前提知識の解説
このコミットを理解するためには、以下の技術的知識が役立ちます。
- Go言語のビルドシステム: Go言語は、
go buildコマンドを通じてソースコードをコンパイルし、実行可能なバイナリを生成します。また、make.bashやmake.batといったスクリプトが、Goのツールチェイン自体をビルドするために使用されます。 - Windows Installer (MSI): Microsoftが提供するソフトウェアのインストール、メンテナンス、削除のためのパッケージ形式です。MSIファイルは、Windowsオペレーティングシステム上でアプリケーションをインストールするための標準的な方法です。
- WiX Toolset (Windows Installer XML): MSIパッケージを作成するためのオープンソースのツールセットです。XMLファイルでインストーラーの構造や内容を定義し、WiXツールがそれをコンパイルしてMSIファイルを生成します。
heat: WiXツールセットの一部で、既存のファイルやディレクトリからWiX XMLフラグメントを「ハーベスト(収集)」するために使用されます。これにより、手動でXMLを記述する手間を省き、ファイル構造を自動的にインストーラー定義に変換できます。candle: WiXツールセットの一部で、WiX XMLソースファイル(.wxs)をコンパイルしてオブジェクトファイル(.wixobj)を生成します。これはC/C++コンパイラにおけるコンパイルステップに相当します。light: WiXツールセットの一部で、コンパイルされたWiXオブジェクトファイル(.wixobj)をリンクして、最終的なMSIパッケージを生成します。これはC/C++リンカにおけるリンクステップに相当します。
bufioパッケージ (Go): Go言語の標準ライブラリの一部で、バッファリングされたI/O操作を提供します。これにより、ファイルからの読み込みやファイルへの書き込みの効率が向上します。特に、bufio.NewReaderは、大きなファイルから行単位で効率的に読み込む際に有用です。io.Copy関数 (Go): Go言語の標準ライブラリの一部で、io.Readerからio.Writerへデータをコピーするためのシンプルな関数です。ファイルの内容を別のファイルにコピーする際などに便利です。GOROOT_FINAL環境変数: Goのビルドプロセスで使用される環境変数で、Goが最終的にインストールされるパスを示します。これにより、ビルドされたGoツールチェインがどのディレクトリに配置されるかを指定できます。
技術的詳細
このコミットの技術的詳細は、主に misc/dist/bindist.go の変更と、misc/dist/windows/dist.bat の削除に集約されます。
-
bindist.goにおけるWindowsサポートの追加:- 条件付きビルドコマンド:
b.OS == "windows"の条件が追加され、Windows環境ではmake.batを使用してGoツールチェインをビルドするように変更されました。それ以外のOSでは引き続きmake.bashが使用されます。if b.OS == "windows" { _, err = b.run(filepath.Join(b.root, "src"), "cmd", "/C", "make.bat") } else { _, err = b.run(filepath.Join(b.root, "src"), "bash", "make.bash") } - WiXツールセットの統合:
switch b.OSステートメントのcase "windows":ブロック内に、Windows MSIインストーラーを生成するためのWiXツールセット(heat,candle,light)の呼び出しが追加されました。heat dir go ...:goディレクトリ内のファイルをハーベストし、AppFiles.wxsというWiX XMLフラグメントを生成します。これは、インストーラーに含めるファイルとその構造を定義します。candle -dVersion=... -dArch=... installer.wxs AppFiles.wxs:installer.wxs(インストーラーのUIや全体構造を定義するファイル) とAppFiles.wxsをコンパイルし、.wixobjファイルを生成します。light -ext WixUIExtension -ext WixUtilExtension installer.wixobj AppFiles.wixobj -o installer.msi: コンパイルされたオブジェクトファイルをリンクし、最終的なinstaller.msiファイルを生成します。cp(targ, msi): 生成されたMSIファイルを、ターゲット名(例:go.1.0.windows-386.msi)でコピーします。
GOROOT_FINALの動的な設定: Windows環境ではGOROOT_FINALがc:\\goに設定されるように変更されました。これにより、WindowsインストーラーがGoをデフォルトでc:\goにインストールするようになります。final := "/usr/local/go" if b.OS == "windows" { final = `c:\go` } // ... "GOROOT_FINAL="+final,readCredentials関数の改善: 以前はioutil.ReadFileを使用して.gobuildkeyファイルを読み込んでいましたが、bufio.NewReaderを使用して行ごとに読み込むように変更されました。これにより、より堅牢なファイル読み込みとエラーハンドリングが可能になります。また、usernameとpasswordの読み込みロジックも改善されています。cpヘルパー関数の追加: ファイルコピーのためのシンプルなcp関数が追加されました。これはio.Copyを利用してファイルの内容をコピーします。
- 条件付きビルドコマンド:
-
misc/dist/windows/README.txtの更新:dist.batの削除に伴い、dist.batに関する記述が削除され、bindistの実行方法が記載されました。- 依存関係から
7Zipが削除されました。これは、bindist.goがMSIパッケージングに焦点を当て、ZIPアーカイブの作成を直接行わないためと考えられます。 MinGWがWindowsビルドの依存関係として追加されました。これは、GoのWindowsビルドプロセスでMinGWが必要となるためです。
-
misc/dist/windows/dist.batの削除:- このバッチスクリプトは、GoのWindows向けバイナリパッケージ(ZIPとMSI)を作成するために使用されていましたが、その機能が
bindist.goに統合されたため、不要となり削除されました。これにより、Goの配布プロセスがGo言語のコードベース内で一元的に管理されるようになりました。
- このバッチスクリプトは、GoのWindows向けバイナリパッケージ(ZIPとMSI)を作成するために使用されていましたが、その機能が
これらの変更により、GoのWindows向けバイナリ配布プロセスは、Go言語のツールチェーン自体によって管理されるようになり、クロスプラットフォームなビルドとパッケージングの自動化がさらに進みました。
コアとなるコードの変更箇所
misc/dist/bindist.go
--- a/misc/dist/bindist.go
+++ b/misc/dist/bindist.go
@@ -91,7 +94,11 @@ func (b *Build) Do() error {
}
// Build.
- _, err = b.run(filepath.Join(work, "go/src"), "bash", "make.bash")
+ if b.OS == "windows" {
+ _, err = b.run(filepath.Join(b.root, "src"), "cmd", "/C", "make.bat")
+ } else {
+ _, err = b.run(filepath.Join(b.root, "src"), "bash", "make.bash")
+ }
if err != nil {
return err
}
@@ -159,6 +167,47 @@ func (b *Build) Do() error {
"--title", "Go",
"--version", "1.0",
"--target", "10.5")
+ case "windows":
+ win := filepath.Join(b.root, "misc/dist/windows")
+ installer := filepath.Join(win, "installer.wxs")
+ appfiles := filepath.Join(work, "AppFiles.wxs")
+ msi := filepath.Join(work, "installer.msi")
+ // Gather files.
+ _, err = b.run(work, "heat", "dir", "go",
+ "-nologo",
+ "-gg", "-g1", "-srd", "-sfrag",
+ "-cg", "AppFiles",
+ "-template", "fragment",
+ "-dr", "INSTALLDIR",
+ "-var", "var.SourceDir",
+ "-out", appfiles)
+ if err != nil {
+ return err
+ }
+ // Build package.
+ _, err = b.run(work, "candle",
+ "-nologo",
+ "-dVersion="+ver,
+ "-dArch="+b.Arch,
+ "-dSourceDir=go",
+ installer, appfiles)
+ if err != nil {
+ return err
+ }
+ appfiles = filepath.Join(work, "AppFiles.wixobj")
+ installer = filepath.Join(work, "installer.wixobj")
+ _, err = b.run(win, "light",
+ "-nologo",
+ "-ext", "WixUIExtension",
+ "-ext", "WixUtilExtension",
+ installer, appfiles,
+ "-o", msi)
+ if err != nil {
+ return err
+ }
+ // Copy installer to target file.
+ targ += ".msi"
+ err = cp(targ, msi)
}
if err == nil && password != "" {
err = b.upload(string(v[2]), targ)
@@ -199,13 +248,17 @@ func (b *Build) env() []string {
}
}
}\n+\tfinal := "/usr/local/go"\n+\tif b.OS == "windows" {\n+\t\tfinal = `c:\\go`\n+\t}\n \tenv = append(env,\n \t\t"GOARCH="+b.Arch,\n \t\t"GOHOSTARCH="+b.Arch,\n \t\t"GOHOSTOS="+b.OS,\n \t\t"GOOS="+b.OS,\n \t\t"GOROOT="+b.root,\n-\t\t"GOROOT_FINAL=/usr/local/go",\n+\t\t"GOROOT_FINAL="+final,\n \t)\n \treturn env
}\n@@ -230,6 +283,9 @@ func (b *Build) upload(version string, filename string) error {
case "darwin":
os_ = "Mac OS X"
labels = append(labels, "Type-Installer", "OpSys-OSX")
+ case "windows":
+ os_ = "Windows"
+ labels = append(labels, "Type-Installer", "OpSys-Windows")
}
summary := fmt.Sprintf("Go %s %s (%s)", version, os_, arch)
@@ -290,15 +346,41 @@ func exists(path string) bool {
return err == nil
}\n \n-func readCredentials() {\n+func readCredentials() error {\n name := filepath.Join(os.Getenv("HOME"), ".gobuildkey")\n-\tc, err := ioutil.ReadFile(name)\n+\tf, err := os.Open(name)\n if err != nil {\n-\t\tlog.Println("readCredentials:", err)\n-\t\treturn\n+\t\treturn err\n }\n-\tv := bytes.Split(c, []byte("\\n"))\n-\tif len(v) >= 3 {\n-\t\tusername, password = string(v[1]), string(v[2])\n+\tdefer f.Close()\n+\tr := bufio.NewReader(f)\n+\tfor i := 0; i < 3; i++ {\n+\t\tb, _, err := r.ReadLine()\n+\t\tif err != nil {\n+\t\t\treturn err\n+\t\t}\n+\t\tb = bytes.TrimSpace(b)\n+\t\tswitch i {\n+\t\tcase 1:\n+\t\t\tusername = string(b)\n+\t\tcase 2:\n+\t\t\tpassword = string(b)\n+\t\t}\n \t}\n+\treturn nil\n+}\n+\n+func cp(dst, src string) error {\n+\tsf, err := os.Open(src)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\tdefer sf.Close()\n+\tdf, err := os.Create(dst)\n+\tif err != nil {\n+\t\treturn err\n+\t}\n+\tdefer df.Close()\n+\t_, err = io.Copy(df, sf)\n+\treturn err\n }
misc/dist/windows/dist.bat
このファイルは完全に削除されました。
コアとなるコードの解説
misc/dist/bindist.go
-
b.runの条件分岐:if b.OS == "windows" { _, err = b.run(filepath.Join(b.root, "src"), "cmd", "/C", "make.bat") } else { _, err = b.run(filepath.Join(b.root, "src"), "bash", "make.bash") }このコードブロックは、Goツールチェインをビルドするためのプラットフォーム固有のスクリプト実行を制御します。
b.OSが "windows" の場合、cmd /C make.batを実行してWindowsのバッチスクリプトを呼び出します。それ以外の場合は、bash make.bashを実行してUnix系のシェルスクリプトを呼び出します。これにより、bindist.goが異なるOS上で適切なビルドコマンドを選択できるようになります。 -
Windows MSIパッケージングロジック:
case "windows": win := filepath.Join(b.root, "misc/dist/windows") installer := filepath.Join(win, "installer.wxs") appfiles := filepath.Join(work, "AppFiles.wxs") msi := filepath.Join(work, "installer.msi") // Gather files. _, err = b.run(work, "heat", "dir", "go", // ... heat arguments ... "-out", appfiles) // ... error handling ... // Build package. _, err = b.run(work, "candle", // ... candle arguments ... installer, appfiles) // ... error handling ... appfiles = filepath.Join(work, "AppFiles.wixobj") installer = filepath.Join(work, "installer.wixobj") _, err = b.run(win, "light", // ... light arguments ... installer, appfiles, "-o", msi) // ... error handling ... // Copy installer to target file. targ += ".msi" err = cp(targ, msi)この
case "windows"ブロックは、Windows MSIインストーラーの生成プロセス全体をカプセル化しています。heatの実行:heat dir goコマンドは、ビルドされたGoのバイナリとライブラリを含むgoディレクトリの内容をスキャンし、それらをインストーラーに含めるためのWiX XMLフラグメント (AppFiles.wxs) を生成します。-nologo,-gg,-g1,-srd,-sfrag,-cg,-template,-dr,-varなどの引数は、生成されるXMLの形式や内容を細かく制御するためのものです。candleの実行:candleコマンドは、installer.wxs(インストーラーのUIや全体的な設定を定義するWiXソースファイル) とAppFiles.wxsをコンパイルし、それぞれinstaller.wixobjとAppFiles.wixobjというオブジェクトファイルを生成します。-dVersion,-dArch,-dSourceDirは、コンパイル時にインストーラーのバージョン、アーキテクチャ、ソースディレクトリなどの変数を定義するために使用されます。lightの実行:lightコマンドは、installer.wixobjとAppFiles.wixobjをリンクし、最終的なMSIインストーラーファイル (installer.msi) を生成します。-ext WixUIExtension,-ext WixUtilExtensionは、WiXの標準UIやユーティリティ機能を使用するための拡張機能を指定します。cpによるファイルコピー: 最後に、生成されたinstaller.msiファイルを、Goのバージョンとアーキテクチャを含む命名規則に従った最終的なファイル名(例:go.1.0.windows-386.msi)にコピーします。
-
GOROOT_FINALの設定:final := "/usr/local/go" if b.OS == "windows" { final = `c:\go` } // ... "GOROOT_FINAL="+final,この部分は、Goの最終的なインストールパスを定義する
GOROOT_FINAL環境変数を設定します。Windowsの場合、慣例的にGoはc:\goにインストールされるため、そのパスが設定されます。これにより、ビルドされたGoツールチェインがインストーラーによって正しい場所に配置されることが保証されます。 -
readCredentials関数の改善:func readCredentials() error { name := filepath.Join(os.Getenv("HOME"), ".gobuildkey") f, err := os.Open(name) if err != nil { return err } defer f.Close() r := bufio.NewReader(f) for i := 0; i < 3; i++ { b, _, err := r.ReadLine() if err != nil { return err } b = bytes.TrimSpace(b) switch i { case 1: username = string(b) case 2: password = string(b) } } return nil }この関数は、ビルド認証情報を含む
.gobuildkeyファイルを読み込むためのものです。以前はioutil.ReadFileでファイル全体を一度に読み込んでいましたが、bufio.NewReaderを使用して行ごとに読み込むように変更されました。これにより、ファイルが非常に大きい場合でもメモリ効率が良くなり、また、行ごとの処理がより明確になります。defer f.Close()は、関数が終了する際にファイルが確実に閉じられるようにします。 -
cpヘルパー関数の追加:func cp(dst, src string) error { sf, err := os.Open(src) if err != nil { return err } defer sf.Close() df, err := os.Create(dst) if err != nil { return err } defer df.Close() _, err = io.Copy(df, sf) return err }この
cp関数は、指定されたソースファイル (src) の内容を、指定されたデスティネーションファイル (dst) にコピーするためのユーティリティ関数です。io.Copyを使用することで、効率的かつ簡潔にファイルコピーを実現しています。エラーハンドリングも適切に行われています。
misc/dist/windows/dist.bat
このファイルは完全に削除されました。これは、このバッチスクリプトが提供していたWindowsパッケージング機能が、bindist.go というGoプログラムに完全に移行されたことを意味します。これにより、Goの配布プロセスがGo言語のコードベース内で一元的に管理されるようになり、メンテナンス性とクロスプラットフォーム対応が向上しました。
関連リンク
- Go言語公式サイト: https://golang.org/
- WiX Toolset公式サイト: https://wixtoolset.org/
- Mercurial (hg) 公式サイト: https://www.mercurial-scm.org/
- MinGW 公式サイト: https://www.mingw-w64.org/
参考にした情報源リンク
- Go言語のドキュメント (特にビルドプロセスに関するもの)
- WiX Toolsetのドキュメント (heat, candle, lightツールの使用方法)
- Go言語の
os,path/filepath,io,bufio,bytes,fmt,log,stringsパッケージのドキュメント - Windows Installer (MSI) の概念に関するMicrosoftのドキュメント