Perl Script to Plot Dependencies

I was interested in seeing a graph of package dependencies, so I could trace which packages were needed by which in a complete tree. portmaster -l can give basic information (leaf, trunk, root, branch) but does not show the relationships.

I wrote the following tool to list dependencies as a directed graph. The output is in DOT format, and readable by graph plotters like graphics/graphviz.
Code:
#!/usr/bin/env perl
use v5.010;
use warnings;

use Data::Dumper;

#############################################
sub parse_pkg_name
{
  my $name = shift;
  if ($name =~ m/(.+)-([^-]+)/)
  {
    return ($1,$2);
  } else {
    return ($name, '');
  }
}

#############################################
# Retrieve all package info
my @pkg_info_output = `pkg info -ad`;

# Record all seen package names, and all seen dependencies
my %pkgs;
my %deps;
# Links between pkg and dep
my %edges;

# Parse the results of the pkg info output
{
  my $pkg_name = '';
  my $pkg_version = '';
  foreach my $line(@pkg_info_output)
  {
    chomp $line;
    if ($line =~ m/^([^\t:]+):$/) {
      # here is pkg definition
      $pkg_name = $1;
      $pkgs{$pkg_name} = 1;

      # Set up empty dependency list
      $edges{$pkg_name} = ();
    } elsif ($line =~ m/^\t([^\t:]+)$/) {
      # here is a dependency
      my $dep_name = $1;
      $deps{$dep_name} = 1;

      # record a dependency
      push (@{$edges{$pkg_name}}, $dep_name);
    } else {
      die 'whoops';
    }
  }
}

#############################################
# print DOT format
# Print output header
say "digraph dependencies {";
# default style
say "\tnode [shape=circle style=filled color=red];";
# style the boxes
foreach my $pkg (sort keys %pkgs)
{
  my ($pkg_name,$pkg_version) = parse_pkg_name($pkg);
  print "\t\"$pkg_name\" [";
  if (!defined ($deps{$pkg})) {
    # root port (has no dependencies)
    print "shape=house color=green";
  } elsif (!defined ($edges{$pkg})) {
    # leaf port (not depended on)
    print "shape=invhouse color=red";
  } else {
    print "shape=box color=yellow";
  }
  say " style=solid label=\"$pkg_name\\n$pkg_version\"];";
}

# dependency chain
foreach my $dep (sort keys %edges)
{
  my ($dep_name,$dep_version) = parse_pkg_name($dep);
  foreach my $parent (@{$edges{$dep}})
  {
    my ($parent_name,$parent_version) = parse_pkg_name($parent);
    say "\t\"$parent_name\" -> \"$dep_name\";";
  }
}
say "}";

Here's an example svg I was able to create using output from this tool. Note the mysql56-client in bright red circle: this is a missing dependency! After rebuilding apr and p5-DBD-mysql the new chain is correct.

chart.png
 
The output is dot format. So I should can make e.g. a svg with dot -Tsvg my.dot -o my.svg.
But I can't show this svg. What have I overlooked?
 
Thanks for that, great script, very helpful. Looking at your attached SVG, what viewer do you use? I've been trying a couple of browsers and viewers and my SVG (created per sfdp -Tsvg my.dot -o my.svg, like talsamon mentioned) looks really crappy, all together in one undecipherable bunch....
 
Last edited by a moderator:
Back
Top