#! /usr/bin/perl -W
use strict;
my $controlfile = 'Control';
my $controlfile_local = 'Control.local';
my %pkgs;
my %requires;
my %provided;
my %maintainer;
my %srcpkgs;
my %aliases;
my %disabled;
my $pcfiledir_for_aliases;
sub set_pcfiledir_for_aliases($)
{
$pcfiledir_for_aliases = shift;
}
sub write_alias_pcfile($@)
{
my $alias = shift;
die "Path for pc-file not set, use '-P dir'"
unless defined $pcfiledir_for_aliases;
open(U, ">$pcfiledir_for_aliases/$alias.pc")
|| die "Cannot create '$pcfiledir_for_aliases/$alias.pc': $!";
print U "Name: $alias\n".
"Version: 0\n".
"Description: Alias Dependency Package\n".
"Requires: ".join(' ', @_)."\n";
close U;
}
sub add_alias($$@)
{
my $alias = shift;
my $do_write_pc_file = shift;
$requires{$alias} = [ @_ ];
$provided{$alias} = $alias;
$aliases{$alias} = 1;
write_alias_pcfile($alias, @_)
if $do_write_pc_file;
}
sub is_alias($)
{
my $a = shift;
defined $aliases{$a};
}
sub read_aliases_dir($$)
{
my $dir = shift;
my $do_write_pc_file = shift;
opendir(A, $dir) || die "Cannot open directory '$dir': $!";
foreach my $file (sort readdir(A))
{
next if $file =~ /^\./;
next if -d $file;
open(F, "$dir/$file") || die "Cannot open file '$dir/$file': $!";
my $line = 0;
while ()
{
$line++;
chomp;
s/\#.*//;
s/^\s+$//;
next if /^$/;
if (/^\s*(\S+)\s*:?=\s*(.+)/)
{
add_alias($1, $do_write_pc_file, split(/\s+/, $2));
}
else
{
die "Invalid syntax in $dir/$file:$line";
}
}
close F;
}
closedir A;
}
sub scan_for_provided_pkg_configs($$$)
{
my $path = shift;
my $pkg = shift;
my $scan_all = shift;
$disabled{$pkg} = 1 if -e "$path/broken" or -e "$path/obsolete";
return if not $scan_all and $disabled{$pkg};
foreach my $ctfn ($controlfile, $controlfile_local)
{
if (open(A, "$path/$ctfn"))
{
my $o;
{
undef local $/;
$o = ;
}
$o =~ s/#.*$//gm;
$o =~ s/\n[ \t]+/ /smg;
while (1)
{
if ($o =~ /^requires:[ \t]*(.+)$/im)
{
push @{$requires{$pkg}}, split /\s+/, $1;
}
elsif ($o =~ /^provides:[ \t]*(.+)$/im)
{
$provided{$_} = $pkg foreach (split /\s+/, $1);
}
elsif ($o =~ /^source-pkg:[ \t]*(.+)$/im)
{
push @{$srcpkgs{$pkg}}, split /\s+/, $1;
}
elsif ($o =~ /^maintainer:[ \t]*(.+)$/im)
{
push @{$maintainer{$pkg}}, split /[,\s]+/, $1;
}
else
{
last;
}
$o = $`."\n".$';
}
close A;
}
}
}
sub scan_files($$)
{
my $base_path = shift;
my $scan_all = shift;
my $dh;
opendir($dh, $base_path) || die "Cannot readdir in '$base_path': $!";
foreach (readdir($dh)) {
my $path = "$base_path/$_";
$path = readlink $path if -l $path;
next unless -d $path;
next if /^\./ or /^CVS$/;
$pkgs{$_} = 1;
scan_for_provided_pkg_configs($path, $_, $scan_all);
}
closedir $dh;
}
sub generate_dep_makefile($)
{
scan_files(shift, 0);
my $error_count = 0;
print "# Automatically generated Makefile for dependencies\n";
print "#\n";
print "# ", `date`;
print "#\n";
my %p = %provided;
foreach my $a (sort keys %requires)
{
print "$a:";
delete $p{$a};
foreach (sort @{$requires{$a}})
{
if (defined $provided{$_})
{
print " $provided{$_}";
}
else
{
if ($disabled{$_})
{
print STDERR "ERROR: Package providing \"$_\" disabled and required by \"$a\".\n";
$error_count++;
}
elsif (!is_alias($a))
{
print STDERR "ERROR: Package providing \"$_\" not found as required by \"$a\".\n";
$error_count++;
}
}
}
print "\n";
}
print "\n";
print "$_: $p{$_}\n" foreach sort keys %p;
print "\n.PHONY: ".join(' ', sort keys %aliases).' '
.join(' ', sort keys %p)."\n" if %aliases or %p;
if ($error_count)
{
print STDERR "PANIC: Detected $error_count dependency error(s).\n";
exit(1);
}
}
sub figure_out_pkg_dependecies(@)
{
my %newpkgs;
foreach (@_)
{
if (not defined $requires{$_})
{
if (not defined $provided{$_} and not is_alias($_))
{
print STDERR "WARNING: '$_' does not exist (forgot -A?).\n";
next;
}
$newpkgs{$provided{$_}} = 1;
}
else
{
$newpkgs{$_} = 1;
}
}
my %pkgh;
my $again;
# get requires of package(s)
do
{
foreach (keys %newpkgs)
{
$newpkgs{$_} = 1 foreach @{$srcpkgs{$_}};
}
my %rdeps;
$again = 0;
foreach my $p (keys %newpkgs)
{
if ($requires{$p})
{
$rdeps{$_} = 1 foreach @{$requires{$p}};
}
$pkgh{$p} = 1;
}
%newpkgs = ();
if (%rdeps)
{
# translate to packages providing those
foreach (keys %rdeps)
{
my $p = $_;
$p = $provided{$p} if defined $provided{$p};
$newpkgs{$p} = 1 unless defined $pkgh{$p};
}
$again = 1;
}
}
while ($again);
# remove aliases
foreach my $p (keys %pkgh)
{
delete $pkgh{$p} if is_alias($p);
}
keys %pkgh;
}
# this sub figures out which packages depend on the given ones
sub figure_out_dependant_pkgs(@)
{
my %h;
my %r;
$h{$_} = 1 foreach @_;
while (1)
{
my %tmp = %r;
foreach my $a (keys %requires)
{
foreach (sort @{$requires{$a}})
{
my $p = $_;
$p = $provided{$_} if defined $provided{$_};
$tmp{$a} = 1 if defined $h{$p} or $tmp{$p};
}
}
last if scalar keys %r == scalar keys %tmp;
%r = %tmp;
}
# remove aliases
foreach my $p (keys %r)
{
delete $r{$p} if is_alias($p);
}
%r;
}
sub generate_dot_file_all($$)
{
my $base_path = shift;
my $output = shift;
scan_files($base_path, 1) if defined $base_path;
my $error_count = 0;
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)
= localtime(time);
$year += 1900;
$mon++;
open O, $output or die "Cannot open '$output': $!";
print O "# Automatically generated\n";
print O "#\n";
print O "# ", `date`;
print O "#\n";
print O "digraph dep {\n";
print O " graph [ label = \"\\nSource based package dependency\\n",
sprintf("%02d. %02d. %04d, %02d:%02d \"];\n", $mday, $mon, $year, $hour, $min);
my %disabled_because_of_deps = figure_out_dependant_pkgs(%disabled);
foreach my $a (sort keys %requires)
{
print O " \"$a\" [shape=septagon]\n" if is_alias($a);
foreach (sort @{$requires{$a}})
{
if (defined $provided{$_})
{
print O " \"$a\" -> \"$provided{$_}\" [color=black];\n";
}
elsif (not defined $disabled{$a} and not is_alias($a))
{
print STDERR "ERROR: Package providing \"$_\" not found as required by \"$a\".\n";
$error_count++;
}
}
print O "\n";
}
foreach my $a (sort keys %requires)
{
if (defined $disabled{$a})
{
print O " \"$a\" [style=filled, fillcolor=red];\n";
}
elsif (defined $disabled_because_of_deps{$a})
{
print O " \"$a\" [style=filled, fillcolor=sandybrown];\n";
}
}
print O "}\n";
close O;
if ($error_count)
{
print STDERR "PANIC: Detected $error_count dependency error(s).\n";
exit(1);
}
}
sub generate_overview_set($$)
{
my $base_path = shift;
my $output_dir = shift;
die "Output directory not given." unless defined $output_dir;
scan_files($base_path, 1);
my $error_count = 0;
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)
= localtime(time);
$year += 1900;
$mon++;
my $datestring = sprintf("%02d. %02d. %04d, %02d:%02d",
$mday, $mon, $year, $hour, $min);
generate_dot_file_all(undef, "| tred | dot -Tsvg -o $output_dir/all.svg");
open INDEX, ">$output_dir/index.html" or die "Cannot create $output_dir/index.html: $!";
print INDEX <
TUD:OS package dependency overview
TUD:OS package dependency overview
Generated: $datestring
Legend of overview graphs:
- Red: Broken package
- Lightred: Package broken because it depends on broken package but is itself not broken
Legend of package graphs:
- Blue: The package itself
- Green: Reverse dependency of package
- Yellow: Dependency of package
General overview
Package | Maintainer(s) | Deps | Reverse Deps |
EOFOO
foreach my $pkg (sort keys %pkgs)
{
open F, "| tred | dot -Tsvg -o $output_dir/p_$pkg.svg" || die "Cannot open $output_dir/p_$pkg.svg: $!";
#open F, ">$output_dir/pkg_$pkg.dot" || die "Cannot open pkg_$pkg.dot: $!";
print F "# Automatically generated\n";
print F "#\n";
print F "# ", `date`;
print F "#\n";
print F "digraph dep {\n";
print F " graph [ label = \"\\nSource based package dependency for package '$pkg'\\n",
"$datestring\"];\n";
foreach my $a (sort keys %requires)
{
print F " \"$a\" [shape=septagon]\n" if is_alias($a);
foreach (sort @{$requires{$a}})
{
if (defined $provided{$_})
{
print F " \"$a\" -> \"$provided{$_}\" [color=black];\n";
}
elsif (not defined $disabled{$a} and not is_alias($a))
{
print STDERR "ERROR: Package providing \"$_\" not found as required by \"$a\".\n";
$error_count++;
}
}
print F "\n";
}
my %rev_deps = figure_out_dependant_pkgs($pkg);
my @deps = figure_out_pkg_dependecies($pkg);
print F " \"$_\" [style=filled, fillcolor=seagreen1];\n"
foreach keys %rev_deps;
print F " \"$_\" [style=filled, fillcolor=yellow1];\n"
foreach @deps;
print F " \"$pkg\" [style=filled, fillcolor=dodgerblue];\n";
print F "}\n";
close F;
# Generate HTML content
print INDEX "$pkg | \n";
print INDEX defined $maintainer{$pkg}
? (join " ", map { "$_" } @{$maintainer{$pkg}}) : "none";
print INDEX " | \n";
foreach my $a (sort @deps)
{
print INDEX " $a ";
}
print INDEX " | \n";
foreach my $a (sort keys %rev_deps)
{
print INDEX " $a ";
}
print INDEX " |
\n";
}
print INDEX "
\n";
print INDEX "\n