#!/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}";