codegraph 0.7.1

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 (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
+