Skip to content

Instantly share code, notes, and snippets.

@nucleartide
Forked from intvoker/binary_dsym_plist_gen.rb
Created August 27, 2023 18:25
Show Gist options
  • Save nucleartide/14d672bed6d14acfe0e77f06eb66d998 to your computer and use it in GitHub Desktop.
Save nucleartide/14d672bed6d14acfe0e77f06eb66d998 to your computer and use it in GitHub Desktop.
require 'fileutils'
require 'optimist'
require 'plist'
def run(path, source_path)
process(path, source_path, '**/*.app', true)
process(path, source_path, '**/*.dylib', false)
end
def process(path, source_path, search_pattern, is_app)
binary_paths = Dir.glob(File.join(path, search_pattern)).sort
binary_paths.each do |binary_path|
dsym_path = File.join(path, "#{file_name_without_extension(binary_path)}.dSYM")
next unless File.exist?(dsym_path)
if is_app
binary_path_for_uuids = File.join(binary_path, 'Contents', 'MacOS', file_name_without_extension(binary_path))
else
binary_path_for_uuids = binary_path
end
binary_uuids = get_uuids_of_dwarf(binary_path_for_uuids)
dsym_uuids = get_uuids_of_dwarf(dsym_path)
verify_uuids(binary_uuids, dsym_uuids)
build_source_path = find_source_path_to_map(binary_path_for_uuids)
generate_plist_for_dsym(binary_path, binary_uuids, dsym_path, source_path, build_source_path)
create_link_to_dsym(binary_path, dsym_path)
end
end
# https://gist.github.com/benasher44/fcf92fc12ff8b539bee7cc50fb52ed32
def get_uuids_of_dwarf(binary_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(binary_path)
lines = `nm -pa "#{binary_path}" | grep "SO /"`.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]
# doing some guessing here
# interesting, could be other stuff there, like /Users/mtrepka/Epic/UE4-Fortnite/Engine/..
next unless potential_original_path.start_with?('/Users/build/')
# taking everything before /Engine/
# in UE4 4.23 /Users/build/Build/++UE4/Sync/Engine/..
# in UE4 4.25 /Users/build/Build/++UE4+Licensee/Sync/Engine/..
potential_original_path_parts = potential_original_path.split(/\/Engine\//)
next unless potential_original_path_parts.count > 0
return potential_original_path_parts.first
end
raise 'Unable to find path match, sorry :-( failing miserably!'
end
def generate_plist_for_dsym(binary_path, binary_uuids, dsym_path, source_path, build_source_path)
binary_uuids.each do |arch, uuid|
# DBGDSYMPath, DBGSymbolRichExecutable are not really needed for some reason. Works fine without them.
plist_dict = {
DBGArchitecture: arch,
DBGBuildSourcePath: build_source_path,
DBGSourcePath: source_path,
#DBGDSYMPath: dsym_path,
#DBGSymbolRichExecutable: binary_path
}
plist_dir = plist_dir_path(binary_path)
FileUtils.mkdir_p(plist_dir) unless Dir.exist?(plist_dir)
plist_path = File.join(plist_dir, "#{uuid}.plist")
File.write(plist_path, plist_dict.to_plist)
puts "Generated plist #{plist_path}"
end
end
def create_link_to_dsym(binary_path, dsym_path)
dsym_link_dir = dwarf_dir_path(binary_path)
FileUtils.mkdir_p(dsym_link_dir) unless Dir.exist?(dsym_link_dir)
# actually dsym link file name can be anything, dummy.xyz works fine as well. What the heck, lldb?
dsym_link_path = File.join(dsym_link_dir, File.basename(dsym_path))
`ln -s "#{dsym_path}" "#{dsym_link_path}"` unless File.exist?(dsym_link_path)
puts "Created link #{dsym_link_path}"
end
def file_name_without_extension(file_path)
File.basename(file_path, File.extname(file_path))
end
def plist_dir_path(binary_path)
File.join("#{binary_path}.dSYM", 'Contents', 'Resources')
end
def dwarf_dir_path(binary_path)
File.join(plist_dir_path(binary_path), 'DWARF')
end
options = Optimist.options do
opt :path, 'Path to the binaries and dSYMs (e.g. "/Users/Shared/Epic Games/UE_4.23/Engine/Binaries/Mac")', type: :string, default: '/Users/Shared/Epic Games/UE_4.23/Engine/Binaries/Mac'#, required: true
opt :source_path, 'Path to the local source directory (e.g. "/Users/Shared/Epic Games/UE_4.23")', type: :string, default: '/Users/Shared/Epic Games/UE_4.23'#, required: true
end
run options[:path], options[:source_path]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment