visualboyadvance-m/tools/macOS/third_party_libs_tool

312 lines
7.3 KiB
Plaintext
Raw Normal View History

#!/bin/sh
version=1.2
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
*.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"
mkdir -p "$frameworks"
scan_libs "$@" | fully_resolve_links | sort -u | \
while read lib; do
if [ -n "$list" ]; then
echo "$lib"
else
cp -f "$lib" "$frameworks" 2>/dev/null
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
# if binary is already processed, continue
[ -d "$scratch_dir/$bin" ] && continue
# otherwise mark it processed
mkdir -p "$scratch_dir/$bin"
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}"
;;
esac
echo "$lib"
set -- "$@" "$lib"
done
IFS=$OLDIFS
# recurse
[ $# -ne 0 ] && lib_scan "$@"
done
}
fully_resolve_links() {
while read -r file; do
# get initial part for non-absolute path, or blank for absolute
path=${file%%/*}
# and set $file to the rest
file=${file#*/}
OLDIFS=$IFS
IFS='/'
for part in $file; do
[ ! -z "$part" ] && path=$(resolve_link "$path/$part")
done
IFS=$OLDIFS
# remove 'foo/..' path parts
while :; do
case "$path" in
*/../*|*/..)
path=$(echo "$path" | sed 's,//*[^/][^/]*//*\.\./*,/,g')
;;
*)
break
;;
esac
done
# remove trailing /s
while [ "$path" != "${path%/}" ]; do
path=${path%/}
done
echo "$path"
done
}
resolve_link() {
file=$1
while [ -h "$file" ]; do
ls0=$(ls -ld "$file")
new_link=$(expr "$ls0" : '.* -> \(.*\)$')
if expr "$new_link" : '/.*' > /dev/null; then
file="$new_link"
else
file="${file%/*}"/"$new_link"
fi
done
echo "$file"
}
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##*/}
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
install_name_tool -change "$lib_link_path" "@rpath/$lib_basename" "$target"
;;
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 "$@"