Skip to content

Instantly share code, notes, and snippets.

@handylearn
Last active August 29, 2015 14:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save handylearn/e5538945565c57e21509 to your computer and use it in GitHub Desktop.
Save handylearn/e5538945565c57e21509 to your computer and use it in GitHub Desktop.
Analyses the header dependencies of C/C++ source directories, this can help to refactor it for faster builds. Orginal version für ruby 1.6/1.8
#!/usr/bin/env ruby -w
#
# construct dependency graph for C/C++ projects.
# input is directory with c and h files.
# output as plain text or as dot file for processing with graphviz
#
# Copyright (c) 2004-2015 by Karsten Meier, http://meier-online.com
# This program is free software.
# You can distribute/modify this program under the terms of
# the GNU General Public License version as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
require 'find'
require 'optparse'
# $/ = "\r" # uncomment for macintosh linebreaks
# get all source files under a directory.
# a source file is either a .c file or a .h file
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)
# puts "Source #{source}"
# puts "Includes #{includes.join(' ')}"
}
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
# ----- analysing of command line arguments
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"){
|filename| @dotoutputfile = filename
}
opts.parse!
end
# ----- main program
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment