5 my $controlfile = 'Control';
6 my $controlfile_local = 'Control.local';
15 my $pcfiledir_for_aliases;
17 sub set_pcfiledir_for_aliases($)
19 $pcfiledir_for_aliases = shift;
22 sub write_alias_pcfile($@)
26 die "Path for pc-file not set, use '-P dir'"
27 unless defined $pcfiledir_for_aliases;
29 open(U, ">$pcfiledir_for_aliases/$alias.pc")
30 || die "Cannot create '$pcfiledir_for_aliases/$alias.pc': $!";
32 print U "Name: $alias\n".
34 "Description: Alias Dependency Package\n".
35 "Requires: ".join(' ', @_)."\n";
43 my $do_write_pc_file = shift;
45 $requires{$alias} = [ @_ ];
46 $provided{$alias} = $alias;
49 write_alias_pcfile($alias, @_)
59 sub read_aliases_dir($$)
62 my $do_write_pc_file = shift;
64 opendir(A, $dir) || die "Cannot open directory '$dir': $!";
66 foreach my $file (sort readdir(A))
68 next if $file =~ /^\./;
71 open(F, "$dir/$file") || die "Cannot open file '$dir/$file': $!";
80 if (/^\s*(\S+)\s*:?=\s*(.+)/)
82 add_alias($1, $do_write_pc_file, split(/\s+/, $2));
86 die "Invalid syntax in $dir/$file:$line";
96 sub scan_for_provided_pkg_configs($$$)
100 my $scan_all = shift;
102 $disabled{$pkg} = 1 if -e "$path/broken" or -e "$path/obsolete";
104 return if not $scan_all and $disabled{$pkg};
106 foreach my $ctfn ($controlfile, $controlfile_local)
108 if (open(A, "$path/$ctfn"))
117 $o =~ s/\n[ \t]+/ /smg;
121 if ($o =~ /^requires:[ \t]*(.+)$/im)
123 push @{$requires{$pkg}}, split /\s+/, $1;
125 elsif ($o =~ /^provides:[ \t]*(.+)$/im)
127 $provided{$_} = $pkg foreach (split /\s+/, $1);
129 elsif ($o =~ /^source-pkg:[ \t]*(.+)$/im)
131 push @{$srcpkgs{$pkg}}, split /\s+/, $1;
133 elsif ($o =~ /^maintainer:[ \t]*(.+)$/im)
135 push @{$maintainer{$pkg}}, split /[,\s]+/, $1;
151 my $base_path = shift;
152 my $scan_all = shift;
154 opendir($dh, $base_path) || die "Cannot readdir in '$base_path': $!";
156 foreach (readdir($dh)) {
157 my $path = "$base_path/$_";
158 $path = readlink $path if -l $path;
159 next unless -d $path;
160 next if /^\./ or /^CVS$/;
162 scan_for_provided_pkg_configs($path, $_, $scan_all);
167 sub generate_dep_makefile($)
169 scan_files(shift, 0);
172 print "# Automatically generated Makefile for dependencies\n";
177 foreach my $a (sort keys %requires)
180 foreach (sort @{$requires{$a}})
182 if (defined $provided{$_})
184 print " $provided{$_}";
190 print STDERR "ERROR: Package providing \"$_\" disabled and required by \"$a\".\n";
193 elsif (!is_alias($a))
195 print STDERR "ERROR: Package providing \"$_\" not found as required by \"$a\".\n";
203 print "\n.PHONY: ".join(' ', sort keys %aliases)."\n" if %aliases;
207 print STDERR "PANIC: Detected $error_count dependency error(s).\n";
214 sub figure_out_pkg_dependecies(@)
219 if (not defined $requires{$_})
221 if (not defined $provided{$_})
223 print STDERR "WARNING: '$_' does not exist.\n";
226 $newpkgs{$provided{$_}} = 1;
236 # get requires of package(s)
239 foreach (keys %newpkgs)
241 $newpkgs{$_} = 1 foreach @{$srcpkgs{$_}};
246 foreach my $p (keys %newpkgs)
250 $rdeps{$_} = 1 foreach @{$requires{$p}};
257 # translate to packages providing those
258 foreach (keys %rdeps)
261 $p = $provided{$p} if defined $provided{$p};
262 $newpkgs{$p} = 1 unless defined $pkgh{$p};
270 foreach my $p (keys %pkgh)
272 delete $pkgh{$p} if is_alias($p);
278 # this sub figures out which packages depend on the given ones
279 sub figure_out_dependant_pkgs(@)
283 $h{$_} = 1 foreach @_;
288 foreach my $a (keys %requires)
290 foreach (sort @{$requires{$a}})
293 $p = $provided{$_} if defined $provided{$_};
294 $tmp{$a} = 1 if defined $h{$p} or $tmp{$p};
298 last if scalar keys %r == scalar keys %tmp;
303 foreach my $p (keys %r)
305 delete $r{$p} if is_alias($p);
311 sub generate_dot_file_all($$)
313 my $base_path = shift;
315 scan_files($base_path, 1) if defined $base_path;
318 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)
323 open O, $output or die "Cannot open '$output': $!";
325 print O "# Automatically generated\n";
327 print O "# ", `date`;
329 print O "digraph dep {\n";
330 print O " graph [ label = \"\\nSource based package dependency\\n",
331 sprintf("%02d. %02d. %04d, %02d:%02d \"];\n", $mday, $mon, $year, $hour, $min);
333 my %disabled_because_of_deps = figure_out_dependant_pkgs(%disabled);
335 foreach my $a (sort keys %requires)
337 print O " \"$a\" [shape=septagon]\n" if is_alias($a);
338 foreach (sort @{$requires{$a}})
340 if (defined $provided{$_})
342 print O " \"$a\" -> \"$provided{$_}\" [color=black];\n";
344 elsif (not defined $disabled{$a} and not is_alias($a))
346 print STDERR "ERROR: Package providing \"$_\" not found as required by \"$a\".\n";
353 foreach my $a (sort keys %requires)
355 if (defined $disabled{$a})
357 print O " \"$a\" [style=filled, fillcolor=red];\n";
359 elsif (defined $disabled_because_of_deps{$a})
361 print O " \"$a\" [style=filled, fillcolor=sandybrown];\n";
371 print STDERR "PANIC: Detected $error_count dependency error(s).\n";
376 sub generate_overview_set($$)
378 my $base_path = shift;
379 my $output_dir = shift;
381 die "Output directory not given." unless defined $output_dir;
383 scan_files($base_path, 1);
386 my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)
390 my $datestring = sprintf("%02d. %02d. %04d, %02d:%02d",
391 $mday, $mon, $year, $hour, $min);
393 generate_dot_file_all(undef, "| tred | dot -Tsvg -o $output_dir/all.svg");
395 open INDEX, ">$output_dir/index.html" or die "Cannot create $output_dir/index.html: $!";
399 <title>TUD:OS package dependency overview</title>
402 <h2>TUD:OS package dependency overview</h2>
403 <p>Generated: $datestring</p>
405 Legend of overview graphs:
407 <li>Red: Broken package</li>
408 <li>Lightred: Package broken because it depends on broken package but is itself not broken</li>
410 Legend of package graphs:
412 <li>Blue: The package itself</li>
413 <li>Green: Reverse dependency of package</li>
414 <li>Yellow: Dependency of package</li>
417 <p><a href=\"all.svg\">General overview</a><br/></p>
418 <table border=\"1\"><tr><td>Package</td><td>Maintainer(s)</td><td>Deps</td><td>Reverse Deps</td></tr>
421 foreach my $pkg (sort keys %pkgs)
423 open F, "| tred | dot -Tsvg -o $output_dir/p_$pkg.svg" || die "Cannot open $output_dir/p_$pkg.svg: $!";
424 #open F, ">$output_dir/pkg_$pkg.dot" || die "Cannot open pkg_$pkg.dot: $!";
426 print F "# Automatically generated\n";
428 print F "# ", `date`;
430 print F "digraph dep {\n";
431 print F " graph [ label = \"\\nSource based package dependency for package '$pkg'\\n",
434 foreach my $a (sort keys %requires)
436 print F " \"$a\" [shape=septagon]\n" if is_alias($a);
437 foreach (sort @{$requires{$a}})
439 if (defined $provided{$_})
441 print F " \"$a\" -> \"$provided{$_}\" [color=black];\n";
443 elsif (not defined $disabled{$a} and not is_alias($a))
445 print STDERR "ERROR: Package providing \"$_\" not found as required by \"$a\".\n";
452 my %rev_deps = figure_out_dependant_pkgs($pkg);
453 my @deps = figure_out_pkg_dependecies($pkg);
455 print F " \"$_\" [style=filled, fillcolor=seagreen1];\n"
456 foreach keys %rev_deps;
457 print F " \"$_\" [style=filled, fillcolor=yellow1];\n"
460 print F " \"$pkg\" [style=filled, fillcolor=dodgerblue];\n";
465 # Generate HTML content
467 print INDEX "<tr><td><a href=\"p_$pkg.svg\">$pkg</a></td><td>\n";
468 print INDEX defined $maintainer{$pkg}
469 ? (join " ", map { "<a href=\"mailto:$_\">$_</a>" } @{$maintainer{$pkg}}) : "none";
470 print INDEX "</td><td>\n";
471 foreach my $a (sort @deps)
473 print INDEX " <a href=\"p_$a.svg\">$a</a> ";
475 print INDEX "</td><td>\n";
476 foreach my $a (sort keys %rev_deps)
478 print INDEX " <a href=\"p_$a.svg\">$a</a> ";
480 print INDEX "</td></tr>\n";
483 print INDEX "</table>\n";
484 print INDEX "</body>\n</html>\n";
489 print STDERR "PANIC: Detected $error_count dependency error(s).\n";
496 my $base_path = shift;
498 scan_files($base_path, 0);
500 foreach my $p (keys %pkgs)
504 open(F, "find '$base_path/$p' -name Makefile -o -name Make.rules |")
505 || die "Cannot start find: $!";
506 while (my $file = <F>)
509 my $found_pc_filename;
512 open(M, $file) || die "Cannot open \"$_\": $!";
516 $found_pc_filename = $1
517 if /^\s*PC_FILENAME\s*:?=\s*(.+)\s*$/;
519 if /^\s*include\s+.+\/mk\/lib.mk\s*$/;
521 if /^\s*NOTARGETSTOINSTALL\s*:?=\s/;
525 print "ERROR: $p: Not public but PC_FILENAME given\n"
526 if defined $not_public and defined $found_pc_filename;
528 unless (defined $not_public)
530 if (defined $found_pc_filename)
532 $found_pc_filename =~ s/\$\(PKGNAME\)/$p/;
533 $pc_filenames{$found_pc_filename} = 1;
535 elsif (defined $is_lib_build)
537 $pc_filenames{$p} = 1;
538 push @libs_wo_pcfile, $file;
543 foreach my $a (keys %pc_filenames)
545 print "ERROR: $p: Missing in provides '$a'\n"
546 if not defined $provided{$a} or $provided{$a} ne $p;
549 foreach my $a (keys %provided)
551 print "ERROR: $p: Provide not found as PC_FILENAME '$a'\n"
552 if $provided{$a} eq $p and not defined $pc_filenames{$a};
555 print "ERROR: $p: Contains multiple libs without PC_FILENAME:\n",
556 " ", join("\n ", @libs_wo_pcfile), "\n"
557 if scalar @libs_wo_pcfile > 1;
562 print "TODO: check if something is provided by multiple packages\n";
566 sub show_pkg_deps($$@)
568 my $base_path = shift;
570 scan_files($base_path, 0);
572 my @p = figure_out_pkg_dependecies(@_);
574 print join("\n", map { "$prefix$_" } sort @p), "\n" if $prefix;
575 print join(' ', sort @p), "\n" unless $prefix;
578 sub show_pkg_deps_and_rdeps($$@)
580 my $base_path = shift;
582 scan_files($base_path, 0);
584 my %r = figure_out_dependant_pkgs(@_);
585 my @p = figure_out_pkg_dependecies(@_, keys %r);
587 print join("\n", map { "$prefix$_" } sort @p), "\n" if $prefix;
588 print join(' ', sort @p), "\n" unless $prefix;
591 sub show_maintainer($@)
593 scan_files(shift, 1);
603 $m{$_}++ foreach @{$maintainer{$_}};
607 $m{'NO MAINTAINER'} = 1;
611 print join(", ", keys %m), "\n";
620 print "$_: ", join(', ', @{$maintainer{$_}}), "\n";
621 $m{$_}++ foreach @{$maintainer{$_}};
625 print "$_: NO MAINTAINER\n";
628 print "Stats:\n", join("\n", map { sprintf "%3d: %s", $m{$_}, $_ } sort { $m{$b} <=> $m{$a} } keys %m), "\n";
633 sub smooth_control_file($$)
638 scan_for_provided_pkg_configs($path, $pkg, 1);
640 print "Provides: ".join(' ', keys %provided)."\n";
641 print "Requires: ".join(' ', @{$requires{$pkg}})."\n";
644 # a bit of hand-crafted option parsing, if it gets more use getopt
648 last unless defined $ARGV[0];
649 if ($ARGV[0] eq '-A')
652 push @aliases_dirs, shift;
654 elsif ($ARGV[0] eq '-P')
657 set_pcfiledir_for_aliases(shift),
666 my $base_path = $ARGV[1];
668 die "Missing arguments"
669 if not defined $cmd or not defined $base_path;
671 read_aliases_dir($_, $cmd eq 'generate') foreach @aliases_dirs;
673 if ($cmd eq 'generate') {
674 generate_dep_makefile($base_path);
675 } elsif ($cmd eq 'dot') {
676 generate_dot_file_all($base_path, '>-');
677 } elsif ($cmd eq 'overviewset') {
678 generate_overview_set($base_path, $ARGV[2]);
679 } elsif ($cmd eq 'pkgdeps') {
680 show_pkg_deps($base_path, undef, @ARGV[2 .. $#ARGV]);
681 } elsif ($cmd eq 'pkgdepspath') {
682 show_pkg_deps($base_path, $ARGV[2], @ARGV[3 .. $#ARGV]);
683 } elsif ($cmd eq 'pkgdepsandrdeps') {
684 show_pkg_deps_and_rdeps($base_path, undef, @ARGV[2 .. $#ARGV]);
685 } elsif ($cmd eq 'collect') {
686 die "Missing argument" unless defined $ARGV[2];
687 smooth_control_file("$base_path/$ARGV[2]", $ARGV[2]);
688 } elsif ($cmd eq 'maintainer') {
689 show_maintainer($base_path, @ARGV[2 .. $#ARGV]);
690 } elsif ($cmd eq 'check') {
691 check_control($base_path);
693 die "Invalid command '$cmd'";