-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
yaml --- r: 122988 b: refs/heads/master c: 179efcb h: refs/heads/master v: v3
- Loading branch information
Vegard Nossum
authored and
Sam Ravnborg
committed
Dec 18, 2008
1 parent
f1f5418
commit cd57e55
Showing
3 changed files
with
200 additions
and
2 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
--- | ||
refs/heads/master: a6ba0cb35da64d658b7a01ea4597416f8522d5e1 | ||
refs/heads/master: 179efcb47d5a5dd34a45be3f0eca4bffa717c6b4 |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
#! /usr/bin/perl | ||
# | ||
# Detect cycles in the header file dependency graph | ||
# Vegard Nossum <vegardno@ifi.uio.no> | ||
# | ||
|
||
use strict; | ||
use warnings; | ||
|
||
use Getopt::Long; | ||
|
||
my $opt_all; | ||
my @opt_include; | ||
my $opt_graph; | ||
|
||
&Getopt::Long::Configure(qw(bundling pass_through)); | ||
&GetOptions( | ||
help => \&help, | ||
version => \&version, | ||
|
||
all => \$opt_all, | ||
I => \@opt_include, | ||
graph => \$opt_graph, | ||
); | ||
|
||
push @opt_include, 'include'; | ||
my %deps = (); | ||
my %linenos = (); | ||
|
||
my @headers = grep { strip($_) } @ARGV; | ||
|
||
parse_all(@headers); | ||
|
||
if($opt_graph) { | ||
graph(); | ||
} else { | ||
detect_cycles(@headers); | ||
} | ||
|
||
|
||
sub help { | ||
print "Usage: $0 [options] file...\n"; | ||
print "\n"; | ||
print "Options:\n"; | ||
print " --all\n"; | ||
print " --graph\n"; | ||
print "\n"; | ||
print " -I includedir\n"; | ||
print "\n"; | ||
print "To make nice graphs, try:\n"; | ||
print " $0 --graph include/linux/kernel.h | dot -Tpng -o graph.png\n"; | ||
exit; | ||
} | ||
|
||
sub version { | ||
print "headerdep version 2\n"; | ||
exit; | ||
} | ||
|
||
# Get a file name that is relative to our include paths | ||
sub strip { | ||
my $filename = shift; | ||
|
||
for my $i (@opt_include) { | ||
my $stripped = $filename; | ||
$stripped =~ s/^$i\///; | ||
|
||
return $stripped if $stripped ne $filename; | ||
} | ||
|
||
return $filename; | ||
} | ||
|
||
# Search for the file name in the list of include paths | ||
sub search { | ||
my $filename = shift; | ||
return $filename if -f $filename; | ||
|
||
for my $i (@opt_include) { | ||
my $path = "$i/$filename"; | ||
return $path if -f $path; | ||
} | ||
|
||
return undef; | ||
} | ||
|
||
sub parse_all { | ||
# Parse all the headers. | ||
my @queue = @_; | ||
while(@queue) { | ||
my $header = pop @queue; | ||
next if exists $deps{$header}; | ||
|
||
$deps{$header} = [] unless exists $deps{$header}; | ||
|
||
my $path = search($header); | ||
next unless $path; | ||
|
||
open(my $file, '<', $path) or die($!); | ||
chomp(my @lines = <$file>); | ||
close($file); | ||
|
||
for my $i (0 .. $#lines) { | ||
my $line = $lines[$i]; | ||
if(my($dep) = ($line =~ m/^#\s*include\s*<(.*?)>/)) { | ||
push @queue, $dep; | ||
push @{$deps{$header}}, [$i + 1, $dep]; | ||
} | ||
} | ||
} | ||
} | ||
|
||
sub print_cycle { | ||
# $cycle[n] includes $cycle[n + 1]; | ||
# $cycle[-1] will be the culprit | ||
my $cycle = shift; | ||
|
||
# Adjust the line numbers | ||
for my $i (0 .. $#$cycle - 1) { | ||
$cycle->[$i]->[0] = $cycle->[$i + 1]->[0]; | ||
} | ||
$cycle->[-1]->[0] = 0; | ||
|
||
my $first = shift @$cycle; | ||
my $last = pop @$cycle; | ||
|
||
my $msg = "In file included"; | ||
printf "%s from %s,\n", $msg, $last->[1] if defined $last; | ||
|
||
for my $header (reverse @$cycle) { | ||
printf "%s from %s:%d%s\n", | ||
" " x length $msg, | ||
$header->[1], $header->[0], | ||
$header->[1] eq $last->[1] ? ' <-- here' : ''; | ||
} | ||
|
||
printf "%s:%d: warning: recursive header inclusion\n", | ||
$first->[1], $first->[0]; | ||
} | ||
|
||
# Find and print the smallest cycle starting in the specified node. | ||
sub detect_cycles { | ||
my @queue = map { [[0, $_]] } @_; | ||
while(@queue) { | ||
my $top = pop @queue; | ||
my $name = $top->[-1]->[1]; | ||
|
||
for my $dep (@{$deps{$name}}) { | ||
my $chain = [@$top, [$dep->[0], $dep->[1]]]; | ||
|
||
# If the dep already exists in the chain, we have a | ||
# cycle... | ||
if(grep { $_->[1] eq $dep->[1] } @$top) { | ||
print_cycle($chain); | ||
next if $opt_all; | ||
return; | ||
} | ||
|
||
push @queue, $chain; | ||
} | ||
} | ||
} | ||
|
||
sub mangle { | ||
$_ = shift; | ||
s/\//__/g; | ||
s/\./_/g; | ||
s/-/_/g; | ||
$_; | ||
} | ||
|
||
# Output dependency graph in GraphViz language. | ||
sub graph { | ||
print "digraph {\n"; | ||
|
||
print "\t/* vertices */\n"; | ||
for my $header (keys %deps) { | ||
printf "\t%s [label=\"%s\"];\n", | ||
mangle($header), $header; | ||
} | ||
|
||
print "\n"; | ||
|
||
print "\t/* edges */\n"; | ||
for my $header (keys %deps) { | ||
for my $dep (@{$deps{$header}}) { | ||
printf "\t%s -> %s;\n", | ||
mangle($header), mangle($dep->[1]); | ||
} | ||
} | ||
|
||
print "}\n"; | ||
} |