Managing ports and their dependencies.

Stefan Walter sw at gegenunendlich.de
Tue Feb 10 07:38:43 PST 2004


A while ago on ports at freebsd.org:

Chris BeHanna, 07.12.03, 07:24h CET:

> 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.

I thought I'd let you know I did something similar last week, partly
inspired by your mail. It displays the dependency graph of a single
package only, though, not of all installed ones. But even that is too
much to be really useful for ports with more than a couple of
dependencies, as the package database stores indirect dependencies as
well as direct ones, so you end up with a lot of edges that shouldn't be
there. See [1] and [2] for examples. You'd have to use information
directly from the ports to get the correct graph, but the versions in
the ports tree don't necessarily reflect what's installed on your
system...

My variant of the script is attached to this mail.

Stefan

[1]: http://www.gegenunendlich.de/stuff/depgraph-XFree86.ps
[2]: http://www.gegenunendlich.de/stuff/depgraph-mozilla-firebird.ps
-------------- next part --------------
#!/usr/bin/perl
#
# Copyright (c) 2004 Stefan Walter <sw at gegenunendlich.de>
#
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. 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 THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
#

# Visualize the dependency graph for a given installed package.
#
# Syntax: pkg_depgraph <package> [output file]
# Parameters:
#   package    : name of the package
#   output file: output file name (default: depgraph.png)

use strict;
use GraphViz;

my $dbdir = "/var/db/pkg";
my $outputfile = "depgraph.ps";
my (%packages, %added_pkgs);
my $graph;
my $target_pkg;

#
# Add a new package and those it depends on into graph (recursively)
# Parameter: new package (String)
#
sub add_pkg {
	my $cur_node = shift;
	
	# Add this package as a node
	$graph->add_node($cur_node);
	# Add it to our list of existing nodes
	$added_pkgs{$cur_node} = 1;

	# Find packages that list this one in their +REQUIRED_BY
	PKG: foreach my $pkg (sort keys %packages) {
		foreach my $reqitem (@{$packages{$pkg}}) {
			# Does $cur_node depend on $pkg?
			if ($reqitem eq $cur_node) {
				# Did we already add a node for $pkg?
				if (not defined($added_pkgs{$pkg})) {
					add_pkg($pkg);
				}
				# Add an edge for this dependency
				$graph->add_edge($cur_node => $pkg);
				next PKG;
			}
		} # $reqitem
	} # PKG
}

# Examine command line arguments
if (scalar(@ARGV) == 1) {
	$target_pkg = shift(@ARGV);
}
elsif (scalar(@ARGV) == 2) {
	$target_pkg = shift(@ARGV);
	$outputfile = shift(@ARGV);
}
else {
	print STDERR "Usage: $0 <package> [output file]\n";
	exit(1);
}

# Create a hash (name => reqlist) of all packages
opendir(DBDIR, $dbdir)
	or die "Can't open package db directory $dbdir!";
while(defined(my $file = readdir(DBDIR))) {
	my $path = $dbdir . '/' . $file;
	my $reqlistfile = $path . '/+REQUIRED_BY';
	my @reqlist = ();
	# Exclude non-directories, "." and ".."
	if (($file ne ".") && ($file ne "..") && (-d $path)) {
		# Read +REQUIRED_BY if it exists and is not empty
		if (-s $reqlistfile) {
			open(REQLIST, $reqlistfile)
				or die "Can't open $reqlistfile!";
			while (<REQLIST>) {
				# Removing trailing '\n'...
				chomp;
				# ...also prevents empty lines from being added here
				push @reqlist, $_;
			}
			close(REQLIST);
		}
		$packages{$file} = [@reqlist];
	}
}
closedir(DBDIR);

# Check if the given package really exists
if (not defined($packages{$target_pkg})) {
	print STDERR "Package '$target_pkg' does not exist!\n";
	exit(1);
}

# Start building the dependency graph with the given package
$graph = GraphViz->new(rankdir => 1, concentrate => 1);
add_pkg($target_pkg);

$graph->as_ps($outputfile);
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 650 bytes
Desc: not available
Url : http://lists.freebsd.org/pipermail/freebsd-ports/attachments/20040210/f3fce473/attachment.bin


More information about the freebsd-ports mailing list