require 'find'
require 'optparse'
def get_source_files(startpath)
sources = []
Find.find(startpath) do |filename|
if /\.c[p]*$/.match(filename)
sources.push filename
end
if /\.h$/.match(filename)
sources.push filename
end
end
sources
end
class IncludeAnalyser
def initialize
@dependend = Hash.new{Array.new}
end
def extract_dependency(filename)
File.open(filename).each { |line|
if /^\#include\s*\"([\w\.]+)\"/.match(line)
add_dependency(filename, $1)
end
if /^\#include\s*\<([\w\.]+)\>/.match(line)
add_dependency(filename, $1)
end
}
end
def add_dependency(sourcepath, included)
base = File.basename(sourcepath)
if @dependend[base].empty?
@dependend[base] = [included]
else
@dependend[base].push(included)
end
end
def print_all
@dependend.each{|source, includes|
print_childs(source, 0)
}
end
def print_childs(source, level)
puts(" " * level + source)
for child in @dependend[source]
print_childs(child, level + 1)
end
end
def print_dot(stream)
stream.puts "digraph G {"
stream.puts " rankdir=LR;"
stream.puts " node [shape=rect];"
@dependend.each{|source, includes|
for include in includes
stream.puts " #{dotname(source)} -> #{dotname(include)};"
end
}
stream.puts "}"
end
def dotname(filename)
filename.sub(/\./,'_')
end
end
ARGV.options do |opts|
opts.banner = "Usage: ruby #{$0} [-d DOTOUTPUTFILE] INPUTDIRECTORY"
opts.on("-h", "--help", "show this message"){
puts opts
exit
}
opts.on("-d", "--dotoutput=FILE", String, "Create dot output file"){
|@dotoutputfile|
}
opts.parse!
end
files = []
for path in ARGV
files = files + get_source_files(path)
end
analyser = IncludeAnalyser.new
for file in files
analyser.extract_dependency(file)
end
if defined? @dotoutputfile
File.open(@dotoutputfile, "w"){ |stream|
analyser.print_dot(stream)
}
else
analyser.print_all
end
|