2016-10-28 05:06:06 +00:00
|
|
|
|
#!/bin/sh
|
|
|
|
|
|
2024-04-16 02:13:37 +00:00
|
|
|
|
version=1.4
|
2016-10-28 05:06:06 +00:00
|
|
|
|
|
|
|
|
|
main() {
|
|
|
|
|
# parse options
|
|
|
|
|
list=
|
|
|
|
|
while [ $# -gt 0 ]; do
|
|
|
|
|
case "$1" in
|
|
|
|
|
-h|--help|--usage)
|
|
|
|
|
usage
|
|
|
|
|
quit 0
|
|
|
|
|
;;
|
|
|
|
|
-v|--version)
|
|
|
|
|
echo "third_party_libs_tool $version"
|
|
|
|
|
quit 0
|
|
|
|
|
;;
|
|
|
|
|
-l|--list)
|
|
|
|
|
list=1
|
|
|
|
|
shift
|
|
|
|
|
;;
|
|
|
|
|
*)
|
|
|
|
|
break
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
if [ $# -ne 1 ]; then
|
|
|
|
|
usage
|
|
|
|
|
quit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
mktmp
|
|
|
|
|
|
2016-11-03 17:49:13 +00:00
|
|
|
|
app_bundle=$(echo "$1" | fully_resolve_links)
|
2016-10-28 05:06:06 +00:00
|
|
|
|
|
|
|
|
|
case "$app_bundle" in
|
|
|
|
|
*.app|*.APP)
|
|
|
|
|
if [ ! -d "$app_bundle" ]; then
|
|
|
|
|
usage
|
|
|
|
|
quit 1
|
|
|
|
|
fi
|
|
|
|
|
;;
|
|
|
|
|
*)
|
|
|
|
|
usage
|
|
|
|
|
quit 1
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
|
|
|
|
|
set --
|
|
|
|
|
|
|
|
|
|
OLDIFS=$IFS
|
|
|
|
|
IFS='
|
|
|
|
|
'
|
|
|
|
|
for file in $(find "$app_bundle/Contents/MacOS" -type f); do
|
|
|
|
|
case "$file" in
|
|
|
|
|
*.dylib)
|
|
|
|
|
set -- "$@" "$file"
|
|
|
|
|
;;
|
|
|
|
|
*)
|
|
|
|
|
[ -x "$file" ] && set -- "$@" "$file"
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
done
|
|
|
|
|
IFS=$OLDIFS
|
|
|
|
|
|
|
|
|
|
frameworks="$app_bundle/Contents/Frameworks"
|
|
|
|
|
|
2020-06-05 10:36:59 +00:00
|
|
|
|
mkdir -p "$frameworks"
|
2016-10-28 05:06:06 +00:00
|
|
|
|
|
2024-04-16 02:13:37 +00:00
|
|
|
|
scan_libs "$@" | sort -u | \
|
2016-10-28 05:06:06 +00:00
|
|
|
|
while read lib; do
|
|
|
|
|
if [ -n "$list" ]; then
|
|
|
|
|
echo "$lib"
|
|
|
|
|
else
|
2024-04-16 02:13:37 +00:00
|
|
|
|
resolved=$(echo "$lib" | fully_resolve_links)
|
|
|
|
|
resolved_basename=${resolved##*/}
|
|
|
|
|
if [ ! -f "$frameworks/$resolved_basename" ]; then
|
|
|
|
|
cp -f "$resolved" "$frameworks" 2>/dev/null
|
|
|
|
|
fi
|
|
|
|
|
if [ "$resolved" != "$lib" ]; then
|
|
|
|
|
lib_basename=${lib##*/}
|
|
|
|
|
if [ ! -f "$frameworks/$lib_basename" ]; then
|
|
|
|
|
ln -s "$resolved_basename" "$frameworks/$lib_basename"
|
|
|
|
|
fi
|
|
|
|
|
fi
|
2016-10-28 05:06:06 +00:00
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# fix dynamic link info in executables and just copied libs
|
|
|
|
|
[ -z "$list" ] && relink_all "$@"
|
|
|
|
|
|
|
|
|
|
quit 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
usage() {
|
|
|
|
|
cat <<'EOF'
|
|
|
|
|
Usage: [32mthird_party_libs_tool [1;34m[[1;35mOPTION[1;34m][0m [1;35mBUNDLE.app[0m
|
|
|
|
|
Bundle third party dylibs into [1;35mBUNDLE.app[0m and fix up linkages.
|
|
|
|
|
|
|
|
|
|
Binaries are searched for in [1;35mBUNDLE.app[0m/Contents/MacOS .
|
|
|
|
|
|
|
|
|
|
The dylibs are copied into [1;35mBUNDLE.app[0m/Contents/Frameworks .
|
|
|
|
|
|
|
|
|
|
[1m-h, --help, --usage[0m Show this help screen and exit.
|
|
|
|
|
[1m-v, --version[0m Show version information and exit.
|
|
|
|
|
[1m-l, --list[0m Only list dylibs used by binaries, do not copy or link.
|
|
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
[32mthird_party_libs_tool [1;35m./MyApp.app[0m # bundle and link [1;35m./MyApp.app[0m
|
|
|
|
|
[32mthird_party_libs_tool [1m--list[0m [1;35m./MyApp.app[0m # list third party libs used by [1;35m./MyApp.app[0m
|
|
|
|
|
|
|
|
|
|
Project homepage and documentation: <[1;34mhttp://github.com/rkitover/mac-third-party-libs-tool[0m>
|
|
|
|
|
EOF
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mktmp() {
|
|
|
|
|
tmp="/tmp/third_party_libs_tool_$$"
|
|
|
|
|
mkdir "$tmp" || quit 1
|
|
|
|
|
chmod 700 "$tmp" 2>/dev/null
|
|
|
|
|
trap "quit 1" PIPE HUP INT QUIT ILL TRAP KILL BUS TERM
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
quit() {
|
|
|
|
|
[ -n "$tmp" ] && rm -rf "$tmp" 2>/dev/null
|
|
|
|
|
exit ${1:-0}
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-23 11:07:44 +00:00
|
|
|
|
scan_libs() {
|
|
|
|
|
scratch_dir="$tmp/lib_scan"
|
|
|
|
|
mkdir -p "$scratch_dir"
|
|
|
|
|
|
|
|
|
|
lib_scan "$@"
|
|
|
|
|
|
|
|
|
|
rm -rf "$scratch_dir"
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-28 05:06:06 +00:00
|
|
|
|
lib_scan() {
|
|
|
|
|
for bin in "$@"; do
|
|
|
|
|
case "$bin" in
|
|
|
|
|
*.dylib)
|
|
|
|
|
;;
|
|
|
|
|
*)
|
|
|
|
|
[ ! -x "$bin" ] && continue
|
|
|
|
|
;;
|
|
|
|
|
esac
|
|
|
|
|
|
2023-08-27 17:19:08 +00:00
|
|
|
|
# Remove path parts for mark file
|
|
|
|
|
bin_mark_file=$(echo "$bin" | sed 's,/,_,g')
|
|
|
|
|
|
2016-11-23 11:07:44 +00:00
|
|
|
|
# if binary is already processed, continue
|
2023-08-27 17:19:08 +00:00
|
|
|
|
[ -d "$scratch_dir/$bin_mark_file" ] && continue
|
2016-11-23 11:07:44 +00:00
|
|
|
|
|
|
|
|
|
# otherwise mark it processed
|
2023-08-27 17:19:08 +00:00
|
|
|
|
mkdir -p "$scratch_dir/$bin_mark_file"
|
2016-11-23 11:07:44 +00:00
|
|
|
|
|
2016-10-28 05:06:06 +00:00
|
|
|
|
set --
|
|
|
|
|
|
|
|
|
|
OLDIFS=$IFS
|
|
|
|
|
IFS='
|
|
|
|
|
'
|
2016-11-23 11:07:44 +00:00
|
|
|
|
for lib in $(otool -L "$bin" 2>/dev/null \
|
2020-06-05 10:36:59 +00:00
|
|
|
|
| sed -E '1d; s/^[[:space:]]*//; \,^(/System|/usr/lib),d; s/[[:space:]]+\([^()]+\)[[:space:]]*$//'); do
|
2016-11-23 11:07:44 +00:00
|
|
|
|
|
2016-10-28 05:06:06 +00:00
|
|
|
|
[ "$lib" = "$bin" ] && continue
|
|
|
|
|
|
2017-02-24 20:24:20 +00:00
|
|
|
|
# check for libs already linked as @rpath/ which usually means /usr/local/lib/
|
|
|
|
|
case "$lib" in
|
|
|
|
|
'@rpath/'*)
|
|
|
|
|
lib='/usr/local/lib'"${lib#@rpath}"
|
|
|
|
|
;;
|
2023-08-27 17:19:08 +00:00
|
|
|
|
'@loader_path/../../../../'*)
|
|
|
|
|
lib='/usr/local/'"${lib#@loader_path/../../../../}"
|
|
|
|
|
;;
|
|
|
|
|
'@loader_path/'*)
|
|
|
|
|
lib='/usr/local/lib'"${lib#@loader_path}"
|
|
|
|
|
;;
|
2017-02-24 20:24:20 +00:00
|
|
|
|
esac
|
|
|
|
|
|
2016-10-28 05:06:06 +00:00
|
|
|
|
echo "$lib"
|
|
|
|
|
set -- "$@" "$lib"
|
|
|
|
|
done
|
|
|
|
|
IFS=$OLDIFS
|
|
|
|
|
|
|
|
|
|
# recurse
|
|
|
|
|
[ $# -ne 0 ] && lib_scan "$@"
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fully_resolve_links() {
|
|
|
|
|
while read -r file; do
|
2023-08-27 17:19:08 +00:00
|
|
|
|
while [ -h "$file" ]; do
|
|
|
|
|
file=$(readlink -f "$file")
|
2016-10-28 05:06:06 +00:00
|
|
|
|
done
|
2023-08-27 17:19:08 +00:00
|
|
|
|
echo "$file"
|
2016-10-28 05:06:06 +00:00
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
relink_all() {
|
|
|
|
|
for exe in "$@"; do
|
|
|
|
|
# dylib search path for executable
|
|
|
|
|
install_name_tool -add_rpath '@loader_path/../Frameworks' "$exe"
|
|
|
|
|
|
|
|
|
|
OLDIFS=$IFS
|
|
|
|
|
IFS='
|
|
|
|
|
'
|
|
|
|
|
set --
|
|
|
|
|
for lib in $(find "$app_bundle/Contents/Frameworks" -name '*.dylib'); do
|
|
|
|
|
set -- "$@" "$lib"
|
|
|
|
|
done
|
|
|
|
|
IFS=$OLDIFS
|
|
|
|
|
|
|
|
|
|
for lib in "$@"; do
|
|
|
|
|
# make lib writable
|
|
|
|
|
chmod u+w "$lib"
|
|
|
|
|
|
|
|
|
|
# change id of lib
|
|
|
|
|
install_name_tool -id "@rpath/${lib##*/}" "$lib"
|
|
|
|
|
|
|
|
|
|
# set search path of lib
|
|
|
|
|
install_name_tool -add_rpath '@loader_path/../Frameworks' "$lib"
|
|
|
|
|
|
|
|
|
|
# relink executable and all other libs to this lib
|
|
|
|
|
for target in "$exe" "$@"; do
|
|
|
|
|
relink "$lib" "$target"
|
|
|
|
|
done
|
|
|
|
|
done
|
|
|
|
|
done
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
relink() {
|
|
|
|
|
lib=$1
|
|
|
|
|
target=$2
|
|
|
|
|
|
|
|
|
|
lib_basename=${lib##*/}
|
2024-04-16 02:13:37 +00:00
|
|
|
|
lib_basename_unversioned_re=$(echo "$lib_basename" | sed 's/\.[0-9.-]*\.dylib$//; s/\./\\./g')
|
2016-10-28 05:06:06 +00:00
|
|
|
|
|
|
|
|
|
# remove full path and version of lib in executable
|
|
|
|
|
lib_link_path=$(
|
|
|
|
|
otool -l "$target" 2>/dev/null | \
|
2024-04-16 02:13:37 +00:00
|
|
|
|
sed -n 's,^ *name \(.*/*'"$lib_basename_unversioned_re"'\.[0-9.-]*\.dylib\) (offset .*,\1,p' | \
|
|
|
|
|
head -1
|
2016-10-28 05:06:06 +00:00
|
|
|
|
)
|
|
|
|
|
|
2016-11-03 17:49:13 +00:00
|
|
|
|
[ -z "$lib_link_path" ] && return 0
|
|
|
|
|
|
|
|
|
|
# check that the shorter basename is the prefix of the longer basename
|
|
|
|
|
# that is, the lib versions match
|
|
|
|
|
lib1=${lib_basename%.dylib}
|
|
|
|
|
lib2=${lib_link_path##*/}
|
|
|
|
|
lib2=${lib2%.dylib}
|
|
|
|
|
|
|
|
|
|
if [ "${#lib1}" -le "${#lib2}" ]; then
|
|
|
|
|
shorter=$lib1
|
|
|
|
|
longer=$lib2
|
|
|
|
|
else
|
|
|
|
|
shorter=$lib2
|
|
|
|
|
longer=$lib1
|
2016-10-28 05:06:06 +00:00
|
|
|
|
fi
|
2016-11-03 17:49:13 +00:00
|
|
|
|
|
|
|
|
|
case "$longer" in
|
|
|
|
|
"$shorter"*)
|
|
|
|
|
# and if so, relink target to the lib
|
|
|
|
|
install_name_tool -change "$lib_link_path" "@rpath/$lib_basename" "$target"
|
|
|
|
|
;;
|
|
|
|
|
esac
|
2016-10-28 05:06:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# try with sudo in case it fails,
|
|
|
|
|
# also suppress duplicate path errors
|
|
|
|
|
install_name_tool() {
|
|
|
|
|
out_file="$tmp/install_name_tool.out"
|
|
|
|
|
|
|
|
|
|
if ! command install_name_tool "$@" >"$out_file" 2>&1; then
|
|
|
|
|
if grep -Eq -i 'permission denied|bad file descriptor' "$out_file"; then
|
2020-06-05 10:36:59 +00:00
|
|
|
|
if ! command sudo install_name_tool "$@"; then
|
2016-10-28 05:06:06 +00:00
|
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
elif ! grep -Eq -i 'would duplicate path' "$out_file"; then
|
|
|
|
|
cat "$out_file" >&2
|
|
|
|
|
return 1
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
main "$@"
|