objc-dependency-tree-generator 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8b6b0351faa1f74bd5a917b14aa2e7128a691072
|
4
|
+
data.tar.gz: da939623b1714a74fc5d169138aa05811a9f77dc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6c8f1df4fb705e21ac0483e70d5a970eabf971b930adb4cd61135c854588b78fbfa31c28aef2dfc14f0edc42aec5c2c7d8ef90209d3e7ce8fdc23ffb00826ae7
|
7
|
+
data.tar.gz: 543b476b3a16346fc2438dbcb812925d3eac677198b1e864940cfd01ffdc66a2a2386725a8932be0f5c0e6111227852c84d86524e05e2a081d9216de66fa7a29
|
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# encoding: UTF-8
|
3
|
+
|
4
|
+
require 'objc_dependency_tree_generator'
|
5
|
+
|
6
|
+
# resolve options from command line
|
7
|
+
options = ObjCDependencyTreeGenerator.parse_command_line_options
|
8
|
+
|
9
|
+
# creating generator
|
10
|
+
generator = ObjCDependencyTreeGenerator.new options
|
11
|
+
|
12
|
+
puts generator.dependencies_to_s
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class ObjcDependenciesGenerator
|
2
|
+
|
3
|
+
def generate_dependencies(object_files_dir, include_dwarf_info)
|
4
|
+
|
5
|
+
#Searching all the .o files and showing its information through nm call
|
6
|
+
symbol_names_in_files_in_dir(object_files_dir) do |line|
|
7
|
+
# See of output here
|
8
|
+
# https://gist.github.com/PaulTaykalo/8d2618ea9741ea772004
|
9
|
+
|
10
|
+
# Capturing filename as a source and class name as the dest
|
11
|
+
match = /[^\w]*([^\.\/]+)\.o.*_OBJC_CLASS_\$_(.*)/.match(line)
|
12
|
+
next unless match
|
13
|
+
|
14
|
+
source, dest = match[1, 2]
|
15
|
+
|
16
|
+
yield source, dest
|
17
|
+
end
|
18
|
+
|
19
|
+
return unless include_dwarf_info
|
20
|
+
|
21
|
+
object_files_in_dir(object_files_dir) do |filename|
|
22
|
+
|
23
|
+
source = /.*\/(.+)\.o/.match(filename)[1]
|
24
|
+
yield source, nil
|
25
|
+
|
26
|
+
# Full output example https://gist.github.com/PaulTaykalo/62cd5d545301c8355cb5
|
27
|
+
# With grep output example https://gist.github.com/PaulTaykalo/9d5ecbce8a30a412cdbe
|
28
|
+
dwarfdump_tag_pointers_in_file(filename) do |tag_pointer_line|
|
29
|
+
|
30
|
+
# Finding the name in types
|
31
|
+
# AT_type( {0x00000456} ( objc_object ) )
|
32
|
+
tag_pointer_for_class = /.*?AT_type\(\s\{.*?\}.*\(\s((function|const)\s)?([A-Z][^\)]+?)\*?\s\).*/.match(tag_pointer_line)
|
33
|
+
next unless tag_pointer_for_class
|
34
|
+
|
35
|
+
dest = tag_pointer_for_class[3]
|
36
|
+
|
37
|
+
yield source, dest
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def symbol_names_in_files_in_dir(object_files_dir)
|
43
|
+
IO.popen("find \"#{object_files_dir}\" -name \"*.o\" -exec /usr/bin/nm -o {} \\;") { |f|
|
44
|
+
f.each { |line| yield line }
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def object_files_in_dir(object_files_dir)
|
49
|
+
IO.popen("find \"#{object_files_dir}\" -name \"*.o\"") { |f|
|
50
|
+
f.each { |line| yield line }
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def dwarfdump_tag_pointers_in_file(filename)
|
55
|
+
IO.popen("dwarfdump \"#{filename.strip}\" | grep -A1 TAG_pointer_type") { |fd|
|
56
|
+
fd.each { |line| yield line }
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'optparse'
|
3
|
+
require 'yaml'
|
4
|
+
require 'json'
|
5
|
+
require 'objc_dependency_tree_generator_helper'
|
6
|
+
require 'swift_dependencies_generator'
|
7
|
+
require 'objc_dependencies_generator'
|
8
|
+
|
9
|
+
class ObjCDependencyTreeGenerator
|
10
|
+
|
11
|
+
def initialize(options)
|
12
|
+
@options = options
|
13
|
+
@options[:derived_data_project_pattern] = '*-*' unless @options[:derived_data_project_pattern]
|
14
|
+
|
15
|
+
@exclusion_prefixes = @options[:exclusion_prefixes] ? @options[:exclusion_prefixes] : 'NS|UI|CA|CG|CI|CF'
|
16
|
+
@object_files_directory = @options[:search_directory]
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.parse_command_line_options
|
20
|
+
options = {}
|
21
|
+
|
22
|
+
#Defaults
|
23
|
+
options[:derived_data_paths] = ['~/Library/Developer/Xcode/DerivedData', '~/Library/Caches/appCode*/DerivedData']
|
24
|
+
options[:project_name] = ''
|
25
|
+
options[:output_format] = 'json'
|
26
|
+
|
27
|
+
|
28
|
+
parser = OptionParser.new do |o|
|
29
|
+
o.separator 'General options:'
|
30
|
+
|
31
|
+
o.on('-p PATH', 'Path to directory where are your .o files were placed by the compiler') { |directory|
|
32
|
+
options[:search_directory] = directory
|
33
|
+
}
|
34
|
+
o.on('-D DERIVED_DATA', 'Path to directory where DerivedData is') { |derived_data|
|
35
|
+
options[:derived_data_paths] = [derived_data]
|
36
|
+
options[:derived_data_project_pattern] = '*'
|
37
|
+
}
|
38
|
+
o.on('-s PROJECT_NAME', 'Search project .o files by specified project name') { |project_name|
|
39
|
+
options[:project_name] = project_name
|
40
|
+
}
|
41
|
+
o.on('-t TARGET_NAME', 'Target of project') { |target_name|
|
42
|
+
options[:target_name] = target_name
|
43
|
+
}
|
44
|
+
o.on('-e PREFIXES', "Prefixes of classes those will be exсluded from visualization. \n\t\t\t\t\tNS|UI\n\t\t\t\t\tUI|CA|MF") { |exclusion_prefixes|
|
45
|
+
options[:exclusion_prefixes] = exclusion_prefixes
|
46
|
+
}
|
47
|
+
|
48
|
+
o.on('-d', '--use-dwarf-info', 'Use DWARF Information also') { |v|
|
49
|
+
options[:use_dwarf] = v
|
50
|
+
}
|
51
|
+
o.on('-w', '--swift-dependencies', 'Generate swift project dependencies') { |v|
|
52
|
+
options[:swift_dependencies] = v
|
53
|
+
}
|
54
|
+
o.on('-f FORMAT', 'Output format. json by default. Possible values are [json-pretty|json|json-var|yaml]') { |f|
|
55
|
+
options[:output_format] = f
|
56
|
+
}
|
57
|
+
|
58
|
+
o.separator 'Common options:'
|
59
|
+
o.on_tail('-h', 'Prints this help') { puts o; exit }
|
60
|
+
o.parse!
|
61
|
+
end
|
62
|
+
|
63
|
+
options
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
def find_dependencies
|
68
|
+
if !@options or @options.empty?
|
69
|
+
return {}
|
70
|
+
end
|
71
|
+
|
72
|
+
unless @object_files_directory
|
73
|
+
@object_files_directory =
|
74
|
+
find_project_output_directory(@options[:derived_data_paths],
|
75
|
+
@options[:project_name],
|
76
|
+
@options[:derived_data_project_pattern],
|
77
|
+
@options[:target_name])
|
78
|
+
return {} unless @object_files_directory
|
79
|
+
end
|
80
|
+
|
81
|
+
unless @object_files_directory
|
82
|
+
puts parser.help
|
83
|
+
exit 1
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
links = {}
|
88
|
+
links_block = lambda { |source, dest|
|
89
|
+
links[source] = {} unless links[source]
|
90
|
+
if source != dest and is_valid_dest?(dest, @exclusion_prefixes)
|
91
|
+
links[source][dest] = 'set up'
|
92
|
+
end
|
93
|
+
}
|
94
|
+
|
95
|
+
if @options[:swift_dependencies]
|
96
|
+
SwiftDependenciesGenerator.new.generate_dependencies(@object_files_directory, &links_block)
|
97
|
+
else
|
98
|
+
ObjcDependenciesGenerator.new.generate_dependencies(@object_files_directory, @options[:use_dwarf], &links_block)
|
99
|
+
end
|
100
|
+
|
101
|
+
links
|
102
|
+
end
|
103
|
+
|
104
|
+
def dependencies_to_s
|
105
|
+
links = find_dependencies
|
106
|
+
s = ''
|
107
|
+
if @options[:output_format] == 'json-var'
|
108
|
+
s+= <<-THEEND
|
109
|
+
var dependencies =
|
110
|
+
THEEND
|
111
|
+
end
|
112
|
+
|
113
|
+
json_result = {}
|
114
|
+
json_links = []
|
115
|
+
|
116
|
+
links_count = 0
|
117
|
+
links.each do |source, dest_hash|
|
118
|
+
links_count = links_count + dest_hash.length
|
119
|
+
dest_hash.each do |dest, _|
|
120
|
+
json_links += [{'source' => source, 'dest' => dest}]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
json_result['links'] = json_links
|
124
|
+
json_result['source_files_count'] = links.length
|
125
|
+
json_result['links_count'] = links_count
|
126
|
+
|
127
|
+
|
128
|
+
s = s + JSON.pretty_generate(json_result) if @options[:output_format] == 'json-pretty'
|
129
|
+
s = s + json_result.to_json if @options[:output_format] == 'json' || @options[:output_format] == 'json-var'
|
130
|
+
s = s + json_result.to_yaml if @options[:output_format] == 'yaml'
|
131
|
+
s
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
def find_project_output_directory(derived_data_paths, project_prefix, project_suffix_pattern, target_name)
|
2
|
+
|
3
|
+
return nil unless derived_data_paths
|
4
|
+
|
5
|
+
paths = []
|
6
|
+
|
7
|
+
# looking for derived data
|
8
|
+
derived_data_paths.each do |derived_data_path|
|
9
|
+
IO.popen("find #{derived_data_path} -depth 1 -name \"#{project_prefix}#{project_suffix_pattern}\" -type d -exec find {} -name \"i386\" -o -name \"armv*\" -o -name \"x86_64\" -type d \\; ") { |f|
|
10
|
+
f.each do |line|
|
11
|
+
paths << line
|
12
|
+
end
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
$stderr.puts "There were #{paths.length} directories found"
|
17
|
+
if paths.empty?
|
18
|
+
$stderr.puts "Cannot find projects that starts with '#{project_prefix}'"
|
19
|
+
exit 1
|
20
|
+
end
|
21
|
+
|
22
|
+
filtered_by_target_paths = paths
|
23
|
+
|
24
|
+
if target_name
|
25
|
+
filtered_by_target_paths = paths.find_all { |path| /#{target_name}[^\.]*\.build\/Objects-normal/.match path }
|
26
|
+
$stderr.puts "After target filtration there is #{filtered_by_target_paths.length} directories left"
|
27
|
+
if paths.empty?
|
28
|
+
$stderr.puts "Cannot find projects that starts with '#{project_prefix}'' and has target name that starts with '#{target_name}'"
|
29
|
+
exit 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
paths_sorted_by_time = filtered_by_target_paths.sort_by { |f| File.ctime(f.chomp) }
|
34
|
+
|
35
|
+
last_modified_dir = paths_sorted_by_time.last.chomp
|
36
|
+
$stderr.puts "Last modifications were in\n#{last_modified_dir}\ndirectory at\n#{File.ctime(last_modified_dir)}"
|
37
|
+
|
38
|
+
last_modified_dir
|
39
|
+
end
|
40
|
+
|
41
|
+
def is_primitive_swift_type?(dest)
|
42
|
+
/^(BOOL|Int|Int32|Int64|Int16|Int8|UInt|UInt32|UInt64|UInt16|UInt8|String|Character|Bool|Float|Double|Dictionary|Array|Set|AnyObject|Void)$/.match(dest) != nil
|
43
|
+
end
|
44
|
+
|
45
|
+
def is_filtered_swift_type?(dest)
|
46
|
+
/(ClusterType|ScalarType|LiteralType)$/.match(dest) != nil #or /^([a-z])/.match(dest) != nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def is_filtered_objc_type?(dest)
|
50
|
+
/^(dispatch_)|(DISPATCH_)/.match(dest) != nil #or /^([a-z])/.match(dest) != nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def is_valid_dest?(dest, exclusion_prefixes)
|
54
|
+
dest != nil and /^(#{exclusion_prefixes})/.match(dest) == nil and /^(<\s)?\w/.match(dest) != nil and !is_primitive_swift_type?(dest) and !is_filtered_swift_type?(dest) and !is_filtered_objc_type?(dest)
|
55
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
class SwiftDependenciesGenerator
|
2
|
+
|
3
|
+
def generate_dependencies(object_files_dir)
|
4
|
+
# This thing need to be commented :) It's removes too many connections
|
5
|
+
# YAML.add_domain_type("", "private") { |type, val|
|
6
|
+
# 'AnyObject'
|
7
|
+
# }
|
8
|
+
|
9
|
+
swift_deps_files_in_dir(object_files_dir) do |my_text_file|
|
10
|
+
# puts my_text_file
|
11
|
+
begin
|
12
|
+
dependencies = YAML.load_file(my_text_file)
|
13
|
+
rescue Exception => e
|
14
|
+
$stderr.puts 'Cannot read file ' + my_text_file + ' : This is possibly because output file was changed:' + e.message
|
15
|
+
next
|
16
|
+
end
|
17
|
+
provided_objs = dependencies['provides']
|
18
|
+
top_level_deps = dependencies['top-level']
|
19
|
+
|
20
|
+
# support Xcode 7 format
|
21
|
+
provided_objs = dependencies['provides-top-level'] if provided_objs.nil?
|
22
|
+
top_level_deps = dependencies['depends-top-level'] if top_level_deps.nil?
|
23
|
+
|
24
|
+
next if provided_objs.nil?
|
25
|
+
next if top_level_deps.nil?
|
26
|
+
|
27
|
+
if provided_objs.length == 1
|
28
|
+
provided_objs.each do |source|
|
29
|
+
yield source, nil
|
30
|
+
top_level_deps.each do |dest|
|
31
|
+
yield source, dest unless provided_objs.include?(dest)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
else
|
36
|
+
|
37
|
+
filename = '< ' + File.basename(my_text_file, '.swiftdeps') +' >'
|
38
|
+
provided_objs.each do |source|
|
39
|
+
yield source, filename
|
40
|
+
end
|
41
|
+
|
42
|
+
yield filename, nil
|
43
|
+
|
44
|
+
top_level_deps.each do |dest|
|
45
|
+
yield filename, dest unless provided_objs.include?(dest)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def swift_deps_files_in_dir(object_files_dir)
|
52
|
+
Dir.glob("#{object_files_dir}/*.swiftdeps") { |file| yield file }
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
end
|
metadata
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: objc-dependency-tree-generator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.4
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Paul Taykalo
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-08-20 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: |
|
14
|
+
Tool that allows to generate Objective-C and Swift dependency tree from object files
|
15
|
+
For usages examples run:
|
16
|
+
objc_dependency_tree_generator -h
|
17
|
+
email: tt.kilew@gmail.com
|
18
|
+
executables:
|
19
|
+
- objc_dependency_tree_generator
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- lib/objc_dependency_tree_generator.rb
|
24
|
+
- lib/objc_dependency_tree_generator_helper.rb
|
25
|
+
- lib/objc_dependencies_generator.rb
|
26
|
+
- lib/swift_dependencies_generator.rb
|
27
|
+
- bin/objc_dependency_tree_generator
|
28
|
+
homepage: https://github.com/PaulTaykalo/objc-dependency-visualizer
|
29
|
+
licenses:
|
30
|
+
- MIT
|
31
|
+
metadata: {}
|
32
|
+
post_install_message:
|
33
|
+
rdoc_options: []
|
34
|
+
require_paths:
|
35
|
+
- lib
|
36
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
requirements: []
|
47
|
+
rubyforge_project:
|
48
|
+
rubygems_version: 2.0.14
|
49
|
+
signing_key:
|
50
|
+
specification_version: 4
|
51
|
+
summary: Objective-C and Swift dependency tree generator
|
52
|
+
test_files: []
|