Skip to content

Instantly share code, notes, and snippets.

@handylearn
Last active August 29, 2015 14:13

Revisions

  1. Karsten Meier revised this gist Jan 18, 2015. 1 changed file with 5 additions and 3 deletions.
    8 changes: 5 additions & 3 deletions dependencygraph.rb
    Original file line number Diff line number Diff line change
    @@ -1,8 +1,10 @@
    #!/usr/bin/env ruby -w
    #
    # construct dependency graph for C/C++ projects
    # 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 by Karsten Meier, http://meier-online.com
    # 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
    @@ -100,7 +102,7 @@ def dotname(filename)
    exit
    }
    opts.on("-d", "--dotoutput=FILE", String, "Create dot output file"){
    |@dotoutputfile|
    |filename| @dotoutputfile = filename
    }
    opts.parse!
    end
  2. handylearn created this gist Jan 18, 2015.
    125 changes: 125 additions & 0 deletions dependencygraph.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,125 @@
    #!/usr/bin/env ruby -w
    #
    # construct dependency graph for C/C++ projects
    #
    # Copyright (c) 2004 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"){
    |@dotoutputfile|
    }
    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