summaryrefslogtreecommitdiff
path: root/chromium/build/mac
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/build/mac
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/build/mac')
-rw-r--r--chromium/build/mac/OWNERS2
-rw-r--r--chromium/build/mac/asan.gyp32
-rwxr-xr-xchromium/build/mac/change_mach_o_flags.py273
-rwxr-xr-xchromium/build/mac/change_mach_o_flags_from_xcode.sh15
-rw-r--r--chromium/build/mac/chrome_mac.croc36
-rwxr-xr-xchromium/build/mac/copy_asan_runtime_dylib.sh63
-rwxr-xr-xchromium/build/mac/copy_framework_unversioned.sh118
-rwxr-xr-xchromium/build/mac/edit_xibs.sh17
-rwxr-xr-xchromium/build/mac/find_sdk.py82
-rwxr-xr-xchromium/build/mac/make_more_helpers.sh91
-rwxr-xr-xchromium/build/mac/strip_from_xcode62
-rwxr-xr-xchromium/build/mac/strip_save_dsym341
-rwxr-xr-xchromium/build/mac/tweak_info_plist.py305
-rwxr-xr-xchromium/build/mac/verify_no_objc.sh43
14 files changed, 1480 insertions, 0 deletions
diff --git a/chromium/build/mac/OWNERS b/chromium/build/mac/OWNERS
new file mode 100644
index 00000000000..c56e89dd123
--- /dev/null
+++ b/chromium/build/mac/OWNERS
@@ -0,0 +1,2 @@
+mark@chromium.org
+thomasvl@chromium.org
diff --git a/chromium/build/mac/asan.gyp b/chromium/build/mac/asan.gyp
new file mode 100644
index 00000000000..73e86416c69
--- /dev/null
+++ b/chromium/build/mac/asan.gyp
@@ -0,0 +1,32 @@
+# Copyright (c) 2013 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.
+
+{
+ 'targets': [
+ {
+ 'target_name': 'asan_dynamic_runtime',
+ 'type': 'none',
+ 'variables': {
+ # Every target is going to depend on asan_dynamic_runtime, so allow
+ # this one to depend on itself.
+ 'prune_self_dependency': 1,
+ # Path is relative to this GYP file.
+ 'asan_rtl_mask_path':
+ '../../third_party/llvm-build/Release+Asserts/lib/clang/*/lib/darwin/libclang_rt.asan_osx_dynamic.dylib',
+ },
+ 'conditions': [
+ ['OS=="mac"', {
+ 'copies': [
+ {
+ 'destination': '<(PRODUCT_DIR)',
+ 'files': [
+ '<!(/bin/ls <(asan_rtl_mask_path))',
+ ],
+ },
+ ],
+ }],
+ ],
+ },
+ ],
+}
diff --git a/chromium/build/mac/change_mach_o_flags.py b/chromium/build/mac/change_mach_o_flags.py
new file mode 100755
index 00000000000..c2aeaec9b10
--- /dev/null
+++ b/chromium/build/mac/change_mach_o_flags.py
@@ -0,0 +1,273 @@
+#!/usr/bin/env python
+# Copyright (c) 2011 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.
+
+"""Usage: change_mach_o_flags.py [--executable-heap] [--no-pie] <executablepath>
+
+Arranges for the executable at |executable_path| to have its data (heap)
+pages protected to prevent execution on Mac OS X 10.7 ("Lion"), and to have
+the PIE (position independent executable) bit set to enable ASLR (address
+space layout randomization). With --executable-heap or --no-pie, the
+respective bits are cleared instead of set, making the heap executable or
+disabling PIE/ASLR.
+
+This script is able to operate on thin (single-architecture) Mach-O files
+and fat (universal, multi-architecture) files. When operating on fat files,
+it will set or clear the bits for each architecture contained therein.
+
+NON-EXECUTABLE HEAP
+
+Traditionally in Mac OS X, 32-bit processes did not have data pages set to
+prohibit execution. Although user programs could call mprotect and
+mach_vm_protect to deny execution of code in data pages, the kernel would
+silently ignore such requests without updating the page tables, and the
+hardware would happily execute code on such pages. 64-bit processes were
+always given proper hardware protection of data pages. This behavior was
+controllable on a system-wide level via the vm.allow_data_exec sysctl, which
+is set by default to 1. The bit with value 1 (set by default) allows code
+execution on data pages for 32-bit processes, and the bit with value 2
+(clear by default) does the same for 64-bit processes.
+
+In Mac OS X 10.7, executables can "opt in" to having hardware protection
+against code execution on data pages applied. This is done by setting a new
+bit in the |flags| field of an executable's |mach_header|. When
+MH_NO_HEAP_EXECUTION is set, proper protections will be applied, regardless
+of the setting of vm.allow_data_exec. See xnu-1699.22.73/osfmk/vm/vm_map.c
+override_nx and xnu-1699.22.73/bsd/kern/mach_loader.c load_machfile.
+
+The Apple toolchain has been revised to set the MH_NO_HEAP_EXECUTION when
+producing executables, provided that -allow_heap_execute is not specified
+at link time. Only linkers shipping with Xcode 4.0 and later (ld64-123.2 and
+later) have this ability. See ld64-123.2.1/src/ld/Options.cpp
+Options::reconfigureDefaults() and
+ld64-123.2.1/src/ld/HeaderAndLoadCommands.hpp
+HeaderAndLoadCommandsAtom<A>::flags().
+
+This script sets the MH_NO_HEAP_EXECUTION bit on Mach-O executables. It is
+intended for use with executables produced by a linker that predates Apple's
+modifications to set this bit itself. It is also useful for setting this bit
+for non-i386 executables, including x86_64 executables. Apple's linker only
+sets it for 32-bit i386 executables, presumably under the assumption that
+the value of vm.allow_data_exec is set in stone. However, if someone were to
+change vm.allow_data_exec to 2 or 3, 64-bit x86_64 executables would run
+without hardware protection against code execution on data pages. This
+script can set the bit for x86_64 executables, guaranteeing that they run
+with appropriate protection even when vm.allow_data_exec has been tampered
+with.
+
+POSITION-INDEPENDENT EXECUTABLES/ADDRESS SPACE LAYOUT RANDOMIZATION
+
+This script sets or clears the MH_PIE bit in an executable's Mach-O header,
+enabling or disabling position independence on Mac OS X 10.5 and later.
+Processes running position-independent executables have varying levels of
+ASLR protection depending on the OS release. The main executable's load
+address, shared library load addresess, and the heap and stack base
+addresses may be randomized. Position-independent executables are produced
+by supplying the -pie flag to the linker (or defeated by supplying -no_pie).
+Executables linked with a deployment target of 10.7 or higher have PIE on
+by default.
+
+This script is never strictly needed during the build to enable PIE, as all
+linkers used are recent enough to support -pie. However, it's used to
+disable the PIE bit as needed on already-linked executables.
+"""
+
+import optparse
+import os
+import struct
+import sys
+
+
+# <mach-o/fat.h>
+FAT_MAGIC = 0xcafebabe
+FAT_CIGAM = 0xbebafeca
+
+# <mach-o/loader.h>
+MH_MAGIC = 0xfeedface
+MH_CIGAM = 0xcefaedfe
+MH_MAGIC_64 = 0xfeedfacf
+MH_CIGAM_64 = 0xcffaedfe
+MH_EXECUTE = 0x2
+MH_PIE = 0x00200000
+MH_NO_HEAP_EXECUTION = 0x01000000
+
+
+class MachOError(Exception):
+ """A class for exceptions thrown by this module."""
+
+ pass
+
+
+def CheckedSeek(file, offset):
+ """Seeks the file-like object at |file| to offset |offset| and raises a
+ MachOError if anything funny happens."""
+
+ file.seek(offset, os.SEEK_SET)
+ new_offset = file.tell()
+ if new_offset != offset:
+ raise MachOError, \
+ 'seek: expected offset %d, observed %d' % (offset, new_offset)
+
+
+def CheckedRead(file, count):
+ """Reads |count| bytes from the file-like |file| object, raising a
+ MachOError if any other number of bytes is read."""
+
+ bytes = file.read(count)
+ if len(bytes) != count:
+ raise MachOError, \
+ 'read: expected length %d, observed %d' % (count, len(bytes))
+
+ return bytes
+
+
+def ReadUInt32(file, endian):
+ """Reads an unsinged 32-bit integer from the file-like |file| object,
+ treating it as having endianness specified by |endian| (per the |struct|
+ module), and returns it as a number. Raises a MachOError if the proper
+ length of data can't be read from |file|."""
+
+ bytes = CheckedRead(file, 4)
+
+ (uint32,) = struct.unpack(endian + 'I', bytes)
+ return uint32
+
+
+def ReadMachHeader(file, endian):
+ """Reads an entire |mach_header| structure (<mach-o/loader.h>) from the
+ file-like |file| object, treating it as having endianness specified by
+ |endian| (per the |struct| module), and returns a 7-tuple of its members
+ as numbers. Raises a MachOError if the proper length of data can't be read
+ from |file|."""
+
+ bytes = CheckedRead(file, 28)
+
+ magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = \
+ struct.unpack(endian + '7I', bytes)
+ return magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags
+
+
+def ReadFatArch(file):
+ """Reads an entire |fat_arch| structure (<mach-o/fat.h>) from the file-like
+ |file| object, treating it as having endianness specified by |endian|
+ (per the |struct| module), and returns a 5-tuple of its members as numbers.
+ Raises a MachOError if the proper length of data can't be read from
+ |file|."""
+
+ bytes = CheckedRead(file, 20)
+
+ cputype, cpusubtype, offset, size, align = struct.unpack('>5I', bytes)
+ return cputype, cpusubtype, offset, size, align
+
+
+def WriteUInt32(file, uint32, endian):
+ """Writes |uint32| as an unsinged 32-bit integer to the file-like |file|
+ object, treating it as having endianness specified by |endian| (per the
+ |struct| module)."""
+
+ bytes = struct.pack(endian + 'I', uint32)
+ assert len(bytes) == 4
+
+ file.write(bytes)
+
+
+def HandleMachOFile(file, options, offset=0):
+ """Seeks the file-like |file| object to |offset|, reads its |mach_header|,
+ and rewrites the header's |flags| field if appropriate. The header's
+ endianness is detected. Both 32-bit and 64-bit Mach-O headers are supported
+ (mach_header and mach_header_64). Raises MachOError if used on a header that
+ does not have a known magic number or is not of type MH_EXECUTE. The
+ MH_PIE and MH_NO_HEAP_EXECUTION bits are set or cleared in the |flags| field
+ according to |options| and written to |file| if any changes need to be made.
+ If already set or clear as specified by |options|, nothing is written."""
+
+ CheckedSeek(file, offset)
+ magic = ReadUInt32(file, '<')
+ if magic == MH_MAGIC or magic == MH_MAGIC_64:
+ endian = '<'
+ elif magic == MH_CIGAM or magic == MH_CIGAM_64:
+ endian = '>'
+ else:
+ raise MachOError, \
+ 'Mach-O file at offset %d has illusion of magic' % offset
+
+ CheckedSeek(file, offset)
+ magic, cputype, cpusubtype, filetype, ncmds, sizeofcmds, flags = \
+ ReadMachHeader(file, endian)
+ assert magic == MH_MAGIC or magic == MH_MAGIC_64
+ if filetype != MH_EXECUTE:
+ raise MachOError, \
+ 'Mach-O file at offset %d is type 0x%x, expected MH_EXECUTE' % \
+ (offset, filetype)
+
+ original_flags = flags
+
+ if options.no_heap_execution:
+ flags |= MH_NO_HEAP_EXECUTION
+ else:
+ flags &= ~MH_NO_HEAP_EXECUTION
+
+ if options.pie:
+ flags |= MH_PIE
+ else:
+ flags &= ~MH_PIE
+
+ if flags != original_flags:
+ CheckedSeek(file, offset + 24)
+ WriteUInt32(file, flags, endian)
+
+
+def HandleFatFile(file, options, fat_offset=0):
+ """Seeks the file-like |file| object to |offset| and loops over its
+ |fat_header| entries, calling HandleMachOFile for each."""
+
+ CheckedSeek(file, fat_offset)
+ magic = ReadUInt32(file, '>')
+ assert magic == FAT_MAGIC
+
+ nfat_arch = ReadUInt32(file, '>')
+
+ for index in xrange(0, nfat_arch):
+ cputype, cpusubtype, offset, size, align = ReadFatArch(file)
+ assert size >= 28
+
+ # HandleMachOFile will seek around. Come back here after calling it, in
+ # case it sought.
+ fat_arch_offset = file.tell()
+ HandleMachOFile(file, options, offset)
+ CheckedSeek(file, fat_arch_offset)
+
+
+def main(me, args):
+ parser = optparse.OptionParser('%prog [options] <executable_path>')
+ parser.add_option('--executable-heap', action='store_false',
+ dest='no_heap_execution', default=True,
+ help='Clear the MH_NO_HEAP_EXECUTION bit')
+ parser.add_option('--no-pie', action='store_false',
+ dest='pie', default=True,
+ help='Clear the MH_PIE bit')
+ (options, loose_args) = parser.parse_args(args)
+ if len(loose_args) != 1:
+ parser.print_usage()
+ return 1
+
+ executable_path = loose_args[0]
+ executable_file = open(executable_path, 'rb+')
+
+ magic = ReadUInt32(executable_file, '<')
+ if magic == FAT_CIGAM:
+ # Check FAT_CIGAM and not FAT_MAGIC because the read was little-endian.
+ HandleFatFile(executable_file, options)
+ elif magic == MH_MAGIC or magic == MH_CIGAM or \
+ magic == MH_MAGIC_64 or magic == MH_CIGAM_64:
+ HandleMachOFile(executable_file, options)
+ else:
+ raise MachOError, '%s is not a Mach-O or fat file' % executable_file
+
+ executable_file.close()
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv[0], sys.argv[1:]))
diff --git a/chromium/build/mac/change_mach_o_flags_from_xcode.sh b/chromium/build/mac/change_mach_o_flags_from_xcode.sh
new file mode 100755
index 00000000000..1824f8db52f
--- /dev/null
+++ b/chromium/build/mac/change_mach_o_flags_from_xcode.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+# Copyright (c) 2011 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.
+
+# This is a small wrapper script around change_mach_o_flags.py allowing it to
+# be invoked easily from Xcode. change_mach_o_flags.py expects its arguments
+# on the command line, but Xcode puts its parameters in the environment.
+
+set -e
+
+exec "$(dirname "${0}")/change_mach_o_flags.py" \
+ "${@}" \
+ "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}"
diff --git a/chromium/build/mac/chrome_mac.croc b/chromium/build/mac/chrome_mac.croc
new file mode 100644
index 00000000000..8cde00ce206
--- /dev/null
+++ b/chromium/build/mac/chrome_mac.croc
@@ -0,0 +1,36 @@
+# -*- python -*-
+# Crocodile config file for Chromium mac
+
+{
+ # List of rules, applied in order
+ 'rules' : [
+ # Specify inclusions before exclusions, since rules are in order.
+
+ # Don't include chromeos, linux, or windows specific files
+ {
+ 'regexp' : '.*(_|/)(chromeos|linux|win|views)(\\.|_)',
+ 'include' : 0,
+ },
+ # Don't include ChromeOS dirs
+ {
+ 'regexp' : '.*/chromeos/',
+ 'include' : 0,
+ },
+
+ # Groups
+ {
+ 'regexp' : '.*_test_mac\\.',
+ 'group' : 'test',
+ },
+
+ # Languages
+ {
+ 'regexp' : '.*\\.m$',
+ 'language' : 'ObjC',
+ },
+ {
+ 'regexp' : '.*\\.mm$',
+ 'language' : 'ObjC++',
+ },
+ ],
+}
diff --git a/chromium/build/mac/copy_asan_runtime_dylib.sh b/chromium/build/mac/copy_asan_runtime_dylib.sh
new file mode 100755
index 00000000000..b67b4346cd5
--- /dev/null
+++ b/chromium/build/mac/copy_asan_runtime_dylib.sh
@@ -0,0 +1,63 @@
+#!/bin/bash
+
+# Copyright (c) 2013 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.
+
+# For app bundles built with ASan, copies the runtime lib
+# (libclang_rt.asan_osx_dynamic.dylib), on which their executables depend, from
+# the compiler installation path into the bundle and fixes the dylib's install
+# name in the binary to be relative to @executable_path.
+
+set -e
+
+BINARY="${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}"
+BINARY_DIR="$(dirname "${BINARY}")"
+ASAN_DYLIB_NAME=libclang_rt.asan_osx_dynamic.dylib
+ASAN_DYLIB=$(find \
+ "${BUILT_PRODUCTS_DIR}/../../third_party/llvm-build/Release+Asserts/lib/clang/" \
+ -type f -path "*${ASAN_DYLIB_NAME}")
+
+# Find the link to the ASan runtime encoded in the binary.
+BUILTIN_DYLIB_PATH=$(otool -L "${BINARY}" | \
+ sed -Ene 's/^[[:blank:]]+(.*libclang_rt\.asan_osx_dynamic\.dylib).*$/\1/p')
+
+if [[ -z "${BUILTIN_DYLIB_PATH}" ]]; then
+ echo "${BINARY} does not depend on the ASan runtime library!" >&2
+ # TODO(glider): make this return 1 when we fully switch to the dynamic
+ # runtime in ASan.
+ exit 0
+fi
+
+DYLIB_BASENAME=$(basename "${ASAN_DYLIB}")
+if [[ "${DYLIB_BASENAME}" != "${ASAN_DYLIB_NAME}" ]]; then
+ echo "basename(${ASAN_DYLIB}) != ${ASAN_DYLIB_NAME}" >&2
+ exit 1
+fi
+
+# Check whether the directory containing the executable binary is named
+# "MacOS". In this case we're building a full-fledged OSX app and will put
+# the runtime into appname.app/Contents/Libraries/. Otherwise this is probably
+# an iOS gtest app, and the ASan runtime is put next to the executable.
+UPPER_DIR=$(dirname "${BINARY_DIR}")
+if [ "${UPPER_DIR}" == "MacOS" ]; then
+ LIBRARIES_DIR="${UPPER_DIR}/Libraries"
+ mkdir -p "${LIBRARIES_DIR}"
+ NEW_LC_ID_DYLIB="@executable_path/../Libraries/${ASAN_DYLIB_NAME}"
+else
+ LIBRARIES_DIR="${BINARY_DIR}"
+ NEW_LC_ID_DYLIB="@executable_path/${ASAN_DYLIB_NAME}"
+fi
+
+cp "${ASAN_DYLIB}" "${LIBRARIES_DIR}"
+
+# Make LC_ID_DYLIB of the runtime copy point to its location.
+install_name_tool \
+ -id "${NEW_LC_ID_DYLIB}" \
+ "${LIBRARIES_DIR}/${ASAN_DYLIB_NAME}"
+
+# Fix the rpath to the runtime library recorded in the binary.
+install_name_tool \
+ -change "${BUILTIN_DYLIB_PATH}" \
+ "${NEW_LC_ID_DYLIB}" \
+ "${BINARY}"
diff --git a/chromium/build/mac/copy_framework_unversioned.sh b/chromium/build/mac/copy_framework_unversioned.sh
new file mode 100755
index 00000000000..380cc908407
--- /dev/null
+++ b/chromium/build/mac/copy_framework_unversioned.sh
@@ -0,0 +1,118 @@
+#!/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
+
+RUN_INSTALL_NAME_TOOL=1
+if [ $# -eq 3 ] && [ "${1}" = "-I" ] ; then
+ shift
+ RUN_INSTALL_NAME_TOOL=
+fi
+
+if [ $# -ne 2 ] ; then
+ echo "usage: ${0} [-I] FRAMEWORK DESTINATION_DIR" >& 2
+ exit 1
+fi
+
+# 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.
+
+FRAMEWORK="${1}"
+DESTINATION_DIR="${2}"
+
+FRAMEWORK_NAME="$(basename "${FRAMEWORK}")"
+if [ "${FRAMEWORK_NAME: -10}" != ".framework" ] ; then
+ echo "${0}: ${FRAMEWORK_NAME} does not end in .framework" >& 2
+ exit 1
+fi
+FRAMEWORK_NAME_NOEXT="${FRAMEWORK_NAME:0:$((${#FRAMEWORK_NAME} - 10))}"
+
+# Find the current version.
+VERSIONS="${FRAMEWORK}/Versions"
+CURRENT_VERSION_LINK="${VERSIONS}/Current"
+CURRENT_VERSION_ID="$(readlink "${VERSIONS}/Current")"
+CURRENT_VERSION="${VERSIONS}/${CURRENT_VERSION_ID}"
+
+# Make sure that the framework's structure makes sense as a versioned bundle.
+if [ ! -e "${CURRENT_VERSION}/${FRAMEWORK_NAME_NOEXT}" ] ; then
+ echo "${0}: ${FRAMEWORK_NAME} does not contain a dylib" >& 2
+ exit 1
+fi
+
+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}" ]]; then
+ # 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.
+ FRAMEWORK_DYLIB="${DESTINATION}/${FRAMEWORK_NAME_NOEXT}"
+ 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/')"
+ VERSION_PATH="/Versions/${CURRENT_VERSION_ID}/${FRAMEWORK_NAME_NOEXT}"
+ LC_ID_DYLIB_NEW="$(echo "${LC_ID_DYLIB_OLD}" |
+ sed -Ee "s%${VERSION_PATH}$%/${FRAMEWORK_NAME_NOEXT}%")"
+
+ if [ "${LC_ID_DYLIB_NEW}" != "${LC_ID_DYLIB_OLD}" ] ; then
+ install_name_tool -id "${LC_ID_DYLIB_NEW}" "${FRAMEWORK_DYLIB}"
+ fi
+fi
diff --git a/chromium/build/mac/edit_xibs.sh b/chromium/build/mac/edit_xibs.sh
new file mode 100755
index 00000000000..a3054557b37
--- /dev/null
+++ b/chromium/build/mac/edit_xibs.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+# 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.
+
+# This script is a convenience to run GYP for /src/chrome/chrome_nibs.gyp
+# with the Xcode generator (as you likely use ninja). Documentation:
+# http://dev.chromium.org/developers/design-documents/mac-xib-files
+
+set -e
+
+RELSRC=$(dirname "$0")/../..
+SRC=$(cd "$RELSRC" && pwd)
+GYP_GENERATORS=xcode python "$SRC/tools/gyp/gyp" "$SRC/chrome/chrome_nibs.gyp"
+echo "You can now edit XIB files in Xcode using:"
+echo " $SRC/chrome/chrome_nibs.xcodeproj"
diff --git a/chromium/build/mac/find_sdk.py b/chromium/build/mac/find_sdk.py
new file mode 100755
index 00000000000..067be638d21
--- /dev/null
+++ b/chromium/build/mac/find_sdk.py
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+# 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.
+
+import os
+import re
+import subprocess
+import sys
+
+"""Prints the lowest locally available SDK version greater than or equal to a
+given minimum sdk version to standard output.
+
+Usage:
+ python find_sdk.py 10.6 # Ignores SDKs < 10.6
+"""
+
+from optparse import OptionParser
+
+
+def parse_version(version_str):
+ """'10.6' => [10, 6]"""
+ return map(int, re.findall(r'(\d+)', version_str))
+
+
+def main():
+ parser = OptionParser()
+ parser.add_option("--verify",
+ action="store_true", dest="verify", default=False,
+ help="return the sdk argument and warn if it doesn't exist")
+ parser.add_option("--sdk_path",
+ action="store", type="string", dest="sdk_path", default="",
+ help="user-specified SDK path; bypasses verification")
+ (options, args) = parser.parse_args()
+ min_sdk_version = args[0]
+
+ job = subprocess.Popen(['xcode-select', '-print-path'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ out, err = job.communicate()
+ if job.returncode != 0:
+ print >>sys.stderr, out
+ print >>sys.stderr, err
+ raise Exception(('Error %d running xcode-select, you might have to run '
+ '|sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer| '
+ 'if you are using Xcode 4.') % job.returncode)
+ # The Developer folder moved in Xcode 4.3.
+ xcode43_sdk_path = os.path.join(
+ out.rstrip(), 'Platforms/MacOSX.platform/Developer/SDKs')
+ if os.path.isdir(xcode43_sdk_path):
+ sdk_dir = xcode43_sdk_path
+ else:
+ sdk_dir = os.path.join(out.rstrip(), 'SDKs')
+ sdks = [re.findall('^MacOSX(10\.\d+)\.sdk$', s) for s in os.listdir(sdk_dir)]
+ sdks = [s[0] for s in sdks if s] # [['10.5'], ['10.6']] => ['10.5', '10.6']
+ sdks = [s for s in sdks # ['10.5', '10.6'] => ['10.6']
+ if parse_version(s) >= parse_version(min_sdk_version)]
+ if not sdks:
+ raise Exception('No %s+ SDK found' % min_sdk_version)
+ best_sdk = sorted(sdks, key=parse_version)[0]
+
+ if options.verify and best_sdk != min_sdk_version and not options.sdk_path:
+ print >>sys.stderr, ''
+ print >>sys.stderr, ' vvvvvvv'
+ print >>sys.stderr, ''
+ print >>sys.stderr, \
+ 'This build requires the %s SDK, but it was not found on your system.' \
+ % min_sdk_version
+ print >>sys.stderr, \
+ 'Either install it, or explicitly set mac_sdk in your GYP_DEFINES.'
+ print >>sys.stderr, ''
+ print >>sys.stderr, ' ^^^^^^^'
+ print >>sys.stderr, ''
+ return min_sdk_version
+
+ return best_sdk
+
+
+if __name__ == '__main__':
+ if sys.platform != 'darwin':
+ raise Exception("This script only runs on Mac")
+ print main()
diff --git a/chromium/build/mac/make_more_helpers.sh b/chromium/build/mac/make_more_helpers.sh
new file mode 100755
index 00000000000..6f5c4749ebf
--- /dev/null
+++ b/chromium/build/mac/make_more_helpers.sh
@@ -0,0 +1,91 @@
+#!/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.
+
+# Usage: make_more_helpers.sh <directory_within_contents> <app_name>
+#
+# This script creates additional helper .app bundles for Chromium, based on
+# the existing helper .app bundle, changing their Mach-O header's flags to
+# enable and disable various features. Based on Chromium Helper.app, it will
+# create Chromium Helper EH.app, which has the MH_NO_HEAP_EXECUTION bit
+# cleared to support Chromium child processes that require an executable heap,
+# and Chromium Helper NP.app, which has the MH_PIE bit cleared to support
+# Chromium child processes that cannot tolerate ASLR.
+#
+# This script expects to be called from the chrome_exe target as a postbuild,
+# and operates directly within the built-up browser app's versioned directory.
+#
+# Each helper is adjusted by giving it the proper bundle name, renaming the
+# executable, adjusting several Info.plist keys, and changing the executable's
+# Mach-O flags.
+
+set -eu
+
+make_helper() {
+ local containing_dir="${1}"
+ local app_name="${2}"
+ local feature="${3}"
+ local flags="${4}"
+
+ local helper_name="${app_name} Helper"
+ local helper_stem="${containing_dir}/${helper_name}"
+ local original_helper="${helper_stem}.app"
+ if [[ ! -d "${original_helper}" ]]; then
+ echo "${0}: error: ${original_helper} is a required directory" >& 2
+ exit 1
+ fi
+ local original_helper_exe="${original_helper}/Contents/MacOS/${helper_name}"
+ if [[ ! -f "${original_helper_exe}" ]]; then
+ echo "${0}: error: ${original_helper_exe} is a required file" >& 2
+ exit 1
+ fi
+
+ local feature_helper="${helper_stem} ${feature}.app"
+
+ rsync -acC --delete --include '*.so' "${original_helper}/" "${feature_helper}"
+
+ local helper_feature="${helper_name} ${feature}"
+ local helper_feature_exe="${feature_helper}/Contents/MacOS/${helper_feature}"
+ mv "${feature_helper}/Contents/MacOS/${helper_name}" "${helper_feature_exe}"
+
+ local change_flags="$(dirname "${0}")/change_mach_o_flags.py"
+ "${change_flags}" ${flags} "${helper_feature_exe}"
+
+ local feature_info="${feature_helper}/Contents/Info"
+ local feature_info_plist="${feature_info}.plist"
+
+ defaults write "${feature_info}" "CFBundleDisplayName" "${helper_feature}"
+ defaults write "${feature_info}" "CFBundleExecutable" "${helper_feature}"
+
+ cfbundleid="$(defaults read "${feature_info}" "CFBundleIdentifier")"
+ feature_cfbundleid="${cfbundleid}.${feature}"
+ defaults write "${feature_info}" "CFBundleIdentifier" "${feature_cfbundleid}"
+
+ cfbundlename="$(defaults read "${feature_info}" "CFBundleName")"
+ feature_cfbundlename="${cfbundlename} ${feature}"
+ defaults write "${feature_info}" "CFBundleName" "${feature_cfbundlename}"
+
+ # As usual, defaults might have put the plist into whatever format excites
+ # it, but Info.plists get converted back to the expected XML format.
+ plutil -convert xml1 "${feature_info_plist}"
+
+ # `defaults` also changes the file permissions, so make the file
+ # world-readable again.
+ chmod a+r "${feature_info_plist}"
+}
+
+if [[ ${#} -ne 2 ]]; then
+ echo "usage: ${0} <directory_within_contents> <app_name>" >& 2
+ exit 1
+fi
+
+DIRECTORY_WITHIN_CONTENTS="${1}"
+APP_NAME="${2}"
+
+CONTENTS_DIR="${BUILT_PRODUCTS_DIR}/${CONTENTS_FOLDER_PATH}"
+CONTAINING_DIR="${CONTENTS_DIR}/${DIRECTORY_WITHIN_CONTENTS}"
+
+make_helper "${CONTAINING_DIR}" "${APP_NAME}" "EH" "--executable-heap"
+make_helper "${CONTAINING_DIR}" "${APP_NAME}" "NP" "--no-pie"
diff --git a/chromium/build/mac/strip_from_xcode b/chromium/build/mac/strip_from_xcode
new file mode 100755
index 00000000000..c26b9fb492b
--- /dev/null
+++ b/chromium/build/mac/strip_from_xcode
@@ -0,0 +1,62 @@
+#!/bin/bash
+
+# Copyright (c) 2008 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.
+
+# This is a handy wrapper script that figures out how to call the strip
+# utility (strip_save_dsym in this case), if it even needs to be called at all,
+# and then does it. This script should be called by a post-link phase in
+# targets that might generate Mach-O executables, dynamic libraries, or
+# loadable bundles.
+#
+# An example "Strip If Needed" build phase placed after "Link Binary With
+# Libraries" would do:
+# exec "${XCODEPROJ_DEPTH}/build/mac/strip_from_xcode"
+
+if [ "${CONFIGURATION}" != "Release" ] ; then
+ # Only strip in release mode.
+ exit 0
+fi
+
+declare -a FLAGS
+
+# MACH_O_TYPE is not set for a command-line tool, so check PRODUCT_TYPE too.
+# Weird.
+if [ "${MACH_O_TYPE}" = "mh_execute" ] || \
+ [ "${PRODUCT_TYPE}" = "com.apple.product-type.tool" ] ; then
+ # Strip everything (no special flags). No-op.
+ true
+elif [ "${MACH_O_TYPE}" = "mh_dylib" ] || \
+ [ "${MACH_O_TYPE}" = "mh_bundle" ]; then
+ # Strip debugging symbols and local symbols
+ FLAGS[${#FLAGS[@]}]=-S
+ FLAGS[${#FLAGS[@]}]=-x
+elif [ "${MACH_O_TYPE}" = "staticlib" ] ; then
+ # Don't strip static libraries.
+ exit 0
+else
+ # Warn, but don't treat this as an error.
+ echo $0: warning: unrecognized MACH_O_TYPE ${MACH_O_TYPE}
+ exit 0
+fi
+
+if [ -n "${STRIPFLAGS}" ] ; then
+ # Pick up the standard STRIPFLAGS Xcode setting, used for "Additional Strip
+ # Flags".
+ for stripflag in "${STRIPFLAGS}" ; do
+ FLAGS[${#FLAGS[@]}]="${stripflag}"
+ done
+fi
+
+if [ -n "${CHROMIUM_STRIP_SAVE_FILE}" ] ; then
+ # An Xcode project can communicate a file listing symbols to saved in this
+ # environment variable by setting it as a build setting. This isn't a
+ # standard Xcode setting. It's used in preference to STRIPFLAGS to
+ # eliminate quoting ambiguity concerns.
+ FLAGS[${#FLAGS[@]}]=-s
+ FLAGS[${#FLAGS[@]}]="${CHROMIUM_STRIP_SAVE_FILE}"
+fi
+
+exec "$(dirname ${0})/strip_save_dsym" "${FLAGS[@]}" \
+ "${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}"
diff --git a/chromium/build/mac/strip_save_dsym b/chromium/build/mac/strip_save_dsym
new file mode 100755
index 00000000000..ef08d831f12
--- /dev/null
+++ b/chromium/build/mac/strip_save_dsym
@@ -0,0 +1,341 @@
+#!/usr/bin/python
+
+# Copyright (c) 2011 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.
+
+# Usage: strip_save_dsym <whatever-arguments-you-would-pass-to-strip>
+#
+# strip_save_dsym is a wrapper around the standard strip utility. Given an
+# input Mach-O file, strip_save_dsym will save a copy of the file in a "fake"
+# .dSYM bundle for debugging, and then call strip to strip the Mach-O file.
+# Note that the .dSYM file is a "fake" in that it's not a self-contained
+# .dSYM bundle, it just contains a copy of the original (unstripped) Mach-O
+# file, and therefore contains references to object files on the filesystem.
+# The generated .dSYM bundle is therefore unsuitable for debugging in the
+# absence of these .o files.
+#
+# If a .dSYM already exists and has a newer timestamp than the Mach-O file,
+# this utility does nothing. That allows strip_save_dsym to be run on a file
+# that has already been stripped without trashing the .dSYM.
+#
+# Rationale: the "right" way to generate dSYM bundles, dsymutil, is incredibly
+# slow. On the other hand, doing a file copy (which is really all that
+# dsymutil does) is comparatively fast. Since we usually just want to strip
+# a release-mode executable but still be able to debug it, and we don't care
+# so much about generating a hermetic dSYM bundle, we'll prefer the file copy.
+# If a real dSYM is ever needed, it's still possible to create one by running
+# dsymutil and pointing it at the original Mach-O file inside the "fake"
+# bundle, provided that the object files are available.
+
+import errno
+import os
+import re
+import shutil
+import subprocess
+import sys
+import time
+
+# Returns a list of architectures contained in a Mach-O file. The file can be
+# a universal (fat) file, in which case there will be one list element for
+# each contained architecture, or it can be a thin single-architecture Mach-O
+# file, in which case the list will contain a single element identifying the
+# architecture. On error, returns an empty list. Determines the architecture
+# list by calling file.
+def macho_archs(macho):
+ macho_types = ["executable",
+ "dynamically linked shared library",
+ "bundle"]
+ macho_types_re = "Mach-O (?:64-bit )?(?:" + "|".join(macho_types) + ")"
+
+ file_cmd = subprocess.Popen(["/usr/bin/file", "-b", "--", macho],
+ stdout=subprocess.PIPE)
+
+ archs = []
+
+ type_line = file_cmd.stdout.readline()
+ type_match = re.match("^%s (.*)$" % macho_types_re, type_line)
+ if type_match:
+ archs.append(type_match.group(1))
+ return [type_match.group(1)]
+ else:
+ type_match = re.match("^Mach-O universal binary with (.*) architectures$",
+ type_line)
+ if type_match:
+ for i in range(0, int(type_match.group(1))):
+ arch_line = file_cmd.stdout.readline()
+ arch_match = re.match(
+ "^.* \(for architecture (.*)\):\t%s .*$" % macho_types_re,
+ arch_line)
+ if arch_match:
+ archs.append(arch_match.group(1))
+
+ if file_cmd.wait() != 0:
+ archs = []
+
+ if len(archs) == 0:
+ print >> sys.stderr, "No architectures in %s" % macho
+
+ return archs
+
+# Returns a dictionary mapping architectures contained in the file as returned
+# by macho_archs to the LC_UUID load command for that architecture.
+# Architectures with no LC_UUID load command are omitted from the dictionary.
+# Determines the UUID value by calling otool.
+def macho_uuids(macho):
+ uuids = {}
+
+ archs = macho_archs(macho)
+ if len(archs) == 0:
+ return uuids
+
+ for arch in archs:
+ if arch == "":
+ continue
+
+ otool_cmd = subprocess.Popen(["/usr/bin/otool", "-arch", arch, "-l", "-",
+ macho],
+ stdout=subprocess.PIPE)
+ # state 0 is when nothing UUID-related has been seen yet. State 1 is
+ # entered after a load command begins, but it may not be an LC_UUID load
+ # command. States 2, 3, and 4 are intermediate states while reading an
+ # LC_UUID command. State 5 is the terminal state for a successful LC_UUID
+ # read. State 6 is the error state.
+ state = 0
+ uuid = ""
+ for otool_line in otool_cmd.stdout:
+ if state == 0:
+ if re.match("^Load command .*$", otool_line):
+ state = 1
+ elif state == 1:
+ if re.match("^ cmd LC_UUID$", otool_line):
+ state = 2
+ else:
+ state = 0
+ elif state == 2:
+ if re.match("^ cmdsize 24$", otool_line):
+ state = 3
+ else:
+ state = 6
+ elif state == 3:
+ # The UUID display format changed in the version of otool shipping
+ # with the Xcode 3.2.2 prerelease. The new format is traditional:
+ # uuid 4D7135B2-9C56-C5F5-5F49-A994258E0955
+ # and with Xcode 3.2.6, then line is indented one more space:
+ # uuid 4D7135B2-9C56-C5F5-5F49-A994258E0955
+ # The old format, from cctools-750 and older's otool, breaks the UUID
+ # up into a sequence of bytes:
+ # uuid 0x4d 0x71 0x35 0xb2 0x9c 0x56 0xc5 0xf5
+ # 0x5f 0x49 0xa9 0x94 0x25 0x8e 0x09 0x55
+ new_uuid_match = re.match("^ {3,4}uuid (.{8}-.{4}-.{4}-.{4}-.{12})$",
+ otool_line)
+ if new_uuid_match:
+ uuid = new_uuid_match.group(1)
+
+ # Skip state 4, there is no second line to read.
+ state = 5
+ else:
+ old_uuid_match = re.match("^ uuid 0x(..) 0x(..) 0x(..) 0x(..) "
+ "0x(..) 0x(..) 0x(..) 0x(..)$",
+ otool_line)
+ if old_uuid_match:
+ state = 4
+ uuid = old_uuid_match.group(1) + old_uuid_match.group(2) + \
+ old_uuid_match.group(3) + old_uuid_match.group(4) + "-" + \
+ old_uuid_match.group(5) + old_uuid_match.group(6) + "-" + \
+ old_uuid_match.group(7) + old_uuid_match.group(8) + "-"
+ else:
+ state = 6
+ elif state == 4:
+ old_uuid_match = re.match("^ 0x(..) 0x(..) 0x(..) 0x(..) "
+ "0x(..) 0x(..) 0x(..) 0x(..)$",
+ otool_line)
+ if old_uuid_match:
+ state = 5
+ uuid += old_uuid_match.group(1) + old_uuid_match.group(2) + "-" + \
+ old_uuid_match.group(3) + old_uuid_match.group(4) + \
+ old_uuid_match.group(5) + old_uuid_match.group(6) + \
+ old_uuid_match.group(7) + old_uuid_match.group(8)
+ else:
+ state = 6
+
+ if otool_cmd.wait() != 0:
+ state = 6
+
+ if state == 5:
+ uuids[arch] = uuid.upper()
+
+ if len(uuids) == 0:
+ print >> sys.stderr, "No UUIDs in %s" % macho
+
+ return uuids
+
+# Given a path to a Mach-O file and possible information from the environment,
+# determines the desired path to the .dSYM.
+def dsym_path(macho):
+ # If building a bundle, the .dSYM should be placed next to the bundle. Use
+ # WRAPPER_NAME to make this determination. If called from xcodebuild,
+ # WRAPPER_NAME will be set to the name of the bundle.
+ dsym = ""
+ if "WRAPPER_NAME" in os.environ:
+ if "BUILT_PRODUCTS_DIR" in os.environ:
+ dsym = os.path.join(os.environ["BUILT_PRODUCTS_DIR"],
+ os.environ["WRAPPER_NAME"])
+ else:
+ dsym = os.environ["WRAPPER_NAME"]
+ else:
+ dsym = macho
+
+ dsym += ".dSYM"
+
+ return dsym
+
+# Creates a fake .dSYM bundle at dsym for macho, a Mach-O image with the
+# architectures and UUIDs specified by the uuids map.
+def make_fake_dsym(macho, dsym):
+ uuids = macho_uuids(macho)
+ if len(uuids) == 0:
+ return False
+
+ dwarf_dir = os.path.join(dsym, "Contents", "Resources", "DWARF")
+ dwarf_file = os.path.join(dwarf_dir, os.path.basename(macho))
+ try:
+ os.makedirs(dwarf_dir)
+ except OSError, (err, error_string):
+ if err != errno.EEXIST:
+ raise
+ shutil.copyfile(macho, dwarf_file)
+
+ # info_template is the same as what dsymutil would have written, with the
+ # addition of the fake_dsym key.
+ info_template = \
+'''<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.apple.xcode.dsym.%(root_name)s</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>dSYM</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleVersion</key>
+ <string>1</string>
+ <key>dSYM_UUID</key>
+ <dict>
+%(uuid_dict)s </dict>
+ <key>fake_dsym</key>
+ <true/>
+ </dict>
+</plist>
+'''
+
+ root_name = os.path.basename(dsym)[:-5] # whatever.dSYM without .dSYM
+ uuid_dict = ""
+ for arch in sorted(uuids):
+ uuid_dict += "\t\t\t<key>" + arch + "</key>\n"\
+ "\t\t\t<string>" + uuids[arch] + "</string>\n"
+ info_dict = {
+ "root_name": root_name,
+ "uuid_dict": uuid_dict,
+ }
+ info_contents = info_template % info_dict
+ info_file = os.path.join(dsym, "Contents", "Info.plist")
+ info_fd = open(info_file, "w")
+ info_fd.write(info_contents)
+ info_fd.close()
+
+ return True
+
+# For a Mach-O file, determines where the .dSYM bundle should be located. If
+# the bundle does not exist or has a modification time older than the Mach-O
+# file, calls make_fake_dsym to create a fake .dSYM bundle there, then strips
+# the Mach-O file and sets the modification time on the .dSYM bundle and Mach-O
+# file to be identical.
+def strip_and_make_fake_dsym(macho):
+ dsym = dsym_path(macho)
+ macho_stat = os.stat(macho)
+ dsym_stat = None
+ try:
+ dsym_stat = os.stat(dsym)
+ except OSError, (err, error_string):
+ if err != errno.ENOENT:
+ raise
+
+ if dsym_stat is None or dsym_stat.st_mtime < macho_stat.st_mtime:
+ # Make a .dSYM bundle
+ if not make_fake_dsym(macho, dsym):
+ return False
+
+ # Strip the Mach-O file
+ remove_dsym = True
+ try:
+ strip_path = ""
+ if "SYSTEM_DEVELOPER_BIN_DIR" in os.environ:
+ strip_path = os.environ["SYSTEM_DEVELOPER_BIN_DIR"]
+ else:
+ strip_path = "/usr/bin"
+ strip_path = os.path.join(strip_path, "strip")
+ strip_cmdline = [strip_path] + sys.argv[1:]
+ strip_cmd = subprocess.Popen(strip_cmdline)
+ if strip_cmd.wait() == 0:
+ remove_dsym = False
+ finally:
+ if remove_dsym:
+ shutil.rmtree(dsym)
+
+ # Update modification time on the Mach-O file and .dSYM bundle
+ now = time.time()
+ os.utime(macho, (now, now))
+ os.utime(dsym, (now, now))
+
+ return True
+
+def main(argv=None):
+ if argv is None:
+ argv = sys.argv
+
+ # This only supports operating on one file at a time. Look at the arguments
+ # to strip to figure out what the source to be stripped is. Arguments are
+ # processed in the same way that strip does, although to reduce complexity,
+ # this doesn't do all of the same checking as strip. For example, strip
+ # has no -Z switch and would treat -Z on the command line as an error. For
+ # the purposes this is needed for, that's fine.
+ macho = None
+ process_switches = True
+ ignore_argument = False
+ for arg in argv[1:]:
+ if ignore_argument:
+ ignore_argument = False
+ continue
+ if process_switches:
+ if arg == "-":
+ process_switches = False
+ # strip has these switches accept an argument:
+ if arg in ["-s", "-R", "-d", "-o", "-arch"]:
+ ignore_argument = True
+ if arg[0] == "-":
+ continue
+ if macho is None:
+ macho = arg
+ else:
+ print >> sys.stderr, "Too many things to strip"
+ return 1
+
+ if macho is None:
+ print >> sys.stderr, "Nothing to strip"
+ return 1
+
+ if not strip_and_make_fake_dsym(macho):
+ return 1
+
+ return 0
+
+if __name__ == "__main__":
+ sys.exit(main(sys.argv))
diff --git a/chromium/build/mac/tweak_info_plist.py b/chromium/build/mac/tweak_info_plist.py
new file mode 100755
index 00000000000..eb38acea4e2
--- /dev/null
+++ b/chromium/build/mac/tweak_info_plist.py
@@ -0,0 +1,305 @@
+#!/usr/bin/env python
+
+# 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.
+
+#
+# Xcode supports build variable substitutions and CPP; sadly, that doesn't work
+# because:
+#
+# 1. Xcode wants to do the Info.plist work before it runs any build phases,
+# this means if we were to generate a .h file for INFOPLIST_PREFIX_HEADER
+# we'd have to put it in another target so it runs in time.
+# 2. Xcode also doesn't check to see if the header being used as a prefix for
+# the Info.plist has changed. So even if we updated it, it's only looking
+# at the modtime of the info.plist to see if that's changed.
+#
+# So, we work around all of this by making a script build phase that will run
+# during the app build, and simply update the info.plist in place. This way
+# by the time the app target is done, the info.plist is correct.
+#
+
+import optparse
+import os
+from os import environ as env
+import plistlib
+import re
+import subprocess
+import sys
+import tempfile
+
+TOP = os.path.join(env['SRCROOT'], '..')
+
+
+def _GetOutput(args):
+ """Runs a subprocess and waits for termination. Returns (stdout, returncode)
+ of the process. stderr is attached to the parent."""
+ proc = subprocess.Popen(args, stdout=subprocess.PIPE)
+ (stdout, stderr) = proc.communicate()
+ return (stdout, proc.returncode)
+
+
+def _GetOutputNoError(args):
+ """Similar to _GetOutput() but ignores stderr. If there's an error launching
+ the child (like file not found), the exception will be caught and (None, 1)
+ will be returned to mimic quiet failure."""
+ try:
+ proc = subprocess.Popen(args, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ except OSError:
+ return (None, 1)
+ (stdout, stderr) = proc.communicate()
+ return (stdout, proc.returncode)
+
+
+def _RemoveKeys(plist, *keys):
+ """Removes a varargs of keys from the plist."""
+ for key in keys:
+ try:
+ del plist[key]
+ except KeyError:
+ pass
+
+
+def _AddVersionKeys(plist, version=None):
+ """Adds the product version number into the plist. Returns True on success and
+ False on error. The error will be printed to stderr."""
+ if version:
+ match = re.match('\d+\.\d+\.(\d+\.\d+)$', version)
+ if not match:
+ print >>sys.stderr, 'Invalid version string specified: "%s"' % version
+ return False
+
+ full_version = match.group(0)
+ bundle_version = match.group(1)
+
+ else:
+ # Pull in the Chrome version number.
+ VERSION_TOOL = os.path.join(TOP, 'chrome/tools/build/version.py')
+ VERSION_FILE = os.path.join(TOP, 'chrome/VERSION')
+
+ (stdout, retval1) = _GetOutput([VERSION_TOOL, '-f', VERSION_FILE, '-t',
+ '@MAJOR@.@MINOR@.@BUILD@.@PATCH@'])
+ full_version = stdout.rstrip()
+
+ (stdout, retval2) = _GetOutput([VERSION_TOOL, '-f', VERSION_FILE, '-t',
+ '@BUILD@.@PATCH@'])
+ bundle_version = stdout.rstrip()
+
+ # If either of the two version commands finished with non-zero returncode,
+ # report the error up.
+ if retval1 or retval2:
+ return False
+
+ # Add public version info so "Get Info" works.
+ plist['CFBundleShortVersionString'] = full_version
+
+ # Honor the 429496.72.95 limit. The maximum comes from splitting 2^32 - 1
+ # into 6, 2, 2 digits. The limitation was present in Tiger, but it could
+ # have been fixed in later OS release, but hasn't been tested (it's easy
+ # enough to find out with "lsregister -dump).
+ # http://lists.apple.com/archives/carbon-dev/2006/Jun/msg00139.html
+ # BUILD will always be an increasing value, so BUILD_PATH gives us something
+ # unique that meetings what LS wants.
+ plist['CFBundleVersion'] = bundle_version
+
+ # Return with no error.
+ return True
+
+
+def _DoSCMKeys(plist, add_keys):
+ """Adds the SCM information, visible in about:version, to property list. If
+ |add_keys| is True, it will insert the keys, otherwise it will remove them."""
+ scm_revision = None
+ if add_keys:
+ # Pull in the Chrome revision number.
+ VERSION_TOOL = os.path.join(TOP, 'chrome/tools/build/version.py')
+ LASTCHANGE_FILE = os.path.join(TOP, 'build/util/LASTCHANGE')
+ (stdout, retval) = _GetOutput([VERSION_TOOL, '-f', LASTCHANGE_FILE, '-t',
+ '@LASTCHANGE@'])
+ if retval:
+ return False
+ scm_revision = stdout.rstrip()
+
+ # See if the operation failed.
+ _RemoveKeys(plist, 'SCMRevision')
+ if scm_revision != None:
+ plist['SCMRevision'] = scm_revision
+ elif add_keys:
+ print >>sys.stderr, 'Could not determine SCM revision. This may be OK.'
+
+ return True
+
+
+def _DoPDFKeys(plist, add_keys):
+ """Adds PDF support to the document types list. If add_keys is True, it will
+ add the type information dictionary. If it is False, it will remove it if
+ present."""
+
+ PDF_FILE_EXTENSION = 'pdf'
+
+ def __AddPDFKeys(sub_plist):
+ """Writes the keys into a sub-dictionary of the plist."""
+ sub_plist['CFBundleTypeExtensions'] = [PDF_FILE_EXTENSION]
+ sub_plist['CFBundleTypeIconFile'] = 'document.icns'
+ sub_plist['CFBundleTypeMIMETypes'] = 'application/pdf'
+ sub_plist['CFBundleTypeName'] = 'PDF Document'
+ sub_plist['CFBundleTypeRole'] = 'Viewer'
+
+ DOCUMENT_TYPES_KEY = 'CFBundleDocumentTypes'
+
+ # First get the list of document types, creating it if necessary.
+ try:
+ extensions = plist[DOCUMENT_TYPES_KEY]
+ except KeyError:
+ # If this plist doesn't have a type dictionary, create one if set to add the
+ # keys. If not, bail.
+ if not add_keys:
+ return
+ extensions = plist[DOCUMENT_TYPES_KEY] = []
+
+ # Loop over each entry in the list, looking for one that handles PDF types.
+ for i, ext in enumerate(extensions):
+ # If an entry for .pdf files is found...
+ if 'CFBundleTypeExtensions' not in ext:
+ continue
+ if PDF_FILE_EXTENSION in ext['CFBundleTypeExtensions']:
+ if add_keys:
+ # Overwrite the existing keys with new ones.
+ __AddPDFKeys(ext)
+ else:
+ # Otherwise, delete the entry entirely.
+ del extensions[i]
+ return
+
+ # No PDF entry exists. If one needs to be added, do so now.
+ if add_keys:
+ pdf_entry = {}
+ __AddPDFKeys(pdf_entry)
+ extensions.append(pdf_entry)
+
+
+def _AddBreakpadKeys(plist, branding):
+ """Adds the Breakpad keys. This must be called AFTER _AddVersionKeys() and
+ also requires the |branding| argument."""
+ plist['BreakpadReportInterval'] = '3600' # Deliberately a string.
+ plist['BreakpadProduct'] = '%s_Mac' % branding
+ plist['BreakpadProductDisplay'] = branding
+ plist['BreakpadVersion'] = plist['CFBundleShortVersionString']
+ # These are both deliberately strings and not boolean.
+ plist['BreakpadSendAndExit'] = 'YES'
+ plist['BreakpadSkipConfirm'] = 'YES'
+
+
+def _RemoveBreakpadKeys(plist):
+ """Removes any set Breakpad keys."""
+ _RemoveKeys(plist,
+ 'BreakpadURL',
+ 'BreakpadReportInterval',
+ 'BreakpadProduct',
+ 'BreakpadProductDisplay',
+ 'BreakpadVersion',
+ 'BreakpadSendAndExit',
+ 'BreakpadSkipConfirm')
+
+
+def _AddKeystoneKeys(plist, bundle_identifier):
+ """Adds the Keystone keys. This must be called AFTER _AddVersionKeys() and
+ also requires the |bundle_identifier| argument (com.example.product)."""
+ plist['KSVersion'] = plist['CFBundleShortVersionString']
+ plist['KSProductID'] = bundle_identifier
+ plist['KSUpdateURL'] = 'https://tools.google.com/service/update2'
+
+
+def _RemoveKeystoneKeys(plist):
+ """Removes any set Keystone keys."""
+ _RemoveKeys(plist,
+ 'KSVersion',
+ 'KSProductID',
+ 'KSUpdateURL')
+
+
+def Main(argv):
+ parser = optparse.OptionParser('%prog [options]')
+ parser.add_option('--breakpad', dest='use_breakpad', action='store',
+ type='int', default=False, help='Enable Breakpad [1 or 0]')
+ parser.add_option('--breakpad_uploads', dest='breakpad_uploads',
+ action='store', type='int', default=False,
+ help='Enable Breakpad\'s uploading of crash dumps [1 or 0]')
+ parser.add_option('--keystone', dest='use_keystone', action='store',
+ type='int', default=False, help='Enable Keystone [1 or 0]')
+ parser.add_option('--scm', dest='add_scm_info', action='store', type='int',
+ default=True, help='Add SCM metadata [1 or 0]')
+ parser.add_option('--pdf', dest='add_pdf_support', action='store', type='int',
+ default=False, help='Add PDF file handler support [1 or 0]')
+ parser.add_option('--branding', dest='branding', action='store',
+ type='string', default=None, help='The branding of the binary')
+ parser.add_option('--bundle_id', dest='bundle_identifier',
+ action='store', type='string', default=None,
+ help='The bundle id of the binary')
+ parser.add_option('--version', dest='version', action='store', type='string',
+ default=None, help='The version string [major.minor.build.patch]')
+ (options, args) = parser.parse_args(argv)
+
+ if len(args) > 0:
+ print >>sys.stderr, parser.get_usage()
+ return 1
+
+ # Read the plist into its parsed format.
+ DEST_INFO_PLIST = os.path.join(env['TARGET_BUILD_DIR'], env['INFOPLIST_PATH'])
+ plist = plistlib.readPlist(DEST_INFO_PLIST)
+
+ # Insert the product version.
+ if not _AddVersionKeys(plist, version=options.version):
+ return 2
+
+ # Add Breakpad if configured to do so.
+ if options.use_breakpad:
+ if options.branding is None:
+ print >>sys.stderr, 'Use of Breakpad requires branding.'
+ return 1
+ _AddBreakpadKeys(plist, options.branding)
+ if options.breakpad_uploads:
+ plist['BreakpadURL'] = 'https://clients2.google.com/cr/report'
+ else:
+ # This allows crash dumping to a file without uploading the
+ # dump, for testing purposes. Breakpad does not recognise
+ # "none" as a special value, but this does stop crash dump
+ # uploading from happening. We need to specify something
+ # because if "BreakpadURL" is not present, Breakpad will not
+ # register its crash handler and no crash dumping will occur.
+ plist['BreakpadURL'] = 'none'
+ else:
+ _RemoveBreakpadKeys(plist)
+
+ # Only add Keystone in Release builds.
+ if options.use_keystone and env['CONFIGURATION'] == 'Release':
+ if options.bundle_identifier is None:
+ print >>sys.stderr, 'Use of Keystone requires the bundle id.'
+ return 1
+ _AddKeystoneKeys(plist, options.bundle_identifier)
+ else:
+ _RemoveKeystoneKeys(plist)
+
+ # Adds or removes any SCM keys.
+ if not _DoSCMKeys(plist, options.add_scm_info):
+ return 3
+
+ # Adds or removes the PDF file handler entry.
+ _DoPDFKeys(plist, options.add_pdf_support)
+
+ # Now that all keys have been mutated, rewrite the file.
+ temp_info_plist = tempfile.NamedTemporaryFile()
+ plistlib.writePlist(plist, temp_info_plist.name)
+
+ # Info.plist will work perfectly well in any plist format, but traditionally
+ # applications use xml1 for this, so convert it to ensure that it's valid.
+ proc = subprocess.Popen(['plutil', '-convert', 'xml1', '-o', DEST_INFO_PLIST,
+ temp_info_plist.name])
+ proc.wait()
+ return proc.returncode
+
+
+if __name__ == '__main__':
+ sys.exit(Main(sys.argv[1:]))
diff --git a/chromium/build/mac/verify_no_objc.sh b/chromium/build/mac/verify_no_objc.sh
new file mode 100755
index 00000000000..955f9befff2
--- /dev/null
+++ b/chromium/build/mac/verify_no_objc.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+# Copyright (c) 2011 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.
+
+# This script makes sure that no __OBJC,__image_info section appears in the
+# executable file built by the Xcode target that runs the script. If such a
+# section appears, the script prints an error message and exits nonzero.
+#
+# Why is this important?
+#
+# On 10.5, there's a bug in CFBundlePreflightExecutable that causes it to
+# crash when operating in an executable that has not loaded at its default
+# address (that is, when it's a position-independent executable with the
+# MH_PIE bit set in its mach_header) and the executable has an
+# __OBJC,__image_info section. See http://crbug.com/88697.
+#
+# Chrome's main executables don't use any Objective-C at all, and don't need
+# to carry this section around. Not linking them as Objective-C when they
+# don't need it anyway saves about 4kB in the linked executable, although most
+# of that 4kB is just filled with zeroes.
+#
+# This script makes sure that nobody goofs and accidentally introduces these
+# sections into the main executables.
+
+set -eu
+
+otool="${DEVELOPER_BIN_DIR:-/usr/bin}/otool"
+executable="${BUILT_PRODUCTS_DIR}/${EXECUTABLE_PATH}"
+
+if "${otool}" -arch i386 -o "${executable}" | grep -q '^Contents.*section$'; \
+then
+ echo "${0}: ${executable} has an __OBJC,__image_info section" 2>&1
+ exit 1
+fi
+
+if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
+ echo "${0}: otool failed" 2>&1
+ exit 1
+fi
+
+exit 0