| 1 | #!/bin/bash |
| 2 | # Simple library bundling - copy libs and update binary paths only |
| 3 | # Don't touch the bundled libraries themselves |
| 4 | |
| 5 | set -e |
| 6 | |
| 7 | APP_BUNDLE="Sniffly.app" |
| 8 | FRAMEWORKS="${APP_BUNDLE}/Contents/Frameworks" |
| 9 | MACOS_BIN="${APP_BUNDLE}/Contents/MacOS/sniffly" |
| 10 | |
| 11 | echo "=== Simple Library Bundling ===" |
| 12 | |
| 13 | # Copy all GTK4-related libraries recursively |
| 14 | echo "Step 1: Copying GTK4 libraries..." |
| 15 | mkdir -p "$FRAMEWORKS" |
| 16 | |
| 17 | # Function to copy a library and its dependencies |
| 18 | copy_lib_and_deps() { |
| 19 | local lib=$1 |
| 20 | local libname=$(basename "$lib") |
| 21 | |
| 22 | if [ -f "$FRAMEWORKS/$libname" ]; then |
| 23 | return # Already copied |
| 24 | fi |
| 25 | |
| 26 | if [ ! -f "$lib" ]; then |
| 27 | return # Doesn't exist |
| 28 | fi |
| 29 | |
| 30 | echo " Copying $libname" |
| 31 | cp "$lib" "$FRAMEWORKS/" |
| 32 | chmod 644 "$FRAMEWORKS/$libname" |
| 33 | |
| 34 | # Get dependencies and copy them too |
| 35 | otool -L "$lib" | grep "/opt/homebrew" | awk '{print $1}' | while read dep; do |
| 36 | if [ -f "$dep" ]; then |
| 37 | copy_lib_and_deps "$dep" |
| 38 | fi |
| 39 | done |
| 40 | } |
| 41 | |
| 42 | # Start with the main libraries the binary needs |
| 43 | for lib in libgtk-4-fortran libgtk-4 libcairo libgio libglib libgfortran; do |
| 44 | find /opt/homebrew -name "${lib}*.dylib" -o -name "${lib}*.*.dylib" 2>/dev/null | head -1 | while read libpath; do |
| 45 | if [ -n "$libpath" ]; then |
| 46 | copy_lib_and_deps "$libpath" |
| 47 | fi |
| 48 | done |
| 49 | done |
| 50 | |
| 51 | # Also copy from /usr/local for gtk-4-fortran |
| 52 | if [ -f "/usr/local/lib/libgtk-4-fortran.4.8.0.dylib" ]; then |
| 53 | copy_lib_and_deps "/usr/local/lib/libgtk-4-fortran.4.8.0.dylib" |
| 54 | fi |
| 55 | |
| 56 | echo "" |
| 57 | echo "Step 2: Updating binary to use bundled libraries..." |
| 58 | # Only update the main binary's paths - don't touch the libraries |
| 59 | for lib in "$FRAMEWORKS"/*.dylib; do |
| 60 | if [ -f "$lib" ]; then |
| 61 | libname=$(basename "$lib") |
| 62 | # Try to change the binary's reference to this library |
| 63 | install_name_tool -change "/opt/homebrew/opt/*/lib/$libname" "@executable_path/../Frameworks/$libname" "$MACOS_BIN" 2>/dev/null || true |
| 64 | install_name_tool -change "/usr/local/lib/$libname" "@executable_path/../Frameworks/$libname" "$MACOS_BIN" 2>/dev/null || true |
| 65 | fi |
| 66 | done |
| 67 | |
| 68 | # Update specific known paths in the binary |
| 69 | install_name_tool -change "@rpath/libgtk-4-fortran.4.8.0.dylib" "@executable_path/../Frameworks/libgtk-4-fortran.4.8.0.dylib" "$MACOS_BIN" 2>/dev/null || true |
| 70 | install_name_tool -change "/opt/homebrew/opt/gtk4/lib/libgtk-4.1.dylib" "@executable_path/../Frameworks/libgtk-4.1.dylib" "$MACOS_BIN" 2>/dev/null || true |
| 71 | install_name_tool -change "/opt/homebrew/opt/cairo/lib/libcairo.2.dylib" "@executable_path/../Frameworks/libcairo.2.dylib" "$MACOS_BIN" 2>/dev/null || true |
| 72 | install_name_tool -change "/opt/homebrew/opt/glib/lib/libgio-2.0.0.dylib" "@executable_path/../Frameworks/libgio-2.0.0.dylib" "$MACOS_BIN" 2>/dev/null || true |
| 73 | install_name_tool -change "/opt/homebrew/opt/glib/lib/libglib-2.0.0.dylib" "@executable_path/../Frameworks/libglib-2.0.0.dylib" "$MACOS_BIN" 2>/dev/null || true |
| 74 | install_name_tool -change "/opt/homebrew/opt/gcc/lib/gcc/current/libgfortran.5.dylib" "@executable_path/../Frameworks/libgfortran.5.dylib" "$MACOS_BIN" 2>/dev/null || true |
| 75 | |
| 76 | echo "" |
| 77 | echo "Step 3: Fixing bundled libraries to reference each other..." |
| 78 | # Fix all the bundled libraries to reference each other instead of /opt/homebrew |
| 79 | for lib in "$FRAMEWORKS"/*.dylib; do |
| 80 | if [ -f "$lib" ]; then |
| 81 | libname=$(basename "$lib") |
| 82 | |
| 83 | # Fix the library's ID |
| 84 | install_name_tool -id "@executable_path/../Frameworks/$libname" "$lib" 2>/dev/null || true |
| 85 | |
| 86 | # Fix only the actual dependencies (much faster than trying all combinations) |
| 87 | otool -L "$lib" | grep "/opt/homebrew\|/usr/local" | awk '{print $1}' | while read dep_path; do |
| 88 | dep_name=$(basename "$dep_path") |
| 89 | if [ -f "$FRAMEWORKS/$dep_name" ]; then |
| 90 | install_name_tool -change "$dep_path" "@executable_path/../Frameworks/$dep_name" "$lib" 2>/dev/null || true |
| 91 | fi |
| 92 | done |
| 93 | fi |
| 94 | done |
| 95 | |
| 96 | echo "" |
| 97 | echo "Step 4: Re-signing everything (CRITICAL)..." |
| 98 | # After install_name_tool modifications, code signatures are invalid |
| 99 | # macOS will SIGKILL the app if signatures don't match |
| 100 | # Use ad-hoc signing (no certificate needed) |
| 101 | echo " Re-signing main binary..." |
| 102 | codesign --remove-signature "$MACOS_BIN" 2>/dev/null || true |
| 103 | codesign -s - -f "$MACOS_BIN" |
| 104 | |
| 105 | echo " Re-signing bundled libraries..." |
| 106 | for lib in "$FRAMEWORKS"/*.dylib; do |
| 107 | if [ -f "$lib" ]; then |
| 108 | codesign --remove-signature "$lib" 2>/dev/null || true |
| 109 | codesign -s - -f "$lib" 2>/dev/null || true |
| 110 | fi |
| 111 | done |
| 112 | |
| 113 | echo "" |
| 114 | echo "Step 5: Verifying..." |
| 115 | TOTAL_LIBS=$(ls -1 "$FRAMEWORKS" | wc -l | tr -d ' ') |
| 116 | echo "Total libraries bundled: $TOTAL_LIBS" |
| 117 | |
| 118 | echo "" |
| 119 | echo "=== Bundling Complete ===" |