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
  • Select an option

  • Save nucleartide/14d672bed6d14acfe0e77f06eb66d998 to your computer and use it in GitHub Desktop.

Select an option

Save nucleartide/14d672bed6d14acfe0e77f06eb66d998 to your computer and use it in GitHub Desktop.

Revisions

  1. @intvoker intvoker revised this gist Oct 11, 2020. 1 changed file with 35 additions and 10 deletions.
    45 changes: 35 additions & 10 deletions binary_dsym_plist_gen.rb
    Original file line number Diff line number Diff line change
    @@ -2,12 +2,12 @@
    require 'optimist'
    require 'plist'

    def run(path, local_source_path, build_source_path)
    process(path, local_source_path, build_source_path, '**/*.app', true)
    process(path, local_source_path, build_source_path, '**/*.dylib', false)
    def run(path, source_path)
    process(path, source_path, '**/*.app', true)
    process(path, source_path, '**/*.dylib', false)
    end

    def process(path, local_source_path, build_source_path, search_pattern, is_app)
    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")
    @@ -23,7 +23,8 @@ def process(path, local_source_path, build_source_path, search_pattern, is_app)
    dsym_uuids = get_uuids_of_dwarf(dsym_path)
    verify_uuids(binary_uuids, dsym_uuids)

    generate_plist_for_dsym(binary_path, binary_uuids, dsym_path, local_source_path, build_source_path)
    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
    @@ -56,13 +57,38 @@ def verify_uuids(binary_uuids, dsym_uuids)
    end
    end

    def generate_plist_for_dsym(binary_path, binary_uuids, dsym_path, local_source_path, build_source_path)
    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: local_source_path,
    DBGSourcePath: source_path,
    #DBGDSYMPath: dsym_path,
    #DBGSymbolRichExecutable: binary_path
    }
    @@ -103,8 +129,7 @@ def dwarf_dir_path(binary_path)

    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 :local_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
    opt :build_source_path, 'Path to the build source directory (default is "/Users/build/Build/++UE4/Sync")', type: :string, default: '/Users/build/Build/++UE4/Sync'
    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[:local_source_path], options[:build_source_path]
    run options[:path], options[:source_path]
  2. @intvoker intvoker created this gist Oct 11, 2020.
    110 changes: 110 additions & 0 deletions binary_dsym_plist_gen.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,110 @@
    require 'fileutils'
    require 'optimist'
    require 'plist'

    def run(path, local_source_path, build_source_path)
    process(path, local_source_path, build_source_path, '**/*.app', true)
    process(path, local_source_path, build_source_path, '**/*.dylib', false)
    end

    def process(path, local_source_path, build_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)

    generate_plist_for_dsym(binary_path, binary_uuids, dsym_path, local_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 generate_plist_for_dsym(binary_path, binary_uuids, dsym_path, local_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: local_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 :local_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
    opt :build_source_path, 'Path to the build source directory (default is "/Users/build/Build/++UE4/Sync")', type: :string, default: '/Users/build/Build/++UE4/Sync'
    end

    run options[:path], options[:local_source_path], options[:build_source_path]