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: []