78e5a6c026
Memind CI / Test, build, and release guards (pull_request) Failing after 18s
Bundle vendored imgproxy with Mach-O dylibs for 103 launchd, fix install to exec the binary directly with launchctl enable, and document the live native release in the runtime topology. Co-authored-by: Cursor <cursoragent@cursor.com>
224 lines
6.0 KiB
Bash
Executable File
224 lines
6.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Recursively copy non-system dylibs next to a Mach-O binary and rewrite load paths.
|
|
set -euo pipefail
|
|
|
|
find_homebrew_lib() {
|
|
local base="$1"
|
|
find "${HOMEBREW_PREFIX:-/opt/homebrew}/Cellar" "${HOMEBREW_PREFIX:-/opt/homebrew}/opt" \
|
|
-name "${base}" -path '*/lib/*' 2>/dev/null | head -1
|
|
}
|
|
|
|
resolve_dependency_path() {
|
|
local owner="$1"
|
|
local dep="$2"
|
|
local reference="${3:-${owner}}"
|
|
|
|
if [[ "${dep}" != @* ]]; then
|
|
[[ -f "${dep}" ]] || return 1
|
|
printf '%s\n' "${dep}"
|
|
return 0
|
|
fi
|
|
|
|
local owner_dir reference_dir
|
|
owner_dir="$(cd "$(dirname "${owner}")" && pwd)"
|
|
reference_dir="$(cd "$(dirname "${reference}")" && pwd)"
|
|
|
|
case "${dep}" in
|
|
@loader_path/*)
|
|
local candidate="${owner_dir}/${dep#@loader_path/}"
|
|
[[ -f "${candidate}" ]] || return 1
|
|
printf '%s\n' "${candidate}"
|
|
;;
|
|
@executable_path/*)
|
|
local rel="${dep#@executable_path/}"
|
|
local candidate="${owner_dir}/${rel}"
|
|
if [[ ! -f "${candidate}" ]]; then
|
|
candidate="${reference_dir}/${rel}"
|
|
fi
|
|
if [[ ! -f "${candidate}" ]]; then
|
|
candidate="$(find_homebrew_lib "$(basename "${rel}")")"
|
|
fi
|
|
[[ -f "${candidate}" ]] || return 1
|
|
printf '%s\n' "${candidate}"
|
|
;;
|
|
@rpath/*)
|
|
local rel="${dep#@rpath/}"
|
|
local rpath candidate
|
|
while IFS= read -r rpath; do
|
|
case "${rpath}" in
|
|
@loader_path/*) candidate="${owner_dir}/${rpath#@loader_path/}/${rel}" ;;
|
|
@executable_path/*) candidate="${owner_dir}/${rpath#@executable_path/}/${rel}" ;;
|
|
*) candidate="${rpath}/${rel}" ;;
|
|
esac
|
|
if [[ -f "${candidate}" ]]; then
|
|
printf '%s\n' "${candidate}"
|
|
return 0
|
|
fi
|
|
done < <(
|
|
otool -l "${owner}" | awk '
|
|
/cmd LC_RPATH/ { want=1; next }
|
|
want && /path / {
|
|
sub(/^.*path /, "", $0)
|
|
sub(/ \(offset.*$/, "", $0)
|
|
print
|
|
want=0
|
|
}
|
|
'
|
|
)
|
|
candidate="${owner_dir}/${rel}"
|
|
[[ -f "${candidate}" ]] || return 1
|
|
printf '%s\n' "${candidate}"
|
|
;;
|
|
*)
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
list_load_dylibs() {
|
|
local target="$1"
|
|
otool -l "${target}" | awk '
|
|
/cmd LC_LOAD_DYLIB/ { want=1; next }
|
|
want && /name / {
|
|
sub(/^.*name /, "", $0)
|
|
sub(/ \(offset.*$/, "", $0)
|
|
print
|
|
want=0
|
|
}
|
|
'
|
|
}
|
|
|
|
bundle_mach_dylibs() {
|
|
local binary="${1:?binary path required}"
|
|
local lib_dir="${2:?lib directory required}"
|
|
local reference_binary="${3:-${binary}}"
|
|
|
|
[[ -f "${binary}" ]] || {
|
|
echo "bundle_mach_dylibs: missing binary: ${binary}" >&2
|
|
return 1
|
|
}
|
|
|
|
mkdir -p "${lib_dir}"
|
|
|
|
local -a queue=("${binary}")
|
|
local seen_list=" "
|
|
local -a sources=()
|
|
|
|
is_system_lib() {
|
|
case "$1" in
|
|
/usr/lib/*|/System/*|/Library/*) return 0 ;;
|
|
esac
|
|
return 1
|
|
}
|
|
|
|
already_seen() {
|
|
[[ "${seen_list}" == *" $1 "* ]]
|
|
}
|
|
|
|
remember() {
|
|
seen_list+=" $1 "
|
|
}
|
|
|
|
while ((${#queue[@]} > 0)); do
|
|
local target="${queue[0]}"
|
|
queue=("${queue[@]:1}")
|
|
[[ -f "${target}" ]] || continue
|
|
|
|
local dep resolved
|
|
while IFS= read -r dep; do
|
|
[[ -n "${dep}" ]] || continue
|
|
is_system_lib "${dep}" && continue
|
|
already_seen "${dep}" && continue
|
|
remember "${dep}"
|
|
|
|
resolved="$(resolve_dependency_path "${target}" "${dep}" "${reference_binary}")" || {
|
|
echo "bundle_mach_dylibs: missing dependency ${dep} for ${target}" >&2
|
|
return 1
|
|
}
|
|
already_seen "${resolved}" || remember "${resolved}"
|
|
|
|
if [[ "${resolved}" != "${binary}" ]]; then
|
|
sources+=("${resolved}")
|
|
queue+=("${resolved}")
|
|
fi
|
|
done < <(list_load_dylibs "${target}")
|
|
done
|
|
|
|
local src base
|
|
for src in "${sources[@]}"; do
|
|
base="$(basename "${src}")"
|
|
cp -f "${src}" "${lib_dir}/${base}"
|
|
chmod 755 "${lib_dir}/${base}"
|
|
done
|
|
|
|
rewrite_dep() {
|
|
local target="$1"
|
|
local dep="$2"
|
|
local new_path="$3"
|
|
install_name_tool -change "${dep}" "${new_path}" "${target}" 2>/dev/null || true
|
|
}
|
|
|
|
rewrite_targets() {
|
|
local target="$1"
|
|
local prefix="$2"
|
|
local dep base
|
|
while IFS= read -r dep; do
|
|
[[ -n "${dep}" ]] || continue
|
|
is_system_lib "${dep}" && continue
|
|
[[ "${dep}" == @* ]] && continue
|
|
base="$(basename "${dep}")"
|
|
[[ -f "${lib_dir}/${base}" ]] || continue
|
|
rewrite_dep "${target}" "${dep}" "${prefix}${base}"
|
|
done < <(list_load_dylibs "${target}")
|
|
}
|
|
|
|
rewrite_rpath_targets() {
|
|
local target="$1"
|
|
local prefix="$2"
|
|
local dep base
|
|
while IFS= read -r dep; do
|
|
[[ -n "${dep}" ]] || continue
|
|
[[ "${dep}" == @rpath/* ]] || continue
|
|
base="$(basename "${dep}")"
|
|
[[ -f "${lib_dir}/${base}" ]] || continue
|
|
rewrite_dep "${target}" "${dep}" "${prefix}${base}"
|
|
done < <(list_load_dylibs "${target}")
|
|
}
|
|
|
|
local changed=1
|
|
while ((changed)); do
|
|
changed=0
|
|
rewrite_targets "${binary}" "@executable_path/../lib/"
|
|
rewrite_rpath_targets "${binary}" "@executable_path/../lib/"
|
|
for staged in "${lib_dir}"/*.dylib; do
|
|
[[ -f "${staged}" ]] || continue
|
|
before="$(list_load_dylibs "${staged}" | { grep -E 'homebrew|^@rpath/' || true; } | wc -l | tr -d ' ')"
|
|
rewrite_targets "${staged}" "@loader_path/"
|
|
rewrite_rpath_targets "${staged}" "@loader_path/"
|
|
after="$(list_load_dylibs "${staged}" | { grep -E 'homebrew|^@rpath/' || true; } | wc -l | tr -d ' ')"
|
|
[[ "${before}" != "${after}" ]] && changed=1
|
|
done
|
|
done
|
|
|
|
local remaining
|
|
remaining="$(list_load_dylibs "${binary}" | grep homebrew || true)"
|
|
if [[ -n "${remaining}" ]]; then
|
|
echo "bundle_mach_dylibs: unresolved homebrew dependencies remain:" >&2
|
|
printf '%s\n' "${remaining}" >&2
|
|
return 1
|
|
fi
|
|
|
|
install_name_tool -add_rpath "@executable_path/../lib" "${binary}" 2>/dev/null || true
|
|
|
|
local staged
|
|
for staged in "${lib_dir}"/*.dylib; do
|
|
[[ -f "${staged}" ]] || continue
|
|
codesign --force --sign - "${staged}" >/dev/null
|
|
done
|
|
codesign --force --sign - "${binary}" >/dev/null
|
|
}
|
|
|
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
bundle_mach_dylibs "$@"
|
|
fi
|