objc-dependency-tree-generator 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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: []