mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-09-30 14:54:13 +02:00
4381c6caa3
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>
600 lines
22 KiB
Bash
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
|