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