|
| 1 | +#!/bin/sh |
| 2 | +set -eu |
| 3 | + |
| 4 | +homebrew_path="" |
| 5 | +exec_path="target/debug/bundle/osx/Lan Mouse.app/Contents/MacOS/lan-mouse" |
| 6 | + |
| 7 | +usage() { |
| 8 | + cat <<EOF |
| 9 | +$0: Copy all Homebrew libraries into the macOS app bundle. |
| 10 | +USAGE: $0 [-h] [-b homebrew_path] [exec_path] |
| 11 | +
|
| 12 | +OPTIONS: |
| 13 | + -h, --help Show this help message and exit |
| 14 | + -b Path to Homebrew installation (default: $homebrew_path) |
| 15 | + exec_path Path to the main executable in the app bundle |
| 16 | + (default: get from `brew --prefix`) |
| 17 | +
|
| 18 | +When macOS apps are linked to dynamic libraries (.dylib files), |
| 19 | +the fully qualified path to the library is embedded in the binary. |
| 20 | +If the libraries come from Homebrew, that means that Homebrew must be present |
| 21 | +and the libraries must be installed in the same location on the user's machine. |
| 22 | +
|
| 23 | +This script copies all of the Homebrew libraries that an executable links to into the app bundle |
| 24 | +and tells all the binaries in the bundle to look for them there. |
| 25 | +EOF |
| 26 | +} |
| 27 | + |
| 28 | +# Gather command-line arguments |
| 29 | +while test $# -gt 0; do |
| 30 | + case "$1" in |
| 31 | + -h | --help ) usage; exit 0;; |
| 32 | + -b | --homebrew ) homebrew_path="$1"; shift 2;; |
| 33 | + * ) exec_path="$1"; shift;; |
| 34 | + esac |
| 35 | +done |
| 36 | + |
| 37 | +if [ -z "$homebrew_path" ]; then |
| 38 | + homebrew_path="$(brew --prefix)" |
| 39 | +fi |
| 40 | + |
| 41 | +# Path to the .app bundle |
| 42 | +bundle_path=$(dirname "$(dirname "$(dirname "$exec_path")")") |
| 43 | +# Path to the Frameworks directory |
| 44 | +fwks_path="$bundle_path/Contents/Frameworks" |
| 45 | +mkdir -p "$fwks_path" |
| 46 | + |
| 47 | +# Copy and fix references for a binary (executable or dylib) |
| 48 | +# |
| 49 | +# This function will: |
| 50 | +# - Copy any referenced dylibs from /opt/homebrew to the Frameworks directory |
| 51 | +# - Update the binary to reference the local copy instead |
| 52 | +# - Add the Frameworks directory to the binary's RPATH |
| 53 | +# - Recursively process the copied dylibs |
| 54 | +fix_references() { |
| 55 | + local bin="$1" |
| 56 | + |
| 57 | + # Get all Homebrew libraries referenced by the binary |
| 58 | + libs=$(otool -L "$bin" | awk -v homebrew="$homebrew_path" '$0 ~ homebrew {print $1}') |
| 59 | + |
| 60 | + echo "$libs" | while IFS= read -r old_path; do |
| 61 | + local base_name="$(basename "$old_path")" |
| 62 | + local dest="$fwks_path/$base_name" |
| 63 | + |
| 64 | + if [ ! -e "$dest" ]; then |
| 65 | + echo "Copying $old_path -> $dest" |
| 66 | + cp -f "$old_path" "$dest" |
| 67 | + # Ensure the copied dylib is writable so that xattr -rd /path/to/Lan\ Mouse.app works. |
| 68 | + chmod 644 "$dest" |
| 69 | + |
| 70 | + echo "Updating $dest to have install_name of @rpath/$base_name..." |
| 71 | + install_name_tool -id "@rpath/$base_name" "$dest" |
| 72 | + |
| 73 | + # Recursively process this dylib |
| 74 | + fix_references "$dest" |
| 75 | + fi |
| 76 | + |
| 77 | + echo "Updating $bin to reference @rpath/$base_name..." |
| 78 | + install_name_tool -change "$old_path" "@rpath/$base_name" "$bin" |
| 79 | + done |
| 80 | +} |
| 81 | + |
| 82 | +fix_references "$exec_path" |
| 83 | + |
| 84 | +# Ensure the main executable has our Frameworks path in its RPATH |
| 85 | +if ! otool -l "$exec_path" | grep -q "@executable_path/../Frameworks"; then |
| 86 | + echo "Adding RPATH to $exec_path" |
| 87 | + install_name_tool -add_rpath "@executable_path/../Frameworks" "$exec_path" |
| 88 | +fi |
| 89 | + |
| 90 | +# Se-sign the .app |
| 91 | +codesign --force --deep --sign - "$bundle_path" |
| 92 | + |
| 93 | +echo "Done!" |
0 commit comments