Add a --renderer flag
[deps.git] / graph-includes
blob6920b1d1b51d2ffdaf1f1eb0a978f517d5655713
1 #!/usr/bin/env perl
3 # graph-includes - create a graphviz graph of source-files
4 # dependencies, with an emphasis on getting usable graphs even for
5 # large projects
7 # (c) 2005 Yann Dirson <ydirson@altern.org>
8 # Distributed under version 2 of the GNU GPL.
10 use warnings;
11 use strict;
13 use File::Basename qw(dirname);
14 use lib dirname($0).'/lib';
16 use Getopt::Long qw(GetOptions);
17 use List::Util qw(sum);
18 use File::Find qw(find);
19 use graphincludes::params;
21 our $showalldeps=0;
22 our $class='default';
23 our $language='C';
24 our (@colors);
25 our ($outfile, $prefixstrip, $paper);
26 our $rendererclass = 'graphincludes::renderer::dot';
28 our $usage = <<EOF;
29 Usage: $0 [options] src/*.[ch]
30 Options:
31 -class {default|uniqueincludes|<your-own-class>}
32 Select "class" of source code
33 -language <lang> Select language syntax for dependency extraction (default: C)
34 -fileregexp <perl-regexp>
35 Use this regexp to identify interesting files inside directories
36 (overrides per-language default regexp)
37 -Include <directory> Adds a directory to the path where to look for project's include files
38 -sysInclude <directory> Adds a directory to the path where to look for system include files
39 -prefixstrip <prefix> Strip <prefix> (eg. "src/") from filenames in the graph
40 -group <min>-<max> Draw file groups of levels <min> through <max> (default: 1 1)
41 -color <n>:<label>=<color>[,<label>=<color>...]
42 Use specified colors to show members of level-<n> group labelled <label>
43 -alldeps Do not apply transitive reduction to the graph
45 -showdropped Show in special color edges dropped during transitive reduction
46 -focus <node-label> Like -showdropped but only for edges starting from given node
48 -renderer <engine> Select the rendering program to produce a graph for (default: dot)
49 -output <outfile>.<fmt>
50 Format to output file, using <fmt> as target format
51 -paper a4|a3|letter Select paper size of multi-paged postscript output
53 -verbose Show progress
54 -debug Loads of debuging output
56 -version Display this program's version
57 -help This help text
58 EOF
60 our @colspecs;
62 # memorize command-line for the report
63 our @commandline = @ARGV;
65 GetOptions ('alldeps' => \$showalldeps,
66 'showdropped' => \$graphincludes::params::showdropped,
68 'focus=s' => \@graphincludes::params::focus,
69 'class=s' => \$class,
70 'language=s' => \$language,
71 'fileregexp=s' => \$graphincludes::params::filename_regexp,
73 'renderer=s' => sub {
74 my (undef, $renderer) = @_;
75 $rendererclass = 'graphincludes::renderer::' . $renderer;
78 'Include=s' => \@graphincludes::params::inclpath,
79 'sysInclude=s' => \@graphincludes::params::sysinclpath,
81 'group=s' => sub {
82 my (undef, $range) = @_;
83 ($graphincludes::params::minshow, $graphincludes::params::maxshow) = split /-/, $range;
85 'color=s@' => sub {
86 my (undef, $colspec) = @_;
87 my @temp = split /:/, $colspec;
88 push @colspecs, [$temp[1], $temp[2]];
90 'output=s' => \$outfile,
91 'paper=s' => \$paper,
93 'prefixstrip=s' => \$prefixstrip,
95 'verbose+' => \$graphincludes::params::verbose,
96 'debug' => \$graphincludes::params::debug,
97 'help' => sub { print $usage; exit 0; },
98 'version' => sub { print "$0 version $graphincludes::params::VERSION\n"; exit 0; },
100 ) or print STDERR $usage and exit 1;
102 if (@ARGV == 0) {
103 print STDERR $usage;
104 exit 1;
107 eval "require $rendererclass" or die "cannot load $rendererclass from " . join ':', @INC;
108 my $renderer = new $rendererclass;
110 # deal with non-default output formats
112 $renderer->set_multipage($paper) if defined $paper;
113 $renderer->set_outputfile($outfile) if defined $outfile;
115 # create a project with specified files
116 our $classmodule = "graphincludes::project::" . $class;
117 eval "require $classmodule" or die "cannot load $classmodule from " . join ':', @INC;
118 $classmodule->set_language ($language) or die "cannot set language to '$language'";
119 our @files;
120 foreach my $arg (@ARGV) {
121 if (-d $arg) {
122 find ( { no_chdir => 0,
123 wanted => sub {
124 if ($classmodule->accepts_file ($_)) {
125 push @files, $File::Find::name;
126 print STDERR "Adding $File::Find::name\n" if $graphincludes::params::debug;
128 } }, $arg);
129 } elsif (-r $arg) {
130 push @files, $arg;
131 } else {
132 die "file does not exist: $arg";
135 our $project = ($classmodule)->new(prefixstrip => $prefixstrip,
136 files => \@files);
137 $project->init();
139 @colors = $project->defaultcolors();
140 foreach my $colspec (@colspecs) {
141 foreach my $coldef (split /,/, $colspec->[2]) {
142 my @coldef = split /=/, $coldef;
143 $colors[$colspec->[1]]->{$coldef[0]} = $coldef[1];
147 our $stat_nfiles = scalar @{$project->{FILES}};
148 # NOTE: $stat_nedges below is a cut'n'paste of $stat_ndeps
149 our $stat_ndeps = sum (map { scalar keys %{$project->{DEPS}{$_}} } (keys %{$project->{DEPS}}));
151 if (!defined $stat_ndeps) {
152 print STDERR "$0: found no dependency\n";
153 exit 0;
156 # maybe get rid of shortcut deps (transitive reduction)
157 $project->reduce() unless ($showalldeps);
159 our $stat_nnodes = scalar keys %{$project->{NODES}};
160 our $stat_nleaves = $stat_nnodes - scalar keys %{$project->{DEPS}};
161 # NOTE: $stat_ndeps above is a cut'n'paste of $stat_nedges
162 our $stat_nedges = sum (map { scalar keys %{$project->{DEPS}{$_}} } (keys %{$project->{DEPS}}));
164 # print graph
166 $renderer->printgraph($project, \@colors);
168 # print report
170 our $report = 'graph-includes.report';
171 $report = $outfile . '.' . $report if defined $outfile;
172 open REPORT, ">$report" or die "cannot open $report for writing: $!";
173 print REPORT "\n Graph-includes report";
174 print REPORT "\n =====================\n";
176 print REPORT "\nGeneral statistics:";
177 print REPORT "\n-------------------\n\n";
178 print REPORT "$stat_nfiles files, $stat_nnodes nodes (",
179 int(100*($stat_nfiles-$stat_nnodes)/$stat_nfiles), "% dropped)\n";
180 print REPORT "$stat_ndeps dependencies, $stat_nedges edges (",
181 int(100*($stat_ndeps-$stat_nedges)/$stat_ndeps), "% dropped)\n";
182 print REPORT "$stat_nleaves leaf node(s)\n";
184 print REPORT "\n";
185 print REPORT scalar keys %{$project->{REPORT}->{HDR}}, " dependencies not found\n";
186 print REPORT scalar keys %{$project->{REPORT}->{SYS}}, " dependencies identified as system headers\n";
188 print REPORT "\nDeclared dependencies not found:";
189 print REPORT "\n--------------------------------\n\n";
190 for my $dep (sort keys %{$project->{REPORT}->{HDR}}) {
191 print REPORT " $dep\n";
192 for my $src (@{$project->{REPORT}->{HDR}->{$dep}}) {
193 print REPORT " from $src\n";
197 print REPORT "\nUsed system headers:";
198 print REPORT "\n--------------------\n\n";
199 for my $dep (sort keys %{$project->{REPORT}->{SYS}}) {
200 print REPORT " $dep\n";
203 print REPORT "\nCommand-line used:";
204 print REPORT "\n------------------\n\n";
205 # display arguments separated by space, quoting any argument with embedded whitespace
206 print REPORT "$0 ", join ' ', map { m/\s/ ? "\"$_\"" : $_ } @commandline;
208 print REPORT "\n\nThis was $0 version $graphincludes::params::VERSION\n";
209 print REPORT "\n=== End of report ===\n";
210 close REPORT;
212 # wait for renderer to finish if needed
213 $renderer->wait();