Last active
August 29, 2015 14:13
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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