visualboyadvance-m/tools/macOS/third_party_libs_tool

324 lines
7.7 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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 "$@"