@@ -14,6 +14,8 @@ SIGN_ID="Developer ID Application: Illia Barkov ($TEAM_ID)"
1414NOTARY_PROFILE=" OpenCloud"
1515
1616CRAFT_LIB=" $HOME /Documents/craft/macos-clang-arm64/lib"
17+ CRAFT_PLUGINS=" $HOME /Documents/craft/macos-clang-arm64/plugins"
18+ CRAFT_QML=" $HOME /Documents/craft/macos-clang-arm64/qml"
1719BUILD_BIN=" $HOME /Documents/craft/macos-clang-arm64/build/opencloud/opencloud-desktop/work/build/bin"
1820BUILD_APP=" $BUILD_BIN /OpenCloud.app"
1921
@@ -120,6 +122,64 @@ mkdir -p "$STAGE_DIR"
120122cp -R " $BUILD_APP " " $STAGE_APP "
121123echo " Copied to $STAGE_APP "
122124
125+ # ─── QT PLUGINS ─────────────────────────────────────────────────────────────
126+
127+ step " Bundling Qt plugins"
128+
129+ PLUGIN_DIR=" $STAGE_APP /Contents/PlugIns"
130+
131+ # Qt plugin categories needed at runtime
132+ QT_PLUGIN_DIRS=(platforms imageformats styles tls iconengines sqldrivers)
133+
134+ for plugin_cat in " ${QT_PLUGIN_DIRS[@]} " ; do
135+ src_dir=" $CRAFT_PLUGINS /$plugin_cat "
136+ dst_dir=" $PLUGIN_DIR /$plugin_cat "
137+ if [ -d " $src_dir " ]; then
138+ mkdir -p " $dst_dir "
139+ for dylib in " $src_dir " /* .dylib; do
140+ [ -f " $dylib " ] || continue
141+ cp " $dylib " " $dst_dir /"
142+ echo " + $plugin_cat /$( basename " $dylib " ) "
143+ done
144+ else
145+ echo " WARNING: $src_dir not found"
146+ fi
147+ done
148+
149+ # ─── QML MODULES ────────────────────────────────────────────────────────────
150+
151+ step " Bundling QML modules"
152+
153+ QML_DIR=" $STAGE_APP /Contents/Resources/qml"
154+ mkdir -p " $QML_DIR "
155+
156+ # Copy required QML module trees
157+ QML_MODULES=(QtQuick QtQml QtCore eu)
158+
159+ for mod in " ${QML_MODULES[@]} " ; do
160+ if [ -d " $CRAFT_QML /$mod " ]; then
161+ cp -R " $CRAFT_QML /$mod " " $QML_DIR /"
162+ echo " + $mod /"
163+ else
164+ echo " WARNING: QML module $mod not found"
165+ fi
166+ done
167+
168+ # Also copy top-level qmldir/qmltypes if present
169+ for f in " $CRAFT_QML " /builtins.qmltypes " $CRAFT_QML " /jsroot.qmltypes; do
170+ [ -f " $f " ] && cp " $f " " $QML_DIR /"
171+ done
172+
173+ # qt.conf tells Qt where to find plugins and QML modules
174+ mkdir -p " $STAGE_APP /Contents/Resources"
175+ cat > " $STAGE_APP /Contents/Resources/qt.conf" << 'QTCONF '
176+ [Paths]
177+ Plugins = PlugIns
178+ QmlImports = Resources/qml
179+ QTCONF
180+
181+ echo " Created qt.conf"
182+
123183# ─── BUNDLE DYLIBS ──────────────────────────────────────────────────────────
124184
125185step " Bundling dylibs and frameworks"
@@ -187,52 +247,51 @@ copy_framework() {
187247 return 0
188248}
189249
190- # Collect all deps: both @rpath and absolute Craft paths
250+ # Find all Mach-O binaries (cached per pass to avoid repeated scanning)
251+ find_machos () {
252+ find " $1 " -type f -print0 | xargs -0 -P8 file 2> /dev/null | grep ' Mach-O' | cut -d: -f1
253+ }
254+
255+ # Collect all deps: both @rpath and absolute Craft paths (parallelized)
191256collect_deps () {
192257 local dir=" $1 "
193- local deps=" "
194- while IFS= read -r bin; do
195- local bin_deps
196- # @rpath deps → strip prefix
197- bin_deps=$( otool -L " $bin " 2> /dev/null | grep ' @rpath/' | awk ' {print $1}' | sed ' s|@rpath/||' || true)
198- if [ -n " $bin_deps " ]; then
199- deps=" $deps " $' \n ' " $bin_deps "
200- fi
201- # Absolute Craft lib paths → extract basename
202- bin_deps=$( otool -L " $bin " 2> /dev/null | grep " $HOME /Documents/craft/" | awk ' {print $1}' || true)
203- if [ -n " $bin_deps " ]; then
204- while IFS= read -r abspath; do
205- deps=" $deps " $' \n ' " $( basename " $abspath " ) "
206- done <<< " $bin_deps"
207- fi
208- done < <( find " $dir " -type f -exec sh -c ' file "$1" 2>/dev/null | grep -q "Mach-O"' _ {} \; -print)
209- echo " $deps " | sort -u | grep -v ' ^$' || true
258+ find_machos " $dir " | xargs -P8 -I{} otool -L {} 2> /dev/null | awk -v home=" $HOME " '
259+ /@rpath\// { sub(/^[[:space:]]+/, ""); sub(/ \(.*/, ""); sub(/@rpath\//, ""); print }
260+ index($0, home"/Documents/craft/") { sub(/^[[:space:]]+/, ""); sub(/ \(.*/, ""); n=split($0, a, "/"); print a[n] }
261+ ' | sort -u || true
210262}
211263
212264RPATH_NEW=" @executable_path/../Frameworks"
213265
214- # Rewrite absolute paths and rpaths on all Mach-O binaries in the staged app
266+ # Rewrite absolute paths and rpaths on a single Mach-O binary
267+ fix_one_binary () {
268+ local bin=" $1 "
269+ local home=" $2 "
270+ local rpath_new=" $3 "
271+ # Remove old absolute rpaths (LC_RPATH entries)
272+ for old_rpath in $( otool -l " $bin " 2> /dev/null | grep -A2 LC_RPATH | grep ' path /Users' | awk ' {print $2}' || true) ; do
273+ install_name_tool -delete_rpath " $old_rpath " " $bin " 2> /dev/null || true
274+ done
275+ # Rewrite absolute Craft lib paths in LC_LOAD_DYLIB to @rpath/name
276+ for abs_dep in $( otool -L " $bin " 2> /dev/null | grep " $home /Documents/craft/" | awk ' {print $1}' || true) ; do
277+ local_name=$( basename " $abs_dep " )
278+ install_name_tool -change " $abs_dep " " @rpath/$local_name " " $bin " 2> /dev/null || true
279+ done
280+ # Rewrite the library's own install name if it's an absolute craft path
281+ old_id=$( otool -D " $bin " 2> /dev/null | tail -1 || true)
282+ if [[ " $old_id " == * " /Documents/craft/" * ]]; then
283+ install_name_tool -id " @rpath/$( basename " $old_id " ) " " $bin " 2> /dev/null || true
284+ fi
285+ # Add @executable_path/../Frameworks if missing
286+ if ! otool -l " $bin " 2> /dev/null | grep -q " $rpath_new " ; then
287+ install_name_tool -add_rpath " $rpath_new " " $bin " 2> /dev/null || true
288+ fi
289+ }
290+ export -f fix_one_binary
291+
292+ # Rewrite absolute paths and rpaths on all Mach-O binaries (parallelized)
215293fix_paths () {
216- while IFS= read -r bin; do
217- # Remove old absolute rpaths (LC_RPATH entries)
218- for old_rpath in $( otool -l " $bin " 2> /dev/null | grep -A2 LC_RPATH | grep ' path /Users' | awk ' {print $2}' || true) ; do
219- install_name_tool -delete_rpath " $old_rpath " " $bin " 2> /dev/null || true
220- done
221- # Rewrite absolute Craft lib paths in LC_LOAD_DYLIB to @rpath/name
222- for abs_dep in $( otool -L " $bin " 2> /dev/null | grep " $HOME /Documents/craft/" | awk ' {print $1}' || true) ; do
223- local_name=$( basename " $abs_dep " )
224- install_name_tool -change " $abs_dep " " @rpath/$local_name " " $bin " 2> /dev/null || true
225- done
226- # Rewrite the library's own install name if it's an absolute craft path
227- old_id=$( otool -D " $bin " 2> /dev/null | tail -1 || true)
228- if [[ " $old_id " == * " /Documents/craft/" * ]]; then
229- install_name_tool -id " @rpath/$( basename " $old_id " ) " " $bin " 2> /dev/null || true
230- fi
231- # Add @executable_path/../Frameworks if missing
232- if ! otool -l " $bin " 2> /dev/null | grep -q " $RPATH_NEW " ; then
233- install_name_tool -add_rpath " $RPATH_NEW " " $bin " 2> /dev/null || true
234- fi
235- done < <( find " $STAGE_APP " -type f -exec sh -c ' file "$1" 2>/dev/null | grep -q "Mach-O"' _ {} \; -print)
294+ find_machos " $STAGE_APP " | xargs -P8 -I{} bash -c ' fix_one_binary "$@"' _ {} " $HOME " " $RPATH_NEW "
236295}
237296
238297# Iteratively: copy deps → fix paths → check for new deps → repeat
@@ -289,33 +348,48 @@ for lib in "$FW_DIR"/*.dylib; do
289348 sign_binary " $lib "
290349done
291350
292- # 2. PlugIns — standalone binaries
293- echo " Signing plugins..."
351+ # 2. Qt plugins (in subdirectories)
352+ echo " Signing Qt plugins..."
353+ for plugin_cat in " ${QT_PLUGIN_DIRS[@]} " ; do
354+ for plib in " $STAGE_APP /Contents/PlugIns/$plugin_cat " /* .dylib; do
355+ [ -f " $plib " ] || continue
356+ sign_binary " $plib "
357+ done
358+ done
359+
360+ # 2b. QML module dylibs
361+ echo " Signing QML module plugins..."
362+ while IFS= read -r qml_dylib; do
363+ sign_binary " $qml_dylib "
364+ done < <( find " $STAGE_APP /Contents/Resources/qml" -name ' *.dylib' -type f 2> /dev/null)
365+
366+ # 3. PlugIns — standalone binaries (.so)
367+ echo " Signing VFS plugins..."
294368for so in " $STAGE_APP /Contents/PlugIns" /* .so; do
295369 [ -f " $so " ] || continue
296370 sign_binary " $so "
297371done
298372
299- # 3 . FinderSyncExt.appex
373+ # 4 . FinderSyncExt.appex
300374if [ -d " $STAGE_APP /Contents/PlugIns/FinderSyncExt.appex" ]; then
301375 echo " Signing FinderSyncExt.appex..."
302376 sign_binary " $STAGE_APP /Contents/PlugIns/FinderSyncExt.appex"
303377fi
304378
305- # 4 . FileProviderExt.appex (with entitlements)
379+ # 5 . FileProviderExt.appex (with entitlements)
306380if [ -d " $STAGE_APP /Contents/PlugIns/FileProviderExt.appex" ]; then
307381 echo " Signing FileProviderExt.appex..."
308382 sign_binary " $STAGE_APP /Contents/PlugIns/FileProviderExt.appex" " $ENTITLEMENTS_DIR /appex.plist"
309383fi
310384
311- # 5 . Helper executables
385+ # 6 . Helper executables
312386echo " Signing helper executables..."
313387for helper in " $STAGE_APP /Contents/MacOS/opencloudcmd" " $STAGE_APP /Contents/MacOS/opencloud_crash_reporter" ; do
314388 [ -f " $helper " ] || continue
315389 sign_binary " $helper "
316390done
317391
318- # 6 . Main app (last)
392+ # 7 . Main app (last)
319393echo " Signing OpenCloud.app..."
320394sign_binary " $STAGE_APP " " $ENTITLEMENTS_DIR /app.plist"
321395
@@ -328,7 +402,16 @@ echo " Signature verified"
328402step " Creating DMG"
329403
330404rm -f " $DMG_PATH "
331- hdiutil create -volname " OpenCloud" -srcfolder " $STAGE_APP " -ov -format UDZO " $DMG_PATH " 2>&1
405+
406+ # Build a temp folder with app + Applications symlink for drag-to-install
407+ DMG_STAGE=" $STAGE_DIR /dmg-stage"
408+ rm -rf " $DMG_STAGE "
409+ mkdir -p " $DMG_STAGE "
410+ cp -R " $STAGE_APP " " $DMG_STAGE /"
411+ ln -s /Applications " $DMG_STAGE /Applications"
412+
413+ hdiutil create -volname " OpenCloud" -srcfolder " $DMG_STAGE " -ov -format UDZO " $DMG_PATH " 2>&1
414+ rm -rf " $DMG_STAGE "
332415sign_binary " $DMG_PATH "
333416echo " Created: $DMG_PATH "
334417ls -lh " $DMG_PATH "
@@ -340,7 +423,7 @@ if [ "$SKIP_NOTARIZE" = false ]; then
340423
341424 xcrun notarytool submit " $DMG_PATH " \
342425 --keychain-profile " $NOTARY_PROFILE " \
343- --wait 2>&1
426+ --wait --verbose 2>&1
344427
345428 step " Stapling notarization ticket"
346429 xcrun stapler staple " $DMG_PATH " 2>&1
0 commit comments