codegraph 0.7.18 → 0.7.20
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/codegraph +54 -80
- data/gemspec +2 -4
- data/lib/codegraph.rb +261 -269
- 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
|
-
:
|
5
|
+
:filelist => [],
|
6
6
|
:mode => nil,
|
7
|
-
:output => "",
|
8
7
|
:function => "",
|
9
8
|
:depth => nil,
|
10
9
|
:adds => [],
|
11
10
|
:excludes => [],
|
12
|
-
:
|
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("-
|
22
|
-
$options[: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("-
|
39
|
-
$options[:
|
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("-
|
43
|
-
$options[:
|
43
|
+
opts.on("-r","--rotate","rotate graph 90deg [default:false]") do
|
44
|
+
$options[:rotate] = true
|
44
45
|
end
|
45
|
-
opts.on("--
|
46
|
-
$options[:
|
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[:
|
61
|
+
$options[:filelist] << Dir.glob(File.expand_path(filedesc))
|
55
62
|
else
|
56
|
-
$options[:
|
63
|
+
$options[:filelist] = filelist
|
57
64
|
end
|
58
65
|
end
|
59
|
-
$options[:
|
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("-
|
85
|
-
$options[:
|
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[:
|
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
|
-
|
106
|
-
#)
|
94
|
+
|
107
95
|
# What kind of graph should be generated?
|
108
96
|
case $options[:mode]
|
109
97
|
when "upper"
|
110
|
-
|
111
|
-
when "single"
|
112
|
-
|
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
|
-
|
102
|
+
g = FunctionGraph.new($options)
|
103
|
+
g.scan
|
117
104
|
end
|
118
105
|
|
119
|
-
|
120
|
-
g.
|
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
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
g
|
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.
|
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('
|
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 '
|
2
|
-
require '
|
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.
|
10
|
+
VERSION = '0.7.20'
|
13
11
|
end
|
14
12
|
|
15
|
-
class
|
16
|
-
|
13
|
+
class CodeParser
|
14
|
+
attr_reader :funx,:files
|
15
|
+
attr_accessor :exclude
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
24
|
+
# come class methods
|
25
|
+
def CodeParser.filesDB
|
26
|
+
@@filesDB
|
27
|
+
end
|
28
|
+
def CodeParser.filesCk
|
29
|
+
@@filesCk
|
30
|
+
end
|
31
|
+
#=============================================================================
|
32
32
|
|
33
|
-
|
34
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
92
|
-
|
93
|
-
|
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
|
-
|
96
|
-
|
97
|
-
|
75
|
+
puts gen4ctags if @debug
|
76
|
+
puts command if @debug
|
77
|
+
system(command)
|
98
78
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
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
|
-
|
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
|
-
|
120
|
-
|
122
|
+
class FunctionGraph < Graph
|
123
|
+
attr_accessor :funx, :adds, :excludes, :debug
|
121
124
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
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
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
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
|
-
|
163
|
-
|
164
|
-
|
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
|
-
|
186
|
-
|
187
|
-
|
188
|
-
print left,' -> ',right,"\n"
|
189
|
-
end
|
190
|
-
end
|
183
|
+
def showBody(name)
|
184
|
+
@funx[name]
|
185
|
+
end
|
191
186
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
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
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
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
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
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
|
-
|
262
|
-
|
241
|
+
end
|
242
|
+
end
|
243
|
+
private :scan
|
263
244
|
end
|
264
245
|
|
265
246
|
class UpperFunctionGraph < SingleFunctionGraph
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
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
|
-
|
290
|
-
|
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(
|
295
|
-
super()
|
296
|
-
@
|
273
|
+
def initialize(config)
|
274
|
+
super(config)
|
275
|
+
@config = config
|
276
|
+
gDown = SingleFunctionGraph.new(@config)
|
277
|
+
gUp = UpperFunctionGraph.new(@config)
|
297
278
|
end
|
298
|
-
|
299
|
-
g_down = SingleFunctionGraph.new(@func)
|
300
|
-
g_down.fill(*args)
|
279
|
+
end
|
301
280
|
|
302
|
-
|
303
|
-
|
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
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
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
|
-
|
5
|
-
|
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
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
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
|
-
|
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
|
-
|
37
|
-
|
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
|
-
|
76
|
-
|
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
|
-
|
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.
|
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
|
-
|