pkgsrc/pkgtools/pkgdep/files/pkgdep.pl
2000-11-27 04:42:48 +00:00

298 lines
6 KiB
Perl

#!/usr/bin/env perl
# $NetBSD: pkgdep.pl,v 1.2 2000/11/27 04:42:48 sakamoto Exp $
require 'getopts.pl';
use strict;
$^W=1;
$|=1;
use vars qw($opt_I $opt_h $opt_e $opt_r $opt_v);
my (%pkgdir, %builddeps, %rundeps, %buildreqd, %runreqd, %printed);
my ($indexfile) = "/usr/pkgsrc/INDEX";
my ($package, $match_pkgs);
my (%node, %arrow, $home_level);
my(%color);
$color{"D"} = "black";
$color{"B"} = "blue";
$color{"R"} = "red";
if (!&Getopts('I:herv') || $opt_h) {&usage_and_exit;}
if (defined($opt_I)) {$indexfile = $opt_I;}
$package = shift;
if (!defined($package)) {&usage_and_exit;}
open(INDEX, $indexfile) || die "$indexfile: $!\n";
while (<INDEX>) {
my (@data) = split(/\|/);
$data[1] =~ s/.*\/([^\/]+\/[^\/]+)$/$1/;
$pkgdir{$data[0]} = $data[1];
$builddeps{$data[0]} = $data[7];
$rundeps{$data[0]} = $data[8];
}
close(INDEX);
%buildreqd = &mkreqd(%builddeps);
%runreqd = &mkreqd(%rundeps);
for (keys %pkgdir) {
my ($match) = 0;
if (defined($opt_e)) {
if (/$package/ || $pkgdir{$_} =~ /$package/) {
$match = 1;
}
} else {
if ($_ eq $package || $pkgdir{$_} eq $package) {
$match = 1;
}
}
if ($match) {
$match_pkgs .= " $_";
}
}
if (!defined($match_pkgs)) {
print "no such package \'$package\'\n";
exit 1;
} else {
$match_pkgs =~ s/^ //;
}
if (split(/ /, $match_pkgs) != 1) {
print "\'$package\' matches the following packages.\n";
for (split(/ /, $match_pkgs)) {
if (!defined($pkgdir{$_})) {$_ = &searchpkg($_);}
print "\t$_ [$pkgdir{$_}]\n";
}
exit 1;
}
if (!defined($opt_v)) {
my ($pkg) = $match_pkgs;
my (@pkglist) = $pkg;
while ($#pkglist >= 0) {
my ($p);
($p, @pkglist) = @pkglist;
if (defined($printed{$p})) {next;}
print "\n$p [$pkgdir{$p}]\n";
$printed{$p} = 1;
@pkglist = (@pkglist,
&printdeps($p, "build", %builddeps));
@pkglist = (@pkglist, &printdeps($p, "run", %rundeps));
@pkglist = (@pkglist,
&printreqd($p, "build", %buildreqd));
@pkglist = (@pkglist, &printreqd($p, "run", %runreqd));
}
} else {
my ($pkg) = $match_pkgs;
my ($min);
print "graph: {\n";
print "title: \"Dependent graph of $pkg\"\n";
$home_level = 0;
&depends_search($home_level, "", "", $pkg);
$min = 0;
for (keys %node) {if ($node{$_} < $min) {$min = $node{$_};}}
for (keys %node) {$node{$_} += -$min;}
$home_level = $node{$pkg};
&required_search($home_level, "", "", $pkg);
for (keys %node) {
print "node: { title: \"$_\"\tlevel: $node{$_}" .
" label: \"$_\n$pkgdir{$_}\"}\n";
}
for (keys %arrow) {
print "$arrow{$_}";
}
print "}\n";
}
sub usage_and_exit {
print <<EOF;
Usage: pkgdep [opts] package
-I INDEX file path (default: $indexfile)
-e match package by regular expression
-r recursive
-v output graph data for xvcg
EOF
exit 1;
}
sub mkreqd {
my (%deps) = @_;
my (%reqd);
for (keys %deps) {
my ($p) = $_;
for (split(/ /, $deps{$p})) {
$_ =~ s/(\+)/\\\1/g;
if (defined($reqd{$_})) {
if (!($reqd{$_} =~ /$_/)) {
$reqd{$_} .= " " . $p;
}
} else {
$reqd{$_} .= $p;
}
}
}
%reqd;
}
sub searchpkg {
my ($pkg) = @_;
$pkg =~ s/\*/.\*/;
$pkg =~ s/-[^-]+/-.\*/;
for (keys %pkgdir) {
if ($_ =~ /$pkg/) {
$pkg = $_;
last;
}
}
$pkg;
}
sub printdeps {
my ($pkg, $depname, %deps) = @_;
my (@rpkg);
print "\t$depname depends on:\n";
if (defined($deps{$pkg}) && $deps{$pkg} ne "") {
for (sort split(/ /, $deps{$pkg})) {
if (!defined($pkgdir{$_})) {$_ = &searchpkg($_);}
print "\t\t$_ [$pkgdir{$_}]\n";
if ($opt_r) {@rpkg = (@rpkg, $_);}
}
} else {
print "\t\t(none)\n";
}
@rpkg;
}
sub printreqd {
my ($pkg, $reqname, %reqd) = @_;
my (@rpkg);
print "\t$reqname required by:\n";
if (defined($reqd{$pkg}) && $reqd{$pkg} ne "") {
for (sort split(/ /, $reqd{$pkg})) {
if (!defined($pkgdir{$_})) {$_ = &searchpkg($_);}
print "\t\t$_ [$pkgdir{$_}]\n";
if ($opt_r) {@rpkg = (@rpkg, $_);}
}
} else {
print "\t\t(none)\n";
}
@rpkg;
}
sub depends_search {
my ($level, $from, $type, $p) = @_;
my (%types);
if ($from ne "") {
$arrow{"$from:$p"} =
"edge: { sourcename: \"$from\" targetname: \"$p\"" .
" color: $color{$type}}\n";
}
if (defined($node{$p})) {
if ($level < $node{$p}) {
$node{$p} = $level;
}
return;
}
$node{$p} = $level;
if ($home_level - $level <= 1) {
%types = &mktypes($builddeps{$p}, $rundeps{$p});
for (sort(keys %types)) {
&depends_search($level - 1, $p, $types{$_}, $_);
}
}
if ($opt_r) {
%types = &mktypes($buildreqd{$p}, $runreqd{$p});
for (sort(keys %types)) {
&required_search($level + 1, $p, $types{$_}, $_);
}
}
}
sub required_search {
my ($level, $from, $type, $p) = @_;
my (%types);
if ($from ne "") {
$arrow{"$p:$from"} =
"edge: { sourcename: \"$p\" targetname: \"$from\"" .
" color: $color{$type}}\n";
if (defined($node{$p})) {
if ($level > $node{$p}) {
$node{$p} = $level;
&level_fit($level, $p);
}
return;
}
$node{$p} = $level;
&level_fit($level, $p);
}
if ($level - $home_level <= 1) {
%types = &mktypes($buildreqd{$p}, $runreqd{$p});
for (sort(keys %types)) {
&required_search($level + 1, $p, $types{$_}, $_);
}
}
if ($opt_r) {
%types = &mktypes($builddeps{$p}, $rundeps{$p});
for (sort(keys %types)) {
&depends_search($level - 1, $p, $types{$_}, $_);
}
}
}
sub mktypes {
my ($build, $run) = @_;
my (%types);
if (defined($build)) {
for (split(/ /, $build)) {
if (!defined($pkgdir{$_})) {$_ = &searchpkg($_);}
if (!defined($run) || $run =~ /$_/) {$types{$_} = "D";}
else {$types{$_} = "B";}
}
}
if (defined($run)) {
for (split(/ /, $run)) {
if (!defined($pkgdir{$_})) {$_ = &searchpkg($_);}
if (!defined($build) || $build =~ /$_/)
{$types{$_} = "D";}
else {$types{$_} = "R";}
}
}
%types;
}
sub level_fit {
my ($level, $p) = @_;
if (!defined($node{$p})) {return;}
if ($node{$p} < $level) {$node{$p} = $level;}
if ($buildreqd{$p}) {
for (sort(split(/ /, $buildreqd{$p}))) {
&level_fit($level + 1, $_);
}
}
if ($runreqd{$p}) {
for (sort(split(/ /, $runreqd{$p}))) {
&level_fit($level + 1, $_);
}
}
}
exit 0;