changeset 34:efb73feb1061

Enough MacOS bits to be able to do a quick proof-of-concept test
author Chris Cannam
date Fri, 13 Dec 2019 14:14:57 +0000
parents dd41d7369ed5
children d88317090948
files .hgignore deploy/osx/Entitlements.plist deploy/osx/Info.plist deploy/osx/copy-qt.sh deploy/osx/deploy-and-package.sh deploy/osx/deploy.sh deploy/osx/notarize.sh deploy/osx/paths.sh deploy/osx/qt.conf deploy/osx/sign.sh installer.cpp version.h
diffstat 12 files changed, 430 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Fri Dec 13 13:54:27 2019 +0000
+++ b/.hgignore	Fri Dec 13 14:14:57 2019 +0000
@@ -41,3 +41,7 @@
 out/.signed
 vamp-plugin-pack-installer
 o/
+.notarization-status
+.notarization-uuid
+*.dmg
+*.app
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/deploy/osx/Entitlements.plist	Fri Dec 13 14:14:57 2019 +0000
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<plist version="1.0">
+    <dict>
+        <key>com.apple.security.app-sandbox</key>
+        <false/>
+    </dict>
+</plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/deploy/osx/Info.plist	Fri Dec 13 14:14:57 2019 +0000
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleName</key>
+	<string>Vamp Plugin Pack Installer</string>
+	<key>CFBundleExecutable</key>
+	<string>Vamp Plugin Pack Installer</string>
+	<key>CFBundleIdentifier</key>
+	<string>org.sonicvisualiser.VampPluginPackInstaller</string>
+	<key>CFBundleShortVersionString</key>
+	<string>PACK_VERSION</string>
+
+	<!-- enable HiDPI -->
+	<key>NSPrincipalClass</key>
+	<string>NSApplication</string>
+	<key>NSHighResolutionCapable</key>
+	<string>True</string>
+  </dict>
+</plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/deploy/osx/copy-qt.sh	Fri Dec 13 14:14:57 2019 +0000
@@ -0,0 +1,62 @@
+#!/bin/bash
+
+set -eu
+
+app="$1"
+if [ -z "$app" ]; then
+	echo "Usage: $0 <appname>"
+	echo "Provide appname without the .app extension, please"
+	exit 2
+fi
+
+frameworks="QtCore QtNetwork QtGui QtXml QtSvg QtWidgets QtPrintSupport QtDBus"
+
+plugins="gif icns ico jpeg tga tiff wbmp webp cocoa minimal offscreen macstyle"
+
+qtdir=$(grep "Command:" Makefile | head -1 | awk '{ print $3; }' | sed s,/bin/.*,,)
+
+if [ ! -d "$qtdir" ]; then
+    echo "Failed to discover Qt installation directory from Makefile, exiting"
+    exit 2
+fi
+
+fdir="$app.app/Contents/Frameworks"
+pdir="$app.app/Contents/plugins"
+
+mkdir -p "$fdir"
+mkdir -p "$pdir"
+
+echo
+echo "Copying frameworks..."
+for fwk in $frameworks; do
+    if [ ! -d "$qtdir/lib/$fwk.framework" ]; then
+	if [ "$fwk" = "QtDBus" ]; then
+	    echo "QtDBus.framework not found, assuming Qt was built without DBus support"
+	    continue
+	fi
+    fi
+    cp -v "$qtdir/lib/$fwk.framework/$fwk" "$fdir" || exit 2
+done
+
+echo "Done"
+
+echo
+echo "Copying plugins..."
+for plug in $plugins; do
+    pfile=$(ls "$qtdir"/plugins/*/libq"$plug".dylib)
+    if [ ! -f "$pfile" ]; then
+	echo "Failed to find plugin $plug, exiting"
+	exit 2
+    fi
+    target="$pdir"/${pfile##?*plugins/}
+    tdir=`dirname "$target"`
+    mkdir -p "$tdir"
+    cp -v "$pfile" "$target" || exit 2
+done
+
+# Sometimes the copied-in files are read-only: correct that
+chmod -R u+w "$app.app"
+
+echo "Done"
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/deploy/osx/deploy-and-package.sh	Fri Dec 13 14:14:57 2019 +0000
@@ -0,0 +1,97 @@
+#!/bin/bash
+
+# Run this from the project root, without arguments, or with the
+# single argument --no-notarization to skip the notarize step
+
+set -e
+
+notarize=yes
+if [ "$1" = "--no-notarization" ]; then
+    notarize=no
+elif [ -n "$1" ]; then
+    echo "Usage: $0 [--no-notarization]"
+    exit 2
+fi
+
+set -u
+
+app="Vamp Plugin Pack Installer"
+
+version=`perl -p -e 's/^[^"]*"([^"]*)".*$/$1/' version.h`
+
+source="$app.app"
+volume="$app"-"$version"
+target="$volume"/"$app".app
+dmg="$volume".dmg
+
+if [ -d "$volume" ]; then
+    echo "Target directory $volume already exists, not overwriting"
+    exit 2
+fi
+
+if [ -f "$dmg" ]; then
+    echo "Target disc image $dmg already exists, not overwriting"
+    exit 2
+fi
+
+if [ "$notarize" = no ]; then
+    echo
+    echo "Note: The --no-notarization flag is set: won't be submitting for notarization"
+fi
+
+echo
+echo "(Re-)running deploy script..."
+
+deploy/osx/deploy.sh "$app" || exit 1
+
+echo
+echo "Making target tree."
+
+mkdir "$volume" || exit 1
+
+ln -s /Applications "$volume"/Applications
+#cp README.md "$volume/README.txt"
+#cp README.OSC "$volume/README-OSC.txt"
+#cp COPYING "$volume/COPYING.txt"
+#cp CHANGELOG "$volume/CHANGELOG.txt"
+#cp CITATION "$volume/CITATION.txt"
+cp -rp "$source" "$target"
+
+# update file timestamps so as to make the build date apparent
+find "$volume" -exec touch \{\} \;
+
+echo "Done"
+
+echo
+echo "Code-signing volume..."
+
+deploy/osx/sign.sh "$volume" || exit 1
+
+echo "Done"
+
+echo
+echo "Making dmg..."
+
+rm -f "$dmg"
+
+hdiutil create -srcfolder "$volume" "$dmg" -volname "$volume" -fs HFS+ && 
+	rm -r "$volume"
+
+echo "Done"
+
+echo
+echo "Signing dmg..."
+
+codesign -s "Developer ID Application: Chris Cannam" -fv "$dmg"
+
+if [ "$notarize" = no ]; then
+    echo
+    echo "The --no-notarization flag was set: not submitting for notarization"
+else
+    echo
+    echo "Submitting dmg for notarization..."
+
+    deploy/osx/notarize.sh "$dmg" || exit 1
+fi
+
+echo "Done"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/deploy/osx/deploy.sh	Fri Dec 13 14:14:57 2019 +0000
@@ -0,0 +1,58 @@
+#!/bin/bash
+
+set -e
+
+# Execute this from the top-level directory of the project (the one
+# that contains the .app bundle).  Supply the name of the application
+# as argument.
+#
+# This now performs *only* the app deployment step - copying in
+# libraries and setting up paths etc. It does not create a
+# package. Use deploy-and-package.sh for that.
+
+app="$1"
+source="$app.app"
+
+if [ -z "$app" ] || [ ! -d "$source" ] || [ -n "$2" ]; then
+	echo "Usage: $0 <app>"
+	echo "  e.g. $0 MyApplication"
+ 	echo "  The app bundle must exist in ./<app>.app."
+	echo "  Version number will be extracted from version.h."
+	exit 2
+fi
+
+set -u
+
+version=`perl -p -e 's/^[^"]*"([^"]*)".*$/$1/' version.h`
+stem=${version%%-*}
+stem=${stem%%pre*}
+case "$stem" in
+    [0-9].[0-9]) bundleVersion="$stem".0 ;;
+    [0-9].[0-9].[0-9]) bundleVersion="$stem" ;;
+    *) echo "Error: Version stem $stem (of version $version) is neither two- nor three-part number" ;;
+esac
+
+echo
+echo "Copying in frameworks and plugins from Qt installation directory."
+
+deploy/osx/copy-qt.sh "$app" || exit 2
+
+echo
+echo "Fixing up paths."
+
+deploy/osx/paths.sh "$app"
+
+echo
+echo "Copying in qt.conf to set local-only plugin paths."
+echo "Make sure all necessary Qt plugins are in $source/Contents/plugins/*"
+echo "You probably want platforms/, accessible/ and imageformats/ subdirectories."
+cp deploy/osx/qt.conf "$source"/Contents/Resources/qt.conf
+
+echo
+echo "Writing version $bundleVersion in to bundle."
+echo "(This should be a three-part number: major.minor.point)"
+
+perl -p -e "s/PACK_VERSION/$bundleVersion/" deploy/osx/Info.plist \
+    > "$source"/Contents/Info.plist
+
+echo "Done: check $source/Contents/Info.plist for sanity please"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/deploy/osx/notarize.sh	Fri Dec 13 14:14:57 2019 +0000
@@ -0,0 +1,83 @@
+#!/bin/bash
+
+## The following assumes we have generated an app password at
+## appleid.apple.com and then stored it to keychain id "altool" using
+## e.g.
+## security add-generic-password -a "cannam+apple@all-day-breakfast.com" \
+##   -w "generated-app-password" -s "altool"
+
+## NB to verify:
+# spctl -a -v "/Applications/Application.app"
+
+user="cannam+apple@all-day-breakfast.com"
+bundleid="org.sonicvisualiser.VampPluginPackInstaller"
+
+set -e
+
+dmg="$1"
+
+if [ ! -f "$dmg" ] || [ -n "$2" ]; then
+    echo "Usage: $0 <dmg>"
+    echo "  e.g. $0 MyApplication-1.0.dmg"
+    exit 2
+fi
+
+set -u
+
+echo
+echo "Uploading for notarization..."
+
+uuidfile=.notarization-uuid
+statfile=.notarization-status
+rm -f "$uuidfile" "$statfile"
+
+xcrun altool --notarize-app \
+    -f "$dmg" \
+    --primary-bundle-id "$bundleid" \
+    -u "$user" \
+    -p @keychain:altool 2>&1 | tee "$uuidfile"
+
+uuid=$(cat "$uuidfile" | grep RequestUUID | awk '{ print $3; }')
+
+if [ -z "$uuid" ]; then
+    echo
+    echo "Failed (no UUID returned, check output)"
+    exit 1
+fi
+
+echo "Done, UUID is $uuid"
+
+echo
+echo "Waiting and checking for completion..."
+
+while true ; do
+    sleep 30
+
+    xcrun altool --notarization-info \
+	"$uuid" \
+	-u "$user" \
+	-p @keychain:altool 2>&1 | tee "$statfile"
+
+    if grep -q 'Package Approved' "$statfile"; then
+	echo
+	echo "Approved! Status output is:"
+	cat "$statfile"
+	break
+    elif grep -q 'in progress' "$statfile" ; then
+	echo
+	echo "Still in progress... Status output is:"
+	cat "$statfile"
+	echo "Waiting..."
+    else 
+	echo
+	echo "Failure or unknown status in output:"
+	cat "$statfile"
+	exit 2
+    fi
+done
+
+echo
+echo "Stapling to package..."
+
+xcrun stapler staple "$dmg" || exit 1
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/deploy/osx/paths.sh	Fri Dec 13 14:14:57 2019 +0000
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+set -e
+
+app="$1"
+if [ -z "$app" ]; then
+	echo "Usage: $0 <appname>"
+	echo "Provide appname without the .app extension, please"
+	exit 2
+fi
+
+set -u
+
+frameworks="QtCore QtNetwork QtGui QtXml QtSvg QtWidgets QtPrintSupport QtDBus"
+
+echo
+echo "I expect you to have already copied these frameworks from the Qt installation to"
+echo "$app.app/Contents/Frameworks -- expect errors to follow if they're missing:"
+echo "$frameworks"
+echo
+
+echo "Fixing up loader paths in binaries..."
+
+for fwk in $frameworks; do
+    install_name_tool -id $fwk "$app.app/Contents/Frameworks/$fwk"
+done
+
+find "$app.app" -name \*.dylib -print | while read x; do
+    install_name_tool -id "`basename \"$x\"`" "$x"
+done
+
+for fwk in $frameworks; do
+    find "$app.app" -type f -print | while read x; do
+	if [ -x "$x" ]; then
+            current=$(otool -L "$x" | grep "$fwk" | grep amework | grep -v ':$' | awk '{ print $1; }')
+            [ -z "$current" ] && continue
+            echo "$x has $current"
+            relative=$(echo "$x" | sed -e "s,$app.app/Contents/,," \
+				       -e 's,[^/]*/,../,g' \
+				       -e 's,/[^/]*$,/Frameworks/'"$fwk"',' )
+            echo "replacing with relative path $relative"
+            install_name_tool -change "$current" "@loader_path/$relative" "$x"
+	fi
+    done
+done
+
+find "$app.app" -type f -print | while read x; do
+    if [ -x "$x" ]; then
+	qtdep=$(otool -L "$x" | grep Qt | grep amework | grep -v ':$' | grep -v '@loader_path' | awk '{ print $1; }')
+	if [ -n "$qtdep" ]; then
+	    echo
+	    echo "ERROR: File $x depends on Qt framework(s) not apparently present in the bundle:"
+	    echo $qtdep
+	    exit 1
+	fi
+    fi
+done
+
+echo "Done: be sure to run the app and see that it works!"
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/deploy/osx/qt.conf	Fri Dec 13 14:14:57 2019 +0000
@@ -0,0 +1,2 @@
+[Paths]
+Plugins = plugins
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/deploy/osx/sign.sh	Fri Dec 13 14:14:57 2019 +0000
@@ -0,0 +1,25 @@
+#!/bin/bash 
+
+set -eu
+
+# Execute this from the top-level directory of the project (the one
+# that contains the .app bundle).  Supply the name of the .app bundle
+# as argument
+dir="$1"
+if [ -z "$dir" ] || [ ! -d "$dir" ]; then
+	echo "Usage: $0 <pkgdir>"
+	echo "Where pkgdir is the directory containing <MyApplication>.app"
+	echo "All .app bundles in pkgdir will be signed"
+	exit 2
+fi
+
+entitlements=deploy/osx/Entitlements.plist
+
+for app in "$dir"/*.app; do
+    find "$app" -name \*.dylib -print | while read fr; do
+	codesign -s "Developer ID Application: Chris Cannam" -fv --deep --options runtime "$fr"
+    done
+    codesign -s "Developer ID Application: Chris Cannam" -fv --deep --options runtime --entitlements "$entitlements" "$app/Contents/MacOS/Vamp Plugin Pack Installer"
+    codesign -s "Developer ID Application: Chris Cannam" -fv --deep --options runtime --entitlements "$entitlements" "$app"
+done
+
--- a/installer.cpp	Fri Dec 13 13:54:27 2019 +0000
+++ b/installer.cpp	Fri Dec 13 14:14:57 2019 +0000
@@ -24,7 +24,15 @@
         if (!f.copy(target + e)) {
             cerr << "Failed to copy " << e.toStdString()
                  << " to target " << (target + e).toStdString() << endl;
+	    continue;
         }
+	if (!QFile::setPermissions(target + e, 
+				   QFile::ReadOwner | QFile::WriteOwner | 
+				   QFile::ExeOwner | QFile::ReadGroup | 
+				   QFile::ReadOther)) {
+            cerr << "Failed to set permissions on " << e.toStdString() << endl;
+	    continue;
+	}
     }
     
     return 0;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/version.h	Fri Dec 13 14:14:57 2019 +0000
@@ -0,0 +1,1 @@
+#define PACK_VERSION "1.0"