Mac用Installerの作り方

表題についてまとめていきたい

# 作り方

PackageMakerは止めて、pkgbuild, productbuildでinstallerを作るのがよさそう
http://stackoverflow.com/questions/11487596/making-os-x-installer-packages-like-a-pro-xcode4-developer-id-mountain-lion-re

基本方針は上記linkと同じ
1) pkgbuild pkgつくる
2) productbuild distribution(といってもextensionは同じpkg)つくる
3) productsign signする
ここまでで ship可能な pkg をつくるところまで。

ここから、さらに 1. dmg をつくり、2. Notary Service に upload して公称をうけ、3. offline時でのinstallのための staple を貼る。ところまでmemoを残します。
4) dmg をつくる
5) Apple Notary Service に upload して公称してもらう。
6) staple をはる


## 1) pkgをつくる

.pkgファイルをつくります。.pkgには、installされるfileや、install時に走るscriptなどを入れます。また、どのようにinstallするのかについてのoptionを付けることも出来ます。
そのようなことを記述するplistファイルをまず用意する必要があります。
それにはanalyzeコマンドでひな形を作ってあげるのが楽です。

### 1.1) pkgbuild で plist(設定file)のひな形をつくる

```
pkgbuild --analyze --root root-path plist-output-path
```

このコマンドでまず、pkgを作るのに必要なplistのひな形をつくります。
rootで指定するdirにinstallするappなどを、"所定の位置に"おきます。
たとえば、Applications以下にFoo.appをinstallするつもりなら
/path/to/root/以下に、
Applications という擬似的なdir構造を作って、そこにFoo.appを置きます。
そして
pkgbuild --analyze --root /path/to/root foo.plistを実行すると
以下のようなplistが得られます。

```html
        <dict>
                <key>BundleHasStrictIdentifier</key>
                <true/>
                <key>BundleIsRelocatable</key>
                <false/>
                <key>BundleIsVersionChecked</key>
                <true/>
                <key>BundleOverwriteAction</key>
                <string>upgrade</string>
                <key>RootRelativeBundlePath</key>
                <string>Applications/Foo.app</string>
                <key>BundlePostInstallScriptPath</key>
                <string></string>
                <key>BundlePreInstallScriptPath</key>
                <string></string>
        </dict>
```

dictはbundle(app, vst など) 毎に用意されます。
以下、key/valueについて；
Relocatableはfalseがいいと思います。developerの環境でこれをtrueにしてinstallかけると、install場所としてbuild dirが勝手に指定されちゃったりして困ることになると思います。

BundlePostInstallScriptPath, BundlePreInstallScriptPathはないのなら削除してください（その意味で上のは悪い例です）。削除しないとcurrent position が scriptだとinstallerに思われてしまうようです。このScriptPathはpkgのscriptとは別に走らせたい場合において記述します。

くわしくは
```man pkgbuild``` してください(darwin manpage は公式からは手繰れなくなってしまった)。


さてでは実際にpkgを作ります。

### 1.2) pkgbuildでpkgファイルをつくる

```
pkgbuild --root rootDir --component-plist plistPath --scripts scriptDir --identifier id --version version --install-location location Foo.pkg
```

root はanalyzeしたときのrootと同じです。

component-plistはさっき作ったplist

scripts は script が入っているfolderを指定します。plist で ScriptPathを指定してる場合はこのfolderにscriptを置きます。また、implicit に このfolderに置いたpre/postInstall は pkgが走らせるものと決まっている模様。

identifier は installer/pkg の jp.co.somewhere... みたいなやつです。

versionはinstaller/pkg のversionです

これで flat pkgが出来上がります。
出来上がった後、中身を操作したい場合は
pkgutil --expand  xx.pkg out で展開。のち、packageする場合は
pkgutil --flatten で出来ます。 .svnとか不必要に入ってたら 展開して削除してもよいかも。(root以下の.svnなどはpkgbuildがやってくれるのだが(--filter optionの解説を見よ), script folder とかはやってくれない。


## 2) 次に pkg を distibution形式にします。

distribution形式とはmpkgみたいな、中に1つ以上のpkgをもったpkgです。
pkgがひとつであっても、必ず包むと考えるべき。

まずは distributionを定義するxmlを作るところからstart。

### 2.1) 設定ファイル(xml)のひな形をつくる

```
productbuild --synthesize --package Foo.pkg Distribution.xml
```

こんな感じ。これで出来たxmlをひな形にして、titleやbg, versionなどを適宜変更する。逆に言うと さっきつくったpkgにはbgやtitleを独自に入れるinterfaceがなかった(=公開用のpkgではない)ってことです！

xmlの定義は以下を参照
https://developer.apple.com/library/mac/#documentation/DeveloperTools/Reference/DistributionDefinitionRef/Chapters/Distribution_XML_Ref.html#//apple_ref/doc/uid/TP40005370-CH100-SW14


で実際にdistribution形式にパッケージする。

### 2.2) pkgをdistribution形式にする

```
productbuild  --distribution "Distribution.xml" --package-path /path/to/pkgDir --resources "/path/to/rsrcDir" "Bar.pkg"
```

こんな感じ。parameterについてはみれば分かると思うので、割愛します。
ここで分かるのはscriptを指定する方法がdistributionにはないということですね。つまり、scriptは個別のpkgで持つしかない、です。
mpkgと比較していえば、mpkgにつけてたscriptどうすんの？てのがあるんですけど、1)使わないで何とかする, 2)裏技としてpayload=0 つまり、実際には何もinstallしないpkgをつくってこれにscriptだけを持たせるという方法があります。
僕のケースでいえば、使わないようにしました。


## 3)最後にsignしておわり

```
productsign  --sign "Developer ID Installer: Somewhere Inc."  "Bar.pkg" "Installer.pkg"
```

## 4)dmg をつくる
実際にuserに配るときにはdmgにするのが、macOSだと基本でしょうか。ということでつくったpkgをdmgに入れます。

```
hdiutil create -srcfolder "/path/to/folder" -fs HFS+ -format UDZO -volname "name_you_see_when_mounting_the_volume" "/path/to/filename.dmg"
```
このコマンドの前に、 srcfolder に必要なfileを入れておきます。 pkg とか readme とかそういうやつです。.DS_Storeを用意しておくと、imageをmountしたときに見える風景を調整できるのでぜひしましょう。(実際に finder で そのfolderをひらいて、fileをよい場所において、できる.DS_Storeを何らか保存しておき、それを /path/to/folderに配置する)
-fs とか -format は好きなものにしましょう。ここはなんかtipsあったかもなので、追記するかも。

## 5) 公称をうける。
https://developer.apple.com/documentation/security/notarizing_your_app_before_distribution/customizing_the_notarization_workflow?language=objc
ここを読んでね。なのですが、


```
#比較的新しい方法
notarytool submit --keychain-profile TOKEN_IN_KEYCHAIN --wait "/path/to/file.dmg"
```
- tokenのkeychain への登録などは、こちらなどを参照https://qiita.com/spc_gmorimoto/items/6b31d82ab28202eff603

``` 
#少し古い方法
xcrun altool --notarize-app -f "/path/to/file.dmg" --primary-bundle-id "jp.mycompany.myappname" -u me@mail.jp -p @keychain:PWD_IN_KEYCHAIN
```
- primary-bundle-idはここでしか使われないidentifier。適宜名前をつけましょう。
- Upload には AppleID が必要です。さらに2step validationしてる場合、tokenが必要になるので、appleid.apple.comにいってtokenをgetしましょう。
- このtoken を keychain にて PWD_IN_KEYCHAIN (名前はもちろん自由) として store すれば、上記のような形式で呼び出し可能です。

### notarization で error が出た場合
まずは、どんなerrorが出ているか調べます。

```
xcrun altool --notarization-info MAGIC_NUMBER_DISTRIBUTED_BY_APPLE -u me@mail.jp -p @keychain:PWD_IN_KEYCHAIN
```

- app の場合、hardened してないから、と言われることがあります。xcodeで Hardened Runtime を on にします
- code_sign してないから、といわれることがあります。Xcodeで Code Sign するように組みましょう (Developer ID Application)
- 有効なtimestamp がついてないから、と言われることがあります。 Archive から、Exportするときには timestampが付くようですが、Release build して、それをpackagingするようになってたりする場合、```OTHER_CODE_SIGN_FLAGS``` に ```--timestamp``` をつけます
- The executable requests the com.apple.security.get-task-allow entitlement. の場合は、release時のみ、```CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO``` などして、回避しましょう

## 6) stapleをはります。
appなどを起動する際、初めての場合は公称を受けているのかappleのサーバーに確認に行くのですが、offlineの場合はできません。なので、package自体にラベルを貼って、公称済みかどうかを知らせます。これが staple です。

```
xcrun stapler staple /path/to/dmg
```
これは、Appleから公称を受けた後に行います。以上で作成された dmg を distribute します。


# その他メモ
## pkgbuildでつくったpkgのpayloadも開きたい。
```pkgutil --expand-full [pkg] [dir] ```
## pkgの内容物をunarchiveせずに閲覧したい
```lsbom [options] /path/to/BOM```
## uninstaller をつくりたい
```pkgutil --files ${PKG_ID}```
でinstallしたfileリストはでるので、あとはお好みで削除してください