Managing ports and their dependencies.
Chris BeHanna
chris at behanna.org
Sat Dec 6 22:24:50 PST 2003
On Saturday 06 December 2003 13:15, Josef Karthauser wrote:
> I've got loads and loads of ports installed on my box, most of which I
> probably don't use, but I've no idea which ones these are. Things like
> kde install lots of dependancies, and so I've no idea just by looking at
> the installed port names which ones I need and which ones I don't need.
>
> It should be possible to display ports and their dependancies as a
> connected graph, and then the ports that appear on the leaves of the
> tree would be the ones that I can consider removing.
>
> Does anyone have any code for doing something like this? I thought
> I'd ask before I started knocking it up myself.
I started out trying to do this with p5-GraphViz, but the result
was an unreadable mess.
I'll attach what I have, which I intended to submit as a port
someday, but it's not ready for that just now.
--
Chris BeHanna
Software Engineer (Remove "bogus" before responding.)
chris at bogus.behanna.org
Turning coffee into software since 1990.
-------------- next part --------------
#!/usr/bin/perl
#
# $RCSfile: portgraph,v $
#
# Copyright 2002 Christopher G. BeHanna, all rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY CHRISTOPHER G. BEHANNA ("THE AUTHOR") "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE THE AUTHOR BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# $Author: behanna $
# $Date: 2003/03/09 07:31:59 $
# $Revision: 1.1 $
# $Source: /raid5/chris_cvs/personal/utils/portgraph/portgraph,v $
#
# Analyze FreeBSD installed ports dependencies.
#
use POSIX;
use GraphViz;
use strict;
my $usage = "portsgraph [-g] [dbdir]\n" .
"\t-g Generate image of dependency graph, named portdeps.png\n";
my $graphics = "false";
my $pngfile = "portdeps.png";
if (scalar(@ARGV) < 1 or $ARGV[0] =~ "/\-help/") {
die "$usage\n";
}
if ($ARGV[0] eq "-g") {
$graphics = "true";
shift;
}
#
# Yes, we grovel through all the +REQUIRES files. It would be faster to take
# advantage of portupgrade's pkgdb, but portupgrade doesn't have a "show me only
# the immediate dependencies rather than all the recursive dependencies" mode.
#
my $dbdir = $ARGV[0] || "/var/db/pkg";
my %node = (
'name' => undef,
'deps' => undef,
'parents' => undef,
'seen' => undef
);
my $blanks = " ";
my %depshash = ();
my $dbdirp = POSIX::opendir($dbdir) or die "Couldn't open $dbdir: $!\n";
my @portslist = POSIX::readdir($dbdirp);
POSIX::closedir($dbdirp);
my $line = "";
my $port = "";
for my $i (@portslist) {
$port = $i;
$port =~ s/\-[0-9].*$//;
my $reqfile = "$dbdir/$i/+REQUIRED_BY";
if ( -e "$reqfile" ) {
open(REQFILE, $reqfile) or die "Couldn't open $reqfile: $!\n";
while ($line = <REQFILE>) {
chomp($line);
$line =~ s/\-[0-9].*$//;
push(@{$depshash{$line}}, $port);
}
close( REQFILE );
}
}
#
# Now we have a hash of arrays. Each key is a port, and each value is a
# list of that port's dependencies.
#
if (1 eq 0) {
for my $i (keys %depshash) {
print "$i depends upon:\n";
for my $j (@{$depshash{$i}}) {
print "\t$j\n";
}
}
}
#
# If we're generating a PNG file, build it and get out.
#
if ($graphics eq "true") {
my $graph = GraphViz->new('nooverlap' => 1,
'directed' => 1,
'concentrate' => 1);
for my $i (keys %depshash) {
$graph->add_node($i);
}
for my $i (keys %depshash) {
for my $j (@{$depshash{$i}}) {
$graph->add_edge($i => $j,
'color' => 'black',
'dir' => 'forward',
'arrowhead' => 'normal');
}
}
open(PNGFILE, ">$pngfile") or die "$pngfile: $!\n";
binmode(\*PNGFILE);
$graph->as_png(\*PNGFILE);
close( PNGFILE );
exit 0;
}
#
# Build a dependency tree. Start by making a node for each project.
#
my %deptree = ();
for my $i (keys %depshash) {
%{$deptree{$i}} = %node;
$deptree{$i}{'name'} = $i;
}
#
# Now, for each dependency listed in the original hash of arrays, insert the
# reference to that dependency's node in the port's dependency list.
#
for my $i (keys %deptree) {
for my $j (@{$depshash{$i}}) {
if (not defined $deptree{$i}{'deps'}) {
$deptree{$i}{'deps'} = {};
}
$deptree{$i}{'deps'}->{$j} = $deptree{$j};
if (not defined $deptree{$j}{'parents'}) {
$deptree{$j}{'parents'} = {};
}
$deptree{$j}{'parents'}->{$i} = $deptree{$i};
}
}
#
# Identify the root(s). These are nodes that have no parents.
#
my @roots = ();
for my $i (keys %deptree) {
if (not defined $deptree{$i}{'parents'}) {
push(@roots, ($deptree{$i}));
}
}
#
# Print the tree.
#
my $indent = 0;
if ($graphics eq "false") {
for my $i (@roots) {
printdeps($i);
}
}
#####
#
# sub printdeps
#
# Param: reference to a node.
#
#####
sub printdeps {
my $root = shift;
$indent+=2;
# if (defined $root->{'seen'}) {
# print substr($blanks, 0, $indent) .
# "Cycle detected for node $root->{'name'}!\n";
# $indent--;
# return;
# }
print substr($blanks, 0, $indent) . "$root->{'name'}\n";
$root->{'seen'} = "true";
for my $i (keys %{$root->{'deps'}}) {
printdeps($root->{'deps'}->{$i});
}
$indent-=2;
}
More information about the freebsd-ports
mailing list