#!/usr/bin/perl -w use strict; use English; my (@pkg, %opr, %pri, %dep, %rep, %rdp, %ign, %lop, %deldeps, %bas); my @useddeps; # deps used for visualization of cross dependencies my %pkg_redone; # packages built in stages 6-8 my $config = ""; while ($_ = shift @ARGV) { if ( $_ eq "-cfg" ) { $config = shift @ARGV; } elsif ( /^-/ ) { print "\n"; print "Usgage: scripts/Check-Deps-2 [ -cfg config-name ]\n"; print "\n"; print "This script does some dependency checking and suggests\n"; print "package priority reorderings (if neccessary).\n"; print "\n"; print "The data from scripts/dep_db.txt and scripts/dep_fixes.txt\n"; print "is used for the dependency analysis.\n"; print "\n"; exit 1; } else { $ign{$_} = 1; } } unlink $_ foreach qw/dependencies.dbg dependencies.dot dependencies.patch dependencies.png dependencies.ps/; print "Reading package priorities ...\n"; if ( $config eq "" ) { open(F, "./scripts/Create-PkgList |") || die $!; } else { open(F, "< config/$config/packages") || die $!; } while () { @_ = split /\s+/; next if $_[1] =~ /[1234]/ or $_[1] !~ /5/; $pkg_redone{$_[4]} = 1 if $_[1] =~ /[678]/; next if defined $ign{$_[3]} || defined $ign{$_[4]}; my ($b, $p) = ($_[4], $_[4]); ($b, $p) = ($1, $2) if $_[4] =~ /(.*)=(.*)/; $opr{$p} = $_[2]; $pri{$p} = $_[2]; $rep{$p} = $_[3]; $bas{$p} = $b; $pkg[$#pkg+1] = $p; } close F; print "Reading dependency fixes ...\n"; open(F, "scripts/dep_fixes.txt") or die $!; while () { chomp; if (/^([^#\s]\S*)\s+del\s+(.*)$/) { my ($p, $l) = ($1, $2); $deldeps{$p}{$_} = 1 foreach (split /\s+/, $l); next; } if (/^([^#\s]\S*)\s+add\s+(.*)$/) { my ($p, $l) = ($1, $2); foreach ( split /\s+/, $l ) { push @{$dep{$p}}, $_; push @{$rdp{$_}}, $p; } next; } } close F; print "Reading package dependencies ...\n"; open(F, "scripts/dep_db.txt") || die $!; while () { chomp; if ( ! /^(\S+): \d+ \d+ (.*)$/ ) { print "Format Error: $_\n"; exit 1; } my ($p, $l) = ($1, $2); next if defined $pkg_redone{$p}; foreach ( split /\s+/, $l ) { next if defined $deldeps{$p}{$_}; push @{$dep{$p}}, $_; push @{$rdp{$_}}, $p; } } close F; sub count_errs($) { my $package = $_[0]; my $dependency; my $errors = 0; foreach $dependency (@{$dep{$package}}) { next unless defined $pri{$dependency}; $errors++ if $pri{$package} < $pri{$dependency}; } foreach $dependency (@{$rdp{$package}}) { next unless defined $pri{$dependency}; $errors++ if $pri{$package} > $pri{$dependency}; } return $errors; } my ($iteration, $package, $dependency, $a, $b); my $did_something=0; print "\nLoop Old/New Errors Package Dependency\n". "------------------------------------------------------------------------\n"; for $iteration (1..99) { my $looplog = ''; foreach $package (@pkg) { foreach $dependency (@{$dep{$package}}) { next unless defined $pri{$dependency}; if ( $pri{$package} < $pri{$dependency} ) { $a = count_errs($package) + count_errs($dependency); $_ = $pri{$dependency}; $pri{$dependency} = $pri{$package}; $pri{$package} = $_; $b = count_errs($package) + count_errs($dependency); $looplog.="[$package,$dependency]"; $_ = sprintf "[%02d] %-7d %-7d %-25s %s\n", $iteration, $a, $b, $pri{$dependency}." ".$package, $pri{$package}." ".$dependency; $useddeps[$iteration]{$package}{$dependency} = 1; s/ / . /g; s/\. /.. /g; s/\. /.. /g; s/\. (\s*)\./..$1./g; s/\. (\s*)\./..$1./g; print; $did_something=1; } } } last if $looplog eq ""; if (defined $lop{$looplog}) { my %crossdeps; print "[XX] Detected endless-loop ". "(cross-dependency) -> Aborting now.\n"; print "[XX] Debug graph printed to dependencies.dot.\n"; foreach my $i ($lop{$looplog} .. $iteration) { foreach my $p (keys %{$useddeps[$i]}) { foreach my $d (keys %{$useddeps[$i]{$p}}) { $crossdeps{$d}{$p} = 1; } } } open(F, ">dependencies.dot") || die $!; print F "# run this thru e.g. 'dot -Tps dependencies.dot -o dependencies.ps'\n"; print F "digraph \"Cross-Dependencies Graph\" {\n"; print F " Package_X -> Has_X_in_Dep_List;\n"; foreach my $p (sort keys %crossdeps) { foreach my $d (sort keys %{$crossdeps{$p}}) { my $pt = `gawk '/^.TIMESTAMP/ && !/ERROR/ { print \$2; exit; }' package/*/$p/*.cache 2> /dev/null`; my $dt = `gawk '/^.TIMESTAMP/ && !/ERROR/ { print \$2; exit; }' package/*/$d/*.cache 2> /dev/null`; chomp $pt; chomp $dt; my $p_ = $p; $p_ =~ s/[^a-z0-9]/_/g; my $d_ = $d; $d_ =~ s/[^a-z0-9]/_/g; if ( $pt eq "" || $dt eq "" || $pt < $dt ) { print F "\t$p_ -> $d_;\n"; } else { print F "#\t$p = ($pt), $d = ($dt)\n"; print F "\t$p_ -> $d_ [color=red];\n"; } } } print F "}\n"; close F; open(F, ">dependencies.dbg") || die $!; foreach my $p (sort keys %crossdeps) { foreach my $d (sort keys %{$crossdeps{$p}}) { print F "$p $d\n"; } } close F; system("dot -Tps dependencies.dot -o dependencies.ps"); system("convert dependencies.ps dependencies.png"); last; } $lop{$looplog} = $iteration; } sub patchfile($$$$) { my ($tmpfile, $descfile, $re1, $re2) = @_; if ( ! open(IN, $descfile) ) { print "ERROR: $descfile: $!\n"; return; } if ( ! open(OUT, ">$tmpfile") ) { print "ERROR: $descfile: $!\n"; close IN; return; } $did_something = 0; while () { $did_something = 1 if eval "s/$re1/$re2/i"; print OUT; } close IN; close OUT; if (not $did_something) { print "ERROR: Can't patch $descfile!\n"; print "ERROR: Regex was s/$re1/$re2/\n"; } system("diff -U 0 ./$descfile $tmpfile >> dependencies.patch"); } sub setpri($$$$$$) { my ($pri, $opr, $rep, $bas, $package, $tmpfile) = @_; if ($bas eq "cpan") { my $r = $package; $r =~ s/^cpan-//g; $r =~ s/-/(-|::)/g; patchfile($tmpfile, "package/import/cpan/hosted_cpan.txt", "$opr ($r)", "$pri \$1"); patchfile($tmpfile, "package/import/cpan/hosted_cpan.cfg", "(pkgfork cpan $package .*) $opr;", "\$1 $pri;"); return; } patchfile($tmpfile, "package/$rep/$bas/$package.desc", "(\\[P\\] . \\S+) $opr", "\$1 $pri"); } if ( $did_something ) { print "\nCreate dependencies.patch ...\n"; my $tmpfile = `mktemp`; chomp $tmpfile; foreach $package (@pkg) { if ($pri{$package} != $opr{$package}) { print "Setting priority $pri{$package} on package $rep{$package}/$bas{$package}=$package.\n"; setpri($pri{$package}, $opr{$package}, $rep{$package}, $bas{$package}, $package, $tmpfile); } } unlink $tmpfile; print "Done. Please check moves manually before applying the patch.\n"; } else { print "No unresolved dependencies found.\n"; }