Files
qelectrotech-source-mirror/misc/MacQetDeploy_arm64_cmake.sh
T
ispyisail 4381c6caa3 Read the 3D mouse through 3DxWare on macOS when it is installed
On macOS, 3DxWare installs a driver extension that takes the 3D mouse
over. With it installed, HidBackend opens the device but receives
nothing, so the mouse did nothing in QElectroTech until 3DxWare was
uninstalled (discussion #599, PR #1028). Most Mac owners of a 3D mouse
have 3DxWare installed.

ConnexionBackend asks 3DxWare for the motion instead, through
3DconnexionClient.framework, as Blender does. SpaceMouseListener tries
it first. When 3DxWare is not installed, or is installed but its driver
is not running, it falls back to HidBackend, so the device works in
both setups. Take-over mode stops 3DxWare's own actions in QET, so the
view does not move twice.

The library is loaded at run time from where 3DxWare installs it:
nothing is linked or bundled, and the build needs no SDK. The few
declarations are written here, from Blender's
GHOST_NDOFManagerCocoa.mm, because 3Dconnexion's SDK headers may not be
redistributed. 3DxWare's axes are y up and z away from the user; they
are mapped to QET's raw USB convention by comparing Blender's 3DxWare
and spacenavd code paths.

The release script signs with the hardened runtime, which refuses a
library another team signed. misc/qelectrotech.entitlements adds
com.apple.security.cs.disable-library-validation (Blender's notarized
build carries the same one), and MacQetDeploy_arm64_cmake.sh now passes
it to all four signings of the app, including the re-sign inside the
DMG.

Tested: tst_spacemouseconnexion runs the backend on every platform
against fakeconnexion, a stand-in library that answers from its own
thread as 3DxWare does: registration, the axis mapping, buttons, other
clients' messages, 3DxWare not installed or not running, deletion
with a message in flight. Flipping an axis sign or dropping the client
check turns it red. realLibrary() loads the real framework when
3DxWare is installed. Linux Qt 6 build with the 3D mouse enabled: all
18 tests pass.

Not tested: on a Mac with a real device. The axis signs and whether
buttons arrive as a bitmask with current 3DxWare are unverified.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 22:54:07 +12:00

600 lines
22 KiB
Bash

#!/bin/sh
# Copyright 2026 The QElectroTech Team
# This file is part of QElectroTech.
# QElectroTech is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# Suppose que l'environnement a ete prepare via macos_homebrew_setup.sh
# (Homebrew, Qt6, cmake, ninja, kf6-kwidgetsaddons, kf6-kcoreaddons).
#
# Remplace l'ancien misc/MacQetDeploy_arm64.sh (qmake/Qt5) par un
# build CMake/Qt6/KF6. La chaine de signature/notarization/DMG est
# reprise a l'identique de l'ancien script (eprouvee, ne pas y toucher
# sans raison).
# configuration
APPNAME='qelectrotech'
BUNDLE=$APPNAME.app
IDENTITY="Developer ID Application: Laurent TRINQUES (Y73WZ6WZ5X)"
QT_MAJOR="${QT_MAJOR:-6}"
BUILD_WITH_KF="${BUILD_WITH_KF:-ON}"
BUILD_DIR="build-macos-arm64"
# Temp paths
RW_DMG="/tmp/qet_rw.dmg"
MOUNT_POINT="/tmp/qet_dmg_mount"
STAGING="/tmp/qet_dmg_staging"
# Script location
current_dir=$(dirname "$0")
cd "${current_dir}/../"
current_dir=$(PWD)
### get system configuration ########################################
echo
echo "______________________________________________________________"
echo "This script prepares a Qt6/KF6 application bundle for deployment."
echo "This script :"
echo "\t - update the git depot"
echo "\t - configure and build via CMake,"
echo "\t - copy over required Qt frameworks,"
echo "\t - copy additional files: translations, titleblocks and elements,"
echo "\t - notarize the .app, then create a signed DMG."
echo
QT_PREFIX=$(brew --prefix qt 2>/dev/null || true)
if [ -z "$QT_PREFIX" ] || [ ! -d "$QT_PREFIX/lib/QtCore.framework" ] ; then
echo "ERROR: cannot find Qt6 via Homebrew. Run macos_homebrew_setup.sh first."
exit 1
fi
export CMAKE_PREFIX_PATH="$QT_PREFIX:$CMAKE_PREFIX_PATH"
### GIT ####################################################
echo
echo "______________________________________________________________"
echo "Run GIT:"
git submodule init
git submodule update
git pull --recurse-submodules
git pull
GITCOMMIT=$(git rev-parse --short HEAD)
A=$(git rev-list HEAD --count)
HEAD=$(($A+473))
VERSION=$(cat sources/qetversion.cpp | grep "return QVersionNumber{"| head -n 1| awk -F "{" '{ print $2 }' | awk -F "}" '{ print $1 }' | sed -e 's/,/./g' -e 's/ //g')
DMG_NAME="${APPNAME}-$VERSION-r$HEAD-arm64.dmg"
DMG_PATH="build-aux/mac-osx/$DMG_NAME"
if [ -e "$DMG_PATH" ] ; then
echo "There are not new updates, make disk image can"
echo "take a lot of time (5 min). Can you continu?"
echo "[y/n]"
read userinput
if [ "$userinput" == "n" ] ; then
echo
echo "Process is stopped."
echo
exit
fi
fi
### build with CMake #################################################
echo
echo "______________________________________________________________"
echo "Run CMake configure + build (Qt${QT_MAJOR}, BUILD_WITH_KF=${BUILD_WITH_KF}):"
if [ -d $BUNDLE ] ; then
echo "Removing old bundle..."
rm -rf $BUNDLE
fi
if [ -d "$BUILD_DIR" ] ; then
echo "Removing old build directory..."
rm -rf "$BUILD_DIR"
fi
cmake -S . -B "$BUILD_DIR" -G Ninja \
-DCMAKE_BUILD_TYPE=Release \
-DQT_VERSION_MAJOR=$QT_MAJOR \
-DBUILD_WITH_KF=$BUILD_WITH_KF \
-DBUILD_KF=OFF \
-DQET_EXPORT_PROJECT_DB=ON \
-DPACKAGE_TESTS=OFF \
-DQET_ENABLE_SPACEMOUSE=ON \
-DQET_SPACEMOUSE_BACKEND=hid
if [ $? -ne 0 ]; then
echo "ERROR: cmake configure failed."
exit 1
fi
START_TIME=$SECONDS
coeur=$(sysctl hw.ncpu | awk '{print $2}')
cmake --build "$BUILD_DIR" -j$(($coeur + 1))
if [ $? -ne 0 ]; then
ELAPSED_TIME=$(($SECONDS - $START_TIME))
echo
echo "cmake build failed - $(($ELAPSED_TIME/60)) min $(($ELAPSED_TIME%60)) sec"
exit 1
fi
ELAPSED_TIME=$(($SECONDS - $START_TIME))
echo
echo "The time of compilation is $(($ELAPSED_TIME/60)) min $(($ELAPSED_TIME%60)) sec"
# Le .app sort a la racine de $BUILD_DIR : add_executable(... MACOSX_BUNDLE)
# sans RUNTIME_OUTPUT_DIRECTORY dans CMakeLists.txt.
echo "Copying built bundle into place..."
cp -R "$BUILD_DIR/qelectrotech.app" "./$BUNDLE"
if [ ! -d "$BUNDLE" ] ; then
echo "ERROR: expected bundle \"$BUNDLE\" not found after cmake build."
exit 1
fi
### copy over frameworks ############################################
echo
echo "______________________________________________________________"
echo "Copy Qt libraries and private frameworks:"
if [ ! -d $BUNDLE ] ; then
echo "ERROR: cannot find application bundle \"$BUNDLE\" in current directory"
exit 1
fi
macdeployqt $BUNDLE
### fix Homebrew dependencies macdeployqt could not handle ##########
# Recent Homebrew bottles (brotli, webp, sharpyuv...) reference their own
# dependencies as @rpath/libX.dylib. macdeployqt only resolves @rpath in
# Contents/lib and in Qt's lib dir (-libpath does not help): it prints
# "Cannot resolve rpath" and leaves some references untouched, either
# absolute /opt/homebrew paths or @rpath libs missing from the bundle.
# Fix both here: every /opt/homebrew reference is rewritten to
# @rpath/libX.dylib (install ids too), and every @rpath/libX.dylib is
# copied into Contents/Frameworks, where the executable's LC_RPATH
# (@executable_path/../Frameworks) lets dyld find it. Everything is signed
# below with the rest of Frameworks/.
echo
echo "______________________________________________________________"
echo "Fix Homebrew dependencies left by macdeployqt:"
FW="$BUNDLE/Contents/Frameworks"
chmod -R u+w "$BUNDLE/Contents/MacOS" "$FW" "$BUNDLE/Contents/PlugIns" 2>/dev/null
# Every Mach-O candidate of the bundle (otool silently ignores the others)
list_macho() {
find "$BUNDLE/Contents/MacOS" "$FW" "$BUNDLE/Contents/PlugIns" -type f \
\( -name '*.dylib' -o -perm -u+x \) 2>/dev/null
}
# Dependencies of one binary, without its own install id (dylibs only)
list_deps() {
_id=$(otool -D "$1" 2>/dev/null | sed -n 2p)
otool -L "$1" 2>/dev/null | awk 'NR>1 { print $1 }' | while read _dep; do
[ "$_dep" = "$_id" ] || echo "$_dep"
done
}
# The @rpath/libX.dylib names referenced anywhere in the bundle
list_rpath_libs() {
list_macho | while read bin; do
list_deps "$bin" | sed -n 's#^@rpath/\([^/]*\.dylib\)$#\1#p'
done | LC_ALL=C sort -u
}
if ! otool -l "$BUNDLE/Contents/MacOS/$APPNAME" | grep -q "@executable_path/../Frameworks" ; then
install_name_tool -add_rpath "@executable_path/../Frameworks" "$BUNDLE/Contents/MacOS/$APPNAME"
echo " Added LC_RPATH @executable_path/../Frameworks to $APPNAME"
fi
# 3 passes, since each copied library can bring its own dependencies:
# a. rewrite absolute /opt/homebrew references (install ids included),
# copying the referenced library into Frameworks/ if needed
# b. copy the @rpath/libX.dylib still missing from Frameworks/
for PASS in 1 2 3; do
list_macho | while read bin; do
_id=$(otool -D "$bin" 2>/dev/null | sed -n 2p)
case "$_id" in
/opt/homebrew/*)
install_name_tool -id "@rpath/$(basename "$_id")" "$bin" 2>/dev/null
echo " Fixed id (pass $PASS): $(basename "$bin")"
;;
esac
list_deps "$bin" | grep '^/opt/homebrew/' | while read ref; do
name=$(basename "$ref")
if [ ! -e "$FW/$name" ]; then
cp -L "$ref" "$FW/$name" && chmod u+w "$FW/$name"
echo " Copied (pass $PASS): $name"
fi
install_name_tool -change "$ref" "@rpath/$name" "$bin" 2>/dev/null
echo " Fixed ref (pass $PASS): $(basename "$bin") -> @rpath/$name"
done
done
list_rpath_libs | while read lib; do
if [ ! -e "$FW/$lib" ] && [ -e "/opt/homebrew/lib/$lib" ]; then
cp -L "/opt/homebrew/lib/$lib" "$FW/$lib" && chmod u+w "$FW/$lib"
echo " Copied (pass $PASS): $lib"
fi
done
done
# 3. Checks
UNRESOLVED=$(list_rpath_libs | while read lib; do [ -e "$FW/$lib" ] || echo "$lib"; done)
if [ -n "$UNRESOLVED" ]; then
echo "ERROR: @rpath libraries still missing from Frameworks/:" $UNRESOLVED
exit 1
fi
HOMEBREW_REFS=$(list_macho | while read bin; do
otool -L "$bin" 2>/dev/null | awk 'NR>1 { print $1 }' | grep '^/opt/homebrew/' \
| sed "s#^# $(basename "$bin") -> #"
done)
if [ -n "$HOMEBREW_REFS" ]; then
echo "ERROR: bundle still references Homebrew paths:"
echo "$HOMEBREW_REFS"
exit 1
fi
echo "All dependencies resolved inside the bundle."
### install Info.plist and app icon #################################
# NOTE: this must run AFTER macdeployqt, not before. macdeployqt
# rewrites/regenerates parts of Contents/Resources, and files copied
# there beforehand (e.g. the .icns icons) do not reliably survive it
# and end up missing from the final bundle, causing the app to show
# the generic placeholder icon instead of the real one. Info.plist
# itself lives directly in Contents/ and happens to survive either
# way, but keep it here too so this whole "final metadata" step stays
# in one place, after macdeployqt is done touching the bundle.
echo
echo "______________________________________________________________"
echo "Install Info.plist and app icon:"
cp -R ${current_dir}/misc/Info.plist $BUNDLE/Contents/
cp -R ${current_dir}/ico/mac_icon/*.icns $BUNDLE/Contents/Resources/
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $VERSION r$HEAD" "$BUNDLE/Contents/Info.plist"
### add missing files ###############################################
echo
echo "______________________________________________________________"
echo "Copy missing files:"
QET_ELMT_DIR="${current_dir}/elements/"
QET_TBT_DIR="${current_dir}/titleblocks/"
QET_LANG_DIR="${current_dir}/lang/"
QET_EXAMPLES_DIR="${current_dir}/examples/"
QET_FONTS_DIR="${current_dir}/fonts/"
QET_LICENSES_DIR="${current_dir}/licenses/"
if [ -d "${QET_ELMT_DIR}" ]; then
cp -R ${QET_ELMT_DIR} $BUNDLE/Contents/Resources/elements
fi
if [ -d "${QET_TBT_DIR}" ]; then
cp -R ${QET_TBT_DIR} $BUNDLE/Contents/Resources/titleblocks
fi
# Traductions : depuis la PR #751, les .qm ne sont plus versionnes ; lrelease
# les genere dans $BUILD_DIR/lang/. Jeu attendu = les .ts listes dans TS_FILES
# (cmake/qet_compilation_vars.cmake) : un .qm manquant arrete le script, un
# .ts present dans lang/ mais absent de TS_FILES donne seulement un WARNING.
# Fichiers temporaires plutot que <(...) : /bin/sh de macOS (bash 3.2 en mode
# POSIX) n'a pas la substitution de processus.
QM_SRC="${current_dir}/${BUILD_DIR}/lang"
QM_TMP=$(mktemp -d /tmp/qet_qm.XXXXXX)
grep -o 'lang/qet_[A-Za-z_]*\.ts' "${current_dir}/cmake/qet_compilation_vars.cmake" \
| sed -e 's#^lang/##' -e 's#\.ts$##' | LC_ALL=C sort -u > "$QM_TMP/listed"
if [ ! -s "$QM_TMP/listed" ]; then
echo "ERROR: cannot read TS_FILES from cmake/qet_compilation_vars.cmake"
rm -rf "$QM_TMP"
exit 1
fi
find "${QET_LANG_DIR}" -maxdepth 1 -name 'qet_*.ts' -exec basename {} .ts \; | LC_ALL=C sort > "$QM_TMP/present"
UNLISTED=$(LC_ALL=C comm -13 "$QM_TMP/listed" "$QM_TMP/present")
if [ -n "$UNLISTED" ]; then
echo "WARNING: .ts files not in TS_FILES, no .qm built:" $UNLISTED
fi
mkdir -p $BUNDLE/Contents/Resources/lang
find "${QM_SRC}" -maxdepth 1 -name 'qet_*.qm' -exec cp {} $BUNDLE/Contents/Resources/lang/ \; 2>/dev/null
find $BUNDLE/Contents/Resources/lang -maxdepth 1 -name 'qet_*.qm' -exec basename {} .qm \; | LC_ALL=C sort > "$QM_TMP/built"
MISSING=$(LC_ALL=C comm -23 "$QM_TMP/listed" "$QM_TMP/built")
echo "$(wc -l < "$QM_TMP/built" | tr -d ' ') .qm files copied to Contents/Resources/lang (expected: $(wc -l < "$QM_TMP/listed" | tr -d ' '))"
# Traductions de Qt lui-meme (boutons OK/Annuler, dialogues standard...) :
# elles viennent de qtbase_XX.qm, pas des .ts de QET, et macdeployqt ne les
# deploie pas. QETApp::setLanguage() charge "qt_XX" depuis le chemin de
# traductions de Qt (absent du bundle), puis depuis le dossier lang/ de QET :
# on y depose donc chaque qtbase_XX.qm sous le nom qt_XX.qm. qtbase_XX.qm est
# autonome, contrairement aux qt_XX.qm de Qt qui dependent de tous les modules.
# Premier dossier contenant des qtbase_*.qm : celui annonce par qtpaths, puis
# la formule Homebrew separee qttranslations, puis les autres emplacements
# Homebrew possibles. Homebrew fait de ces dossiers des liens symboliques
# (-> Cellar/qttranslations/...) : find doit donc les suivre (-L), sinon il
# ne voit que le lien lui-meme et ne trouve aucun fichier dedans.
QT_TR_DIR=""
for d in "$("$QT_PREFIX/bin/qtpaths" --query QT_INSTALL_TRANSLATIONS 2>/dev/null)" \
"$(brew --prefix qttranslations 2>/dev/null)/share/qt/translations" \
"$QT_PREFIX/share/qt/translations" \
/opt/homebrew/share/qt/translations \
/opt/homebrew/opt/*/share/qt/translations ; do
if ls "$d"/qtbase_*.qm >/dev/null 2>&1 ; then
QT_TR_DIR="$d"
break
fi
done
LANG_DST="$BUNDLE/Contents/Resources/lang"
find -L "$QT_TR_DIR" -maxdepth 1 -name 'qtbase_*.qm' 2>/dev/null | while read f; do
l=$(basename "$f" .qm | sed 's/^qtbase_//')
cp "$f" "$LANG_DST/qt_$l.qm"
done
# Langues QET sans equivalent Qt exact (pt -> pt_PT, zh -> zh_CN...) :
# QTranslator ne sait que raccourcir le code (fr_FR -> fr), pas l'allonger.
# On prefere la variante "principale" (pt_PT), sinon la premiere trouvee.
sed 's/^qet_//' "$QM_TMP/listed" | while read l; do
if [ ! -e "$LANG_DST/qt_$l.qm" ]; then
main="$QT_TR_DIR/qtbase_${l}_$(echo "$l" | tr 'a-z' 'A-Z').qm"
if [ -e "$main" ]; then
alt="$main"
else
alt=$(find -L "$QT_TR_DIR" -maxdepth 1 -name "qtbase_${l}_*.qm" 2>/dev/null | LC_ALL=C sort | head -1)
fi
[ -n "$alt" ] && cp "$alt" "$LANG_DST/qt_$l.qm"
fi
done
QT_QM_COUNT=$(find "$LANG_DST" -maxdepth 1 -name 'qt_*.qm' | wc -l | tr -d ' ')
echo "${QT_QM_COUNT} Qt translation files (qt_*.qm) copied from ${QT_TR_DIR:-<not found>}"
if [ "${QT_QM_COUNT}" -eq 0 ]; then
echo "ERROR: no qtbase_*.qm found (Qt translations not installed?)."
echo " Check with: find /opt/homebrew -name 'qtbase_fr.qm'"
rm -rf "$QM_TMP"
exit 1
fi
rm -rf "$QM_TMP"
if [ -n "$MISSING" ]; then
echo "ERROR: missing translations:" $MISSING
exit 1
fi
if [ -d "${QET_EXAMPLES_DIR}" ]; then
mkdir $BUNDLE/Contents/Resources/examples
cp ${current_dir}/examples/*.qet $BUNDLE/Contents/Resources/examples
fi
if [ -d "${QET_FONTS_DIR}" ]; then
mkdir $BUNDLE/Contents/Resources/fonts
cp ${current_dir}/fonts/*.ttf $BUNDLE/Contents/Resources/fonts
fi
if [ -d "${QET_LICENSES_DIR}" ]; then
cp -R -L ${QET_LICENSES_DIR} $BUNDLE/Contents/Resources/licenses
fi
### Sign the bundle #################################################
# Sign in correct order: all dylibs first (including flat libs copied
# by macdeployqt from Homebrew), then frameworks, plugins, bundle last.
echo
echo "______________________________________________________________"
echo "Code signing (dylibs -> frameworks -> plugins -> bundle):"
echo "-- Signing dylibs in Frameworks/..."
find "$BUNDLE/Contents/Frameworks" -name "*.dylib" | while read lib; do
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$lib"
done
echo "-- Signing .framework bundles..."
find "$BUNDLE/Contents/Frameworks" -maxdepth 1 -name "*.framework" | while read fw; do
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$fw"
done
echo "-- Signing plugins..."
find "$BUNDLE/Contents/PlugIns" \( -name "*.dylib" -o -name "*.so" \) | while read lib; do
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$lib"
done
echo "-- Signing dylibs in MacOS/..."
find "$BUNDLE/Contents/MacOS" -name "*.dylib" | while read lib; do
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$lib"
done
echo "-- Signing main executable..."
codesign --force --sign "$IDENTITY" --timestamp --options=runtime \
--entitlements "${current_dir}/misc/qelectrotech.entitlements" \
"$BUNDLE/Contents/MacOS/$APPNAME"
echo "-- Signing bundle..."
codesign --force --sign "$IDENTITY" --timestamp --options=runtime \
--entitlements "${current_dir}/misc/qelectrotech.entitlements" "$BUNDLE"
echo
echo "Verifying bundle signature..."
codesign --verify --deep --strict --verbose=2 "$BUNDLE"
if [ $? -ne 0 ]; then
echo "ERROR: bundle signature verification failed, aborting."
exit 1
fi
spctl -a -vv "$BUNDLE"
echo "Bundle signature OK."
### Notarize the .app (via temporary ZIP) ###########################
echo
echo "______________________________________________________________"
echo "Create temporary ZIP for notarization:"
NOTARIZE_ZIP="/tmp/${APPNAME}-$VERSION-r$HEAD-arm64-notarize.zip"
/usr/bin/ditto -c -k --keepParent "$BUNDLE" "$NOTARIZE_ZIP"
echo -e "\033[1;31mWould you like to notarize the .app \"${APPNAME}-${VERSION}-r${HEAD}\", n/Y?\033[m"
read a
if [[ $a == "Y" || $a == "y" ]]; then
echo
echo "______________________________________________________________"
echo "Notarizing .app:"
xcrun notarytool submit "$NOTARIZE_ZIP" --keychain-profile "org.qelectrotech" --wait
if [ $? -ne 0 ]; then
echo "ERROR: notarization failed. Check the log with:"
echo " xcrun notarytool log <submission-id> --keychain-profile org.qelectrotech"
rm -f "$NOTARIZE_ZIP"
exit 1
fi
else
echo -e "\033[1;33mExit.\033[m"
fi
rm -f "$NOTARIZE_ZIP"
### Staple the .app #################################################
echo -e "\033[1;31mWould you like to staple the .app \"${APPNAME}-${VERSION}-r${HEAD}\", n/Y?\033[m"
read a
if [[ $a == "Y" || $a == "y" ]]; then
xcrun stapler staple -v "$BUNDLE"
if [ $? -ne 0 ]; then
echo "ERROR: stapling .app failed."
exit 1
fi
xcrun stapler validate -v "$BUNDLE"
spctl -a -vv "$BUNDLE"
echo ".app stapled OK."
else
echo -e "\033[1;33mExit.\033[m"
fi
### Create staging folder with Applications symlink #################
echo
echo "______________________________________________________________"
echo "Preparing DMG staging folder:"
rm -rf "$STAGING"
mkdir -p "$STAGING"
cp -R "$BUNDLE" "$STAGING/"
ln -s /Applications "$STAGING/Applications"
### Create writable DMG (UDRW) ######################################
echo
echo "______________________________________________________________"
echo "Create writable DMG (UDRW) and re-sign .app inside:"
rm -f "$RW_DMG"
hdiutil create \
-volname "QElectroTech $VERSION" \
-srcfolder "$STAGING" \
-ov \
-format UDRW \
-fs HFS+ \
"$RW_DMG"
if [ $? -ne 0 ]; then
echo "ERROR: hdiutil failed to create writable DMG."
rm -rf "$STAGING"
exit 1
fi
rm -rf "$MOUNT_POINT"
mkdir -p "$MOUNT_POINT"
hdiutil attach "$RW_DMG" -mountpoint "$MOUNT_POINT" -nobrowse -noverify
if [ $? -ne 0 ]; then
echo "ERROR: failed to mount writable DMG."
rm -f "$RW_DMG"
rm -rf "$STAGING"
exit 1
fi
echo "-- Re-signing dylibs inside DMG..."
find "$MOUNT_POINT/$BUNDLE/Contents/Frameworks" -name "*.dylib" | while read lib; do
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$lib"
done
find "$MOUNT_POINT/$BUNDLE/Contents/Frameworks" -maxdepth 1 -name "*.framework" | while read fw; do
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$fw"
done
find "$MOUNT_POINT/$BUNDLE/Contents/PlugIns" \( -name "*.dylib" -o -name "*.so" \) | while read lib; do
codesign --force --sign "$IDENTITY" --timestamp --options=runtime "$lib"
done
codesign --force --sign "$IDENTITY" --timestamp --options=runtime \
--entitlements "${current_dir}/misc/qelectrotech.entitlements" \
"$MOUNT_POINT/$BUNDLE/Contents/MacOS/$APPNAME"
codesign --force --sign "$IDENTITY" --timestamp --options=runtime \
--entitlements "${current_dir}/misc/qelectrotech.entitlements" \
"$MOUNT_POINT/$BUNDLE"
echo "Verifying bundle signature inside DMG..."
codesign --verify --deep --strict --verbose=2 "$MOUNT_POINT/$BUNDLE"
if [ $? -ne 0 ]; then
echo "ERROR: bundle signature invalid inside DMG, aborting."
hdiutil detach "$MOUNT_POINT"
rm -f "$RW_DMG"
rm -rf "$STAGING" "$MOUNT_POINT"
exit 1
fi
echo "Bundle signature inside DMG OK."
hdiutil detach "$MOUNT_POINT"
### Convert UDRW to final compressed UDZO ###########################
echo
echo "______________________________________________________________"
echo "Convert to final compressed DMG (UDZO):"
mkdir -p "build-aux/mac-osx"
rm -f "$DMG_PATH"
hdiutil convert "$RW_DMG" \
-format UDZO \
-o "$DMG_PATH"
if [ $? -ne 0 ]; then
echo "ERROR: hdiutil convert failed."
rm -f "$RW_DMG"
rm -rf "$STAGING" "$MOUNT_POINT"
exit 1
fi
rm -f "$RW_DMG"
rm -rf "$STAGING" "$MOUNT_POINT"
### Sign the final DMG ##############################################
echo "Signing final DMG..."
codesign --sign "$IDENTITY" --timestamp "$DMG_PATH"
### Notarize and staple the final DMG ###############################
echo -e "\033[1;31mWould you like to notarize the DMG \"${DMG_NAME}\", n/Y?\033[m"
read a
if [[ $a == "Y" || $a == "y" ]]; then
echo
echo "______________________________________________________________"
echo "Notarizing DMG:"
xcrun notarytool submit "$DMG_PATH" --keychain-profile "org.qelectrotech" --wait
if [ $? -ne 0 ]; then
echo "ERROR: DMG notarization failed. Check the log with:"
echo " xcrun notarytool log <submission-id> --keychain-profile org.qelectrotech"
exit 1
fi
echo "Stapling DMG..."
xcrun stapler staple "$DMG_PATH"
if [ $? -ne 0 ]; then
echo "ERROR: stapling DMG failed."
exit 1
fi
echo "DMG notarized and stapled OK."
spctl -a -vv "$DMG_PATH"
else
echo -e "\033[1;33mExit.\033[m"
fi
### Clean up bundle #################################################
rm -rf "$BUNDLE"
echo
echo "______________________________________________________________"
echo "The process is done."
echo "DMG is in the folder 'build-aux/mac-osx'."
### Upload via rsync ################################################
echo -e "\033[1;31mWould you like to upload MacOS package \"${DMG_NAME}\", n/Y?\033[m"
read a
if [[ $a == "Y" || $a == "y" ]]; then
cp -Rf "$DMG_PATH" /Users/laurent/MAC_OS_X/
rsync -e ssh -av --delete-after --no-owner --no-g --chmod=g+w \
--progress --exclude='.DS_Store' \
/Users/laurent/MAC_OS_X/ \
server:download.qelectrotech.org/qet/builds/MAC_OS_X/arm64/
if [ $? != 0 ]; then
echo "RSYNC ERROR: problem syncing ${DMG_NAME}, retrying..."
rsync -e ssh -av --delete-after --no-owner --no-g --chmod=g+w \
--progress --exclude='.DS_Store' \
/Users/laurent/MAC_OS_X/ \
server:download.qelectrotech.org/qet/builds/MAC_OS_X/arm64/
fi
else
echo -e "\033[1;33mExit.\033[m"
fi