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