11#! /usr/bin/env bash
22# Build macOS release artefacts for the MRT2 AUv3 host app:
3+ # - .dmg disk image (drag MRT2 (AU).app to Applications)
34# - .pkg installer (installs to /Applications and registers the AU extension)
4- # - .dmg disk image (drag MRT2 (AU).app to Applications — no zip)
55#
66# Run from repo root after:
77# cmake --build build --target package_mrt2_au
88#
9- # Uses a single app copy (build/dist by default) to minimize peak disk usage.
9+ # Uses a single app copy (build/dist by default). DMG is built before PKG and
10+ # uses an explicitly sized read-write image — hdiutil -srcfolder often creates
11+ # a volume that is too small for signed .appex bundles (mlx.metallib copied last).
1012#
1113# Usage:
1214# ./scripts/build-installer-pkg.sh [--version 0.1.0] [--app path/to/MRT2\ \(AU\).app]
1315# ./scripts/build-installer-pkg.sh --sign-app
14- #
15- # Output (under release-artifacts/ by default):
16- # MRT2-AU3-<version>-macOS-Installer.pkg
17- # MRT2-AU3-<version>-macOS.dmg
1816
1917set -euo pipefail
2018
@@ -23,6 +21,7 @@ REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
2321cd " $REPO_ROOT "
2422
2523APP_BUNDLE_NAME=" MRT2 (AU).app"
24+ VOL_NAME=" MRT2 AU3"
2625PKG_ID=" com.audiohacking.mrt2-au3"
2726OUT_DIR=" ${OUT_DIR:- release-artifacts} "
2827SIGN_APP=false
@@ -36,7 +35,7 @@ while [ $# -gt 0 ]; do
3635 --app) APP_PATH=" $2 " ; shift 2 ;;
3736 --out-dir) OUT_DIR=" $2 " ; shift 2 ;;
3837 -h|--help)
39- sed -n ' 1,20p ' " $0 "
38+ sed -n ' 1,22p ' " $0 "
4039 exit 0
4140 ;;
4241 * ) echo " Unknown option: $1 " >&2 ; exit 1 ;;
5857
5958if [ ! -d " $APP_PATH " ]; then
6059 echo " Error: app bundle not found at: $APP_PATH " >&2
61- echo " Build first:" >&2
62- echo " cmake --build build --target package_mrt2_au" >&2
6360 exit 1
6461fi
6562
6663mkdir -p " $OUT_DIR "
6764PKG_FILE=" ${OUT_DIR} /MRT2-AU3-${PKG_VERSION} -macOS-Installer.pkg"
6865DMG_FILE=" ${OUT_DIR} /MRT2-AU3-${PKG_VERSION} -macOS.dmg"
6966
67+ disk_free () { df -h . | awk ' NR==2 {print $4}' ; }
68+
69+ create_dmg_from_app () {
70+ local app_path=" $1 "
71+ local dmg_path=" $2 "
72+ local volname=" $3 "
73+
74+ local src_mb dmg_mb tmp_rw mount_point dev
75+ src_mb=$( du -sm " $app_path " | awk ' {print $1}' )
76+ # Signed bundles + HFS+ metadata need headroom beyond du(1).
77+ dmg_mb=$(( src_mb + src_mb / 2 + 256 ))
78+ if [ " $dmg_mb " -lt 512 ]; then dmg_mb=512; fi
79+
80+ tmp_rw=" ${dmg_path% .dmg} .rw.dmg"
81+ mount_point=" /Volumes/${volname} "
82+ rm -f " $tmp_rw " " $dmg_path "
83+
84+ echo " Creating ${dmg_mb} MB DMG for ${src_mb} MB app (free: $( disk_free) )..."
85+ hdiutil create \
86+ -size " ${dmg_mb} m" \
87+ -volname " $volname " \
88+ -fs HFS+ \
89+ -layout SPUD \
90+ -format UDRW \
91+ -ov \
92+ " $tmp_rw "
93+
94+ dev=" "
95+ cleanup_dmg () {
96+ if [ -n " $dev " ]; then
97+ hdiutil detach " $dev " -quiet 2> /dev/null || hdiutil detach " $dev " -force 2> /dev/null || true
98+ fi
99+ rm -f " $tmp_rw "
100+ }
101+ trap cleanup_dmg RETURN
102+
103+ dev=$( hdiutil attach -readwrite -noverify -noautoopen " $tmp_rw " | awk ' /^\/dev\// {print $1; exit}' )
104+ ditto " $app_path " " ${mount_point} /$( basename " $app_path " ) "
105+ sync
106+ hdiutil detach " $dev " -quiet
107+ dev=" "
108+
109+ hdiutil convert " $tmp_rw " -format UDZO -imagekey zlib-level=9 -o " $dmg_path "
110+ rm -f " $tmp_rw "
111+ trap - RETURN
112+ }
113+
70114echo " === Packaging from single app copy ==="
71115echo " App: $APP_PATH "
72- echo " Free: $( df -h . | awk ' NR==2 {print $4} ' ) "
116+ echo " Free: $( disk_free ) "
73117du -sh " $APP_PATH "
74118
75119if [ " $SIGN_APP " = true ]; then
76- echo " Ad-hoc signing app bundle in place (including mlx.metallib) ..."
120+ echo " Ad-hoc signing app bundle in place..."
77121 METALLIB=" $( find " $APP_PATH " -name mlx.metallib -print -quit || true) "
78122 if [ -n " $METALLIB " ]; then
79123 xcrun codesign --force --sign - " $METALLIB "
@@ -85,11 +129,14 @@ if [ "$SIGN_APP" = true ]; then
85129 xcrun codesign --force --sign - " $APP_PATH "
86130fi
87131
88- echo " Building .pkg: $PKG_FILE "
132+ echo " Building .dmg: $DMG_FILE "
133+ create_dmg_from_app " $APP_PATH " " $DMG_FILE " " $VOL_NAME "
134+
135+ echo " Building .pkg: $PKG_FILE (free: $( disk_free) )"
89136PAYLOAD_DIR=" $( mktemp -d) "
90137SCRIPTS_DIR=" $( mktemp -d) "
91- cleanup_temps () { rm -rf " $PAYLOAD_DIR " " $SCRIPTS_DIR " ; }
92- trap cleanup_temps EXIT
138+ cleanup_pkg_temps () { rm -rf " $PAYLOAD_DIR " " $SCRIPTS_DIR " ; }
139+ trap cleanup_pkg_temps EXIT
93140
94141mkdir -p " ${PAYLOAD_DIR} /Applications"
95142ditto " $APP_PATH " " ${PAYLOAD_DIR} /Applications/${APP_BUNDLE_NAME} "
@@ -104,25 +151,7 @@ pkgbuild \
104151 --install-location / \
105152 " $PKG_FILE "
106153
107- cleanup_temps
108- trap - EXIT
109-
110- echo " Building .dmg: $DMG_FILE "
111- echo " Free before hdiutil: $( df -h . | awk ' NR==2 {print $4}' ) "
112- rm -f " $DMG_FILE "
113- # -format UDZO compresses; peak usage is lower when no extra staging copy exists.
114- hdiutil create \
115- -volname " MRT2 AU3" \
116- -srcfolder " $APP_PATH " \
117- -ov \
118- -format UDZO \
119- " $DMG_FILE "
120-
121154echo " "
122155echo " Created:"
123- echo " ${PKG_FILE} ($( du -h " $PKG_FILE " | awk ' {print $1}' ) )"
124156echo " ${DMG_FILE} ($( du -h " $DMG_FILE " | awk ' {print $1}' ) )"
125- echo " "
126- echo " Install with GUI: open \" ${PKG_FILE} \" "
127- echo " Install with CLI: sudo installer -pkg \" ${PKG_FILE} \" -target /"
128- echo " Manual install: open \" ${DMG_FILE} \" and drag ${APP_BUNDLE_NAME} to Applications"
157+ echo " ${PKG_FILE} ($( du -h " $PKG_FILE " | awk ' {print $1}' ) )"
0 commit comments