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

[インデックス 12937] ファイルの概要

このコミットは、Go言語のディストリビューションツール (misc/dist) におけるmacOS (OS X) 向けパッケージ (.pkg) の生成方法を改善するものです。具体的には、従来の PackageMaker ユーティリティの使用を廃止し、macOSのネイティブツールである pkgbuildproductbuild を採用することで、より堅牢で柔軟なパッケージ作成プロセスを実現しています。これにより、OS X 10.6以降のバージョンを必須とするようになり、古いOSバージョンでインストールしようとした際に、よりユーザーフレンドリーなエラーメッセージを表示できるようになりました。また、postinstall スクリプトのいくつかの問題も修正されています。

コミット

commit c17d09a65774e03376fc1a7fd536646297f408cb
Author: Mikkel Krautz <mikkel@krautz.dk>
Date:   Mon Apr 23 14:56:03 2012 -0700

    misc/dist: require 10.6 or later for OS X .pkgs
    
    This changes the misc/dist program to generate OS X
    packages using pkgbuild and productbuild.
    
    The productbuild utility makes it easy to generate
    packages with a custom Distribution file.  This allows
    us to add an installcheck script that presents a
    friendly message to users who are running on an old
    version of Mac OS X.
    
    The change also fixes a few issues with the
    postinstall script:
    
     - In-repo version of the script has been made
       executable. Installers generated using the new
       tools couldn't execute it otherwise.
    
     - It now uses -d for checking for the existence
       of the Xcode specs directory.
    
     - The call to sudo.bash has been dropped since cov
       and prof aren't bundled with the binary
       distributions.
    
    Fixes #3455.
    
    Tested on 10.5.8, 10.6.0, 10.6.8 and 10.7.3.
    
    R=adg, golang-dev
    CC=golang-dev
    https://golang.org/cl/5987044

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/c17d09a65774e03376fc1a7fd536646297f408cb

元コミット内容

misc/dist: require 10.6 or later for OS X .pkgs

This changes the misc/dist program to generate OS X
packages using pkgbuild and productbuild.

The productbuild utility makes it easy to generate
packages with a custom Distribution file.  This allows
us to add an installcheck script that presents a
friendly message to users who are running on an old
version of Mac OS X.

The change also fixes a few issues with the
postinstall script:

 - In-repo version of the script has been made
   executable. Installers generated using the new
   tools couldn't execute it otherwise.

 - It now uses -d for checking for the existence
   of the Xcode specs directory.

 - The call to sudo.bash has been dropped since cov
   and prof aren't bundled with the binary
   distributions.

Fixes #3455.

Tested on 10.5.8, 10.6.0, 10.6.8 and 10.7.3.

R=adg, golang-dev
CC=golang-dev
https://golang.org/cl/5987044

変更の背景

この変更の主な背景には、macOS (旧称 OS X) 向けのGoバイナリディストリビューションのインストール体験の向上と、パッケージ作成プロセスの近代化があります。

  1. OSバージョンの要件明確化とユーザー体験の改善: 以前のGoディストリビューションは、古いOS Xバージョンでもインストールを試みることができましたが、Goの特定の機能やビルド環境が新しいOSバージョンに依存している場合、インストール後に問題が発生する可能性がありました。このコミットでは、GoのmacOSパッケージがOS X 10.6 (Snow Leopard) 以降を必要とすることを明確にし、それ以前のバージョンでインストールしようとしたユーザーに対して、より親切で明確なエラーメッセージを表示することを目的としています。これにより、ユーザーは無駄なインストール作業を避け、互換性の問題を事前に把握できるようになります。

  2. パッケージ作成ツールの移行: 従来のmacOSパッケージ作成には PackageMaker というツールが使用されていました。しかし、PackageMaker はAppleによって非推奨とされ、より新しい pkgbuild および productbuild コマンドラインツールが推奨されるようになりました。これらの新しいツールは、より柔軟なパッケージ作成オプションを提供し、特に productbuild はカスタムの Distribution ファイルを組み込むことで、インストールの事前チェックやUIのカスタマイズを容易にします。このコミットは、Goのビルドプロセスを最新のmacOS開発ツールチェーンに合わせるための重要なステップでした。

  3. postinstall スクリプトの堅牢化: インストール後に実行される postinstall スクリプトにはいくつかの問題がありました。

    • 実行権限の問題: リポジトリ内のスクリプトが実行可能になっていなかったため、新しいパッケージツールで生成されたインストーラが正しく実行できない可能性がありました。
    • Xcode関連パスのチェック方法の改善: Xcodeの仕様ディレクトリの存在チェックがファイル (-f) ではなくディレクトリ (-d) として行われるべきでした。
    • 不要な処理の削除: バイナリディストリビューションには cov (カバレッジツール) や prof (プロファイリングツール) がバンドルされていないため、それらに関連する sudo.bash の呼び出しは不要であり、削除することでスクリプトの簡素化とセキュリティの向上を図りました。

これらの変更は、GoのmacOSユーザーにとって、よりスムーズで信頼性の高いインストールプロセスを提供することを目的としています。

前提知識の解説

このコミットを理解するためには、以下のmacOSパッケージングとシェルスクリプトに関する知識が役立ちます。

  1. macOSインストーラパッケージ (.pkg):

    • macOSアプリケーションやシステムコンポーネメントを配布・インストールするための標準的な形式です。
    • 通常、インストーラアプリケーション (Installer.app) を介して実行されます。
    • 内部的には、ペイロード(インストールされるファイル群)、スクリプト(インストール前後に実行される処理)、メタデータ(パッケージ情報、インストール要件など)を含んでいます。
  2. PackageMaker:

    • かつてmacOSでインストーラパッケージを作成するために広く使われていたGUIツールおよびコマンドラインツールです。
    • Xcodeの一部として提供されていましたが、後に非推奨となりました。
    • このコミット以前のGoのビルドプロセスで利用されていました。
  3. pkgbuild:

    • macOSのコマンドラインツールで、コンポーネントパッケージ(Component Package)を作成するために使用されます。
    • コンポーネントパッケージは、特定のファイルセット(ペイロード)と、それに関連するスクリプト(preinstall, postinstallなど)をまとめたものです。
    • pkgbuild は、インストールされる実際のファイル群と、それらをどこに配置するか、どのようなスクリプトを実行するかといった基本的な情報をカプセル化します。
  4. productbuild:

    • macOSのコマンドラインツールで、配布パッケージ(Distribution Package)を作成するために使用されます。
    • 複数のコンポーネントパッケージを一つにまとめたり、インストーラのUIや動作をカスタマイズするための Distribution ファイルを組み込んだりする際に使用されます。
    • productbuild は、インストーラの全体的なフロー、表示される情報、インストール条件(OSバージョンチェックなど)を制御する能力を提供します。
  5. Distribution ファイル:

    • productbuild で使用されるXML形式のファイルです。
    • インストーラのタイトル、オプション、ドメイン、そして最も重要な installation-check スクリプトを定義します。
    • installation-check スクリプトはJavaScriptで記述され、インストールの開始前に実行され、システムがインストール要件を満たしているかを確認します。このスクリプトが false を返すと、インストールは中止され、指定されたエラーメッセージが表示されます。
  6. postinstall スクリプト:

    • macOSインストーラパッケージに含まれるシェルスクリプトの一つで、パッケージのファイルがターゲットシステムにコピーされた後に実行されます。
    • インストール後のクリーンアップ、パーミッションの調整、環境変数の設定、追加の構成など、様々なタスクを実行するために使用されます。
    • このコミットでは、このスクリプトの実行権限と内部ロジックが修正されています。
  7. シェルスクリプトの条件式:

    • [ -f path ]: path が通常のファイルとして存在するかどうかをチェックします。
    • [ -d path ]: path がディレクトリとして存在するかどうかをチェックします。
    • このコミットでは、postinstall スクリプト内でXcode関連のディレクトリの存在チェックが -f から -d に変更されています。

これらのツールと概念を理解することで、GoのmacOSパッケージングプロセスがどのように進化し、なぜこれらの変更が行われたのかを深く把握できます。

技術的詳細

このコミットにおける技術的な変更は、主にGoのビルドスクリプト (misc/dist/bindist.go) とmacOSインストーラ関連ファイル (misc/dist/darwin/Distribution, misc/dist/darwin/scripts/postinstall) に集中しています。

  1. PackageMaker から pkgbuild/productbuild への移行:

    • misc/dist/bindist.go 内で、PackageMaker へのパスを定義していた定数 packageMaker が削除されました。
    • パッケージ作成ロジックが大幅に変更され、b.run 関数(シェルコマンドを実行するGoのヘルパー関数)を使って pkgbuildproductbuild が呼び出されるようになりました。
    • pkgbuild の使用:
      • まず、ioutil.TempDir で一時ディレクトリ (pkgdest) を作成し、そこに中間的なコンポーネントパッケージを生成します。
      • pkgbuild コマンドは、--identifier (パッケージの識別子)、--version--scripts (postinstallスクリプトの場所)、--root (インストールされるファイルのルートディレクトリ)、そして出力パス (filepath.Join(pkgdest, "com.googlecode.go.pkg")) を引数に取ります。これにより、Goのバイナリと関連ファイルを含む基本的なパッケージが作成されます。
    • productbuild の使用:
      • 次に、productbuild コマンドが呼び出され、最終的な配布パッケージが生成されます。
      • --distribution 引数には、新しく追加された misc/dist/darwin/Distribution ファイルのパスが指定されます。このファイルはインストーラの動作とUIを制御します。
      • --package-path 引数には、pkgbuild で生成された中間パッケージが格納されている一時ディレクトリ (pkgdest) が指定されます。
      • 最後の引数 targ は、最終的な .pkg ファイルの出力パスです。
    • これにより、Goのインストーラは、より現代的なmacOSのパッケージング標準に準拠し、Distribution ファイルによる高度な制御が可能になりました。
  2. Distribution ファイルの導入 (misc/dist/darwin/Distribution):

    • このコミットで新規に追加されたXMLファイルです。
    • installer-script ルート要素を持ち、インストーラのメタデータと動作を定義します。
    • OSバージョンチェック: 最も重要な変更は、installation-check スクリプトの導入です。
      <installation-check script="installCheck();"/>
      <script>
      function installCheck() {
          if(!(system.compareVersions(system.version.ProductVersion, '10.6.0') >= 0)) {
              my.result.title = 'Unable to install';
              my.result.message = 'Go requires Mac OS X 10.6 or later.';
              my.result.type = 'Fatal';
              return false;
          }
          return true;
      }
      </script>
      
      このJavaScript関数 installCheck() は、インストーラが起動する前に実行されます。system.compareVersions を使用して現在のOSのバージョン (system.version.ProductVersion) が 10.6.0 以上であるかをチェックします。もし条件を満たさない場合、my.result オブジェクトにエラーメッセージとタイトルを設定し、Fatal タイプとしてインストールを中止します。これにより、ユーザーはインストールを開始する前に互換性の問題を明確に知ることができます。
  3. postinstall スクリプトの修正 (misc/dist/darwin/scripts/postinstall):

    • 実行権限の変更: ファイルのパーミッションが 100644 (rw-r--r--) から 100755 (rwxr-xr-x) に変更されました。これにより、スクリプトがインストーラによって正しく実行可能になります。
    • sudo.bash の削除:
      -echo "Fixing debuggers via sudo.bash"
      -# setgrp procmod the debuggers (sudo.bash)
      -cd $GOROOT/src
      -./sudo.bash
      
      Goのバイナリディストリビューションには、デバッガに関連する covprof といったツールがバンドルされていないため、それらの設定を行う sudo.bash の呼び出しは不要となり、削除されました。これはスクリプトの簡素化と、不要な特権操作の回避に貢献します。
    • Xcodeディレクトリチェックの修正:
      -if [ -f $XCODE_MISC_DIR ]; then
      +if [ -d "$XCODE_MISC_DIR" ]; then
      
      Xcodeの仕様ディレクトリ (/Library/Application Support/Developer/Shared/Xcode/Specifications/) の存在チェックが、ファイル (-f) ではなくディレクトリ (-d) として行われるように修正されました。これは、パスがディレクトリを指している場合に正しいチェックを行うための重要な修正です。

これらの技術的な変更により、GoのmacOSパッケージはより現代的で、ユーザーフレンドリーなインストール体験を提供できるようになりました。

コアとなるコードの変更箇所

misc/dist/bindist.go

--- a/misc/dist/bindist.go
+++ b/misc/dist/bindist.go
@@ -13,7 +13,6 @@ import (
 	"bytes"
 	"compress/gzip"
 	"encoding/base64"
-	"errors"
 	"flag"
 	"fmt"
 	"io"
@@ -41,8 +40,7 @@ var (
 )
 
 const (
-	packageMaker = "/Applications/Utilities/PackageMaker.app/Contents/MacOS/PackageMaker"
-	uploadURL    = "https://go.googlecode.com/files"
+	uploadURL = "https://go.googlecode.com/files"
 )
 
 var preBuildCleanFiles = []string{
@@ -231,7 +229,7 @@ func (b *Build) Do() error {
 			return err
 		}
 		localDir := filepath.Join(work, "usr/local")
-		err = os.MkdirAll(localDir, 0744)
+		err = os.MkdirAll(localDir, 0755)
 		if err != nil {
 			return err
 		}
@@ -240,27 +238,29 @@ func (b *Build) Do() error {
 			return err
 		}
 		// build package
-		pkginfo, err := createPackageInfo(work)
+		pkgdest, err := ioutil.TempDir("", "pkgdest")
 		if err != nil {
 			return err
 		}
-		defer os.Remove(pkginfo)
-		pm := packageMaker
-		if !exists(pm) {
-			pm = "/Developer" + pm
-			if !exists(pm) {
-				return errors.New("couldn't find PackageMaker")
-			}
+		defer os.RemoveAll(pkgdest)
+		dist := filepath.Join(runtime.GOROOT(), "misc/dist")
+		_, err = b.run("", "pkgbuild",
+			"--identifier", "com.googlecode.go",
+			"--version", "1.0",
+			"--scripts", filepath.Join(dist, "darwin/scripts"),
+			"--root", work,
+			filepath.Join(pkgdest, "com.googlecode.go.pkg"))
+		if err != nil {
+			return err
 		}
 		targ := base + ".pkg"
-		scripts := filepath.Join(work, "usr/local/go/misc/dist/darwin/scripts")
-		_, err = b.run("", pm, "-v",
-			"-r", work,
-			"-o", targ,
-			"--info", pkginfo,
-			"--scripts", scripts,
-			"--title", "Go",
-			"--target", "10.5")
+		_, err = b.run("", "productbuild",
+			"--distribution", filepath.Join(dist, "darwin/Distribution"),
+			"--package-path", pkgdest,
+			targ)
+		if err != nil {
+			return err
+		}
 		targs = append(targs, targ)
 	case "windows":
 		// Create ZIP file.
@@ -806,30 +806,3 @@ func tarFileInfoHeader(fi os.FileInfo, filename string) (*tar.Header, error) {
 	}
 	return h, nil
 }
-
-// createPackageInfo creates a PackageInfo template file for use with PackageMaker.
-// The returned filename points to a file in a temporary directory on the filesystem,
-// and should be removed after use.
-func createPackageInfo(work string) (filename string, err error) {
-	var size, nfiles int64
-	err = filepath.Walk(work, func(path string, info os.FileInfo, err error) error {
-		nfiles++
-		size += info.Size()
-		return nil
-	})
-	if err != nil {
-		return "", err
-	}
-	pi, err := ioutil.TempFile("", "PackageInfo")
-	if err != nil {
-		return "", err
-	}
-	defer pi.Close()
-	_, err = fmt.Fprintf(pi, "<pkg-info identifier=\"com.googlecode.go\" version=\"1.0\" followSymLinks=\"true\">\\n"+
-		"\\t<payload installKBytes=\"%v\" numberOfFiles=\"%v\"/>\\n"+
-		"</pkg-info>\\n", size/1024, nfiles)
-	if err != nil {
-		return "", err
-	}
-	return pi.Name(), nil
-}

misc/dist/darwin/Distribution (新規ファイル)

<?xml version="1.0" encoding="utf-8" standalone="no"?>
<installer-script minSpecVersion="1.000000">
    <title>Go</title>
    <options customize="never" allow-external-scripts="no"/>
    <domains enable_localSystem="true" />
    <installation-check script="installCheck();"/>
    <script>
function installCheck() {
    if(!(system.compareVersions(system.version.ProductVersion, '10.6.0') >= 0)) {
        my.result.title = 'Unable to install';
        my.result.message = 'Go requires Mac OS X 10.6 or later.';
        my.result.type = 'Fatal';
        return false;
    }
    return true;
}
    </script>
    <choices-outline>
        <line choice="com.googlecode.go.choice"/>
    </choices-outline>
    <choice id="com.googlecode.go.choice" title="Go">
        <pkg-ref id="com.googlecode.go.pkg"/>
    </choice>
    <pkg-ref id="com.googlecode.go.pkg" auth="Root">com.googlecode.go.pkg</pkg-ref>
</installer-script>

misc/dist/darwin/scripts/postinstall

--- a/misc/dist/darwin/scripts/postinstall
+++ b/misc/dist/darwin/scripts/postinstall
@@ -9,14 +9,9 @@ find bin -exec chmod ugo+rx \\{\\} \\;\n find . -type d -exec chmod ugo+rx \\{\\} \\;\n chmod o-w .\n \n-echo "Fixing debuggers via sudo.bash"\n-# setgrp procmod the debuggers (sudo.bash)\n-cd $GOROOT/src\n-./sudo.bash
-\n echo "Installing miscellaneous files:"\n XCODE_MISC_DIR="/Library/Application Support/Developer/Shared/Xcode/Specifications/"\n-if [ -f $XCODE_MISC_DIR ]; then
-+if [ -d "$XCODE_MISC_DIR" ]; then
 \techo "  XCode"\n \tcp $GOROOT/misc/xcode/* $XCODE_MISC_DIR\n fi

コアとなるコードの解説

misc/dist/bindist.go の変更点

  • PackageMaker 関連の削除: packageMaker 定数と createPackageInfo 関数が削除されました。これは、GoのmacOSパッケージ作成プロセスが PackageMaker から完全に移行したことを意味します。
  • ディレクトリパーミッションの変更: os.MkdirAll(localDir, 0744)os.MkdirAll(localDir, 0755) に変更されました。0744 は所有者に読み書き実行、グループとその他に読み取りのみを許可しますが、0755 は所有者に読み書き実行、グループとその他に読み取りと実行を許可します。これは、インストールされるディレクトリの実行権限をより適切に設定するための変更と考えられます。
  • pkgbuildproductbuild の導入:
    • ioutil.TempDir("", "pkgdest") で一時ディレクトリを作成し、pkgbuild コマンドの出力先として利用しています。
    • b.run("", "pkgbuild", ...): pkgbuild を呼び出し、Goのインストールルート (work) をペイロードとして、misc/dist/darwin/scripts をスクリプトディレクトリとして指定し、中間パッケージ (com.googlecode.go.pkg) を生成します。
    • b.run("", "productbuild", ...): productbuild を呼び出し、misc/dist/darwin/Distribution ファイルを配布設定として、pkgbuild で生成された中間パッケージ (pkgdest) をソースとして、最終的な .pkg ファイルを生成します。この Distribution ファイルが、OSバージョンチェックなどのインストーラ動作を定義します。
  • エラーハンドリングの改善: errors パッケージのインポートが不要になったのは、createPackageInfo 関数が削除されたためです。

misc/dist/darwin/Distribution の新規追加

このXMLファイルは、macOSインストーラの振る舞いを定義する中心的な役割を担います。

  • title: インストーラのウィンドウに表示されるタイトルを「Go」と設定します。
  • options: インストーラのカスタマイズを許可しない (customize="never")、外部スクリプトを許可しない (allow-external-scripts="no") といったオプションを設定します。
  • installation-check: ここが最も重要な部分で、installCheck() というJavaScript関数をインストールの事前チェックとして指定しています。
  • script ブロック: installCheck() 関数の実装が含まれています。
    • system.compareVersions(system.version.ProductVersion, '10.6.0') >= 0 は、現在のmacOSのバージョンが10.6.0以上であるかを比較します。
    • もしバージョンが要件を満たさない場合、my.result.titlemy.result.messagemy.result.type を設定し、Fatal タイプのエラーとしてインストールを中止します。これにより、ユーザーは「Go requires Mac OS X 10.6 or later.」という明確なメッセージを受け取ります。
  • choices-outlinechoice: インストールするパッケージの選択肢を定義します。ここでは「Go」という単一の選択肢が提供され、com.googlecode.go.pkg というパッケージを参照しています。
  • pkg-ref: 実際にインストールされるパッケージの参照を定義します。

misc/dist/darwin/scripts/postinstall の変更点

  • 実行権限の変更: ファイルモードが 100644 から 100755 に変更されました。これは、このスクリプトがインストーラによって実行される際に、適切な実行権限を持つことを保証します。
  • sudo.bash 関連コードの削除:
    • echo "Fixing debuggers via sudo.bash"cd $GOROOT/src; ./sudo.bash といった行が削除されました。
    • これは、Goのバイナリディストリビューションには covprof といったデバッガ関連ツールがバンドルされていないため、それらの設定を行うための sudo.bash の呼び出しが不要になったためです。これにより、スクリプトが簡素化され、不要な特権操作が回避されます。
  • Xcodeディレクトリチェックの修正:
    • if [ -f $XCODE_MISC_DIR ]; thenif [ -d "$XCODE_MISC_DIR" ]; then に変更されました。
    • XCODE_MISC_DIR/Library/Application Support/Developer/Shared/Xcode/Specifications/ を指しており、これはディレクトリです。[ -f ... ] はファイルが存在するかをチェックするのに対し、[ -d ... ] はディレクトリが存在するかをチェックします。この修正により、Xcode関連のファイルのコピーが、正しい条件で実行されるようになりました。

これらの変更は、GoのmacOSパッケージのビルドとインストールプロセスを、より現代的で堅牢、かつユーザーフレンドリーなものにするための重要なステップです。

関連リンク

参考にした情報源リンク