@@ -68,6 +68,96 @@ prepare_mariadb() {
6868 cd - > /dev/null || exit 1
6969}
7070
71+ # Bundle non-system dynamic libraries into the app bundle
72+ # so the app runs without Homebrew on end-user machines.
73+ bundle_dylibs () {
74+ local app_path=$1
75+ local binary=" $app_path /Contents/MacOS/TablePro"
76+ local frameworks_dir=" $app_path /Contents/Frameworks"
77+
78+ echo " 📦 Bundling dynamic libraries into app bundle..."
79+ mkdir -p " $frameworks_dir "
80+
81+ # Iteratively discover and copy all non-system dylibs.
82+ # Each pass scans the main binary + already-copied dylibs;
83+ # repeat until no new dylibs are found (handles transitive deps).
84+ local changed=1
85+ while [ " $changed " -eq 1 ]; do
86+ changed=0
87+ for target in " $binary " " $frameworks_dir " /* .dylib; do
88+ [ -f " $target " ] || continue
89+
90+ while IFS= read -r dep; do
91+ # Keep only non-system, non-rewritten absolute paths
92+ case " $dep " in
93+ /usr/lib/* |/System/* |@* |" " ) continue ;;
94+ esac
95+
96+ local name
97+ name=$( basename " $dep " )
98+
99+ # Already bundled
100+ [ -f " $frameworks_dir /$name " ] && continue
101+
102+ if [ -f " $dep " ]; then
103+ echo " Copying $name "
104+ cp " $dep " " $frameworks_dir /$name "
105+ chmod 644 " $frameworks_dir /$name "
106+ changed=1
107+ else
108+ echo " ⚠️ WARNING: $dep not found on disk, skipping"
109+ fi
110+ done < <( otool -L " $target " 2> /dev/null | awk ' NR>1 {print $1}' )
111+ done
112+ done
113+
114+ # Count bundled dylibs
115+ local count
116+ count=$( find " $frameworks_dir " -name ' *.dylib' 2> /dev/null | wc -l | tr -d ' ' )
117+
118+ if [ " $count " -eq 0 ]; then
119+ echo " No non-system dylibs to bundle"
120+ return 0
121+ fi
122+
123+ # Rewrite each dylib's own install name
124+ for fw in " $frameworks_dir " /* .dylib; do
125+ [ -f " $fw " ] || continue
126+ local name
127+ name=$( basename " $fw " )
128+ install_name_tool -id " @executable_path/../Frameworks/$name " " $fw "
129+ done
130+
131+ # Rewrite all references in the main binary and every bundled dylib
132+ for target in " $binary " " $frameworks_dir " /* .dylib; do
133+ [ -f " $target " ] || continue
134+
135+ while IFS= read -r dep; do
136+ case " $dep " in
137+ /usr/lib/* |/System/* |@* |" " ) continue ;;
138+ esac
139+
140+ local name
141+ name=$( basename " $dep " )
142+
143+ if [ -f " $frameworks_dir /$name " ]; then
144+ install_name_tool -change " $dep " " @executable_path/../Frameworks/$name " " $target "
145+ fi
146+ done < <( otool -L " $target " 2> /dev/null | awk ' NR>1 {print $1}' )
147+ done
148+
149+ # Ad-hoc sign everything (required on Apple Silicon)
150+ echo " Signing bundled libraries..."
151+ for fw in " $frameworks_dir " /* .dylib; do
152+ [ -f " $fw " ] || continue
153+ codesign -fs - " $fw " 2> /dev/null || true
154+ done
155+ codesign -fs - " $binary " 2> /dev/null || true
156+
157+ echo " ✅ Bundled $count dynamic libraries into Frameworks/"
158+ ls -lh " $frameworks_dir " /* .dylib 2> /dev/null
159+ }
160+
71161build_for_arch () {
72162 local arch=$1
73163 echo " "
@@ -166,6 +256,9 @@ build_for_arch() {
166256 echo " ⚠️ WARNING: Source icon not found at $SOURCE_ICON "
167257 fi
168258
259+ # Bundle non-system dynamic libraries (libpq, OpenSSL, etc.)
260+ bundle_dylibs " $BUILD_DIR /$OUTPUT_NAME "
261+
169262 # Verify binary exists inside the copied bundle
170263 BINARY_PATH=" $BUILD_DIR /$OUTPUT_NAME /Contents/MacOS/TablePro"
171264 if [ ! -f " $BINARY_PATH " ]; then
0 commit comments