3 # graph-includes - create a graphviz graph of source-files
4 # dependencies, with an emphasis on getting usable graphs even for
7 # (c) 2005 Yann Dirson <ydirson@altern.org>
8 # Distributed under version 2 of the GNU GPL.
13 use Getopt
::Long
qw(GetOptions);
14 use graphincludes
::params
;
20 #FIXME: also set arrow head
21 our $paperparams='-Gnodesep=0.1 -Granksep=0.1 -Nfontsize=5 -Efontsize=5';
32 our ($outfile, $prefixstrip, $paper);
35 Usage: $0 [options] src/*.[ch]
37 --class {default|uniqueincludes|<your-own-class>}
38 Select "class" of source code
39 --language <lang> Select language syntax for dependency extraction (default: C)
40 -I <directory> Adds a directory to the path where to look for include files
41 --prefixstrip <prefix> Strip <prefix> (eg. "src/") from filenames in the graph
42 --group <min>-<max> Draw file groups of levels <min> through <max> (default: 1 1)
43 --color <n>:<label>=<color>[,<label>=<color>...]
44 Use specified colors to show members of level-<n> group labelled <label>
45 --alldeps Do not apply transitive reduction to the graph
47 --showdropped Show in special color edges dropped during transitive reduction
48 --focus <node-label> Like -showdropped but only for edges starting from given node
50 --output <outfile>.<fmt>
51 Format to output file, using <fmt> as target format
53 --verbose Show progress
54 --verbose Show details of ignored include directives
55 --debug Loads of debuging output
62 GetOptions
('alldeps' => \
$showalldeps,
63 'showdropped' => \
$graphincludes::params
::showdropped
,
65 'focus=s' => \
@graphincludes::params
::focus
,
67 'language=s' => \
$language,
69 'Include=s' => \
@graphincludes::params
::inclpath
,
70 'sysInclude=s' => \
@graphincludes::params
::sysinclpath
,
73 my (undef, $range) = @_;
74 ($graphincludes::params
::minshow
, $graphincludes::params
::maxshow
) = split /-/, $range;
77 my (undef, $colspec) = @_;
78 my @temp = split /:/, $colspec;
79 push @colspecs, [$temp[1], $temp[2]];
81 'output=s' => \
$outfile,
84 'prefixstrip=s' => \
$prefixstrip,
86 'verbose+' => \
$graphincludes::params
::verbose
,
87 'debug' => \
$graphincludes::params
::debug
,
88 'help' => sub { print $usage; exit 0; },
90 ) or die "Could not parse command-line: $!";
92 # deal with non-default output formats
98 die "Unkown paper size \`$paper'" unless defined $paper{$paper};
99 # paper output format is postscript on stdout unless otherwise specified
100 $outputformat = 'ps';
101 $dotflags .= " $paperparams -Gpage=" . $paper{$paper};
104 if (defined $outfile) {
105 $dotflags .= " -o $outfile";
107 $outfile =~ m/.*\.([^.]+)$/ or die "cannot guess output format";
111 $dotflags .= " -T$outputformat" if defined $outputformat;
113 if ($dotflags ne '') {
114 print STDERR
"Running through \`dot $dotflags'\n" if $graphincludes::params
::verbose
;
115 open STDOUT
, "| dot $dotflags";
118 # create a project with specified files
119 our $classmodule = "graphincludes::project::" . $class;
120 eval "require $classmodule" or die "cannot load $classmodule from " . join ':', @INC;
121 $classmodule->set_language ($language) or die "cannot set language to '$language'";
122 our $project = ($classmodule)->new(prefixstrip
=> $prefixstrip,
126 @colors = $project->defaultcolors();
127 foreach my $colspec (@colspecs) {
128 foreach my $coldef (split /,/, $colspec->[2]) {
129 my @coldef = split /=/, $coldef;
130 $colors[$colspec->[1]]->{$coldef[0]} = $coldef[1];
134 # maybe get rid of shortcut deps (transitive reduction)
135 $project->reduce() unless ($showalldeps);
140 print "strict digraph dependencies {\nrankdir=LR;\n";
143 my ($file, $min, $max) = @_;
144 my $node = $project->filelabel($file,$max);
145 if (!defined $node and $max > $min) {
146 return sprintnode
($file, $min, $max-1);
147 } elsif ($min == $max) {
149 $fill=",style=filled,fillcolor=" . $colors[2]->{$project->filelabel($file,2)}
150 if defined $colors[2] and defined $project->filelabel($file,2) and
151 defined $colors[2]->{$project->filelabel($file,2)};
152 return $project->{NODEIDS
}->{$node} . " [label=\"$node\"" . $fill . "];";
154 return "subgraph \"cluster $node\" {" . sprintnode
($file, $min, $max-1) . '}';
158 foreach my $file (@
{$project->{FILES
}}) {
159 print sprintnode
($file, $graphincludes::params
::minshow
, $graphincludes::params
::maxshow
), "\n";
162 foreach my $file ($project->get_dep_origins) {
163 foreach my $dest ($project->get_dependencies($file)) {
164 print "$file -> $dest";
165 my $special = $project->special_edge($file, $dest);
166 # special handling for label, as array
167 push @
{$special->{label
}}, '[' . $project->get_dependency_weight($file, $dest) . ']';
168 $special->{label
} = join '\n', @
{$special->{label
}};
170 print " [", join (',', map {$_ . '="' . $special->{$_} . '"'} keys %$special), "]" if defined $special;
180 our $report = 'graph-includes.report';
181 $report = $outfile . '.' . $report if defined $outfile;
182 open REPORT
, ">$report" or die "cannot open $report for writing: $!";
183 print REPORT
" Graph-includes report\n";
184 print REPORT
" =====================\n";
186 print REPORT
"\nDeclared dependencies not found:";
187 print REPORT
"\n--------------------------------\n\n";
188 for my $dep (sort keys %{$project->{REPORT
}->{HDR
}}) {
189 print REPORT
" $dep\n";
190 for my $src (@
{$project->{REPORT
}->{HDR
}->{$dep}}) {
191 print REPORT
" from $src\n";
195 print REPORT
"\nUsed system headers:";
196 print REPORT
"\n--------------------\n\n";
197 for my $dep (sort keys %{$project->{REPORT
}->{SYS
}}) {
198 print REPORT
" $dep\n";
202 # wait for dot if needed
203 if ($dotflags ne '') {