[インデックス 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のドキュメント