codegraph 0.7.18 → 0.7.20

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/bin/codegraph +54 -80
  2. data/gemspec +2 -4
  3. data/lib/codegraph.rb +261 -269
  4. metadata +31 -63
data/bin/codegraph CHANGED
@@ -2,14 +2,16 @@
2
2
  # Which options are availabile ------------------------------------------------
3
3
  # Default settings
4
4
  $options = {
5
- :files => [],
5
+ :filelist => [],
6
6
  :mode => nil,
7
- :output => "",
8
7
  :function => "",
9
8
  :depth => nil,
10
9
  :adds => [],
11
10
  :excludes => [],
12
- :debug => false
11
+ :cluster => false,
12
+ :debug => false,
13
+ :boxes => false,
14
+ :rotate => false
13
15
  }
14
16
 
15
17
  require "codegraph"
@@ -18,71 +20,54 @@ require "optparse"
18
20
  def parse(args)
19
21
  OptionParser.new do |opts|
20
22
  opts.banner = "Usage: codegraph [options]"
21
- opts.on("-y", "--debug","Work in debuging mode") do |debug|
22
- $options[:debug] = debug
23
+ opts.on("-d", "--debug","Work in debuging mode") do
24
+ $options[:debug] = true
23
25
  end
24
26
  opts.on( "-a","--add f1,f2,f3",Array,"Add function for scanning") do |adds|
25
27
  $options[:adds] = adds
26
28
  end
29
+ opts.on( "-x","--exclude f1,f2,f3",Array,"Exclude function from scanning") do |excludes|
30
+ $options[:excludes] = excludes
31
+ end
27
32
  opts.on("-f", "--function FUNCTION") do |func|
28
33
  $options[:function] = func
29
34
  $options[:mode] = 'single'
30
35
  end
31
- opts.on( "-s","--show-body","Display function body") do |show|
32
- $options[:mode] = 'show'
33
- end
34
36
  opts.on("-u", "--upper-funx FUNCTION") do |func|
35
37
  $options[:function] = func
36
38
  $options[:mode] = 'upper'
37
39
  end
38
- opts.on("-8", "--8-funx FUNCTION") do |func|
39
- $options[:function] = func
40
- $options[:mode] = 8
40
+ opts.on("-c","--with-files-cluster","Show filenames in graph") do
41
+ $options[:cluster] = true
41
42
  end
42
- opts.on("-C", "--ctags-opts KIND",String) do |opts|
43
- $options[:ctagsopts] = opts
43
+ opts.on("-r","--rotate","rotate graph 90deg [default:false]") do
44
+ $options[:rotate] = true
44
45
  end
45
- opts.on("--match-before REGEXP",String) do |regexp|
46
- $options[:matchBefore] = regexp
47
- end
48
- opts.on("--match-after REGEXP",String) do |regexp|
49
- $options[:matchAfter] = regexp
46
+ opts.on("-b","--with-boxes","use boxes instead of ovals") do
47
+ $options[:boxes] = true
50
48
  end
49
+ # opts.on("-C", "--ctags-opts KIND",String) do |opts|
50
+ # $options[:ctagsopts] = opts
51
+ # end
52
+ # opts.on("--match-before REGEXP",String) do |regexp|
53
+ # $options[:matchBefore] = regexp
54
+ # end
55
+ # opts.on("--match-after REGEXP",String) do |regexp|
56
+ # $options[:matchAfter] = regexp
57
+ # end
51
58
  opts.on("-F", "--file-list file0,file1,file3", Array,"List of files (alternative \"file0 file1 file3\")") do |filelist|
52
59
  filelist.each do |filedesc|
53
60
  unless File.exist?(filedesc)
54
- $options[:files] << Dir.glob(File.expand_path(filedesc))
61
+ $options[:filelist] << Dir.glob(File.expand_path(filedesc))
55
62
  else
56
- $options[:files] = filelist
63
+ $options[:filelist] = filelist
57
64
  end
58
65
  end
59
- $options[:files].flatten!
60
- end
61
- opts.on("-d", "--depth DEPTH","Set the maximum depth of the graph") do |depth|
62
- $options[:depth] = depth
63
- end
64
- opts.on("-p", "--to-ps FILE", "Write Postscripts output file") do |ofile|
65
- $options[:ofile] = ofile
66
- $options[:output] = 'ps'
67
- end
68
- opts.on("-S", "--to-svg FILE", "Write Scalable Vector Graphic output file") do |ofile|
69
- $options[:ofile] = ofile
70
- $options[:output] = 'svg'
71
- end
72
- opts.on("-P", "--to-png FILE", "Write Portable Network Graphic output file") do |ofile|
73
- $options[:ofile] = ofile
74
- $options[:output] = 'png'
75
- end
76
- opts.on("-J", "--to-jpg FILE", "Write JPEG output file") do |ofile|
77
- $options[:ofile] = ofile
78
- $options[:output] = 'jpg'
79
- end
80
- opts.on("-D", "--to-dot FILE", "Write dot output file") do |ofile|
81
- $options[:ofile] = ofile
82
- $options[:output] = 'dot'
66
+ $options[:filelist].flatten!
83
67
  end
84
- opts.on("-T", "--to-txt","Write to stdout") do
85
- $options[:output] = 'txt'
68
+ opts.on("-o", "--output FILE", "Write image output file (supported formats: ps,png,jpw,svg,..., see 'man dot')") do |ofile|
69
+ $options[:otype] = File.extname(ofile)[1..-1]
70
+ $options[:ofile] = ofile[0..(-$options[:otype].size-2)]
86
71
  end
87
72
  opts.on("-V", "--version","Print version") do
88
73
  puts Codegraph::VERSION
@@ -94,52 +79,41 @@ def parse(args)
94
79
  end
95
80
  end.parse(args)
96
81
 
97
- if $options[:files].flatten.empty?
82
+ if $options[:filelist].flatten.empty?
98
83
  warn 'Please use -F do define input files! Use -h for further help.'
99
84
  exit -1
100
85
  end
86
+ unless $options[:otype]
87
+ warn 'Please provide an output filename.'
88
+ exit -1
89
+ end
101
90
  $options
102
91
  end
103
92
  options = parse(ARGV)
104
93
  pp options if $options[:debug]
105
- # ["--exclude", "-x", GetoptLong::REQUIRED_ARGUMENT],
106
- #)
94
+
107
95
  # What kind of graph should be generated?
108
96
  case $options[:mode]
109
97
  when "upper"
110
- g = UpperFunctionGraph.new($options[:function])
111
- when "single","show"
112
- g = SingleFunctionGraph.new($options[:function])
113
- when 8
114
- g = EightFunctionGraph.new($options[:function])
98
+ g = UpperFunctionGraph.new($options)
99
+ when "single"
100
+ g = SingleFunctionGraph.new($options)
115
101
  else
116
- g = FunctionGraph.new
102
+ g = FunctionGraph.new($options)
103
+ g.scan
117
104
  end
118
105
 
119
- # Set debug mode
120
- g.debug = $options[:debug]
121
-
122
- # Include additional functions?
123
- g.adds = $options[:adds]
124
-
125
- # Creates the Knodes/Edges of the graph
126
- g.fill($options[:files],$options[:excludes])
106
+ g.rotate if $options[:rotate]
107
+ g.node_attribs << g.box if $options[:boxes]
127
108
 
128
- # Remove edges
129
- g.limit($options[:depth]) if $options[:depth]
130
-
131
- # What sort of output should be created?
132
- case $options[:output]
133
- when "ps", "svg", "png", "jpg"
134
- g.to_type($options[:ofile],$options[:output])
135
- when "dot"
136
- g.to_dot($options[:ofile])
137
- when "txt"
138
- g.to_txt
139
- else
140
- if $options[:mode] == "show"
141
- puts g.display_functionbody($options[:function])
142
- else
143
- g.display
144
- end
109
+ if $options[:cluster]
110
+ puts "Clustering wrt files"
111
+ fileGraph = FileGraph.new($options) if $options[:cluster]
112
+ fileGraph.rotate if $options[:rotate]
113
+ fileGraph.node_attribs << fileGraph.box if $options[:boxes]
114
+ fileGraph.subgraph(g)
115
+ g = fileGraph
145
116
  end
117
+ # What sort of output should be created?
118
+ puts "Save graph to #{[$options[:ofile],$options[:otype]].join('.')}...." if $options[:debug]
119
+ g.save($options[:ofile],$options[:otype])
data/gemspec CHANGED
@@ -5,16 +5,14 @@ require 'rubygems'
5
5
  spec = Gem::Specification.new do |s|
6
6
  s.name = "codegraph"
7
7
  s.version = File.open("lib/codegraph.rb").readlines.grep(/VERSION/)[0].split('=').last.chomp.strip.tr("'",'')
8
- s.date = Time.new.to_s
8
+ s.date = Time.new.strftime("%Y-%m-%d")
9
9
  s.platform = Gem::Platform::RUBY
10
10
  s.bindir = 'bin'
11
11
  s.files = ["lib/codegraph.rb","bin/codegraph"] + ["gemspec","LICENSE"]
12
12
  s.executables << 'codegraph'
13
- s.add_dependency('rgl')
14
- s.add_dependency('asciify')
13
+ s.add_dependency('graph')
15
14
  s.rubyforge_project = "codegraph"
16
15
  s.description = "Display functional dependencies in source code (C, Fortran, PHP, Perl).\n"
17
- # "Changes (#{s.version}):"
18
16
  s.summary = "Display functional dependencies in source code (C, Fortran, PHP, Perl)"
19
17
  s.author = "Ralf Mueller"
20
18
  s.email = "stark.dreamdetective@gmail.com"
data/lib/codegraph.rb CHANGED
@@ -1,312 +1,304 @@
1
- require 'pp'
2
- require 'rgl/adjacency'
3
- require 'rgl/dot'
4
- require 'rgl/rdot'
5
- require 'rgl/traversal'
6
- require 'thread'
1
+ require 'jobqueue'
2
+ require 'graph'
7
3
  require 'digest'
8
- require 'asciify'
9
4
  require 'json'
5
+ require 'fileutils'
6
+ require 'pp'
7
+
10
8
 
11
9
  module Codegraph
12
- VERSION = '0.7.18'
10
+ VERSION = '0.7.20'
13
11
  end
14
12
 
15
- class FunctionGraph < RGL::DirectedAdjacencyGraph
16
- attr_accessor :funx, :adds, :excludes, :debug
13
+ class CodeParser
14
+ attr_reader :funx,:files
15
+ attr_accessor :exclude
17
16
 
18
- # Theses Parameters are used by interactive representation and for the file
19
- # generation function to_dot and to_type. They can only be given for the
20
- # whole graph, not for a singel node. This could be done by extending the
21
- # dot.rb file of the Ruby Graph Library
22
- Params = {'rankdir' => 'LR',
23
- 'ranksep' => '4.0',
24
- 'concentrate' => 'TRUE',
25
- 'label' => '',
26
- 'fontsize' => '12'}
17
+ @@ctagsOpts = '--c-kinds=f --fortran-kinds=fsip --php-kinds=f --perl-kinds=f'
18
+ @@workDir = ENV['HOME'] + '/.codegraph'
19
+ @@filesDBfile = @@workDir+'/filesDB.json'
20
+ @@filesDB = File.exist?(@@filesDBfile) ? JSON.parse(File.open(@@filesDBfile).read) : Hash.new
21
+ @@filesCk = @@filesDB.hash
22
+ @@lock = Mutex.new
27
23
 
28
- @@home = ENV['HOME']
29
- @@codehomedir = "#{@@home}/.codegraph"
30
- @@filesDB = @@codehomedir+'/filesDB.json'
31
- @@funxDB = @@codehomedir+'/funxDB.json'
24
+ # come class methods
25
+ def CodeParser.filesDB
26
+ @@filesDB
27
+ end
28
+ def CodeParser.filesCk
29
+ @@filesCk
30
+ end
31
+ #=============================================================================
32
32
 
33
- @@matchBeforFuncName = $options[:matchBefor].nil? ? '[^A-z0-9_]( *)': $options[:matchBefor]
34
- @@matchAfterFuncName = $options[:matchAfter].nil? ? '( *\(| |$)' : $options[:matchAfter]
33
+ def initialize(debug=false,dir="#{ENV['HOME']}/.codegraph")
34
+ @dir = dir
35
+ @debug = debug
36
+ @funx = {}
37
+ @files = {}
38
+ @exclude = []
39
+ end
35
40
 
36
- @@map = Asciify::Mapping.new(:default)
37
-
38
- # Generate the codegraph storage directory
39
- if not FileTest.directory?(@@codehomedir)
40
- system("mkdir #{@@codehomedir}")
41
- end
42
-
43
- def initialize
44
- super
45
- @debug = false
46
- # the following attribute will hold the functionnames and their bodies
47
- @funx = Hash.new
48
- @lock = Mutex.new
49
- @filesDB = File.exist?(@@filesDB) ? JSON.parse(File.open(@@filesDB).read) : Hash.new
50
- @filesCk = @db.hash
51
- @funxDB = File.exist?(@@funxDB) ? JSON.parse(File.open(@@funxDB).read) : Hash.new
52
- @funxCk = @funxDB.hash
53
-
54
- @adds, @excludes = [],[]
55
- end
56
-
57
- # Generates the necessary files:
58
- # 1. One for the whole source code
59
- # 2. One for functions inside the file 1
60
- # and fill the @funx hash
61
- def genFiles(graph, filelist, exclude=[])
62
- threads = []
63
-
64
- filelist.each {|file|
65
- threads << Thread.new(file, @@codehomedir) {|file,codehomedir|
66
- funxFile = "funxnames"
67
- ctagsKinds = $options[:ctagsopts].nil? ? '--c-kinds=f --fortran-kinds=fsip --php-kinds=f --perl-kinds=f' : $options[:ctagsopts]
41
+ def read(*filelist)
42
+ jobqueue = JobQueue.new
68
43
 
44
+ filelist.each {|file|
45
+ jobqueue.push {
46
+ unless File.exist?(file)
47
+ warn "Cannot find file #{file}"
48
+ return
49
+ else
69
50
  puts "Processing #{file} ..." if @debug
70
- checksum = Digest::SHA256.file(file).hexdigest
71
- if @filesDB.has_key?(checksum)
72
- code,funxLocations = @filesDB[checksum]
73
- else
74
- basefile = File.basename(file)
75
- tempfile = "_" + basefile
76
- case File.extname(file)
77
- when '.c','.h'
78
- cppCommand = "cpp -w -fpreprocessed -E -fdirectives-only #{file} -o #{codehomedir}/#{tempfile} 2>/dev/null"
79
- cppCommand = "cpp -fpreprocessed #{file} -o #{codehomedir}/#{tempfile} 2>/dev/null"
80
- grep = "grep -v -e '^$' #{codehomedir}/#{tempfile} | grep -v -e '^#' > #{codehomedir}/#{basefile}"
81
- when '.f','.f77','.f90','.f95'
82
- cppCommand = "cp #{file} #{codehomedir}/#{tempfile}"
83
- grep = "grep -v -e '^$' #{codehomedir}/#{tempfile} | grep -v -e '^ *!' > #{codehomedir}/#{basefile}"
84
- else
85
- cppCommand = "cp #{file} #{codehomedir}/#{tempfile}"
86
- grep = "grep -v -e '^$' #{codehomedir}/#{tempfile} | grep -v -e '^#' > #{codehomedir}/#{basefile}"
87
- end
88
- gen4ctags = "ctags -x #{ctagsKinds} #{codehomedir}/#{basefile} | sort -n -k 3"
89
- command = [cppCommand,grep].join(";")
51
+ end
90
52
 
91
- puts gen4ctags if @debug
92
- puts command if @debug
93
- system(command)
53
+ checksum = Digest::SHA256.file(file).hexdigest
54
+ if @@filesDB.has_key?(checksum) and @@filesDB.has_key?(file)
55
+ code,funxLocations = @@filesDB[checksum]
56
+ @files[file] = @@filesDB[file]
57
+ else
58
+ basefile = File.basename(file)
59
+ tempfile = "_" + basefile
60
+ case File.extname(file)
61
+ when '.c','.h'
62
+ cppCommand = "cpp -w -fpreprocessed -E -fdirectives-only #{file} -o #{@dir}/#{tempfile} 2>/dev/null"
63
+ cppCommand = "cpp -fpreprocessed #{file} -o #{@dir}/#{tempfile} 2>/dev/null"
64
+ grep = "grep -v -e '^$' #{@dir}/#{tempfile} | grep -v -e '^#' > #{@dir}/#{basefile}"
65
+ when '.f','.f77','.f90','.f95'
66
+ cppCommand = "cp #{file} #{@dir}/#{tempfile}"
67
+ grep = "grep -v -e '^$' #{@dir}/#{tempfile} | grep -v -e '^ *!' > #{@dir}/#{basefile}"
68
+ else
69
+ cppCommand = "cp #{file} #{@dir}/#{tempfile}"
70
+ grep = "grep -v -e '^$' #{@dir}/#{tempfile} | grep -v -e '^#' > #{@dir}/#{basefile}"
71
+ end
72
+ gen4ctags = "ctags -x #{@@ctagsOpts} #{@dir}/#{basefile} | sort -n -k 3"
73
+ command = [cppCommand,grep].join(";")
94
74
 
95
- code = open(codehomedir+'/'+ File.basename(file)).readlines
96
- funxLocations = IO.popen(gen4ctags).readlines.map {|l| l.split[0,4]}
97
- @lock.synchronize { @filesDB[checksum] = [code,funxLocations] }
75
+ puts gen4ctags if @debug
76
+ puts command if @debug
77
+ system(command)
98
78
 
99
- # cleanup
100
- FileUtils.rm("#{codehomedir}/#{tempfile}")
101
- FileUtils.rm("#{codehomedir}/#{basefile}")
102
- end
79
+ code = open(@dir+'/'+ File.basename(file)).readlines
80
+ funxLocations = IO.popen(gen4ctags).readlines.map {|l| l.split[0,4]}
81
+ @@lock.synchronize {
82
+ @@filesDB[checksum] = [code,funxLocations]
83
+ @@filesDB[file] = funxLocations.map {|v| v[0]}
84
+ @files[file] = @@filesDB[file]
85
+ }
103
86
 
104
- funxLocations.each_with_index {|ary,i|
105
- name, kind, line, file = ary
106
- puts name if @debug
107
- line = line.to_i
108
- startLineIndex = line - 1
109
- endLineIndex = (i+1 < funxLocations.size) ? funxLocations[i+1][2].to_i - 2 : -1
110
- body = code[startLineIndex..endLineIndex].join
111
- @lock.synchronize {
112
- @funx.store(name,body)
113
- }
87
+ # cleanup
88
+ FileUtils.rm("#{@dir}/#{tempfile}") unless @debug
89
+ FileUtils.rm("#{@dir}/#{basefile}") unless @debug
90
+ end
91
+ funxLocations.each_with_index {|ary,i|
92
+ name, kind, line, file = ary
93
+ next if @exclude.include?(name)
94
+ puts name if @debug
95
+ line = line.to_i
96
+ startLineIndex = line - 1
97
+ endLineIndex = (i+1 < funxLocations.size) ? funxLocations[i+1][2].to_i - 2 : -1
98
+ body = code[startLineIndex..endLineIndex].join
99
+ @@lock.synchronize {
100
+ @funx.store(name,body)
114
101
  }
115
102
  }
116
103
  }
117
- threads.each {|t| t.join}
104
+ }
105
+ jobqueue.run
106
+
107
+ updateFilesDB
108
+ end
109
+ def updateFilesDB
110
+ File.open(@@filesDBfile,"w") {|f| f << JSON.generate(@@filesDB)} unless @@filesCk == @@filesDB.hash
111
+ end
112
+ private :updateFilesDB
113
+ end
114
+
115
+ # needed for svg output
116
+ class Graph
117
+ def empty?
118
+ edges.empty?
119
+ end
120
+ end
118
121
 
119
- # update the code database in case of anything new
120
- updateFilesDB
122
+ class FunctionGraph < Graph
123
+ attr_accessor :funx, :adds, :excludes, :debug
121
124
 
122
- if @funx.empty?
123
- warn "no functions found"
124
- exit -1
125
- end
126
- end
125
+ # internal database for former scans
126
+ @@workDir = ENV['HOME'] + '/.codegraph'
127
+ @@funxDBfile = @@workDir+'/funxDB.json'
128
+ @@funxDB = File.exist?(@@funxDBfile) ? JSON.parse(File.open(@@funxDBfile).read) : Hash.new
129
+ @@lock = Mutex.new
130
+
131
+ def initialize(config)
132
+ super(self.class.to_s)
133
+ @config = config
134
+
135
+ @debug = @config[:debug]
136
+
137
+ @@matchBeforFuncName = @config[:matchBefor].nil? ? '[^A-z0-9_]\s*': @config[:matchBefor]
138
+ @@matchAfterFuncName = @config[:matchAfter].nil? ? '( *\(| |$)' : @config[:matchAfter]
139
+
140
+ @adds = @config[:adds] || []
141
+ @excludes = @config[:excludes] || []
142
+
143
+ @parser = CodeParser.new
144
+ @parser.read(*@config[:filelist])
145
+ @funx = @parser.funx
146
+ end
127
147
 
128
- # fill the graph with all functions found in <filelist>
129
- # while all functions from <exclude> aren't recognized
130
- def fill(filelist,exclude=[])
131
- # generate the necessary files and fill @funx
132
- genFiles(self,filelist,exclude)
133
-
134
- # scan functions for the function names
135
- names = @funx.keys
136
- @funx.each_pair {|name,body|
137
- # threads=[];threads << Thread.new(name,body,names) {|name,body,names|
138
- puts "Add func: #{name}" if @debug
139
- # Check if this body is in the funx DB
140
- bodyCk = Digest::SHA256.hexdigest(body)
141
- if @funxDB.has_key?(bodyCk) and @funxDB[name] == body
142
- edges = @funxDB[bodyCk]
143
- edges.each {|edge| add_edge(*edge)}
144
- else
145
- edges = []
146
- add_vertex(name)
147
- (names - [name] + @adds).each { |func|
148
- puts name if @debug
149
- if/#@@matchBeforFuncName#{func}#@@matchAfterFuncName/.match(body)
150
- edge = ["#{name}","#{func}"]
151
- add_edge(*edge)
152
- edges << edge
153
- end
154
- }
155
- # @lock.synchronize {
156
- @funxDB[bodyCk] = edges
157
- @funxDB[name] = body
158
- # }
159
- end
160
- # }
148
+ def scan
149
+ jobqueue = JobQueue.new
150
+ # scan functions fo all other function names
151
+ names = @parser.funx.keys + @adds - @excludes
152
+ @parser.funx.each_pair {|name,body|
153
+ next unless names.include?(name)
154
+ jobqueue.push {
155
+ puts "Add func: #{name}" if @debug
156
+ # Check if this body is in the funx DB
157
+ bodyCk = Digest::SHA256.hexdigest(body)
158
+ if @@funxDB.has_key?(bodyCk) and @@funxDB[name] == body
159
+ edges = @@funxDB[bodyCk]
160
+ edges.each {|edge| self.edge(*edge)}
161
+ else
162
+ edges = []
163
+ self.node(name)
164
+ (names - [name]).each { |func|
165
+ if/#@@matchBeforFuncName#{Regexp.escape(func)}#@@matchAfterFuncName/.match(body)
166
+ edge = ["#{name}","#{func}"]
167
+ self.edge(*edge)
168
+ edges << edge
169
+ end
170
+ }
171
+ @@lock.synchronize {
172
+ @@funxDB[bodyCk] = edges
173
+ @@funxDB[name] = body
174
+ }
175
+ end
161
176
  }
162
- # threads.each {|t| t.join}
163
- updateFunxDB
164
- end
165
-
166
- def limit(depth)
167
- dv = RGL::DFSVisitor.new(self)
168
- dv.attach_distance_map
169
- self.depth_first_search(dv) {|u|
170
- self.remove_vertex(u) if dv.distance_to_root(u) > depth
171
- }
172
- end
173
-
174
- def display_functionbody(name)
175
- @funx[name]
177
+ }
178
+ jobqueue.run
179
+
180
+ updateFunxDB
176
181
  end
177
- # Creates a simple dot file according to the above <Params>.
178
- # Parameters for the nodes are not supported by rgl.
179
- def to_dot(filename)
180
- File.open(filename,"w") {|f|
181
- print_dotted_on(Params,f)
182
- }
183
- end
184
182
 
185
- # Generates pairs of "func_A -> func_B" to stdout
186
- def to_txt
187
- each_edge do |left,right|
188
- print left,' -> ',right,"\n"
189
- end
190
- end
183
+ def showBody(name)
184
+ @funx[name]
185
+ end
191
186
 
192
- # This function generates a file of the given type using the dot utility.
193
- # Supported Types are PS, PNG, JPG, DOT and SVG.
194
- def to_type(filename,type)
195
- if File.exist?(filename)
196
- system("rm #{filename}")
197
- end
198
- if File.exist?(filename+"."+type)
199
- system("rm #{filename}."+type)
200
- end
201
- to_dot(filename+".dot")
202
- system("dot -T#{type} -o #{filename} -Nshape=box #{filename}.dot")
203
- system("rm #{filename}.dot")
204
- end
205
-
206
- # Display the graph with an interactive viewer
207
- def display
208
- dotty(Params)
209
- system("rm graph.dot") if File.exist?("graph.dot")
210
- end
211
-
212
- def updateFilesDB
213
- File.open(@@filesDB,"w") {|f| f << JSON.generate(@filesDB)} unless @filesCk == @filesDB.hash
214
- end
215
- def updateFunxDB
216
- File.open(@@funxDB,"w") {|f| f << JSON.generate(@funxDB)} unless @funxCk == @filesDB.hash
217
- end
218
- private :genFiles,:updateFilesDB,:updateFunxDB
187
+ def updateFunxDB
188
+ File.open(@@funxDBfile,"w") {|f| f << JSON.generate(@@funxDB)}# unless @funxCk == @parser.filesCk
189
+ end
190
+ private :updateFunxDB
219
191
  end
220
192
 
221
193
  class SingleFunctionGraph < FunctionGraph
222
- attr_accessor :func
223
-
224
- # Constructor, which creates an empty graph for the rootfunction <func>
225
- def initialize(func)
226
- # Holds the func'n names, that are allready scanned
227
- @scannednames = []
228
- # Root func
229
- @func = func
230
- super()
231
- end
232
-
233
- # Works like the one from FunctionGraph except, that it calls 'scan'
234
- # for the recursive descent throug all functions given in <filelist> - exclude
235
- def fill(filelist,exclude=[])
236
- genFiles(self,filelist,exclude)
237
- scan(self, func)
238
- end
239
-
240
- # For the given root function f, scan walks through the graph, and finds any
241
- # other function, that calls f
242
- def scan(graph,f)
243
- if (@scannednames.include?(f))
194
+ attr_accessor :func
195
+
196
+ # Constructor, which creates an empty graph for the rootfunction <func>
197
+ def initialize(config)
198
+ super(config)
199
+ # Holds the func'n names, that are allready scanned
200
+ @scannednames = []
201
+ # Root func
202
+ @func = @config[:function]
203
+ scan(self,@func)
204
+ end
205
+
206
+ # For the given root function f, scan walks through the graph, and finds any
207
+ # other function, that calls f
208
+ def scan(graph,f)
209
+ puts "Scanning #{f} ..." if @config[:debug]
210
+ if (@scannednames.include?(f))
211
+ return
212
+ else
213
+ names = graph.funx.keys + @adds - @excludes
214
+ unless names.include?(f)
215
+ warn "Function #{func} not found."
216
+ return
217
+ end
218
+ @scannednames << f
219
+ body = graph.funx[f]
220
+ bodyCk = Digest::SHA256.hexdigest(body)
221
+ if @@funxDB.has_key?(bodyCk) and @@funxDB[f] == body
222
+ edges = @@funxDB[bodyCk]
223
+ edges.each {|edge| graph.edge(*edge)}
224
+ (edges.flatten.uniq-[f]).each {|g| scan(graph,g)}
244
225
  else
245
- names = graph.funx.keys
246
- if not names.include?(func)
247
- warn "Function #{func} not found."
248
- exit -1
249
- end
250
- @scannednames << f
251
- body = graph.funx[f]
252
- # scan for any other function in the body of f
253
- (names - [f] + @adds).each {|g|
254
- if /#@@matchBeforFuncName#{g}#@@matchAfterFuncName/.match(body)
255
- graph.add_edge(f,g)
256
- # go downstairs for all functions from the scanned files
257
- scan(graph,g) if names.include?(g)
258
- end
259
- }
226
+ edges = []
227
+ # scan for any other function in the body of f
228
+ (names - [f]).each {|g|
229
+ if /#@@matchBeforFuncName#{Regexp.escape(g)}#@@matchAfterFuncName/.match(body)
230
+ graph.edge(f,g)
231
+ edges << [f,g]
232
+ # go downstairs for all functions from the scanned files
233
+ scan(graph,g) if names.include?(g)
234
+ end
235
+ }
236
+ #@@lock.synchronize {
237
+ @@funxDB[bodyCk] = edges
238
+ @@funxDB[f] = body
239
+ #}
260
240
  end
261
- end
262
- private :scan
241
+ end
242
+ end
243
+ private :scan
263
244
  end
264
245
 
265
246
  class UpperFunctionGraph < SingleFunctionGraph
266
-
267
- # scanning upwards unlike SingleFunctionGraph.scan
268
- def scan(graph,func)
269
- if @scannednames.include?(func)
270
- else
271
- if not (graph.funx.keys + @adds).include?(func)
272
- warn "Function '#{func}' not found. If this is an internal function, " +
273
- "please try again with the '-w' option to include the internal " +
274
- "funx before scanning."
275
- exit -1
276
- end
277
- @scannednames << func
278
- graph.funx.each_pair {|g,gbody|
279
- # dont scan a function for itself
280
- next if g == func
281
- puts g if @debug
282
- puts gbody if @debug
283
- if/#@@matchBeforFuncName#{func}#@@matchAfterFuncName/.match(Asciify.new(@@map).convert(gbody))
284
- graph.add_edge(g,func)
285
- scan(graph,g)
286
- end
287
- }
247
+
248
+ # scanning upwards unlike SingleFunctionGraph.scan
249
+ def scan(graph,func)
250
+ if @scannednames.include?(func)
251
+ else
252
+ names = graph.funx.keys + @adds - @excludes
253
+ unless names.include?(func)
254
+ warn "Function '#{func}' not found"
255
+ exit -1
288
256
  end
289
- end
290
- private :scan
257
+ @scannednames << func
258
+ graph.funx.each_pair {|g,gbody|
259
+ # dont scan a function for itself
260
+ next if g == func
261
+ puts g if @debug
262
+ if/#@@matchBeforFuncName#{Regexp.escape(func)}#@@matchAfterFuncName/.match(gbody)
263
+ graph.edge(g,func)
264
+ scan(graph,g)
265
+ end
266
+ }
267
+ end
268
+ end
269
+ private :scan
291
270
  end
292
271
 
293
272
  class EightFunctionGraph < FunctionGraph
294
- def initialize(func)
295
- super()
296
- @func = func
273
+ def initialize(config)
274
+ super(config)
275
+ @config = config
276
+ gDown = SingleFunctionGraph.new(@config)
277
+ gUp = UpperFunctionGraph.new(@config)
297
278
  end
298
- def fill(*args)
299
- g_down = SingleFunctionGraph.new(@func)
300
- g_down.fill(*args)
279
+ end
301
280
 
302
- g_up = UpperFunctionGraph.new(@func)
303
- g_up.fill(*args)
281
+ class FileGraph < Graph
282
+ attr_reader :funx,:files
283
+ def initialize(config)
284
+ super(self.class.to_s)
285
+ @config = config
286
+ @debug = @config[:debug]
287
+ @parser = CodeParser.new
288
+ @parser.read(*@config[:filelist])
289
+ @funx,@files = @parser.funx, @parser.files
304
290
 
305
- g_down.each_edge do |u,v|
306
- self.add_edge(u,v)
307
- end
308
- g_up.each_edge do |u,v|
309
- self.add_edge(u,v)
310
- end
291
+ scan
292
+ end
293
+
294
+ def scan
295
+ leaf_node = white + filled
296
+ @files.each {|file,myfunx|
297
+ subgraph "cluster_#{rand(1000)}_#{file}" do
298
+ label file
299
+ graph_attribs << blue << filled << lightgray
300
+ myfunx.each {|func| node(func)}
301
+ end
302
+ }
311
303
  end
312
304
  end
metadata CHANGED
@@ -1,94 +1,62 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: codegraph
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 7
8
- - 18
9
- version: 0.7.18
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.7.20
5
+ prerelease:
10
6
  platform: ruby
11
- authors:
7
+ authors:
12
8
  - Ralf Mueller
13
9
  autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
-
17
- date: 2011-11-01 09:18: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
12
+ date: 2012-05-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: graph
16
+ requirement: &8260220 !ruby/object:Gem::Requirement
24
17
  none: false
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- segments:
29
- - 0
30
- version: "0"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
31
22
  type: :runtime
32
- version_requirements: *id001
33
- - !ruby/object:Gem::Dependency
34
- name: asciify
35
23
  prerelease: false
36
- requirement: &id002 !ruby/object:Gem::Requirement
37
- none: false
38
- requirements:
39
- - - ">="
40
- - !ruby/object:Gem::Version
41
- segments:
42
- - 0
43
- version: "0"
44
- type: :runtime
45
- version_requirements: *id002
46
- description: |
47
- Display functional dependencies in source code (C, Fortran, PHP, Perl).
24
+ version_requirements: *8260220
25
+ description: ! 'Display functional dependencies in source code (C, Fortran, PHP, Perl).
48
26
 
27
+ '
49
28
  email: stark.dreamdetective@gmail.com
50
- executables:
29
+ executables:
51
30
  - codegraph
52
31
  extensions: []
53
-
54
32
  extra_rdoc_files: []
55
-
56
- files:
33
+ files:
57
34
  - lib/codegraph.rb
58
35
  - bin/codegraph
59
36
  - gemspec
60
37
  - LICENSE
61
- has_rdoc: true
62
38
  homepage: http://codegraph.rubyforge.org
63
39
  licenses: []
64
-
65
40
  post_install_message:
66
41
  rdoc_options: []
67
-
68
- require_paths:
42
+ require_paths:
69
43
  - lib
70
- required_ruby_version: !ruby/object:Gem::Requirement
44
+ required_ruby_version: !ruby/object:Gem::Requirement
71
45
  none: false
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- segments:
76
- - 0
77
- version: "0"
78
- required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
51
  none: false
80
- requirements:
81
- - - ">="
82
- - !ruby/object:Gem::Version
83
- segments:
84
- - 0
85
- version: "0"
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
86
56
  requirements: []
87
-
88
57
  rubyforge_project: codegraph
89
- rubygems_version: 1.3.7
58
+ rubygems_version: 1.8.17
90
59
  signing_key:
91
60
  specification_version: 3
92
61
  summary: Display functional dependencies in source code (C, Fortran, PHP, Perl)
93
62
  test_files: []
94
-