codegraph 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/LICENSE +27 -0
  2. data/bin/codegraph +174 -0
  3. data/gemspec +21 -0
  4. data/lib/codegraph.rb +280 -0
  5. metadata +79 -0
data/LICENSE ADDED
@@ -0,0 +1,27 @@
1
+ Copyright (c) 2009, Ralf Mueller (stark.dreamdetective@googlemail.com)
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice,
8
+ this list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright
11
+ notice, this list of conditions and the following disclaimer in the
12
+ documentation and/or other materials provided with the distribution.
13
+
14
+ * The names of its contributors may not be used to endorse or promote
15
+ products derived from this software without specific prior written
16
+ permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
22
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/bin/codegraph ADDED
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env ruby
2
+ require "codegraph"
3
+ require "getoptlong"
4
+
5
+ def show_usage
6
+ puts <<-END
7
+ Usage: #{__FILE__}:
8
+ --help, -h guess what
9
+
10
+ --file-list, -F "<list>"
11
+ fill the graph with every function,
12
+ that has a definiton inside the given files
13
+
14
+ --function, -f "<funcname>"
15
+ take the given func as the root knode
16
+
17
+ --show-body, -s "<funcname>"
18
+ display the function code in the terminal
19
+
20
+ --upper-funx, -u "<funcname>"
21
+ scan for all funx, which depends directly or
22
+ indirectly on <funcname>
23
+
24
+ --8-funx, -8 "<funcname>"
25
+ scan for all funx, which depends (in)directly
26
+ <funcname> AND which call <funcname>
27
+ (in)directly
28
+
29
+ --exclude, -x "<list>"
30
+ exclude a list of functions from the graph
31
+
32
+ --depth, -d <integer>
33
+ Set maximal Graph depth
34
+
35
+ --to-ps, -p <filename>
36
+ create a postscript file from the graph
37
+
38
+ --to-svg, -S <filename>
39
+ create a SVG file from the graph
40
+
41
+ --to-png, -P <filename>
42
+ create a PNG file from the graph
43
+
44
+ --to-jpg, -J <filename>
45
+ create a JPG file from the graph
46
+
47
+ --to-dot, -D <filename>
48
+ creates a <filename>.dot-file
49
+
50
+ --to-txt, -T
51
+ writes the relations to stdout
52
+ END
53
+ end
54
+ # Which options are availabile ------------------------------------------------
55
+ options = GetoptLong.new(
56
+ ["--help", "-h", GetoptLong::NO_ARGUMENT],
57
+ # ["--local", "-l", GetoptLong::NO_ARGUMENT],
58
+ # ["--source", "-s", GetoptLong::NO_ARGUMENT],
59
+ # ["--with-internal", "-w", GetoptLong::NO_ARGUMENT],
60
+ ["--function", "-f", GetoptLong::REQUIRED_ARGUMENT],
61
+ ["--show-body", "-s", GetoptLong::REQUIRED_ARGUMENT],
62
+ ["--upper-funx", "-u", GetoptLong::REQUIRED_ARGUMENT],
63
+ ["--8-funx", "-8", GetoptLong::REQUIRED_ARGUMENT],
64
+ ["--file-list", "-F", GetoptLong::REQUIRED_ARGUMENT],
65
+ ["--depth", "-d", GetoptLong::REQUIRED_ARGUMENT],
66
+ ["--to-ps", "-p", GetoptLong::REQUIRED_ARGUMENT],
67
+ ["--to-svg", "-S", GetoptLong::REQUIRED_ARGUMENT],
68
+ ["--to-png", "-P", GetoptLong::REQUIRED_ARGUMENT],
69
+ ["--to-jpg", "-J", GetoptLong::REQUIRED_ARGUMENT],
70
+ ["--to-dot", "-D", GetoptLong::REQUIRED_ARGUMENT],
71
+ ["--to-txt", "-T", GetoptLong::NO_ARGUMENT],
72
+ ["--exclude", "-x", GetoptLong::REQUIRED_ARGUMENT],
73
+ )
74
+ options.ordering = GetoptLong::RETURN_IN_ORDER
75
+ # Default values --------------------------------------------------------------
76
+ filelist = ""
77
+ mode = "multiple"
78
+ output = ""
79
+ internal = false
80
+ filename = ""
81
+ dotfile = ""
82
+ function = ""
83
+ depth = nil
84
+ exclude = Array.new
85
+ files = Array.new
86
+ # option parsing --------------------------------------------------------------
87
+ options.each do |opt, arg|
88
+ case opt
89
+ when "--file-list"
90
+ filelist = arg
91
+ files = Dir.glob(filelist.split(' '))
92
+ when "--function"
93
+ mode = "single"
94
+ function = arg
95
+ when "--show-body"
96
+ mode = "show"
97
+ function = arg
98
+ when "--upper-funx"
99
+ mode = "upper"
100
+ function = arg
101
+ when "--8-funx"
102
+ mode = 8
103
+ function = arg
104
+ when "--depth"
105
+ depth = arg.to_i
106
+ when "--to-ps"
107
+ output = "ps"
108
+ filename = arg
109
+ when "--to-svg"
110
+ output = "svg"
111
+ filename = arg
112
+ when "--to-png"
113
+ output = "png"
114
+ filename = arg
115
+ when "--to-jpg"
116
+ output = "jpg"
117
+ filename = arg
118
+ when "--to-dot"
119
+ output = "dot"
120
+ filename = arg
121
+ when "--to-txt"
122
+ output = "txt"
123
+ when "--exclude"
124
+ exclude = arg.split(/ /)
125
+ else
126
+ show_usage
127
+ exit
128
+ end
129
+ end
130
+ options.terminate
131
+ # script controlling ----------------------------------------------------------
132
+ # Is the filelist correct?
133
+ if files.size == 0
134
+ puts 'Please use --file-list "<list>"'
135
+ show_usage
136
+ exit
137
+ end
138
+
139
+ # What kind of graph should be generated?
140
+ case mode
141
+ when "upper"
142
+ g = UpperFunctionGraph.new(function)
143
+ when "single","show"
144
+ g = SingleFunctionGraph.new(function)
145
+ when 8
146
+ g = EightFunctionGraph.new(function)
147
+ else
148
+ g = FunctionGraph.new
149
+ end
150
+
151
+ # Include the internal functions of the language?
152
+ g.internal = internal
153
+
154
+ # Creates the Knodes/Edges of the graph
155
+ g.fill(files,exclude)
156
+
157
+ # Remove edges
158
+ g.limit(depth) if depth
159
+
160
+ # What sort of output should be created?
161
+ case output
162
+ when "ps", "svg", "png", "jpg"
163
+ g.to_type(filename,output)
164
+ when "dot"
165
+ g.to_dot(filename)
166
+ when "txt"
167
+ g.to_txt
168
+ else
169
+ if mode == "show"
170
+ puts g.display_functionbody(function)
171
+ else
172
+ g.display
173
+ end
174
+ end
data/gemspec ADDED
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+
3
+ spec = Gem::Specification.new do |s|
4
+ s.name = "codegraph"
5
+ s.version = "0.7.1"
6
+ s.date = "2010-11-01"
7
+ s.platform = Gem::Platform::RUBY
8
+ s.bindir = 'bin'
9
+ s.files = ["lib/codegraph.rb","bin/codegraph"] + ["gemspec","LICENSE"]
10
+ s.executables << 'codegraph'
11
+ s.add_dependency('rgl')
12
+ s.rubyforge_project = "codegraph"
13
+ s.description = "Display functional dependencies in source code (C, Fortran, PHP, Perl)."
14
+ s.summary = "Display functional dependencies in source code (C, Fortran, PHP, Perl)"
15
+ s.author = "Ralf Mueller"
16
+ s.email = "stark.dreamdetective@gmail.com"
17
+ s.homepage = "http://codegraph.rubyforge.org"
18
+ s.has_rdoc = true
19
+ end
20
+
21
+ # vim:ft=ruby
data/lib/codegraph.rb ADDED
@@ -0,0 +1,280 @@
1
+ require 'rubygems'
2
+ require 'rgl/adjacency'
3
+ require 'rgl/dot'
4
+ require 'rgl/rdot'
5
+ require 'rgl/traversal'
6
+
7
+ class FunctionGraph < RGL::DirectedAdjacencyGraph
8
+ attr_accessor :funx, :internal
9
+
10
+ # Theses Parameters are used by interactive representation and for the file
11
+ # generation function to_dot and to_type. They can only be given for the
12
+ # whole graph, not for a singel node. This could be done by extending the
13
+ # dot.rb file of the Ruby Graph Library
14
+ Params = {'rankdir' => 'LR',
15
+ 'ranksep' => '4.0',
16
+ 'concentrate' => 'TRUE',
17
+ 'label' => '',
18
+ 'fontsize' => '12'}
19
+
20
+ # Hash of supported langs and the ctags marker for functions
21
+ Types = {"php" => "f",
22
+ "perl" => "s",
23
+ "c" => "f",
24
+ "f" => "fs",
25
+ "sh" => "f"}
26
+
27
+ # Hash of how ctags calls function for each lang
28
+ Funx = {"php" => "function",
29
+ "perl" => "subroutine",
30
+ "c" => "function",
31
+ "f" => "subroutine",
32
+ "sh" => "function"}
33
+
34
+ @@home = `echo $HOME`.chomp
35
+ @@codehomedir = "#{@@home}/.codegraph"
36
+ @@internal_funx = []
37
+ @@matchBeforFuncName = '[^A-z0-9_]'
38
+ @@matchAfterFuncName = '\('
39
+
40
+ # Generate the codegraph storage directory
41
+ if not FileTest.directory?(@@codehomedir)
42
+ system("mkdir #{@@codehomedir}")
43
+ end
44
+
45
+ def initialize
46
+ super
47
+ # the following attribute will hold the functionnames and their bodies
48
+ @funx = Hash.new
49
+
50
+ # Should the internal functions be displayed?
51
+ @internal = false
52
+
53
+ end
54
+
55
+ # Generates the necessary files:
56
+ # 1. One for the whole source code
57
+ # 2. One for functions inside the file 1
58
+ # and fill the @funx hash
59
+ def genFiles(graph, filelist, exclude=[])
60
+ names, kinds, lines, files = [],[],[],[]
61
+
62
+ funxFile = "funxnames"
63
+ ctagsKinds='--c-kinds=f --fortran-kinds=fs --php-kinds=f --perl-kinds=f'
64
+ filelist.each {|file|
65
+ puts "Processing #{file} ..."
66
+ basefile = File.basename(file)
67
+ temfile = "_" + basefile
68
+ case File.extname(file)
69
+ when '.c','.h'
70
+ cppCommand = "cpp -w -fpreprocessed -E -fdirectives-only #{file} -o #{@@codehomedir}/#{temfile}"
71
+ grep = "grep -v -e '^$' #{temfile} | grep -v -e '^#' > #{basefile}"
72
+ when '.f','.f77','.f90','.f95'
73
+ cppCommand = "cp #{file} #{@@codehomedir}/#{temfile}"
74
+ grep = "grep -v -e '^$' #{temfile} | grep -v -e '^ *!' > #{basefile}"
75
+ else
76
+ cppCommand = "cp #{file} #{@@codehomedir}/#{temfile}"
77
+ grep = "grep -v -e '^$' #{temfile} | grep -v -e '^#' > #{basefile}"
78
+ end
79
+ cd = "cd #{@@codehomedir}"
80
+ gen4ctags = "ctags -x #{ctagsKinds} #{basefile} | sort -n -k 3 > " + funxFile
81
+ command = [cppCommand,cd,grep,gen4ctags].join(";")
82
+
83
+ puts command
84
+ system(command)
85
+
86
+ open(@@codehomedir+'/'+funxFile).readlines.each {|line|
87
+ name, kind, lineno, file = line.split[0,4]
88
+ names << name
89
+ kinds << kind
90
+ lines << lineno.to_i
91
+ files << file
92
+ }
93
+ }
94
+
95
+ unless File.size?(@@codehomedir+'/'+funxFile)
96
+ puts "no functions found"
97
+ exit
98
+ end
99
+
100
+ meta = [names, kinds, lines, files]
101
+
102
+ # read the source once and save the function bodies as values
103
+ # of the function names
104
+
105
+ metasize = meta[2].size
106
+ lastfile,code = nil,nil
107
+ meta.transpose.each_with_index {|ary,i|
108
+ name, kind, line, file = ary
109
+
110
+ if file != lastfile
111
+ code = open(@@codehomedir+'/'+ File.basename(file)).readlines
112
+ codeLength = code.length
113
+ end
114
+ startLineIndex = meta[2][i]-1
115
+ endLineIndex = (i+1 < metasize) ? meta[2][i+1] - 2 : -1
116
+ # code-Index starts with 0 vs. line numbbers in (meta|m).lines start with 1
117
+ body = code[startLineIndex..endLineIndex].join
118
+
119
+ @funx.store(name,body)
120
+ lastfile = file
121
+ }
122
+ end
123
+
124
+ # fill the graph with all functions found in <filelist>
125
+ # while all functions from <exclude> aren't recognized
126
+ def fill(filelist,exclude=[])
127
+ # generate the necessary files and fill @funx
128
+ genFiles(self,filelist,exclude)
129
+
130
+ # scan functions for the function names
131
+ names = @funx.keys
132
+ @funx.each_pair {|name,body|
133
+ add_vertex(name)
134
+ (names - [name] + @@internal_funx).each { |func|
135
+ add_edge("#{name}","#{func}") if /#@@matchBeforFuncName#{func}/.match(body)
136
+ }
137
+ }
138
+ end
139
+
140
+ def limit(depth)
141
+ dv = RGL::DFSVisitor.new(self)
142
+ dv.attach_distance_map
143
+ self.depth_first_search(dv) {|u|
144
+ self.remove_vertex(u) if dv.distance_to_root(u) > depth
145
+ }
146
+ end
147
+
148
+ def display_functionbody(name)
149
+ @funx[name]
150
+ end
151
+ # Creates a simple dot file according to the above <Params>.
152
+ # Parameters for the nodes are not supported by rgl.
153
+ def to_dot(filename)
154
+ File.open("#{filename}","w") {|f|
155
+ print_dotted_on(Params,f)
156
+ }
157
+ end
158
+
159
+ # Generates pairs of "func_A -> func_B" to stdout
160
+ def to_txt
161
+ each_edge do |left,right|
162
+ print left,' -> ',right,"\n"
163
+ end
164
+ end
165
+
166
+ # This function generates a file of the given type using the dot utility.
167
+ # Supported Types are PS, PNG, JPG, DOT and SVG.
168
+ def to_type(filename,type)
169
+ if File.exist?(filename)
170
+ system("rm #{filename}")
171
+ end
172
+ if File.exist?(filename+"."+type)
173
+ system("rm #{filename}."+type)
174
+ end
175
+ to_dot(filename+".dot")
176
+ system("dot -T#{type} -o #{filename} -Nshape=box #{filename}.dot")
177
+ system("rm #{filename}.dot")
178
+ end
179
+
180
+ # Display the graph with an interactive viewer
181
+ def display
182
+ dotty(Params)
183
+ system("rm graph.dot") if File.exist?("graph.dot")
184
+ end
185
+ private :genFiles
186
+ end
187
+
188
+ class SingleFunctionGraph < FunctionGraph
189
+ attr_accessor :func
190
+
191
+ # Constructor, which creates an empty graph for the rootfunction <func>
192
+ def initialize(func)
193
+ # Holds the func'n names, that are allready scanned
194
+ @scannednames = []
195
+ # Root func
196
+ @func = func
197
+ super()
198
+ end
199
+
200
+ # Works like the one from FunctionGraph except, that it calls 'scan'
201
+ # for the recursive descent throug all functions given in <filelist> - exclude
202
+ def fill(filelist,exclude=[])
203
+ genFiles(self,filelist,exclude)
204
+ scan(self, func)
205
+ end
206
+
207
+ # For the given root function f, scan walks through the graph, and finds any
208
+ # other function, that calls f
209
+ def scan(graph,f)
210
+ if (@scannednames.include?(f))
211
+ else
212
+ names = graph.funx.keys
213
+ if not names.include?(func)
214
+ puts "Function #{func} not found."
215
+ exit
216
+ end
217
+ @scannednames << f
218
+ body = graph.funx[f]
219
+ # scan for any other function in the body of f
220
+ (names - [f] + @@internal_funx).each {|g|
221
+ if /#@@matchBeforFuncName#{g}#@@matchAfterFuncName/.match(body)
222
+ graph.add_edge(f,g)
223
+ # unless its not an internal function, go downstairs
224
+ scan(graph,g) if names.include?(g)
225
+ end
226
+ }
227
+ end
228
+ end
229
+ private :scan
230
+ end
231
+
232
+ class UpperFunctionGraph < SingleFunctionGraph
233
+
234
+ # scanning upwards unlike SingleFunctionGraph.scan
235
+ def scan(graph,func)
236
+ if @scannednames.include?(func)
237
+ else
238
+ if not (graph.funx.keys + @@internal_funx).include?(func)
239
+ puts "Function '#{func}' not found. If this is an internal function, " +
240
+ "please try again with the '-w' option to include the internal " +
241
+ "funx before scanning."
242
+ #system("rm ~/.codegraph/all.#{ng} ~/.codegraph/code-names")
243
+ exit
244
+ end
245
+ @scannednames << func
246
+ graph.funx.each_pair {|g,gbody|
247
+ # dont scan a function for itself
248
+ next if g == func
249
+
250
+ if/#@@matchBeforFuncName#{func}#@@matchAfterFuncName/.match(gbody)
251
+ graph.add_edge(g,func)
252
+ scan(graph,g)
253
+ end
254
+ }
255
+ end
256
+ end
257
+ private :scan
258
+ end
259
+
260
+ class EightFunctionGraph < FunctionGraph
261
+ def initialize(func)
262
+ super()
263
+ @func = func
264
+ end
265
+ def fill(*args)
266
+ g_down = SingleFunctionGraph.new(@func)
267
+ g_down.fill(*args)
268
+
269
+ g_up = UpperFunctionGraph.new(@func)
270
+ g_up.fill(*args)
271
+
272
+ g_down.each_edge do |u,v|
273
+ self.add_edge(u,v)
274
+ end
275
+ g_up.each_edge do |u,v|
276
+ self.add_edge(u,v)
277
+ end
278
+ end
279
+ end
280
+
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: codegraph
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 7
8
+ - 1
9
+ version: 0.7.1
10
+ platform: ruby
11
+ authors:
12
+ - Ralf Mueller
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-11-01 00:00:00 +01:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rgl
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ description: Display functional dependencies in source code (C, Fortran, PHP, Perl).
34
+ email: stark.dreamdetective@gmail.com
35
+ executables:
36
+ - codegraph
37
+ extensions: []
38
+
39
+ extra_rdoc_files: []
40
+
41
+ files:
42
+ - lib/codegraph.rb
43
+ - bin/codegraph
44
+ - gemspec
45
+ - LICENSE
46
+ has_rdoc: true
47
+ homepage: http://codegraph.rubyforge.org
48
+ licenses: []
49
+
50
+ post_install_message:
51
+ rdoc_options: []
52
+
53
+ require_paths:
54
+ - lib
55
+ required_ruby_version: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ requirements: []
72
+
73
+ rubyforge_project: codegraph
74
+ rubygems_version: 1.3.7
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: Display functional dependencies in source code (C, Fortran, PHP, Perl)
78
+ test_files: []
79
+