visualboyadvance-m/tools/macOS/third_party_libs_tool

324 lines
7.7 KiB
Plaintext
Raw Normal View History

#!/bin/sh
version=1.5
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
app_bundle=$(echo "$1" | fully_resolve_links)
case "$app_bundle" in
*.[aA][pP][pP])
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"
mkdir -p "$frameworks"
scan_libs "$@" | sort -u | \
while read lib; do
if [ -n "$list" ]; then
echo "$lib"
else
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
fi
done
# fix dynamic link info in executables and just copied libs
[ -z "$list" ] && relink_all "$@"
quit 0
}
usage() {
cat <<'EOF'
Usage: third_party_libs_tool [OPTION] BUNDLE.app
Bundle third party dylibs into BUNDLE.app and fix up linkages.
Binaries are searched for in BUNDLE.app/Contents/MacOS .
The dylibs are copied into BUNDLE.app/Contents/Frameworks .
-h, --help, --usage Show this help screen and exit.
-v, --version Show version information and exit.
-l, --list Only list dylibs used by binaries, do not copy or link.
Examples:
third_party_libs_tool ./MyApp.app # bundle and link ./MyApp.app
third_party_libs_tool --list ./MyApp.app # list third party libs used by ./MyApp.app
Project homepage and documentation: <http://github.com/rkitover/mac-third-party-libs-tool>
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}
}
scan_libs() {
scratch_dir="$tmp/lib_scan"
mkdir -p "$scratch_dir"
lib_scan "$@"
rm -rf "$scratch_dir"
}
lib_scan() {
for bin in "$@"; do
case "$bin" in
*.dylib)
;;
*)
[ ! -x "$bin" ] && continue
;;
esac
# Remove path parts for mark file
bin_mark_file=$(echo "$bin" | sed 's,/,_,g')
# if binary is already processed, continue
[ -d "$scratch_dir/$bin_mark_file" ] && continue
# otherwise mark it processed
mkdir -p "$scratch_dir/$bin_mark_file"
set --
OLDIFS=$IFS
IFS='
'
for lib in $(otool -L "$bin" 2>/dev/null \
| sed -E '1d; s/^[[:space:]]*//; \,^(/System|/usr/lib),d; s/[[:space:]]+\([^()]+\)[[:space:]]*$//'); do
[ "$lib" = "$bin" ] && continue
# check for libs already linked as @rpath/ which usually means /usr/local/lib/
case "$lib" in
'@rpath/'*)
lib='/usr/local/lib'"${lib#@rpath}"
;;
'@loader_path/../../../../'*)
lib='/usr/local/'"${lib#@loader_path/../../../../}"
;;
'@loader_path/'*)
lib='/usr/local/lib'"${lib#@loader_path}"
;;
esac
echo "$lib"
set -- "$@" "$lib"
done
IFS=$OLDIFS
# recurse
[ $# -ne 0 ] && lib_scan "$@"
done
}
fully_resolve_links() {
while read -r file; do
while [ -h "$file" ]; do
file=$(readlink -f "$file")
done
echo "$file"
done
}
lock() {
mkdir -p "$lock_dir/$1"
}
unlock() {
rm -rf "$lock_dir/$1"
}
wait_lock() {
while [ -d "$lock_dir/$1" ]; do
/bin/bash -c 'sleep 0.1'
done
}
relink_all() {
lock_dir="$tmp/locks"
find "$app_bundle/Contents/Frameworks" -name '*.dylib' > "$tmp/libs"
for exe in "$@"; do (
# dylib search path for executable
wait_lock "$exe"
lock "$exe"
install_name_tool -add_rpath '@loader_path/../Frameworks' "$exe"
unlock "$exe"
OLDIFS=$IFS
IFS='
'
set --
for lib in $(cat "$tmp/libs"); do
set -- "$@" "$lib"
done
IFS=$OLDIFS
for lib in "$@"; do
wait_lock "$lib"
lock "$lib"
# 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"
unlock "$lib"
# relink executable and all other libs to this lib
for target in "$exe" "$@"; do (
relink "$lib" "$target"
) & done
wait
done
) & done
wait
rm -rf "$tmp/libs" "$lock_dir"
}
relink() {
lib=$1
target=$2
lib_basename=${lib##*/}
lib_basename_unversioned_re=$(echo "$lib_basename" | sed 's/\.[0-9.-]*\.dylib$//; s/\./\\./g')
# remove full path and version of lib in executable
lib_link_path=$(
otool -l "$target" 2>/dev/null | \
sed -n 's,^ *name \(.*/*'"$lib_basename_unversioned_re"'\.[0-9.-]*\.dylib\) (offset .*,\1,p' | \
head -1
)
[ -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
fi
case "$longer" in
"$shorter"*)
# and if so, relink target to the lib
wait_lock "$lib"
lock "$lib"
install_name_tool -change "$lib_link_path" "@rpath/$lib_basename" "$target"
unlock "$lib"
;;
esac
}
# 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
if ! command sudo install_name_tool "$@"; then
return 1
fi
elif ! grep -Eq -i 'would duplicate path' "$out_file"; then
cat "$out_file" >&2
return 1
fi
fi
return 0
}
main "$@"