codegraph 0.7.18 → 0.7.20

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