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