michael@0: #!/bin/bash michael@0: michael@0: # Copyright (c) 2012 The Chromium Authors. All rights reserved. michael@0: # Use of this source code is governed by a BSD-style license that can be michael@0: # found in the LICENSE file. michael@0: michael@0: # Copies a framework to its new home, "unversioning" it. michael@0: # michael@0: # Normally, frameworks are versioned bundles. The contents of a framework are michael@0: # stored in a versioned directory within the bundle, and symbolic links michael@0: # provide access to the actual code and resources. See michael@0: # http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html michael@0: # michael@0: # The symbolic links usually found in frameworks create problems. Symbolic michael@0: # links are excluded from code signatures. That means that it's possible to michael@0: # remove or retarget a symbolic link within a framework without affecting the michael@0: # seal. In Chrome's case, the outer .app bundle contains a framework where michael@0: # all application code and resources live. In order for the signature on the michael@0: # .app to be meaningful, it encompasses the framework. Because framework michael@0: # resources are accessed through the framework's symbolic links, this michael@0: # arrangement results in a case where the resources can be altered without michael@0: # affecting the .app signature's validity. michael@0: # michael@0: # Indirection through symbolic links also carries a runtime performance michael@0: # penalty on open() operations, although open() typically completes so quickly michael@0: # that this is not considered a major performance problem. michael@0: # michael@0: # To resolve these problems, the frameworks that ship within Chrome's .app michael@0: # bundle are unversioned. Unversioning is simple: instead of using the michael@0: # original outer .framework directory as the framework that ships within the michael@0: # .app, the inner versioned directory is used. Instead of accessing bundled michael@0: # resources through symbolic links, they are accessed directly. In normal michael@0: # situations, the only hard-coded use of the versioned directory is by dyld, michael@0: # when loading the framework's code, but this is handled through a normal michael@0: # Mach-O load command, and it is easy to adjust the load command to point to michael@0: # the unversioned framework code rather than the versioned counterpart. michael@0: # michael@0: # The resulting framework bundles aren't strictly conforming, but they work michael@0: # as well as normal versioned framework bundles. michael@0: # michael@0: # An option to skip running install_name_tool is available. By passing -I as michael@0: # the first argument to this script, install_name_tool will be skipped. This michael@0: # is only suitable for copied frameworks that will not be linked against, or michael@0: # when install_name_tool will be run on any linker output when something is michael@0: # linked against the copied framework. This option exists to allow signed michael@0: # frameworks to pass through without subjecting them to any modifications that michael@0: # would break their signatures. michael@0: michael@0: set -e michael@0: michael@0: RUN_INSTALL_NAME_TOOL=1 michael@0: if [ $# -eq 3 ] && [ "${1}" = "-I" ] ; then michael@0: shift michael@0: RUN_INSTALL_NAME_TOOL= michael@0: fi michael@0: michael@0: if [ $# -ne 2 ] ; then michael@0: echo "usage: ${0} [-I] FRAMEWORK DESTINATION_DIR" >& 2 michael@0: exit 1 michael@0: fi michael@0: michael@0: # FRAMEWORK should be a path to a versioned framework bundle, ending in michael@0: # .framework. DESTINATION_DIR is the directory that the unversioned framework michael@0: # bundle will be copied to. michael@0: michael@0: FRAMEWORK="${1}" michael@0: DESTINATION_DIR="${2}" michael@0: michael@0: FRAMEWORK_NAME="$(basename "${FRAMEWORK}")" michael@0: if [ "${FRAMEWORK_NAME: -10}" != ".framework" ] ; then michael@0: echo "${0}: ${FRAMEWORK_NAME} does not end in .framework" >& 2 michael@0: exit 1 michael@0: fi michael@0: FRAMEWORK_NAME_NOEXT="${FRAMEWORK_NAME:0:$((${#FRAMEWORK_NAME} - 10))}" michael@0: michael@0: # Find the current version. michael@0: VERSIONS="${FRAMEWORK}/Versions" michael@0: CURRENT_VERSION_LINK="${VERSIONS}/Current" michael@0: CURRENT_VERSION_ID="$(readlink "${VERSIONS}/Current")" michael@0: CURRENT_VERSION="${VERSIONS}/${CURRENT_VERSION_ID}" michael@0: michael@0: # Make sure that the framework's structure makes sense as a versioned bundle. michael@0: if [ ! -e "${CURRENT_VERSION}/${FRAMEWORK_NAME_NOEXT}" ] ; then michael@0: echo "${0}: ${FRAMEWORK_NAME} does not contain a dylib" >& 2 michael@0: exit 1 michael@0: fi michael@0: michael@0: DESTINATION="${DESTINATION_DIR}/${FRAMEWORK_NAME}" michael@0: michael@0: # Copy the versioned directory within the versioned framework to its michael@0: # destination location. michael@0: mkdir -p "${DESTINATION_DIR}" michael@0: rsync -acC --delete --exclude Headers --exclude PrivateHeaders \ michael@0: --include '*.so' "${CURRENT_VERSION}/" "${DESTINATION}" michael@0: michael@0: if [[ -n "${RUN_INSTALL_NAME_TOOL}" ]]; then michael@0: # Adjust the Mach-O LC_ID_DYLIB load command in the framework. This does not michael@0: # change the LC_LOAD_DYLIB load commands in anything that may have already michael@0: # linked against the framework. Not all frameworks will actually need this michael@0: # to be changed. Some frameworks may already be built with the proper michael@0: # LC_ID_DYLIB for use as an unversioned framework. Xcode users can do this michael@0: # by setting LD_DYLIB_INSTALL_NAME to michael@0: # $(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(WRAPPER_NAME)/$(PRODUCT_NAME) michael@0: # If invoking ld via gcc or g++, pass the desired path to -Wl,-install_name michael@0: # at link time. michael@0: FRAMEWORK_DYLIB="${DESTINATION}/${FRAMEWORK_NAME_NOEXT}" michael@0: LC_ID_DYLIB_OLD="$(otool -l "${FRAMEWORK_DYLIB}" | michael@0: grep -A10 "^ *cmd LC_ID_DYLIB$" | michael@0: grep -m1 "^ *name" | michael@0: sed -Ee 's/^ *name (.*) \(offset [0-9]+\)$/\1/')" michael@0: VERSION_PATH="/Versions/${CURRENT_VERSION_ID}/${FRAMEWORK_NAME_NOEXT}" michael@0: LC_ID_DYLIB_NEW="$(echo "${LC_ID_DYLIB_OLD}" | michael@0: sed -Ee "s%${VERSION_PATH}$%/${FRAMEWORK_NAME_NOEXT}%")" michael@0: michael@0: if [ "${LC_ID_DYLIB_NEW}" != "${LC_ID_DYLIB_OLD}" ] ; then michael@0: install_name_tool -id "${LC_ID_DYLIB_NEW}" "${FRAMEWORK_DYLIB}" michael@0: fi michael@0: fi