Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 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 というバッチスクリプトが使用されていました。しかし、このアプローチにはいくつかの課題がありました。

  1. プラットフォーム依存性: バッチスクリプトはWindows環境に特化しており、クロスプラットフォームなビルドシステムとの統合が困難でした。
  2. メンテナンス性: バッチスクリプトはGo言語のコードベースとは異なる言語で書かれており、Go開発者にとってメンテナンスが煩雑になる可能性がありました。
  3. 一貫性: 他のOS(Linux, FreeBSD, Darwin)向けのパッケージングは bindist.go というGoプログラムによって処理されていたため、Windowsだけが異なる方法であることは、プロセスの一貫性を欠いていました。

このコミットは、Windows向けのパッケージングロジックを bindist.go に組み込むことで、これらの課題を解決しようとしています。これにより、すべての主要なOS向けのバイナリ配布プロセスがGo言語で統一され、より堅牢でメンテナンスしやすいシステムが構築されます。

前提知識の解説

このコミットを理解するためには、以下の技術的知識が役立ちます。

  • Go言語のビルドシステム: Go言語は、go build コマンドを通じてソースコードをコンパイルし、実行可能なバイナリを生成します。また、make.bashmake.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 の削除に集約されます。

  1. 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_FINALc:\\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 を使用して行ごとに読み込むように変更されました。これにより、より堅牢なファイル読み込みとエラーハンドリングが可能になります。また、usernamepassword の読み込みロジックも改善されています。
    • cp ヘルパー関数の追加: ファイルコピーのためのシンプルな cp 関数が追加されました。これは io.Copy を利用してファイルの内容をコピーします。
  2. misc/dist/windows/README.txt の更新:

    • dist.bat の削除に伴い、dist.bat に関する記述が削除され、bindist の実行方法が記載されました。
    • 依存関係から 7Zip が削除されました。これは、bindist.go がMSIパッケージングに焦点を当て、ZIPアーカイブの作成を直接行わないためと考えられます。
    • MinGW がWindowsビルドの依存関係として追加されました。これは、GoのWindowsビルドプロセスでMinGWが必要となるためです。
  3. misc/dist/windows/dist.bat の削除:

    • このバッチスクリプトは、GoのWindows向けバイナリパッケージ(ZIPとMSI)を作成するために使用されていましたが、その機能が bindist.go に統合されたため、不要となり削除されました。これにより、Goの配布プロセスがGo言語のコードベース内で一元的に管理されるようになりました。

これらの変更により、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インストーラーの生成プロセス全体をカプセル化しています。

    1. heat の実行: heat dir go コマンドは、ビルドされたGoのバイナリとライブラリを含む go ディレクトリの内容をスキャンし、それらをインストーラーに含めるためのWiX XMLフラグメント (AppFiles.wxs) を生成します。-nologo, -gg, -g1, -srd, -sfrag, -cg, -template, -dr, -var などの引数は、生成されるXMLの形式や内容を細かく制御するためのものです。
    2. candle の実行: candle コマンドは、installer.wxs (インストーラーのUIや全体的な設定を定義するWiXソースファイル) と AppFiles.wxs をコンパイルし、それぞれ installer.wixobjAppFiles.wixobj というオブジェクトファイルを生成します。-dVersion, -dArch, -dSourceDir は、コンパイル時にインストーラーのバージョン、アーキテクチャ、ソースディレクトリなどの変数を定義するために使用されます。
    3. light の実行: light コマンドは、installer.wixobjAppFiles.wixobj をリンクし、最終的なMSIインストーラーファイル (installer.msi) を生成します。-ext WixUIExtension, -ext WixUtilExtension は、WiXの標準UIやユーティリティ機能を使用するための拡張機能を指定します。
    4. 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言語のドキュメント (特にビルドプロセスに関するもの)
  • WiX Toolsetのドキュメント (heat, candle, lightツールの使用方法)
  • Go言語の os, path/filepath, io, bufio, bytes, fmt, log, strings パッケージのドキュメント
  • Windows Installer (MSI) の概念に関するMicrosoftのドキュメント