create a graph-includes.report file to hold deps not found
[deps.git] / graph-includes
blobef86e6b01264a248a06ab2777eae62869d5b10e3
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 Getopt::Long;
14 use graphincludes::params;
16 package foo;
17 our $a=0;
18 package main;
19 $a=1;
20 #FIXME: also set arrow head
21 our $paperparams='-Gnodesep=0.1 -Granksep=0.1 -Nfontsize=5 -Efontsize=5';
22 our %paper = (
23 a4 => '11.6,8.2',
24 a3 => '16.5,11.6',
25 letter => '11,8.5',
28 our $showalldeps=0;
29 our $class='default';
30 our (@colors);
31 our ($outfile, $prefixstrip, $paper);
33 our $usage = <<EOF;
34 Usage: $0 [options] src/*.[ch]
35 Options:
36 --class {default|uniqueincludes|<your-own-class>}
37 Select "class" of source code
38 -I <directory> Adds a directory to the path where to look for 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 --output <outfile>.<fmt>
49 Format to output file, using <fmt> as target format
51 --verbose Show progress
52 --verbose Show details of ignored include directives
53 --debug Loads of debuging output
55 --help This help text
56 EOF
58 our @colspecs;
60 GetOptions ('alldeps' => \$showalldeps,
61 'showdropped' => \$graphincludes::params::showdropped,
63 'focus=s' => \@graphincludes::params::focus,
64 'class=s' => \$class,
66 'I=s' => \@graphincludes::params::inclpath,
68 'group=s' => sub {
69 my (undef, $range) = @_;
70 ($graphincludes::params::minshow, $graphincludes::params::maxshow) = split /-/, $range;
72 'color=s@' => sub {
73 my (undef, $colspec) = @_;
74 my @temp = split /:/, $colspec;
75 push @colspecs, [$temp[1], $temp[2]];
77 'output=s' => \$outfile,
78 'paper=s' => \$paper,
80 'prefixstrip=s' => \$prefixstrip,
82 'verbose+' => \$graphincludes::params::verbose,
83 'debug' => \$graphincludes::params::debug,
84 'help' => sub { print $usage; exit 0; },
86 ) or die "Could not parse command-line: $!";
88 # deal with non-default output formats
90 our $dotflags = '';
91 our $outputformat;
93 if (defined $paper) {
94 die "Unkown paper size \`$paper'" unless defined $paper{$paper};
95 # paper output format is postscript on stdout unless otherwise specified
96 $outputformat = 'ps';
97 $dotflags .= " $paperparams -Gpage=" . $paper{$paper};
100 if (defined $outfile) {
101 $dotflags .= " -o $outfile";
103 $outfile =~ m/.*\.([^.]+)$/ or die "cannot guess output format";
104 $outputformat = $1;
107 $dotflags .= " -T$outputformat" if defined $outputformat;
109 if ($dotflags ne '') {
110 print STDERR "Running through \`dot $dotflags'\n" if $graphincludes::params::verbose;
111 open STDOUT, "| dot $dotflags";
114 # create a project with specified files
115 our $classmodule = "graphincludes::project::" . $class;
116 eval "require $classmodule" or die "cannot locate $classmodule in " . join ':', @INC;
117 our $project = ($classmodule)->new(prefixstrip => $prefixstrip,
118 files => \@ARGV);
119 $project->init();
121 @colors = $project->defaultcolors();
122 foreach my $colspec (@colspecs) {
123 foreach my $coldef (split /,/, $colspec->[2]) {
124 my @coldef = split /=/, $coldef;
125 $colors[$colspec->[1]]->{$coldef[0]} = $coldef[1];
129 # maybe get rid of shortcut deps (transitive reduction)
130 $project->reduce() unless ($showalldeps);
133 # print graph
135 print "strict digraph dependencies {\nrankdir=LR;\n";
137 sub sprintnode {
138 my ($file, $min, $max) = @_;
139 my $node = $project->filelabel($file,$max);
140 if (!defined $node and $max > $min) {
141 return sprintnode($file, $min, $max-1);
142 } elsif ($min == $max) {
143 my $fill='';
144 $fill=",style=filled,fillcolor=" . $colors[2]->{$project->filelabel($file,2)}
145 if defined $colors[2] and defined $project->filelabel($file,2) and
146 defined $colors[2]->{$project->filelabel($file,2)};
147 return $project->{NODEIDS}->{$node} . " [label=\"$node\"" . $fill . "];";
148 } else {
149 return "subgraph \"cluster $node\" {" . sprintnode($file, $min, $max-1) . '}';
153 foreach my $file (@{$project->{FILES}}) {
154 print sprintnode($file, $graphincludes::params::minshow, $graphincludes::params::maxshow), "\n";
157 foreach my $file (keys %{$project->{DEPS}}) {
158 foreach my $dest (@{$project->{DEPS}->{$file}}) {
159 print "$file -> $dest";
160 my $special = $project->special_edge($file, $dest);
161 print " [$special]" if defined $special;
162 print ";\n";
166 print "}\n";
169 # print report
171 our $report = 'graph-includes.report';
172 $report = $outfile . $report if defined $outfile;
173 open REPORT, ">$report" or die "cannot open $report for writing: $!";
174 print REPORT " Graph-includes report\n";
175 print REPORT " =====================\n\n";
177 print REPORT "Declared dependencies not found:\n";
178 print REPORT "--------------------------------\n\n";
179 for my $dep (sort keys %{$project->{REPORT}}) {
180 print REPORT " $dep\n";
181 for my $src (@{$project->{REPORT}->{$dep}}) {
182 print REPORT " from $src\n";
185 close REPORT;
187 # wait for dot if needed
188 if ($dotflags ne '') {
189 close STDOUT;
190 wait;