|
1 #!/bin/bash |
|
2 |
|
3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
|
4 # Use of this source code is governed by a BSD-style license that can be |
|
5 # found in the LICENSE file. |
|
6 |
|
7 # Copies a framework to its new home, "unversioning" it. |
|
8 # |
|
9 # Normally, frameworks are versioned bundles. The contents of a framework are |
|
10 # stored in a versioned directory within the bundle, and symbolic links |
|
11 # provide access to the actual code and resources. See |
|
12 # http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html |
|
13 # |
|
14 # The symbolic links usually found in frameworks create problems. Symbolic |
|
15 # links are excluded from code signatures. That means that it's possible to |
|
16 # remove or retarget a symbolic link within a framework without affecting the |
|
17 # seal. In Chrome's case, the outer .app bundle contains a framework where |
|
18 # all application code and resources live. In order for the signature on the |
|
19 # .app to be meaningful, it encompasses the framework. Because framework |
|
20 # resources are accessed through the framework's symbolic links, this |
|
21 # arrangement results in a case where the resources can be altered without |
|
22 # affecting the .app signature's validity. |
|
23 # |
|
24 # Indirection through symbolic links also carries a runtime performance |
|
25 # penalty on open() operations, although open() typically completes so quickly |
|
26 # that this is not considered a major performance problem. |
|
27 # |
|
28 # To resolve these problems, the frameworks that ship within Chrome's .app |
|
29 # bundle are unversioned. Unversioning is simple: instead of using the |
|
30 # original outer .framework directory as the framework that ships within the |
|
31 # .app, the inner versioned directory is used. Instead of accessing bundled |
|
32 # resources through symbolic links, they are accessed directly. In normal |
|
33 # situations, the only hard-coded use of the versioned directory is by dyld, |
|
34 # when loading the framework's code, but this is handled through a normal |
|
35 # Mach-O load command, and it is easy to adjust the load command to point to |
|
36 # the unversioned framework code rather than the versioned counterpart. |
|
37 # |
|
38 # The resulting framework bundles aren't strictly conforming, but they work |
|
39 # as well as normal versioned framework bundles. |
|
40 # |
|
41 # An option to skip running install_name_tool is available. By passing -I as |
|
42 # the first argument to this script, install_name_tool will be skipped. This |
|
43 # is only suitable for copied frameworks that will not be linked against, or |
|
44 # when install_name_tool will be run on any linker output when something is |
|
45 # linked against the copied framework. This option exists to allow signed |
|
46 # frameworks to pass through without subjecting them to any modifications that |
|
47 # would break their signatures. |
|
48 |
|
49 set -e |
|
50 |
|
51 RUN_INSTALL_NAME_TOOL=1 |
|
52 if [ $# -eq 3 ] && [ "${1}" = "-I" ] ; then |
|
53 shift |
|
54 RUN_INSTALL_NAME_TOOL= |
|
55 fi |
|
56 |
|
57 if [ $# -ne 2 ] ; then |
|
58 echo "usage: ${0} [-I] FRAMEWORK DESTINATION_DIR" >& 2 |
|
59 exit 1 |
|
60 fi |
|
61 |
|
62 # FRAMEWORK should be a path to a versioned framework bundle, ending in |
|
63 # .framework. DESTINATION_DIR is the directory that the unversioned framework |
|
64 # bundle will be copied to. |
|
65 |
|
66 FRAMEWORK="${1}" |
|
67 DESTINATION_DIR="${2}" |
|
68 |
|
69 FRAMEWORK_NAME="$(basename "${FRAMEWORK}")" |
|
70 if [ "${FRAMEWORK_NAME: -10}" != ".framework" ] ; then |
|
71 echo "${0}: ${FRAMEWORK_NAME} does not end in .framework" >& 2 |
|
72 exit 1 |
|
73 fi |
|
74 FRAMEWORK_NAME_NOEXT="${FRAMEWORK_NAME:0:$((${#FRAMEWORK_NAME} - 10))}" |
|
75 |
|
76 # Find the current version. |
|
77 VERSIONS="${FRAMEWORK}/Versions" |
|
78 CURRENT_VERSION_LINK="${VERSIONS}/Current" |
|
79 CURRENT_VERSION_ID="$(readlink "${VERSIONS}/Current")" |
|
80 CURRENT_VERSION="${VERSIONS}/${CURRENT_VERSION_ID}" |
|
81 |
|
82 # Make sure that the framework's structure makes sense as a versioned bundle. |
|
83 if [ ! -e "${CURRENT_VERSION}/${FRAMEWORK_NAME_NOEXT}" ] ; then |
|
84 echo "${0}: ${FRAMEWORK_NAME} does not contain a dylib" >& 2 |
|
85 exit 1 |
|
86 fi |
|
87 |
|
88 DESTINATION="${DESTINATION_DIR}/${FRAMEWORK_NAME}" |
|
89 |
|
90 # Copy the versioned directory within the versioned framework to its |
|
91 # destination location. |
|
92 mkdir -p "${DESTINATION_DIR}" |
|
93 rsync -acC --delete --exclude Headers --exclude PrivateHeaders \ |
|
94 --include '*.so' "${CURRENT_VERSION}/" "${DESTINATION}" |
|
95 |
|
96 if [[ -n "${RUN_INSTALL_NAME_TOOL}" ]]; then |
|
97 # Adjust the Mach-O LC_ID_DYLIB load command in the framework. This does not |
|
98 # change the LC_LOAD_DYLIB load commands in anything that may have already |
|
99 # linked against the framework. Not all frameworks will actually need this |
|
100 # to be changed. Some frameworks may already be built with the proper |
|
101 # LC_ID_DYLIB for use as an unversioned framework. Xcode users can do this |
|
102 # by setting LD_DYLIB_INSTALL_NAME to |
|
103 # $(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(WRAPPER_NAME)/$(PRODUCT_NAME) |
|
104 # If invoking ld via gcc or g++, pass the desired path to -Wl,-install_name |
|
105 # at link time. |
|
106 FRAMEWORK_DYLIB="${DESTINATION}/${FRAMEWORK_NAME_NOEXT}" |
|
107 LC_ID_DYLIB_OLD="$(otool -l "${FRAMEWORK_DYLIB}" | |
|
108 grep -A10 "^ *cmd LC_ID_DYLIB$" | |
|
109 grep -m1 "^ *name" | |
|
110 sed -Ee 's/^ *name (.*) \(offset [0-9]+\)$/\1/')" |
|
111 VERSION_PATH="/Versions/${CURRENT_VERSION_ID}/${FRAMEWORK_NAME_NOEXT}" |
|
112 LC_ID_DYLIB_NEW="$(echo "${LC_ID_DYLIB_OLD}" | |
|
113 sed -Ee "s%${VERSION_PATH}$%/${FRAMEWORK_NAME_NOEXT}%")" |
|
114 |
|
115 if [ "${LC_ID_DYLIB_NEW}" != "${LC_ID_DYLIB_OLD}" ] ; then |
|
116 install_name_tool -id "${LC_ID_DYLIB_NEW}" "${FRAMEWORK_DYLIB}" |
|
117 fi |
|
118 fi |