#!/usr/local/bin/perl
use strict;
use File::Basename;
=pod
=head1 wbw.pl - What Breaks What
=head2 Purpose
What Breaks What processes a make file (e.g., libdeps.mk).
It recursively examines the items in each target : dependencies line and
constructs a table of what breaks what. The left column lists
the items. The right column lists what items will break if the item in
the left column is not present for some reason.
=head2 Modifications
Date Author E-mail Adr Modification
---- ------ ---------- ------------
2001/03/09 D. Ritchie ritchie@fnal.gov Original
2001/03/19 D. Ritchie ritchie@fnal.gov Deal with circular target:components
=cut
#
# debug settings
# debug = 0 -> no invalid format or other error messages, just what breaks what output
# debug = 1 -> invalid format messages and what breaks what output
# debug = 2 -> debugging info
# debug = 3 -> even more debugging info
$main::debug = 0;
@main::AnalysisSummary = ();
my $wait;
#
# internal constants
# Something that can not be a target
# (because anything that begins with tab is ignored)
# (and target names are not allowed to include white space)
my $IsTarget = "\tTARGET";
#
# process arguments
# input and output files with no defaults
my $infile;
my $outfile;
#
# format
my $format = 'h';
my $NewLine = "
\n";
my $BegLine = "";
my $RedOnFont = '';
my $RedOffFont = '';
#
# Target Special Action
my %TargetSpecialActions;
#
# if the number of arguments is less than 4, then error
if ($#ARGV+1 < 4) { &Usage; exit; }
#
# at least four arguments
# (i.e., an input switch, an input file, an output switch, an output file)
while (@ARGV) {
# get the switch and its value
my $SwitchParam = shift @ARGV;
my $ValueParam = shift @ARGV;
#
# if switch param does not match minus sign plus character)
if ($SwitchParam !~ m/^\-(.)/) { &Usage; exit; }
my $Switch = $1;
SWITCH: {
if ($Switch eq 'i') { $infile = $ValueParam; last SWITCH; }
if ($Switch eq 'o') { $outfile = $ValueParam; last SWITCH; }
if ($Switch eq 'f') { $format = $ValueParam; last SWITCH; }
if ($Switch eq 'd') { $main::debug = $ValueParam; last SWITCH; }
if ($Switch eq 'u') { $TargetSpecialActions{$ValueParam} = 'u'; last SWITCH; }
if ($Switch eq 'h') { &Usage; exit; }
#
# unrecognized switch
&Usage;
exit;
}
}
#
# check switch settings and issue message if not ok
#
# debug switch
if ($main::debug !~ m/\d/) { &Usage; exit; }
if ($main::debug < 0 or $main::debug > 3) { &Usage; exit; }
#
# format switch
if ($format eq 'h') {
$NewLine = "
\n";
$BegLine = "";
} elsif ($format eq 't') {
$NewLine = "\n";
$BegLine = "# ";
} else {
&Usage;
exit;
}
#
# warn about no target special actions
if (scalar keys %TargetSpecialActions == 0 ) {
#
# no target special actions
print STDERR "Warning - no target special actions specified.\n",
".PHONY's will be included!\n";
}
#
# input and output files
if (! $infile or ! $outfile) { &Usage; exit; }
if (-e $outfile) { die "Output file $outfile exists";}
open INFILE, $infile or die "Can't open $infile: $!";
open OUTFILE, ">$outfile" or die "Can't open $outfile: $!";
#
# set title
my $Title = "What Breaks What";
#
# put out header
print OUTFILE "\n" if ($format eq 'h');
print OUTFILE "
If a circular reference is detected, the item is shown in $RedOnFont red $RedOffFont and an error message is printed.
EOF print OUTFILE '
ritchie@fnal.gov'; print OUTFILE "
\n"; print OUTFILE $BegLine, "Number of Targets: ", $item - 1, " Number of Items That Can Break Things: $NumberOfItemsThatCanBreakThings", $NewLine; print OUTFILE $BegLine, '',"Items Making Circular References (if any)", $NewLine; if ($main::debug >= 3) { my $i; for ($i = 0; $i < $item ; $i++) { print OUTFILE $BegLine, "Target: $i Target: $Targets[$i]", $NewLine; } } print OUTFILE "
| A Bug in this... | Will break all of these... | \n"; } elsif ($format eq 't') { print OUTFILE "# $Title\n# \n"; print OUTFILE <<'EOF'; # What Breaks What processes a make file (e.g., libdeps.mk). # It recursively examines the items in each target : dependencies # line and constructs a table of what breaks what. # The left column lists the items. The right column lists # what items will break if the item in the left column is not # present for some reason. In parentheses next to each # item in the right column is shown the "break level"-- # how many levels deep the item appears. Within break levels, the # items are sorted alphabetically. # # If a circular reference is detected, the item is shown in # surrounded by asterisks and an error message is printed. # # --ritchie@fnal.gov EOF print OUTFILE "# \n# Summary\n# \n"; print OUTFILE "# Input file: $infile\n"; print OUTFILE "# Output file: $outfile\n"; my @Specials = keys %TargetSpecialActions; print OUTFILE "# Undesired targets: @Specials\n# \n"; print OUTFILE "# Number of Targets: ", $item - 1, " Number of Items That Can Break Things: $NumberOfItemsThatCanBreakThings\n"; print OUTFILE "# See the list of items making circular references at the end of this list.\n"; if ($main::debug >= 3) { my $i; for ($i = 0; $i < $item ; $i++) { print OUTFILE "# Target: $i Target: $Targets[$i]\n"; } } print OUTFILE "# \n# Details\n# \n"; print OUTFILE "# A Bug in this...will break all of these...\n"; print OUTFILE "# ------------------------------------------\n"; } else { print STDERR "Illegal value for $format\n"; } # # for each component that breaks something, make a combined list of what it breaks and what # that thing breaks and so on. my $MaximumNumberOfComponents = ($main::debug >= 3 ) ? 10 : -1 ; my $CountOfComponents = 0; my $ThisComponent; foreach $ThisComponent (sort keys %ListOfWhatIBreak) { $CountOfComponents++; if ($MaximumNumberOfComponents > 0 and $CountOfComponents > $MaximumNumberOfComponents) { print OUTFILE $BegLine, "Number Of Components: $CountOfComponents exceeded $MaximumNumberOfComponents", $NewLine if ($main::debug >= 2); exit; } if (!$ThisComponent) { print STDERR "Component ($ThisComponent) Null\n"; } print OUTFILE $BegLine, "Working on $ThisComponent", $NewLine if ($main::debug >= 2); # # if the hash for this component key does not have a reference (to an array) # as its value, then something is wrong. if (! ref $ListOfWhatIBreak{$ThisComponent} ) { die "Error: Component $ThisComponent is not part of anything"; } # # initialize the break hash for this component %main::BreakHash = (); $main::BreakLevel = 1; $main::BreakStartingComponent = $ThisComponent; AnalyzeComponent($ThisComponent); my $ComponentLineNumber = 0; if ($format eq 'h') { print OUTFILE "|
|---|---|---|
| $ThisComponent | "; } elsif ($format eq 't') { print OUTFILE "$ThisComponent->"; } else { print STDERR "Illegal value for $format\n"; } my $BreakItemsSeen = 0; my $BreakItem; my $NumberItemsOnLine = 4; my $ItemsPutOnLine = 0; # # define a subroutine to assist in sorting by break level sub ByBreakLevel { $main::BreakHash{$a} <=> $main::BreakHash{$b} or $a cmp $b; } foreach $BreakItem ( sort ByBreakLevel keys %main::BreakHash ) { $BreakItemsSeen = 1; if ($ItemsPutOnLine == 0) { if ($ComponentLineNumber != 0) { if ($format eq 'h') { print OUTFILE "||
| "; } elsif ($format eq 't') { print OUTFILE "------>"; } else { print STDERR "Illegal value for $format\n"; } } } if ($format eq 'h') { print OUTFILE " | $RedOnFont$BreakItem (", -$main::BreakHash{$BreakItem}, ") $RedOffFont | " if ($main::BreakHash{$BreakItem} < 0); print OUTFILE "$BreakItem ($main::BreakHash{$BreakItem}) | " if ($main::BreakHash{$BreakItem} > 0); } elsif ($format eq 't') { print OUTFILE "***$BreakItem (", -$main::BreakHash{$BreakItem}, ")*** " if ($main::BreakHash{$BreakItem} < 0); print OUTFILE "$BreakItem ($main::BreakHash{$BreakItem}) " if ($main::BreakHash{$BreakItem} > 0); } $ItemsPutOnLine++; if ($ItemsPutOnLine == $NumberItemsOnLine ) { if ($format eq 'h') { print OUTFILE "
| "; } elsif ($format eq 't') { print OUTFILE "------>"; } else { print STDERR "Illegal value for $format\n"; } } } if ($format eq 'h') { print OUTFILE " | "; } elsif ($format eq 't') { print OUTFILE " "; } else { print STDERR "Illegal value for $format\n"; } $ItemsPutOnLine++; } $ComponentLineNumber++; if ($format eq 'h') { print OUTFILE " |