Last active
December 13, 2023 02:36
-
-
Save benasher44/fcf92fc12ff8b539bee7cc50fb52ed32 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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