Skip to content

Instantly share code, notes, and snippets.

@benasher44
Last active December 13, 2023 02:36
Show Gist options
  • Save benasher44/fcf92fc12ff8b539bee7cc50fb52ed32 to your computer and use it in GitHub Desktop.
Save benasher44/fcf92fc12ff8b539bee7cc50fb52ed32 to your computer and use it in GitHub Desktop.
require 'optimist'
require 'plist'
# Setups of source path mapping for the framework at framework_path,
# which has a dsym at dsym_path. It maps the source paths to the
# source_path root. Implementation borrowed from https://medium.com/@maxraskin/background-1b4b6a9c65be
def setup_dsym_source_mapping(framework_path, dsym_path, source_path)
binary_uuids = get_uuids_of_dwarf(framework_path)
dsym_uuids = get_uuids_of_dwarf(dsym_path)
verify_uuids(binary_uuids, dsym_uuids)
source_path_to_map = find_source_path_to_map(framework_path, source_path)
generate_plist_for_dsym(dsym_path, framework_path, source_path, source_path_to_map, binary_uuids)
end
def get_binary_name(framework_or_dsym_path)
File.split(framework_or_dsym_path)[-1].split('.')[0]
end
def get_binary_path(framework_or_dsym_path)
binary_name = get_binary_name framework_or_dsym_path
if framework_or_dsym_path.end_with? '.framework'
File.join(framework_or_dsym_path, binary_name)
elsif framework_or_dsym_path.end_with? '.dSYM'
File.join(framework_or_dsym_path, 'Contents', 'Resources', 'DWARF', binary_name)
else
raise ArgumentError, "Invalid framework or dSYM path: #{framework_or_dsym_path}. Expected *.framework or *.dSYM."
end
end
def get_uuids_of_dwarf(framework_or_dsym_path)
binary_path = get_binary_path framework_or_dsym_path
lines = `dwarfdump --uuid "#{binary_path}"`.split("\n")
#
# An output line of the dwarfdump tool looks like this:
# "UUID: E29B1FB0-EBFE-3740-BF5F-5B65CE884713 (x86_64) /path/to/binary"
#
archs_to_uuids = {}
lines.each do |line|
elements = line.split(' ')
archs_to_uuids[elements[2].gsub(/[()]/, '')] = elements[1] if elements.length >= 4
end
raise "Unable to obtain UUIDs from #{binary_path}" if archs_to_uuids.empty?
archs_to_uuids
end
def verify_uuids(binary_uuids, dsym_uuids)
binary_uuids.each do |arch, binary_uuid|
raise "Unable to find #{arch} architecture in dSYM" unless dsym_uuids.key? arch
next if binary_uuid == dsym_uuids[arch]
raise "uuid mismatch for arch #{arch}, binary uuid=#{binary_uuid}, dsym uuid=#{dsym_uuids[arch]}"
end
end
def find_source_path_to_map(framework_path, source_path)
binary_name = get_binary_name(framework_path)
lines = `nm -pa '#{File.join(framework_path, binary_name)}' | grep 'SO' | grep '/'`.split("\n")
lines.each do |line|
split_result = line.split
# A line looks like this
# 0000000000000000 - 00 0000 SO /potential/path/in/remote/machine
next if split_result.length < 6
potential_original_path = split_result[5]
potential_original_path_fragments = potential_original_path.split('/')
potential_path_suffix = ''
#
# Here's an example of how the algorithm below works:
#
# let's assume that source_path == /my/path
# potential_original_path == /remote/place/foo/bar/baz
#
# Then we attempt to see if /my/path/baz exists, if not then /my/path/bar/baz, and then
# /my/path/foo/bar/baz and if it does we return /remote/place/
#
potential_original_path_fragments.reverse.each do |fragment|
next if fragment.empty?
potential_path_suffix = if potential_path_suffix.empty?
fragment
else
File.join(fragment, potential_path_suffix)
end
next unless File.exist? File.join(source_path, potential_path_suffix)
path_start = potential_original_path.index(potential_path_suffix)
return File.expand_path(potential_original_path.slice(0, path_start))
end
end
raise 'Unable to find path match, sorry :-( failing miserably!'
end
def generate_plist_for_dsym(dsym_path, framework_path, source_path, source_path_to_map, binary_uuids)
binary_uuids.each do |arch, uuid|
plist_dict = {
'DBGArchitecture': arch,
'DBGBuildSourcePath': source_path_to_map,
'DBGSourcePath': source_path,
'DBGDSYMPath': get_binary_path(dsym_path),
'DBGSymbolRichExecutable': get_binary_path(framework_path)
}
File.write(File.join(dsym_path, 'Contents', 'Resources', "#{uuid}.plist"), plist_dict.to_plist)
end
end
options = Optimist.options do
opt :framework_path, 'Path to the .framework', type: :string, required: true
opt :dsym_path, 'Path to the .framework.dSYM', type: :string, required: true
opt :source_path,
'Path to the root of the source directory, from which the binary was built',
type: :string, required: true
end
setup_dsym_source_mapping options[:framework_path], options[:dsym_path], options[:source_path]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment