13dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen#!/usr/bin/python
23dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
33dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# Copyright (c) 2008 The Chromium Authors. All rights reserved.
43dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# Use of this source code is governed by a BSD-style license that can be
53dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# found in the LICENSE file.
63dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
73dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# Usage: strip_save_dsym <whatever-arguments-you-would-pass-to-strip>
83dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen#
93dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# strip_save_dsym is a wrapper around the standard strip utility.  Given an
103dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# input Mach-O file, strip_save_dsym will save a copy of the file in a "fake"
113dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# .dSYM bundle for debugging, and then call strip to strip the Mach-O file.
123dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# Note that the .dSYM file is a "fake" in that it's not a self-contained
133dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# .dSYM bundle, it just contains a copy of the original (unstripped) Mach-O
143dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# file, and therefore contains references to object files on the filesystem.
153dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# The generated .dSYM bundle is therefore unsuitable for debugging in the
163dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# absence of these .o files.
173dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen#
183dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# If a .dSYM already exists and has a newer timestamp than the Mach-O file,
193dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# this utility does nothing.  That allows strip_save_dsym to be run on a file
203dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# that has already been stripped without trashing the .dSYM.
213dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen#
223dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# Rationale: the "right" way to generate dSYM bundles, dsymutil, is incredibly
233dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# slow.  On the other hand, doing a file copy (which is really all that
243dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# dsymutil does) is comparatively fast.  Since we usually just want to strip
253dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# a release-mode executable but still be able to debug it, and we don't care
263dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# so much about generating a hermetic dSYM bundle, we'll prefer the file copy.
273dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# If a real dSYM is ever needed, it's still possible to create one by running
283dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# dsymutil and pointing it at the original Mach-O file inside the "fake"
293dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# bundle, provided that the object files are available.
303dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
313dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsenimport errno
323dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsenimport os
333dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsenimport re
343dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsenimport shutil
353dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsenimport subprocess
363dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsenimport sys
373dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsenimport time
383dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
393dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# Returns a list of architectures contained in a Mach-O file.  The file can be
403dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# a universal (fat) file, in which case there will be one list element for
413dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# each contained architecture, or it can be a thin single-architecture Mach-O
423dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# file, in which case the list will contain a single element identifying the
433dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# architecture.  On error, returns an empty list.  Determines the architecture
443dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# list by calling file.
453dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsendef macho_archs(macho):
463dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  macho_types = ["executable",
473dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                 "dynamically linked shared library",
483dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                 "bundle"]
493dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  macho_types_re = "Mach-O (?:64-bit )?(?:" + "|".join(macho_types) + ")"
503dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
513dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  file_cmd = subprocess.Popen(["/usr/bin/file", "-b", "--", macho],
523dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                              stdout=subprocess.PIPE)
533dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
543dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  archs = []
553dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
563dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  type_line = file_cmd.stdout.readline()
573dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  type_match = re.match("^%s (.*)$" % macho_types_re, type_line)
583dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  if type_match:
593dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    archs.append(type_match.group(1))
603dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    return [type_match.group(1)]
613dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  else:
623dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    type_match = re.match("^Mach-O universal binary with (.*) architectures$",
633dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                          type_line)
643dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    if type_match:
653dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      for i in range(0, int(type_match.group(1))):
663dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        arch_line = file_cmd.stdout.readline()
673dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        arch_match = re.match(
683dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                     "^.* \(for architecture (.*)\):\t%s .*$" % macho_types_re,
693dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                     arch_line)
703dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        if arch_match:
713dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen          archs.append(arch_match.group(1))
723dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
733dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  if file_cmd.wait() != 0:
743dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    archs = []
753dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
763dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  if len(archs) == 0:
773dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    print >> sys.stderr, "No architectures in %s" % macho
783dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
793dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  return archs
803dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
813dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# Returns a dictionary mapping architectures contained in the file as returned
823dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# by macho_archs to the LC_UUID load command for that architecture.
833dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# Architectures with no LC_UUID load command are omitted from the dictionary.
843dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# Determines the UUID value by calling otool.
853dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsendef macho_uuids(macho):
863dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  uuids = {}
873dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
883dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  archs = macho_archs(macho)
893dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  if len(archs) == 0:
903dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    return uuids
913dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
923dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  for arch in archs:
933dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    if arch == "":
943dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      continue
953dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
963dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    otool_cmd = subprocess.Popen(["/usr/bin/otool", "-arch", arch, "-l", "-",
973dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                                  macho],
983dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                                 stdout=subprocess.PIPE)
993dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    # state 0 is when nothing UUID-related has been seen yet.  State 1 is
1003dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    # entered after a load command begins, but it may not be an LC_UUID load
1013dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    # command.  States 2, 3, and 4 are intermediate states while reading an
1023dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    # LC_UUID command.  State 5 is the terminal state for a successful LC_UUID
1033dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    # read.  State 6 is the error state.
1043dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    state = 0
1053dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    uuid = ""
1063dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    for otool_line in otool_cmd.stdout:
1073dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      if state == 0:
1083dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        if re.match("^Load command .*$", otool_line):
1093dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen          state = 1
1103dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      elif state == 1:
1113dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        if re.match("^     cmd LC_UUID$", otool_line):
1123dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen          state = 2
1133dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        else:
1143dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen          state = 0
1153dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      elif state == 2:
1163dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        if re.match("^ cmdsize 24$", otool_line):
1173dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen          state = 3
1183dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        else:
1193dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen          state = 6
1203dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      elif state == 3:
1213dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        # The UUID display format changed in the version of otool shipping
1223dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        # with the Xcode 3.2.2 prerelease.  The new format is traditional:
1233dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        #    uuid 4D7135B2-9C56-C5F5-5F49-A994258E0955
1243dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        # and with Xcode 3.2.6, then line is indented one more space:
1253dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        #     uuid 4D7135B2-9C56-C5F5-5F49-A994258E0955
1263dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        # The old format, from cctools-750 and older's otool, breaks the UUID
1273dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        # up into a sequence of bytes:
1283dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        #    uuid 0x4d 0x71 0x35 0xb2 0x9c 0x56 0xc5 0xf5
1293dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        #         0x5f 0x49 0xa9 0x94 0x25 0x8e 0x09 0x55
1303dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        new_uuid_match = re.match("^ {3,4}uuid (.{8}-.{4}-.{4}-.{4}-.{12})$",
1313dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                                  otool_line)
1323dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        if new_uuid_match:
1333dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen          uuid = new_uuid_match.group(1)
1343dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
1353dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen          # Skip state 4, there is no second line to read.
1363dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen          state = 5
1373dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        else:
1383dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen          old_uuid_match = re.match("^   uuid 0x(..) 0x(..) 0x(..) 0x(..) "
1393dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                                    "0x(..) 0x(..) 0x(..) 0x(..)$",
1403dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                                    otool_line)
1413dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen          if old_uuid_match:
1423dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen            state = 4
1433dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen            uuid = old_uuid_match.group(1) + old_uuid_match.group(2) + \
1443dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                   old_uuid_match.group(3) + old_uuid_match.group(4) + "-" + \
1453dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                   old_uuid_match.group(5) + old_uuid_match.group(6) + "-" + \
1463dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                   old_uuid_match.group(7) + old_uuid_match.group(8) + "-"
1473dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen          else:
1483dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen            state = 6
1493dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      elif state == 4:
1503dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        old_uuid_match = re.match("^        0x(..) 0x(..) 0x(..) 0x(..) "
1513dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                                  "0x(..) 0x(..) 0x(..) 0x(..)$",
1523dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                                  otool_line)
1533dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        if old_uuid_match:
1543dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen          state = 5
1553dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen          uuid += old_uuid_match.group(1) + old_uuid_match.group(2) + "-" + \
1563dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                  old_uuid_match.group(3) + old_uuid_match.group(4) + \
1573dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                  old_uuid_match.group(5) + old_uuid_match.group(6) + \
1583dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                  old_uuid_match.group(7) + old_uuid_match.group(8)
1593dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        else:
1603dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen          state = 6
1613dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
1623dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    if otool_cmd.wait() != 0:
1633dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      state = 6
1643dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
1653dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    if state == 5:
1663dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      uuids[arch] = uuid.upper()
1673dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
1683dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  if len(uuids) == 0:
1693dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    print >> sys.stderr, "No UUIDs in %s" % macho
1703dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
1713dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  return uuids
1723dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
1733dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# Given a path to a Mach-O file and possible information from the environment,
1743dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# determines the desired path to the .dSYM.
1753dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsendef dsym_path(macho):
1763dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  # If building a bundle, the .dSYM should be placed next to the bundle.  Use
1773dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  # WRAPPER_NAME to make this determination.  If called from xcodebuild,
1783dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  # WRAPPER_NAME will be set to the name of the bundle.
1793dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  dsym = ""
1803dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  if "WRAPPER_NAME" in os.environ:
1813dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    if "BUILT_PRODUCTS_DIR" in os.environ:
1823dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      dsym = os.path.join(os.environ["BUILT_PRODUCTS_DIR"],
1833dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                          os.environ["WRAPPER_NAME"])
1843dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    else:
1853dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      dsym = os.environ["WRAPPER_NAME"]
1863dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  else:
1873dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    dsym = macho
1883dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
1893dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  dsym += ".dSYM"
1903dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
1913dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  return dsym
1923dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
1933dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# Creates a fake .dSYM bundle at dsym for macho, a Mach-O image with the
1943dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# architectures and UUIDs specified by the uuids map.
1953dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsendef make_fake_dsym(macho, dsym):
1963dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  uuids = macho_uuids(macho)
1973dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  if len(uuids) == 0:
1983dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    return False
1993dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
2003dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  dwarf_dir = os.path.join(dsym, "Contents", "Resources", "DWARF")
2013dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  dwarf_file = os.path.join(dwarf_dir, os.path.basename(macho))
2023dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  try:
2033dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    os.makedirs(dwarf_dir)
2043dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  except OSError, (err, error_string):
2053dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    if err != errno.EEXIST:
2063dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      raise
2073dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  shutil.copyfile(macho, dwarf_file)
2083dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
2093dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  # info_template is the same as what dsymutil would have written, with the
2103dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  # addition of the fake_dsym key.
2113dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  info_template = \
2123dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen'''<?xml version="1.0" encoding="UTF-8"?>
2133dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
2143dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen<plist version="1.0">
2153dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen	<dict>
2163dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen		<key>CFBundleDevelopmentRegion</key>
2173dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen		<string>English</string>
2183dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen		<key>CFBundleIdentifier</key>
2193dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen		<string>com.apple.xcode.dsym.%(root_name)s</string>
2203dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen		<key>CFBundleInfoDictionaryVersion</key>
2213dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen		<string>6.0</string>
2223dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen		<key>CFBundlePackageType</key>
2233dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen		<string>dSYM</string>
2243dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen		<key>CFBundleSignature</key>
2253dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen		<string>????</string>
2263dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen		<key>CFBundleShortVersionString</key>
2273dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen		<string>1.0</string>
2283dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen		<key>CFBundleVersion</key>
2293dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen		<string>1</string>
2303dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen		<key>dSYM_UUID</key>
2313dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen		<dict>
2323dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen%(uuid_dict)s		</dict>
2333dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen		<key>fake_dsym</key>
2343dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen		<true/>
2353dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen	</dict>
2363dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen</plist>
2373dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen'''
2383dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
2393dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  root_name = os.path.basename(dsym)[:-5]  # whatever.dSYM without .dSYM
2403dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  uuid_dict = ""
2413dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  for arch in sorted(uuids):
2423dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    uuid_dict += "\t\t\t<key>" + arch + "</key>\n"\
2433dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen                 "\t\t\t<string>" + uuids[arch] + "</string>\n"
2443dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  info_dict = {
2453dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    "root_name": root_name,
2463dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    "uuid_dict": uuid_dict,
2473dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  }
2483dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  info_contents = info_template % info_dict
2493dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  info_file = os.path.join(dsym, "Contents", "Info.plist")
2503dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  info_fd = open(info_file, "w")
2513dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  info_fd.write(info_contents)
2523dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  info_fd.close()
2533dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
2543dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  return True
2553dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
2563dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# For a Mach-O file, determines where the .dSYM bundle should be located.  If
2573dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# the bundle does not exist or has a modification time older than the Mach-O
2583dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# file, calls make_fake_dsym to create a fake .dSYM bundle there, then strips
2593dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# the Mach-O file and sets the modification time on the .dSYM bundle and Mach-O
2603dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen# file to be identical.
2613dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsendef strip_and_make_fake_dsym(macho):
2623dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  dsym = dsym_path(macho)
2633dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  macho_stat = os.stat(macho)
2643dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  dsym_stat = None
2653dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  try:
2663dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    dsym_stat = os.stat(dsym)
2673dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  except OSError, (err, error_string):
2683dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    if err != errno.ENOENT:
2693dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      raise
2703dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
2713dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  if dsym_stat is None or dsym_stat.st_mtime < macho_stat.st_mtime:
2723dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    # Make a .dSYM bundle
2733dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    if not make_fake_dsym(macho, dsym):
2743dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      return False
2753dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
2763dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    # Strip the Mach-O file
2773dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    remove_dsym = True
2783dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    try:
2793dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      strip_path = ""
2803dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      if "SYSTEM_DEVELOPER_BIN_DIR" in os.environ:
2813dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        strip_path = os.environ["SYSTEM_DEVELOPER_BIN_DIR"]
2823dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      else:
2833dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        strip_path = "/usr/bin"
2843dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      strip_path = os.path.join(strip_path, "strip")
2853dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      strip_cmdline = [strip_path] + sys.argv[1:]
2863dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      # Print the strip invocation so that it's obvious something is happening
2873dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      print " ".join(strip_cmdline)
2883dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      strip_cmd = subprocess.Popen(strip_cmdline)
2893dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      if strip_cmd.wait() == 0:
2903dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        remove_dsym = False
2913dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    finally:
2923dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      if remove_dsym:
2933dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        shutil.rmtree(dsym)
2943dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
2953dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    # Update modification time on the Mach-O file and .dSYM bundle
2963dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    now = time.time()
2973dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    os.utime(macho, (now, now))
2983dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    os.utime(dsym, (now, now))
2993dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
3003dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  return True
3013dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
3023dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsendef main(argv=None):
3033dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  if argv is None:
3043dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    argv = sys.argv
3053dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
3063dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  # This only supports operating on one file at a time.  Look at the arguments
3073dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  # to strip to figure out what the source to be stripped is.  Arguments are
3083dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  # processed in the same way that strip does, although to reduce complexity,
3093dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  # this doesn't do all of the same checking as strip.  For example, strip
3103dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  # has no -Z switch and would treat -Z on the command line as an error.  For
3113dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  # the purposes this is needed for, that's fine.
3123dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  macho = None
3133dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  process_switches = True
3143dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  ignore_argument = False
3153dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  for arg in argv[1:]:
3163dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    if ignore_argument:
3173dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      ignore_argument = False
3183dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      continue
3193dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    if process_switches:
3203dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      if arg == "-":
3213dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        process_switches = False
3223dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      # strip has these switches accept an argument:
3233dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      if arg in ["-s", "-R", "-d", "-o", "-arch"]:
3243dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        ignore_argument = True
3253dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      if arg[0] == "-":
3263dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen        continue
3273dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    if macho is None:
3283dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      macho = arg
3293dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    else:
3303dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      print >> sys.stderr, "Too many things to strip"
3313dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen      return 1
3323dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
3333dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  if macho is None:
3343dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    print >> sys.stderr, "Nothing to strip"
3353dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    return 1
3363dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
3373dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  if not strip_and_make_fake_dsym(macho):
3383dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen    return 1
3393dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
3403dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  return 0
3413dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen
3423dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsenif __name__ == "__main__":
3433dff810fe0cc4962a5fa554318e9bf8bc45f5274Kristian Monsen  sys.exit(main(sys.argv))
344