Skip to content

Commit

Permalink
feat: dSYM parser can dump multi dSYM in one zip file
Browse files Browse the repository at this point in the history
  • Loading branch information
icyleaf committed Mar 30, 2023
1 parent 5973493 commit 657a99a
Show file tree
Hide file tree
Showing 10 changed files with 415 additions and 208 deletions.
153 changes: 25 additions & 128 deletions lib/app_info/dsym.rb
Original file line number Diff line number Diff line change
@@ -1,74 +1,24 @@
# frozen_string_literal: true

require 'macho'
require 'app_info/dsym/debug_info'

module AppInfo
# DSYM parser
class DSYM < File
include Helper::Archive

attr_reader :file

def file_type
Format::DSYM
end

def object
@object ||= ::File.basename(app_path)
end

def macho_type
@macho_type ||= ::MachO.open(app_path)
end

def machos
@machos ||= case macho_type
when ::MachO::MachOFile
[MachO.new(macho_type, ::File.size(app_path))]
else
size = macho_type.fat_archs.each_with_object([]) do |arch, obj|
obj << arch.size
end

machos = []
macho_type.machos.each_with_index do |file, i|
machos << MachO.new(file, size[i])
end
machos
end
end

def release_version
info.try(:[], 'CFBundleShortVersionString')
end

def build_version
info.try(:[], 'CFBundleVersion')
end

def identifier
info.try(:[], 'CFBundleIdentifier').sub('com.apple.xcode.dsym.', '')
end
alias bundle_id identifier

def info
return nil unless ::File.exist?(info_path)

@info ||= CFPropertyList.native_types(CFPropertyList::List.new(file: info_path).value)
end

def info_path
@info_path ||= ::File.join(contents, 'Contents', 'Info.plist')
def each_file(&block)
files.each { |file| block.call(file) }
end

def app_path
unless @app_path
path = ::File.join(contents, 'Contents', 'Resources', 'DWARF')
name = Dir.entries(path).reject { |f| ['.', '..'].include?(f) }.first
@app_path = ::File.join(path, name)
def files
@files ||= Dir.children(contents).each_with_object([]) do |file, obj|
obj << DebugInfo.new(::File.join(contents, file))
end

@app_path
end

def clear!
Expand All @@ -77,85 +27,32 @@ def clear!
FileUtils.rm_rf(@contents)

@contents = nil
@app_path = nil
@info = nil
@object = nil
@macho_type = nil
@files = nil
end

def contents
unless @contents
if ::File.directory?(@file)
@contents = @file
else
dsym_dir = nil
@contents = unarchive(@file, prefix: 'dsym') do |path, zip_file|
zip_file.each do |f|
unless dsym_dir
dsym_dir = f.name
# fix filename is xxx.app.dSYM/Contents
dsym_dir = dsym_dir.split('/')[0] if dsym_dir.include?('/')
end
@contents ||= lambda {
return @file if ::File.directory?(@file)

f_path = ::File.join(path, f.name)
FileUtils.mkdir_p(::File.dirname(f_path))
f.extract(f_path) unless ::File.exist?(f_path)
end
end
dsym_filenames = []
unarchive(@file, prefix: 'dsym') do |base_path, zip_file|
zip_file.each do |entry|
file_path = entry.name
next unless file_path.downcase.include?('.dsym/contents/')
next if ::File.basename(file_path).start_with?('.')

@contents = ::File.join(@contents, dsym_dir)
end
end
dsym_filename = file_path.split('/').select { |f| f.downcase.end_with?('.dsym') }.last
dsym_filenames << dsym_filename unless dsym_filenames.include?(dsym_filename)

@contents
end

# DSYM Mach-O
class MachO
include Helper::HumanFileSize

def initialize(file, size = 0)
@file = file
@size = size
end

def cpu_name
@file.cpusubtype
end

def cpu_type
@file.cputype
end

def type
@file.filetype
end

def size(human_size: false)
return number_to_human_size(@size) if human_size

@size
end

def uuid
@file[:LC_UUID][0].uuid_string
end
alias debug_id uuid

def header
@header ||= @file.header
end
unless file_path.start_with?(dsym_filename)
file_path = file_path.split('/')[1..-1].join('/')
end

def to_h
{
uuid: uuid,
type: type,
cpu_name: cpu_name,
cpu_type: cpu_type,
size: size,
human_size: size(human_size: true)
}
end
dest_path = ::File.join(base_path, file_path)
entry.extract(dest_path) unless ::File.exist?(dest_path)
end
end
}.call
end
end
end
72 changes: 72 additions & 0 deletions lib/app_info/dsym/debug_info.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# frozen_string_literal: true

require 'app_info/dsym/macho'

module AppInfo
class DSYM < File
# DSYM Debug Information Format Struct
class DebugInfo
attr_reader :path

def initialize(path)
@path = path
end

def object
@object ||= ::File.basename(bin_path)
end

def macho_type
@macho_type ||= ::MachO.open(bin_path)
end

def machos
@machos ||= case macho_type
when ::MachO::MachOFile
[MachO.new(macho_type, ::File.size(bin_path))]
else
size = macho_type.fat_archs.each_with_object([]) do |arch, obj|
obj << arch.size
end

machos = []
macho_type.machos.each_with_index do |file, i|
machos << MachO.new(file, size[i])
end
machos
end
end

def release_version
info.try(:[], 'CFBundleShortVersionString')
end

def build_version
info.try(:[], 'CFBundleVersion')
end

def identifier
info.try(:[], 'CFBundleIdentifier').sub('com.apple.xcode.dsym.', '')
end
alias bundle_id identifier

def info
return nil unless ::File.exist?(info_path)

@info ||= CFPropertyList.native_types(CFPropertyList::List.new(file: info_path).value)
end

def info_path
@info_path ||= ::File.join(path, 'Contents', 'Info.plist')
end

def bin_path
@bin_path ||= lambda {
dwarf_path = ::File.join(path, 'Contents', 'Resources', 'DWARF')
name = Dir.children(dwarf_path)[0]
::File.join(dwarf_path, name)
}.call
end
end
end
end
55 changes: 55 additions & 0 deletions lib/app_info/dsym/macho.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# frozen_string_literal: true

require 'macho'

module AppInfo
class DSYM < File
# Mach-O Struct
class MachO
include Helper::HumanFileSize

def initialize(file, size = 0)
@file = file
@size = size
end

def cpu_name
@file.cpusubtype
end

def cpu_type
@file.cputype
end

def type
@file.filetype
end

def size(human_size: false)
return number_to_human_size(@size) if human_size

@size
end

def uuid
@file[:LC_UUID][0].uuid_string
end
alias debug_id uuid

def header
@header ||= @file.header
end

def to_h
{
uuid: uuid,
type: type,
cpu_name: cpu_name,
cpu_type: cpu_type,
size: size,
human_size: size(human_size: true)
}
end
end
end
end
12 changes: 6 additions & 6 deletions lib/app_info/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -87,28 +87,28 @@ module Archive
# Unarchive zip file
#
# source: https://github.com/soffes/lagunitas/blob/master/lib/lagunitas/ipa.rb
def unarchive(file, prefix:, base_path: '/tmp')
root_path = Dir.mktmpdir('appinfo-#{prefix}', base_path)
def unarchive(file, prefix:, dest_path: '/tmp')
base_path = Dir.mktmpdir("appinfo-#{prefix}", dest_path)
Zip::File.open(file) do |zip_file|
if block_given?
yield root_path, zip_file
yield base_path, zip_file
else
zip_file.each do |f|
f_path = ::File.join(root_path, f.name)
f_path = ::File.join(base_path, f.name)
FileUtils.mkdir_p(::File.dirname(f_path))
zip_file.extract(f, f_path) unless ::File.exist?(f_path)
end
end
end

root_path
base_path
end

def tempdir(file, prefix:, system: false)
base_path = system ? '/tmp' : ::File.dirname(file)
full_prefix = "appinfo-#{prefix}-#{::File.basename(file, '.*')}"
dest_path = Dir.mktmpdir(full_prefix, base_path)
dest_file = ::File.join(dest_path, ::File.basename(file))
::File.join(dest_path, ::File.basename(file))
end
end

Expand Down
Loading

0 comments on commit 657a99a

Please sign in to comment.