macOSアプリを配布するdmgは、開くと、アプリとApplicationsフォルダのショートカットが並び、背景に説明の画像が表示されているものが多い。ユーザーは、アプリのアイコンをApplicationsにドラッグしてインストールできる。このレイアウトは、dmgの中の/Applicationsへのシンボリックリンクと、Finderのウィンドウ設定を保存する.DS_Storeで作る。

$ ln -s /Applications stage/Applications

ここでは、/Applicationsへのシンボリックリンクを入れる方法と、Finderにosascriptで背景画像とアイコンの配置を設定させる方法を紹介する。実行例はmacOS 27.0.1のもの。

参考: 【hdiutil】フォルダからdmgを作る

dmgに入れるファイルを用意する

dmgに入れるファイルを、1つのフォルダ(ここではstage)にまとめる。アプリ、/Applicationsへのシンボリックリンク、背景画像を入れる.backgroundフォルダを用意する。

$ mkdir -p stage/.background
$ cp -R [アプリ名].app stage/
$ ln -s /Applications stage/Applications
$ cp [背景画像].png stage/.background/background.png

.で始まる.backgroundは隠しフォルダなので、Finderには表示されない。背景画像を、この隠しフォルダに置くと、ユーザーのウィンドウに画像ファイルが表示されない。

シンボリックリンクはdmgに保たれる

/Applicationsへのシンボリックリンクと隠しフォルダは、hdiutil createとdiskutil image create fromのどちらでも、そのままdmgに入る。

$ hdiutil create -volname "[ボリューム名]" -srcfolder stage -format UDZO -ov hd.dmg
$ diskutil image create from --volumeName "[ボリューム名]" stage di.dmg
$ diskutil image attach --readOnly --mountOptions nobrowse --mountPoint [マウントポイント] hd.dmg
$ ls -A [マウントポイント]
.background
Applications
Demo.app
$ readlink [マウントポイント]/Applications
/Applications

この時点でも、dmgを開くと、アプリとApplicationsが表示される。ただし、背景画像はなく、アイコンの位置も決まっていない。背景画像とアイコンの配置は、Finderの設定として、.DS_Storeに保存する必要がある。

背景画像を用意する

背景画像は、ウィンドウの大きさに合わせた画像を用意する。ここでは、600×400ピクセルの画像を使う。sipsで、画像の大きさを変更できる。

$ sips -z 400 600 [元の画像].png --out stage/.background/background.png

sips -zで変更するのは大きさだけで、DPIは元の画像から引き継がれる。

画像のDPIを72にする

Finderは、画像のDPI(解像度の情報)を見て、表示する大きさを決める。DPIが72であれば、1ピクセルが1ポイントで表示される。DPIが96の画像は、4分の3の大きさに縮小されて表示された。

sips -gで、画像のDPIを確認できる。

$ sips -g pixelWidth -g pixelHeight -g dpiWidth -g dpiHeight stage/.background/background.png
  pixelWidth: 600
  pixelHeight: 400
  dpiWidth: 96.000
  dpiHeight: 96.000

DPIが96の600×400ピクセルの画像を背景に指定すると、ウィンドウの左上に450×300ポイントで表示され、右側と下側に空白ができた。

$ sips -s dpiWidth 72 -s dpiHeight 72 stage/.background/background.png

DPIを72に変更した画像では、ウィンドウの幅いっぱいに、等倍で表示された。

Finderにレイアウトを設定させる

背景画像やアイコンの位置は、Finderのウィンドウの設定である。Finderに、AppleScriptでウィンドウの設定をさせる。設定は、dmgの中の.DS_Storeというファイルに保存される。

Finderから操作するため、書き込み可能なdmgを作り、Finderに見える形でマウントする。--mountOptions nobrowseは付けない。

$ hdiutil create -size 20m -fs APFS -volname "[ボリューム名]" -ov layout-rw
$ diskutil image attach layout-rw.dmg

マウントポイントを指定しないと、/Volumes/[ボリューム名]にマウントされる。ファイルをコピーする。

$ cp -R stage/[アプリ名].app "/Volumes/[ボリューム名]/"
$ ln -s /Applications "/Volumes/[ボリューム名]/Applications"
$ mkdir -p "/Volumes/[ボリューム名]/.background"
$ cp stage/.background/background.png "/Volumes/[ボリューム名]/.background/"

osascriptで、Finderにウィンドウの設定をさせる。

$ osascript <<'EOF'
tell application "Finder"
  tell disk "[ボリューム名]"
    open
    set current view of container window to icon view
    set toolbar visible of container window to false
    set statusbar visible of container window to false
    set the bounds of container window to {200, 120, 800, 520}
    set theViewOptions to the icon view options of container window
    set arrangement of theViewOptions to not arranged
    set icon size of theViewOptions to 96
    set background picture of theViewOptions to file ".background:background.png"
    set position of item "[アプリ名].app" of container window to {150, 200}
    set position of item "Applications" of container window to {450, 200}
    close
    open
    update without registering applications
    delay 2
    close
  end tell
end tell
EOF

実行すると、Finderのウィンドウが開いて、数秒後に閉じる。AppleScriptの主な設定は、次のとおりである。

設定内容
current viewアイコン表示にする
toolbar visible・statusbar visibleツールバーとステータスバーの表示
boundsウィンドウの位置と大きさ(左、上、右、下)
arrangementアイコンの整列(not arrangedは自動で整列しない)
icon sizeアイコンの大きさ
background picture背景画像。dmgの中のパスを、:区切りで指定する
position各アイテムの位置(左、上)

disk "[ボリューム名]"で、Finderに操作させるボリュームを指定する。同じ名前のボリュームが、すでにマウントされていないことを確認してから実行する。

.DS_Storeに設定が保存される

AppleScriptが成功すると、ボリュームのルートに、.DS_Storeが作られる。

$ ls -la "/Volumes/[ボリューム名]/.DS_Store"
-rw-r--r-- 1 sue staff 10244 ... /Volumes/[ボリューム名]/.DS_Store

.DS_Storeの中には、ウィンドウの設定(bwsp)、アイコン表示の設定(icvp)、アイテムごとの位置(Iloc)が記録されていた。bwspは、次のような内容だった。

{'ShowStatusBar': False, 'ShowToolbar': False, 'ShowTabView': True, 'ContainerShowSidebar': False,
 'WindowBounds': '{{200, 920}, {600, 400}}', 'ShowSidebar': False}

WindowBoundsの大きさ(600×400)は、AppleScriptのboundsと一致した。位置の値は、boundsで指定した上端(120)とは別の値(920)で記録された。

icvpには、アイコンの大きさ(iconSize: 96.0)と整列(arrangeBy: none)が記録されていた。背景は画像を示す値(backgroundType: 2)で、背景画像の参照も含まれていた。Ilocには、Applicationsの位置(450, 200)と、アプリの位置(150, 200)が記録されていた。

配布用のdmgに変換する

Finderの設定が終わったら、dmgをeject(マウントを解除)して、読み取り専用の圧縮した形式に変換する。

$ diskutil eject "/Volumes/[ボリューム名]"
$ diskutil image create from --format UDZO layout-rw.dmg layout-final.dmg
layout-final.dmg created

変換したdmgをマウントして確認すると、.DS_Storeが残っていた。

$ diskutil image attach --readOnly --mountOptions nobrowse --mountPoint [マウントポイント] layout-final.dmg
$ ls -la [マウントポイント]
drwxr-xr-x  3 sue staff    96 ... .background
-rw-r--r--  1 sue staff 10244 ... .DS_Store
drwx------  5 sue staff   160 ... .fseventsd
lrwxr-xr-x  1 sue staff    13 ... Applications -> /Applications
drwxr-xr-x  3 sue staff    96 ... [アプリ名].app

.fseventsdは、macOSがボリュームに自動で作るディレクトリである。このディレクトリも、dmgに入った。

参考: 【diskutil】image createでdmgを作る

実際の見た目を確認する

作成したdmgを、Finderで開く。

$ open layout-final.dmg

次の結果になった。

  • 背景画像が表示され、[アプリ名]が左、Applicationsが右に、指定した位置に並んだ
  • DPIが96の背景画像は、450×300ポイントに縮小され、右と下に空白ができた。DPIを72にすると、ウィンドウの幅いっぱいに表示された
  • ツールバーは、消えなかった

ツールバーは、.DS_StoreにShowToolbar: Falseと記録されたが、macOS 27.0.1のFinderでは、表示されたままだった。

ウィンドウは、指定した大きさ(600×400)で、タイトルバーとツールバーを含んだ大きさだった。背景画像の縦は、その分だけ、ウィンドウに収まる範囲が小さくなる。背景画像の下側にアイコンの説明などを入れる場合は、ウィンドウの大きさと配置を確認して調整する。

署名と公証

レイアウトを設定したdmgも、署名と公証が必要である。署名と公証の手順は、別の記事で紹介している。

参考: 【notarytool】dmgを公証する (submit)