cpp_dependency_graph 0.1.1 → 0.1.2

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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +2 -0
  3. data/.rubocop_todo.yml +21 -0
  4. data/.vscode/launch.json +1 -1
  5. data/Gemfile +1 -1
  6. data/README.md +36 -4
  7. data/Rakefile +2 -0
  8. data/TODO.md +10 -2
  9. data/bin/console +2 -0
  10. data/cpp_dependency_graph.gemspec +7 -6
  11. data/docs/README.md +12 -0
  12. data/examples/leveldb_cyclic_deps.svg +1 -0
  13. data/examples/rethinkdb_cyclic_deps.svg +1 -0
  14. data/exe/cpp_dependency_graph +14 -8
  15. data/lib/cpp_dependency_graph.rb +33 -15
  16. data/lib/cpp_dependency_graph/bidirectional_hash.rb +14 -13
  17. data/lib/cpp_dependency_graph/circle_packing_visualiser.rb +20 -0
  18. data/lib/cpp_dependency_graph/component_dependency_graph.rb +56 -0
  19. data/lib/cpp_dependency_graph/config.rb +8 -0
  20. data/lib/cpp_dependency_graph/cycle_detector.rb +8 -10
  21. data/lib/cpp_dependency_graph/cyclic_link.rb +12 -15
  22. data/lib/cpp_dependency_graph/dir_tree.rb +41 -0
  23. data/lib/cpp_dependency_graph/file_dependency_graph.rb +56 -0
  24. data/lib/cpp_dependency_graph/graph_to_dot_visualiser.rb +38 -0
  25. data/lib/cpp_dependency_graph/graph_to_graphml_visualiser.rb +8 -0
  26. data/lib/cpp_dependency_graph/graph_to_html_visualiser.rb +25 -0
  27. data/lib/cpp_dependency_graph/include_dependency_graph.rb +21 -6
  28. data/lib/cpp_dependency_graph/include_to_component_resolver.rb +58 -0
  29. data/lib/cpp_dependency_graph/link.rb +42 -0
  30. data/lib/cpp_dependency_graph/project.rb +12 -30
  31. data/lib/cpp_dependency_graph/source_component.rb +8 -7
  32. data/lib/cpp_dependency_graph/source_file.rb +4 -3
  33. data/lib/cpp_dependency_graph/tsortable_hash.rb +1 -0
  34. data/lib/cpp_dependency_graph/version.rb +1 -1
  35. data/views/circle_packing.html.template +105 -0
  36. metadata +51 -25
  37. data/lib/cpp_dependency_graph/component_link.rb +0 -37
  38. data/lib/cpp_dependency_graph/dependency_graph.rb +0 -49
  39. data/lib/cpp_dependency_graph/graph_to_html_converter.rb +0 -7
  40. data/lib/cpp_dependency_graph/graph_visualiser.rb +0 -59
@@ -2,12 +2,14 @@
2
2
 
3
3
  require 'find'
4
4
 
5
+ require_relative 'include_to_component_resolver'
5
6
  require_relative 'source_component'
6
7
 
7
8
  # Parses all components of a project
8
9
  class Project
9
10
  def initialize(path)
10
11
  @path = path
12
+ @include_resolver = IncludeToComponentResolver.new(source_components)
11
13
  end
12
14
 
13
15
  def source_components
@@ -15,15 +17,17 @@ class Project
15
17
  end
16
18
 
17
19
  def source_component(name)
18
- source_components.detect { |c| c.name == name }
20
+ return SourceComponent.new('NULL') unless source_components.key?(name)
21
+ source_components[name]
19
22
  end
20
23
 
21
24
  def dependencies(component)
22
- external_includes(component).map { |include| component_for_include(include) }.reject(&:empty?).uniq
25
+ # TODO: This is repeating the same work twice! component_for_include is called when calling external_includes
26
+ external_includes(component).map { |include| @include_resolver.component_for_include(include) }.reject(&:empty?).uniq
23
27
  end
24
28
 
25
29
  def external_includes(component)
26
- filter_internal_includes(component)
30
+ @include_resolver.external_includes(component)
27
31
  end
28
32
 
29
33
  private
@@ -31,33 +35,11 @@ class Project
31
35
  def build_source_components
32
36
  # TODO: Dealing with source components with same dir name?
33
37
  dirs = fetch_all_dirs(@path)
34
- source_components = dirs.map { |dir| SourceComponent.new(dir) }
35
- source_components.reject { |c| c.source_files.size.zero? }
36
- end
37
-
38
- def filter_internal_includes(component)
39
- # TODO: This is super inefficient, refactor it
40
- source_file_basenames = component.source_files.map(&:basename)
41
- include_components = component.includes.map { |inc| [inc, component_for_include(inc)] }.to_h
42
- filter = include_components.reject { |_, c| c == component.name }
43
- filter.keys
44
- end
45
-
46
- def component_for_include(include)
47
- header_file = source_files.find { |file| file.basename == include }
48
- parent_component(header_file)
49
- end
50
-
51
- def source_files
52
- @source_files ||= source_components.flat_map(&:source_files)
53
- end
54
-
55
- def parent_component(header_file)
56
- return '' if header_file.nil?
57
- files = source_files.select { |file| file.basename_no_extension == header_file.basename_no_extension }
58
- corresponding_files = files.reject { |file| file.basename == header_file.basename }
59
- return header_file.parent_component if corresponding_files.size == 0
60
- corresponding_files[0].parent_component
38
+ components = dirs.map do |dir|
39
+ c = SourceComponent.new(dir)
40
+ [c.name, c]
41
+ end.to_h
42
+ components.delete_if { |_, v| v.source_files.size.zero? }
61
43
  end
62
44
 
63
45
  def fetch_all_dirs(source_dir)
@@ -1,22 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'config'
3
4
  require_relative 'source_file'
4
5
 
6
+ # Abstracts a source directory containing source files
5
7
  class SourceComponent
8
+ include Config
9
+
10
+ attr_reader :path
11
+
6
12
  def initialize(path)
7
13
  @path = path
8
14
  end
9
15
 
10
- def path
11
- @path
12
- end
13
-
14
16
  def name
15
17
  @name ||= File.basename(@path)
16
18
  end
17
19
 
18
20
  def source_files
19
- @source_files ||= parse_source_files('.{h,hpp,hxx,c,cpp,cxx,cc}')
21
+ @source_files ||= parse_source_files(source_file_extensions)
20
22
  end
21
23
 
22
24
  def includes
@@ -31,7 +33,6 @@ class SourceComponent
31
33
 
32
34
  def parse_source_files(extensions)
33
35
  path = File.join(@path, File::SEPARATOR) + '*' + extensions
34
- files = Dir.glob(path).select { |e| File.file?(e) }
35
- files.map { |file| SourceFile.new(file) }
36
+ Dir.glob(path).map { |e| SourceFile.new(e) if File.file?(e) }.compact
36
37
  end
37
38
  end
@@ -19,7 +19,7 @@ class SourceFile
19
19
  end
20
20
 
21
21
  def header?
22
- false # TODO: Implement check extension
22
+ false # TODO: Implement check extension
23
23
  end
24
24
 
25
25
  def parent_component
@@ -41,8 +41,9 @@ class SourceFile
41
41
  end
42
42
 
43
43
  def scan_includes
44
- # file_contents.scan(/#include "([^"]+)"/).uniq.flatten
45
- file_contents.scan(/#include ["|<](.+)["|>]/).uniq.flatten # TODO: use compiler lib to scan includes? llvm/clang?
44
+ includes = file_contents.scan(/#include ["|<](.+)["|>]/) # TODO: use compiler lib to scan includes? llvm/clang?
45
+ includes.uniq!
46
+ includes.flatten
46
47
  end
47
48
 
48
49
  def file_contents
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'tsort'
4
4
 
5
+ # Hash that topologically sorts itself upon insertion of a key
5
6
  class TsortableHash < Hash
6
7
  include TSort
7
8
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CppDependencyGraph
4
- VERSION = '0.1.1'
4
+ VERSION = '0.1.2'
5
5
  end
@@ -0,0 +1,105 @@
1
+ <!DOCTYPE html>
2
+ <meta charset="utf-8">
3
+ <style>
4
+
5
+ .node {
6
+ cursor: pointer;
7
+ }
8
+
9
+ .node:hover {
10
+ stroke: #000;
11
+ stroke-width: 1.5px;
12
+ }
13
+
14
+ .node--leaf {
15
+ fill: white;
16
+ }
17
+
18
+ .label {
19
+ font: 11px "Helvetica Neue", Helvetica, Arial, sans-serif;
20
+ text-anchor: middle;
21
+ text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff, 0 -1px 0 #fff;
22
+ }
23
+
24
+ .label,
25
+ .node--root,
26
+ .node--leaf {
27
+ pointer-events: none;
28
+ }
29
+
30
+ </style>
31
+ <svg width="960" height="960"></svg>
32
+ <script src="https://d3js.org/d3.v4.min.js"></script>
33
+ <script>
34
+
35
+ var svg = d3.select("svg"),
36
+ margin = 20,
37
+ diameter = +svg.attr("width"),
38
+ g = svg.append("g").attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
39
+
40
+ var color = d3.scaleLinear()
41
+ .domain([-1, 5])
42
+ .range(["hsl(152,80%%,80%%)", "hsl(228,30%%,40%%)"])
43
+ .interpolate(d3.interpolateHcl);
44
+
45
+ var pack = d3.pack()
46
+ .size([diameter - margin, diameter - margin])
47
+ .padding(2);
48
+
49
+ var root = %{tree};
50
+
51
+ root = d3.hierarchy(root)
52
+ .sum(function(d) { return d.size; })
53
+ .sort(function(a, b) { return b.value - a.value; });
54
+
55
+ var focus = root,
56
+ nodes = pack(root).descendants(),
57
+ view;
58
+
59
+ var circle = g.selectAll("circle")
60
+ .data(nodes)
61
+ .enter().append("circle")
62
+ .attr("class", function(d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; })
63
+ .style("fill", function(d) { return d.children ? color(d.depth) : null; })
64
+ .on("click", function(d) { if (focus !== d) zoom(d), d3.event.stopPropagation(); });
65
+
66
+ var text = g.selectAll("text")
67
+ .data(nodes)
68
+ .enter().append("text")
69
+ .attr("class", "label")
70
+ .style("fill-opacity", function(d) { return d.parent === root ? 1 : 0; })
71
+ .style("display", function(d) { return d.parent === root ? "inline" : "none"; })
72
+ .text(function(d) { return d.data.name; });
73
+
74
+ var node = g.selectAll("circle,text");
75
+
76
+ svg
77
+ .style("background", color(-1))
78
+ .on("click", function() { zoom(root); });
79
+
80
+ zoomTo([root.x, root.y, root.r * 2 + margin]);
81
+
82
+ function zoom(d) {
83
+ var focus0 = focus; focus = d;
84
+
85
+ var transition = d3.transition()
86
+ .duration(d3.event.altKey ? 7500 : 750)
87
+ .tween("zoom", function(d) {
88
+ var i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2 + margin]);
89
+ return function(t) { zoomTo(i(t)); };
90
+ });
91
+
92
+ transition.selectAll("text")
93
+ .filter(function(d) { return d.parent === focus || this.style.display === "inline"; })
94
+ .style("fill-opacity", function(d) { return d.parent === focus ? 1 : 0; })
95
+ .on("start", function(d) { if (d.parent === focus) this.style.display = "inline"; })
96
+ .on("end", function(d) { if (d.parent !== focus) this.style.display = "none"; });
97
+ }
98
+
99
+ function zoomTo(v) {
100
+ var k = diameter / v[2]; view = v;
101
+ node.attr("transform", function(d) { return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")"; });
102
+ circle.attr("r", function(d) { return d.r * k; });
103
+ }
104
+
105
+ </script>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cpp_dependency_graph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shreyas Balakrishna
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-04-05 00:00:00.000000000 Z
11
+ date: 2018-04-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: docopt
@@ -81,75 +81,75 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '1.16'
83
83
  - !ruby/object:Gem::Dependency
84
- name: coveralls
84
+ name: debase
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0.8'
89
+ version: '0.2'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0.8'
96
+ version: '0.2'
97
97
  - !ruby/object:Gem::Dependency
98
- name: debase
98
+ name: rake
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0.2'
103
+ version: '12.3'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0.2'
110
+ version: '12.3'
111
111
  - !ruby/object:Gem::Dependency
112
- name: rake
112
+ name: rspec
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: '12.3'
117
+ version: '3.7'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: '12.3'
124
+ version: '3.7'
125
125
  - !ruby/object:Gem::Dependency
126
- name: rspec
126
+ name: rubocop
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: '3.7'
131
+ version: '0.55'
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: '3.7'
138
+ version: '0.55'
139
139
  - !ruby/object:Gem::Dependency
140
- name: rubocop
140
+ name: ruby-debug-ide
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
143
  - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: '0.54'
145
+ version: '0.6'
146
146
  type: :development
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
- version: '0.54'
152
+ version: '0.6'
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: ruby-prof
155
155
  requirement: !ruby/object:Gem::Requirement
@@ -165,30 +165,46 @@ dependencies:
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0.17'
167
167
  - !ruby/object:Gem::Dependency
168
- name: ruby-debug-ide
168
+ name: simplecov
169
169
  requirement: !ruby/object:Gem::Requirement
170
170
  requirements:
171
171
  - - "~>"
172
172
  - !ruby/object:Gem::Version
173
- version: '0.6'
173
+ version: '0.16'
174
174
  type: :development
175
175
  prerelease: false
176
176
  version_requirements: !ruby/object:Gem::Requirement
177
177
  requirements:
178
178
  - - "~>"
179
179
  - !ruby/object:Gem::Version
180
- version: '0.6'
180
+ version: '0.16'
181
+ - !ruby/object:Gem::Dependency
182
+ name: simplecov-console
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: '0.4'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: '0.4'
181
195
  description: " Generates interactive dependency visualisations (dot, d3.js) to study
182
196
  the architecture of C/C++ projects in detail\n"
183
197
  email:
184
- - shreyasbharath@users.noreply.github.com>
198
+ - shreyasbharath@gmail.com
185
199
  executables:
186
200
  - cpp_dependency_graph
187
201
  extensions: []
188
202
  extra_rdoc_files: []
189
203
  files:
204
+ - ".gitattributes"
190
205
  - ".gitignore"
191
206
  - ".rspec"
207
+ - ".rubocop_todo.yml"
192
208
  - ".vscode/launch.json"
193
209
  - ".vscode/tasks.json"
194
210
  - CODE_OF_CONDUCT.md
@@ -200,10 +216,13 @@ files:
200
216
  - bin/console
201
217
  - bin/setup
202
218
  - cpp_dependency_graph.gemspec
219
+ - docs/README.md
220
+ - examples/leveldb_cyclic_deps.svg
203
221
  - examples/leveldb_overall.png
204
222
  - examples/leveldb_overall.svg
205
223
  - examples/leveldb_overall_d3.png
206
224
  - examples/leveldb_overall_d3.svg
225
+ - examples/rethinkdb_cyclic_deps.svg
207
226
  - examples/rethinkdb_queue_component.png
208
227
  - examples/rethinkdb_queue_component.svg
209
228
  - examples/rethinkdb_queue_component_d3.png
@@ -217,18 +236,25 @@ files:
217
236
  - exe/cpp_dependency_graph
218
237
  - lib/cpp_dependency_graph.rb
219
238
  - lib/cpp_dependency_graph/bidirectional_hash.rb
220
- - lib/cpp_dependency_graph/component_link.rb
239
+ - lib/cpp_dependency_graph/circle_packing_visualiser.rb
240
+ - lib/cpp_dependency_graph/component_dependency_graph.rb
241
+ - lib/cpp_dependency_graph/config.rb
221
242
  - lib/cpp_dependency_graph/cycle_detector.rb
222
243
  - lib/cpp_dependency_graph/cyclic_link.rb
223
- - lib/cpp_dependency_graph/dependency_graph.rb
224
- - lib/cpp_dependency_graph/graph_to_html_converter.rb
225
- - lib/cpp_dependency_graph/graph_visualiser.rb
244
+ - lib/cpp_dependency_graph/dir_tree.rb
245
+ - lib/cpp_dependency_graph/file_dependency_graph.rb
246
+ - lib/cpp_dependency_graph/graph_to_dot_visualiser.rb
247
+ - lib/cpp_dependency_graph/graph_to_graphml_visualiser.rb
248
+ - lib/cpp_dependency_graph/graph_to_html_visualiser.rb
226
249
  - lib/cpp_dependency_graph/include_dependency_graph.rb
250
+ - lib/cpp_dependency_graph/include_to_component_resolver.rb
251
+ - lib/cpp_dependency_graph/link.rb
227
252
  - lib/cpp_dependency_graph/project.rb
228
253
  - lib/cpp_dependency_graph/source_component.rb
229
254
  - lib/cpp_dependency_graph/source_file.rb
230
255
  - lib/cpp_dependency_graph/tsortable_hash.rb
231
256
  - lib/cpp_dependency_graph/version.rb
257
+ - views/circle_packing.html.template
232
258
  - views/index.html.template
233
259
  homepage: https://github.com/shreyasbharath/cpp_dependency_graph
234
260
  licenses: