#!/bin/sh
##
# Find mach-O executables and:
# - update the install_name of dependent Mach-O libraries.
#
# $Id$
#
# Copyright (c) 2002 Wilfredo Sanchez Vega, wsanchez@mit.edu.
# All rights reserved.
#
# Permission to use, copy, modify, and distribute this software for
# any purpose with or without fee is hereby granted, provided that the
# above copyright notice, this permission, and the following
# disclaimer notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL
# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
# AUTHORS BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
# CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
# OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
##
# Be strict
set -u
set -e
##
# Functions
##
#
# Command line usage information
#
usage ()
{
echo "Usage: $(basename $0) [-L
] ... ...";
echo "Options: -L add to search path for dependent libraries";
exit 1;
}
#
# Find files by type
# Arguments:
# $1 : a type string to match
# $2 : a file mode string (see chmod(3), may be empty)
# $3+: a list of targets
#
find_file_of_type ()
{
match="$1"; shift;
mode="$1"; shift;
if [ -n "${mode}" ]; then mode="-perm +${mode}"; fi;
for dir; do
for file in $(find "${dir}" -type f ${mode}); do
if file "${file}" | egrep ": ${match}" > /dev/null; then
echo "${file}";
fi;
done;
done;
}
#
# Shortcuts for find_file_of_type()
#
find_macho_executables () { find_file_of_type 'Mach-O executable' '+x' "$@"; }
find_macho_dylibs () { find_file_of_type 'Mach-O dynamically linked shared library' '' "$@"; }
find_macho_files () { find_file_of_type 'Mach-O (executable|dynamically linked shared library)' '' "$@"; }
#
# Given a file, list its dependencies.
#
macho_deps ()
{
otool -L "$1" | grep '^ ' | sed -e 's|^ \(.*\) (compatibility version.*|\1|';
}
#
# For each argument, find all Mach-O files if the argument is a
# directory or verify that the argument is a Mach-O file if the
# argument is a file.
# Then proceed to update the dependencies in the file and, if the file
# is a shared library, update the file's install_name.
#
fix_macho_deps ()
{
for file in $(find_macho_files "$@"); do
# If ${file} is a library, update its install_name
for lib in $(find_macho_dylibs "${file}"); do
dirname=$(cd $(dirname "${lib}") && pwd);
basename=$(basename "${lib}");
path="${dirname}/${basename}";
install_name=$(macho_deps "${path}" | head -1);
if [ "${install_name}" != "${path}" ]; then
echo "${lib}: updating install_name";
install_name_tool -id "${path}" "${path}";
fi;
done;
# Update dependencies
for dep in $(macho_deps "${file}"); do
if [ ! -f "${dep}" ]; then
basename=$(basename "${dep}");
if ! path=$(grep "${basename}$" "${LibrariesCache}" | head -1); then
echo "Can't locate library ${dep} for ${file}";
else
echo "${file}: updating path to ${basename}";
install_name_tool -change "${dep}" "${path}" "${file}";
fi;
fi;
done;
done;
}
##
# Handle command line
##
LibrariesCache=$(mktemp -t $(basename $0));
if ! args=$(getopt L: "$@"); then usage; fi;
set -- ${args};
for option; do
case "${option}" in
-L)
dir="$2";
if [ ! -d "${dir}" ]; then echo "${dir}: not a directory"; exit 1; fi;
dir=$(cd "${dir}" && pwd);
find_macho_dylibs "${dir}" >> "${LibrariesCache}";
shift 2;
;;
--)
shift;
break;
;;
esac;
done;
##
# Do The Right Thing
##
fix_macho_deps "$@";
rm -f "${LibrariesCache}";