Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 24 Apr 2001 07:16:25 -0700 (PDT)
From:      Jeff Kletsky <Jeff+freebsd@wagsky.com>
To:        freebsd-stable@freebsd.org
Subject:   Re: pkg/port dependency tool (enclosed)
Message-ID:  <Pine.BSF.4.21.0104240708590.5506-100000@wildside.wagsky.com>
In-Reply-To: <15076.20167.337195.349137@guru.mired.org>

next in thread | previous in thread | raw e-mail | index | archive | help
Thanks to some good pointers, I have an *alpha* of the text-output version
of the tool available. I need to integrate with the code already in
pkg_version and resolve how to detect and include the dependencies
introduced by the USE_* variables. I'll probably be quiet for a while, the
"easy" part is done -- now to make it a robust tool...

Jeff

#!/usr/bin/perl -w

# $Header: /usr/local/cvsroot/pkg_graph/graph_depends.pl,v 1.4 2001/04/24 14:08:28 jeff Exp $

#
# Tool to read from /var/db/pkg to find all pkg/port dependencies
# create a graph and solve for process to rebuild ports in "proper
# order"
#
# Examples of use:
#
# pkg_version -L = | cut -f 1 -d " " | graph_depends.pl
#
# graph_depends.pl -a
#

# Status: ALPHA development

# Author: Jeff Kletsky mailto:jeff+freebsd@wagsky.com

#
# TODO:
#
# * use '/usr/sbin/pkg_version'; to access the ports index, tie nodes
#   to their cannonical names, and ouput code to rebuild ports
#
# * deal with the USE_BZIP2 variables that Kevin Way has pointed out
#
# * Include dependencies of ports about to be built
#
# * Discriminate between existing dependencies and future ones
#   - How are "orphaned" ports to be handled??
#
# * Consider solving the problem where the changes are driven by
#   wishing to rebuild a target, all its predecessors, and all their
#   successors (e.g., " I need a clean version of gnome")
#

#
# Thanks to:
#
# * Mike Meyer for mumbling the words "transitive closure of the 
#   dependency matrix" and getting this rolling in the right direction
# 
# * Garance Drosihn for providing me to Kevin Way's comments on the 
#   USE_ variable issue
#


use strict;
use POSIX qw(EXIT_FAILURE EXIT_SUCCESS);
use FileHandle;

require Graph;  # (not yet in the ports tree)

my $usage = qq(
Usage:
$0 [-a]

Reads a list of packages/ports to be updated from STDIN and prints a 
suggested order for updating those packages/ports based on the dependencies
seen in the installed list of packages/ports in /var/db/pkg

  -a  Ignores STDIN and looks at all registered packages/ports

);

my $doall = 0;

if ( $#ARGV > 0 ) {
    die $usage;
}

if ( $#ARGV == 0 ) {
    $doall = ( shift @ARGV eq "-a" );
    die $usage unless $doall;
}

my $pkg_db_dir = '/var/db/pkg';
$pkg_db_dir =~ s/\/$//;             # prevent "trailing-slash" issue

my $required_by_filename = '+REQUIRED_BY';

#
# Open the directory that contains pkg/port information
#

opendir (VARDBPKG, $pkg_db_dir) or die "Unable to open $pkg_db_dir: $!";

#
# Get the subdirectories, and iterate through them
#

my @installed_pkgs = readdir VARDBPKG;

die "No files read in $pkg_db_dir\n" unless $#installed_pkgs > 1;

#
# Read the list of files from STDIN
#

my $target;
my %targets;

if ( !$doall ) {

    while ( $target = <> ) {
	chomp $target;
	$targets{$target} = 'true';
    }

} else {

    foreach $target (@installed_pkgs) {
	$targets{$target} = 'true';
    }

}	

my $pkg;
my $rqd;
my $required_by_fqn;
my $required_by_fh;

my $pkg_node;
my $rqd_node;

my $graph = new Graph;
$graph->directed('true');

 PKG:

    foreach $pkg (sort @installed_pkgs) {
	
	
	next PKG if $pkg =~ m/^\./;

	$graph->add_vertex($pkg);

	
#
# See if there is a +REQUIRED_BY file, open it, and process lines
#
	
	$required_by_fqn = "${pkg_db_dir}/${pkg}/${required_by_filename}";

	next PKG unless -e $required_by_fqn;
	
	$required_by_fh = new FileHandle "< $required_by_fqn";
	die "Unable to open $required_by_fqn: $!" 
	    unless defined $required_by_fh;
	
	while ($rqd = <$required_by_fh>) {

	    chomp $rqd;
	    
	    $graph->add_edge($pkg,$rqd);

	}

    } # PKG

#
# Now that we have the full graph, check for loops
# Loops should "never" occur or certain ports would be un-buildable    
# It *is* vaguely possible that the dependencies change and create a loop
# after one of the components in the loop were already built.
# Since this is a remote possibility, just die...
#

die "Graph loops back on itself, unable to resolve" 
    if ( $graph->self_loop_vertices );

if (! $doall ) {

#
# If not looking at all pkgs/ports, trim the tree
#

    my $tcgraph = $graph->TransitiveClosure_Floyd_Warshall;

#
# Mark and sweep
#

#
# Note that the output of pkg_version is "unqualified" 
# create a new list that identifies all old and new targets
#

    my %tgt_pkgs;
    my $tgtmatch;

    foreach $pkg ($graph->vertices) {
	$graph->delete_attribute('candidate', $pkg);
	foreach $target (keys %targets) {
	    $tgtmatch = "^" . quotemeta($target);
	    $tgt_pkgs{$pkg} = 'true' if $pkg =~/$tgtmatch/;
	}
    }

    foreach $target (keys %tgt_pkgs) {
#	print "setting $target\n";
	$graph->set_attribute('candidate', $target, 'true') ;
	foreach $pkg ($graph->vertices) {
	    $graph->set_attribute('candidate', $pkg, 'true') 
#		&&  print "setting $pkg as dependent on $target\n"
		    if $tcgraph->has_edge($target, $pkg);
	}
    }
    
    foreach $pkg ($graph->vertices) {
	$graph->delete_vertex($pkg) 
	    unless $graph->get_attribute('candidate', $pkg);
    }
    
}

#
# Start dumping out the order of processing
#

my $level = 0;
my @pkgs;

while ( $graph->vertices ) {

    print "\n\nLevel ${level}:\n";
    print "========\n";
    @pkgs = ($graph->isolated_vertices, $graph->source_vertices);

    print join("\n", sort(@pkgs));

    $graph->delete_vertices(@pkgs);

    die "No packages found which can be built at this level, unable to resolve"
	unless @pkgs;

    $level++;
}

print "\n\nDone\n";

exit EXIT_SUCCESS;


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-stable" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.BSF.4.21.0104240708590.5506-100000>