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