18bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#!/usr/bin/env python
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2011 The Chromium Authors. All rights reserved.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Usage: strip_save_dsym <whatever-arguments-you-would-pass-to-strip>
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# strip_save_dsym is a wrapper around the standard strip utility.  Given an
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# input Mach-O file, strip_save_dsym will save a copy of the file in a "fake"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# .dSYM bundle for debugging, and then call strip to strip the Mach-O file.
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Note that the .dSYM file is a "fake" in that it's not a self-contained
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# .dSYM bundle, it just contains a copy of the original (unstripped) Mach-O
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# file, and therefore contains references to object files on the filesystem.
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# The generated .dSYM bundle is therefore unsuitable for debugging in the
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# absence of these .o files.
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# If a .dSYM already exists and has a newer timestamp than the Mach-O file,
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# this utility does nothing.  That allows strip_save_dsym to be run on a file
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# that has already been stripped without trashing the .dSYM.
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Rationale: the "right" way to generate dSYM bundles, dsymutil, is incredibly
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# slow.  On the other hand, doing a file copy (which is really all that
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# dsymutil does) is comparatively fast.  Since we usually just want to strip
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# a release-mode executable but still be able to debug it, and we don't care
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# so much about generating a hermetic dSYM bundle, we'll prefer the file copy.
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# If a real dSYM is ever needed, it's still possible to create one by running
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# dsymutil and pointing it at the original Mach-O file inside the "fake"
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# bundle, provided that the object files are available.
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import errno
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import re
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import shutil
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import subprocess
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import sys
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import time
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Returns a list of architectures contained in a Mach-O file.  The file can be
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# a universal (fat) file, in which case there will be one list element for
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# each contained architecture, or it can be a thin single-architecture Mach-O
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# file, in which case the list will contain a single element identifying the
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# architecture.  On error, returns an empty list.  Determines the architecture
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# list by calling file.
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def macho_archs(macho):
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  macho_types = ["executable",
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 "dynamically linked shared library",
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 "bundle"]
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  macho_types_re = "Mach-O (?:64-bit )?(?:" + "|".join(macho_types) + ")"
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  file_cmd = subprocess.Popen(["/usr/bin/file", "-b", "--", macho],
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                              stdout=subprocess.PIPE)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  archs = []
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  type_line = file_cmd.stdout.readline()
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  type_match = re.match("^%s (.*)$" % macho_types_re, type_line)
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if type_match:
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    archs.append(type_match.group(1))
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return [type_match.group(1)]
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else:
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    type_match = re.match("^Mach-O universal binary with (.*) architectures$",
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          type_line)
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if type_match:
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for i in range(0, int(type_match.group(1))):
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        arch_line = file_cmd.stdout.readline()
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        arch_match = re.match(
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     "^.* \(for architecture (.*)\):\t%s .*$" % macho_types_re,
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                     arch_line)
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if arch_match:
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          archs.append(arch_match.group(1))
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if file_cmd.wait() != 0:
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    archs = []
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if len(archs) == 0:
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print >> sys.stderr, "No architectures in %s" % macho
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return archs
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Returns a dictionary mapping architectures contained in the file as returned
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# by macho_archs to the LC_UUID load command for that architecture.
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Architectures with no LC_UUID load command are omitted from the dictionary.
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Determines the UUID value by calling otool.
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def macho_uuids(macho):
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uuids = {}
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  archs = macho_archs(macho)
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if len(archs) == 0:
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return uuids
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for arch in archs:
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if arch == "":
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    otool_cmd = subprocess.Popen(["/usr/bin/otool", "-arch", arch, "-l", "-",
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  macho],
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                 stdout=subprocess.PIPE)
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # state 0 is when nothing UUID-related has been seen yet.  State 1 is
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # entered after a load command begins, but it may not be an LC_UUID load
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # command.  States 2, 3, and 4 are intermediate states while reading an
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # LC_UUID command.  State 5 is the terminal state for a successful LC_UUID
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # read.  State 6 is the error state.
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    state = 0
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uuid = ""
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for otool_line in otool_cmd.stdout:
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if state == 0:
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if re.match("^Load command .*$", otool_line):
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          state = 1
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      elif state == 1:
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if re.match("^     cmd LC_UUID$", otool_line):
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          state = 2
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        else:
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          state = 0
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      elif state == 2:
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if re.match("^ cmdsize 24$", otool_line):
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          state = 3
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        else:
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          state = 6
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      elif state == 3:
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # The UUID display format changed in the version of otool shipping
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # with the Xcode 3.2.2 prerelease.  The new format is traditional:
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        #    uuid 4D7135B2-9C56-C5F5-5F49-A994258E0955
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # and with Xcode 3.2.6, then line is indented one more space:
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        #     uuid 4D7135B2-9C56-C5F5-5F49-A994258E0955
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # The old format, from cctools-750 and older's otool, breaks the UUID
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # up into a sequence of bytes:
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        #    uuid 0x4d 0x71 0x35 0xb2 0x9c 0x56 0xc5 0xf5
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        #         0x5f 0x49 0xa9 0x94 0x25 0x8e 0x09 0x55
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        new_uuid_match = re.match("^ {3,4}uuid (.{8}-.{4}-.{4}-.{4}-.{12})$",
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  otool_line)
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if new_uuid_match:
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          uuid = new_uuid_match.group(1)
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          # Skip state 4, there is no second line to read.
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          state = 5
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        else:
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          old_uuid_match = re.match("^   uuid 0x(..) 0x(..) 0x(..) 0x(..) "
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    "0x(..) 0x(..) 0x(..) 0x(..)$",
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    otool_line)
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          if old_uuid_match:
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            state = 4
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            uuid = old_uuid_match.group(1) + old_uuid_match.group(2) + \
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   old_uuid_match.group(3) + old_uuid_match.group(4) + "-" + \
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   old_uuid_match.group(5) + old_uuid_match.group(6) + "-" + \
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                   old_uuid_match.group(7) + old_uuid_match.group(8) + "-"
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          else:
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            state = 6
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      elif state == 4:
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        old_uuid_match = re.match("^        0x(..) 0x(..) 0x(..) 0x(..) "
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  "0x(..) 0x(..) 0x(..) 0x(..)$",
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  otool_line)
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        if old_uuid_match:
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          state = 5
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          uuid += old_uuid_match.group(1) + old_uuid_match.group(2) + "-" + \
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  old_uuid_match.group(3) + old_uuid_match.group(4) + \
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  old_uuid_match.group(5) + old_uuid_match.group(6) + \
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                  old_uuid_match.group(7) + old_uuid_match.group(8)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        else:
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          state = 6
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if otool_cmd.wait() != 0:
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      state = 6
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if state == 5:
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      uuids[arch] = uuid.upper()
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if len(uuids) == 0:
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print >> sys.stderr, "No UUIDs in %s" % macho
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return uuids
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Given a path to a Mach-O file and possible information from the environment,
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# determines the desired path to the .dSYM.
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def dsym_path(macho):
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # If building a bundle, the .dSYM should be placed next to the bundle.  Use
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # WRAPPER_NAME to make this determination.  If called from xcodebuild,
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # WRAPPER_NAME will be set to the name of the bundle.
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dsym = ""
1805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if "WRAPPER_NAME" in os.environ:
1815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if "BUILT_PRODUCTS_DIR" in os.environ:
1825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      dsym = os.path.join(os.environ["BUILT_PRODUCTS_DIR"],
1835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          os.environ["WRAPPER_NAME"])
1845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
1855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      dsym = os.environ["WRAPPER_NAME"]
1865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  else:
1875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dsym = macho
1885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dsym += ".dSYM"
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return dsym
1925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Creates a fake .dSYM bundle at dsym for macho, a Mach-O image with the
1945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# architectures and UUIDs specified by the uuids map.
1955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def make_fake_dsym(macho, dsym):
1965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uuids = macho_uuids(macho)
1975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if len(uuids) == 0:
1985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return False
1995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dwarf_dir = os.path.join(dsym, "Contents", "Resources", "DWARF")
2015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dwarf_file = os.path.join(dwarf_dir, os.path.basename(macho))
2025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try:
2035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    os.makedirs(dwarf_dir)
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  except OSError, (err, error_string):
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if err != errno.EEXIST:
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise
2075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  shutil.copyfile(macho, dwarf_file)
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # info_template is the same as what dsymutil would have written, with the
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # addition of the fake_dsym key.
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  info_template = \
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)'''<?xml version="1.0" encoding="UTF-8"?>
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)<plist version="1.0">
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	<dict>
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		<key>CFBundleDevelopmentRegion</key>
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		<string>English</string>
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		<key>CFBundleIdentifier</key>
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		<string>com.apple.xcode.dsym.%(root_name)s</string>
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		<key>CFBundleInfoDictionaryVersion</key>
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		<string>6.0</string>
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		<key>CFBundlePackageType</key>
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		<string>dSYM</string>
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		<key>CFBundleSignature</key>
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		<string>????</string>
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		<key>CFBundleShortVersionString</key>
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		<string>1.0</string>
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		<key>CFBundleVersion</key>
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		<string>1</string>
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		<key>dSYM_UUID</key>
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		<dict>
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)%(uuid_dict)s		</dict>
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		<key>fake_dsym</key>
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)		<true/>
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)	</dict>
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)</plist>
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)'''
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  root_name = os.path.basename(dsym)[:-5]  # whatever.dSYM without .dSYM
2405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uuid_dict = ""
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for arch in sorted(uuids):
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    uuid_dict += "\t\t\t<key>" + arch + "</key>\n"\
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                 "\t\t\t<string>" + uuids[arch] + "</string>\n"
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  info_dict = {
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "root_name": root_name,
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    "uuid_dict": uuid_dict,
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  info_contents = info_template % info_dict
2495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  info_file = os.path.join(dsym, "Contents", "Info.plist")
2505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  info_fd = open(info_file, "w")
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  info_fd.write(info_contents)
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  info_fd.close()
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return True
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# For a Mach-O file, determines where the .dSYM bundle should be located.  If
2575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# the bundle does not exist or has a modification time older than the Mach-O
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# file, calls make_fake_dsym to create a fake .dSYM bundle there, then strips
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# the Mach-O file and sets the modification time on the .dSYM bundle and Mach-O
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# file to be identical.
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def strip_and_make_fake_dsym(macho):
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dsym = dsym_path(macho)
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  macho_stat = os.stat(macho)
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  dsym_stat = None
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  try:
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    dsym_stat = os.stat(dsym)
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  except OSError, (err, error_string):
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if err != errno.ENOENT:
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if dsym_stat is None or dsym_stat.st_mtime < macho_stat.st_mtime:
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Make a .dSYM bundle
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not make_fake_dsym(macho, dsym):
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return False
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Strip the Mach-O file
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    remove_dsym = True
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
2794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      strip_cmdline = ['xcrun', 'strip'] + sys.argv[1:]
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      strip_cmd = subprocess.Popen(strip_cmdline)
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if strip_cmd.wait() == 0:
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        remove_dsym = False
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    finally:
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if remove_dsym:
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        shutil.rmtree(dsym)
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    # Update modification time on the Mach-O file and .dSYM bundle
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    now = time.time()
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    os.utime(macho, (now, now))
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    os.utime(dsym, (now, now))
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return True
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)def main(argv=None):
2955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if argv is None:
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    argv = sys.argv
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # This only supports operating on one file at a time.  Look at the arguments
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # to strip to figure out what the source to be stripped is.  Arguments are
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # processed in the same way that strip does, although to reduce complexity,
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # this doesn't do all of the same checking as strip.  For example, strip
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # has no -Z switch and would treat -Z on the command line as an error.  For
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  # the purposes this is needed for, that's fine.
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  macho = None
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  process_switches = True
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ignore_argument = False
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for arg in argv[1:]:
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if ignore_argument:
3095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      ignore_argument = False
3105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      continue
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if process_switches:
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if arg == "-":
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        process_switches = False
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      # strip has these switches accept an argument:
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if arg in ["-s", "-R", "-d", "-o", "-arch"]:
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ignore_argument = True
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if arg[0] == "-":
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        continue
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if macho is None:
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      macho = arg
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    else:
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      print >> sys.stderr, "Too many things to strip"
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return 1
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if macho is None:
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    print >> sys.stderr, "Nothing to strip"
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if not strip_and_make_fake_dsym(macho):
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 1
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return 0
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if __name__ == "__main__":
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  sys.exit(main(sys.argv))
336