Back to home page

Project CMSSW displayed by LXR

 
 

    


Warning, /Utilities/ReleaseScripts/ignominy/deps2dot is written in an unsupported language. File is not indexed.

0001 #!/usr/bin/env perl
0002 use warnings;
0003 use strict;
0004 $|=1;
0005 
0006 my $me = $0;
0007 $me =~ s|.*/||;
0008 
0009 ######################################################################
0010 #my @renames = ();
0011 my $SUM_INC_OUT_EDGES   = 0;
0012 my $SUM_INC_OUT_COUNT   = 1;
0013 my $SUM_INC_IN_EDGES    = 2;
0014 my $SUM_INC_IN_COUNT    = 3;
0015 my $SUM_LIB_OUT_EDGES   = 4;
0016 my $SUM_LIB_OUT_COUNT   = 5;
0017 my $SUM_LIB_IN_EDGES    = 6;
0018 my $SUM_LIB_IN_COUNT    = 7;
0019 my $SUM_LOG_OUT_EDGES   = 8;
0020 my $SUM_LOG_IN_EDGES    = 9;
0021 my $SUM_ALL_IN_EDGES    = 10;
0022 my $SUM_ALL_OUT_EDGES   = 11;
0023 my $SUM_N               = 12;
0024 
0025 my $CELL_INC_OUT_COUNT  = 0;
0026 my $CELL_INC_IN_COUNT   = 1;
0027 my $CELL_LIB_OUT_COUNT  = 2;
0028 my $CELL_LIB_IN_COUNT   = 3;
0029 my $CELL_LOG_OUT_EDGES  = 4;
0030 my $CELL_LOG_IN_EDGES   = 5;
0031 my $CELL_N              = 6;
0032 
0033 my @SUM_LABELS = ("Number of outgoing edges from includes",
0034                   "Total count of outgoing includes",
0035                   "Number of incoming edges from includes",
0036                   "Total count of incoming includes",
0037                   "Number of outgoing edges from symbols",
0038                   "Total count of outgoing symbols",
0039                   "Number of incoming edges from symbols",
0040                   "Total count of incoming symbols",
0041                   "Number of (possibly overlapping) outgoing logical edges",
0042                   "Number of (possibly overlapping) incoming logical edges",
0043                   "Total count of incoming edges",
0044                   "Total count of outgoing edges");
0045 
0046 sub min {
0047   my $min = shift;
0048   foreach my $val (@_) {
0049     $min = $min > $val ? $val : $min;
0050   }
0051   return $min;
0052 }
0053 
0054 sub max {
0055   my $max = shift;
0056   foreach my $val (@_) {
0057     $max = $max > $val ? $max : $val;
0058   }
0059   return $max;
0060 }
0061 
0062 sub rgb2hsv {
0063   my ($r, $g, $b) = @_;
0064   my ($h, $s, $v);
0065   my $max = &max($r, $g, $b);
0066   my $min = &min($r, $g, $b);
0067 
0068   $v = $max;
0069   $s = ($max ? ($max - $min)/$max : 0.0);
0070   if (! $s) {
0071     $h = 0;
0072   } else {
0073     my $d = $max - $min;
0074     if ($r == $max) {
0075       $h = 0. + ($g - $b)/$d;
0076     } elsif ($g == $max) {
0077       $h = 2. + ($b - $r)/$d;
0078     } elsif ($b == $max) {
0079       $h = 4. + ($r - $g)/$d;
0080     }
0081     $h /= 6.;
0082     $h += 1. if ($h < 0.);
0083   }
0084 
0085   $h = int($h * 1000 + .5)/1000;
0086   $s = int($s * 1000 + .5)/1000;
0087   $v = int($v * 1000 + .5)/1000;
0088 
0089   return ($h, $s, $v);
0090 }
0091 
0092 sub rgb2yiq {
0093   my ($r, $g, $b) = @_;
0094   return (.299 * $r + .587 * $g + .115 * $b,
0095           .596 * $r - .275 * $g - .321 * $b,
0096           .212 * $r - .523 * $g + .311 * $b);
0097 }
0098 
0099 sub hsv2rgb {
0100   my ($h, $s, $v) = @_;
0101   if ($s eq 0.) {
0102     die "bad HSV H value: $h -- should be 0\n" if $h;
0103     return ($v, $v, $v);
0104   } else {
0105     $h = 0. if $h eq 1.;
0106     $h *= 6.;
0107     my $i = int($h);
0108     my $f = $h - $i;
0109     my $p = $v * (1. - $s);
0110     my $q = $v * (1. - $s * $f);
0111     my $t = $v * (1. - $s * (1. - $f));
0112 
0113     return ($v, $t, $p) if $i == 0;
0114     return ($q, $v, $p) if $i == 1;
0115     return ($p, $v, $t) if $i == 2;
0116     return ($p, $q, $v) if $i == 3;
0117     return ($t, $p, $v) if $i == 4;
0118     return ($v, $p, $q) if $i == 5;
0119     die;
0120   }
0121 }
0122 
0123 sub validate_flag {
0124   my ($value, $opt) = @_;
0125 
0126   return 1 if (grep(lc($value) eq $_, qw(1 y yes)));
0127   return 0 if (grep(lc($value) eq $_, qw(0 n no)));
0128   die "$opt must be (1, y, yes) or (0, n, no)\n";
0129 }
0130 
0131 sub file_part {
0132   my $name = shift;
0133   $name =~ s|.*/||;
0134   return $name;
0135 }
0136 
0137 sub dir_part {
0138   my $name = shift;
0139   return '' if $name !~ m|/|;
0140   $name =~ s|/[^/]+$||;
0141   return $name;
0142 }
0143 
0144 sub check_xedge {
0145   my ($edges, $rinclusive, $rrx, $xinclusive, $xrx, $source, $target) = @_;
0146   return 0 if (grep($target =~ /$_/, @$rrx) && 1) ne $rinclusive;
0147 
0148   my $matches = 0;
0149   foreach my $xrx (@$xrx) {
0150     $matches += scalar grep ($_ ne ':hard'
0151                              && ($xinclusive ? $_ =~ /$xrx/ : $_ !~ /$xrx/),
0152                              @{$edges->{$source}->{$target}});
0153   }
0154   return $matches;
0155 }
0156 
0157 sub include_edge {
0158   my ($edges, $nodes, $rinclusive, $rrx, $xinclusive, $xrx, $source, $target) = @_;
0159   return 0 if !exists $nodes->{$target};
0160   return 0 if (grep($target =~ /$_/, @$rrx) && 1) ne $rinclusive;
0161   return 1 if grep($_ eq ':hard', @{$edges->{$source}->{$target}});
0162   return 2 if &check_xedge($edges,$rinclusive,$rrx,$xinclusive,$xrx,$source,$target) ne 0;
0163   return 0;
0164 }
0165 
0166 sub do_node {
0167   my ($gedges,$gnodes, $edges,$nodes, $rinclusive,$rrx, $xinclusive,$xrx, $from,$to,$include)=@_;
0168   return if ! $include;
0169 
0170   if (defined $from) {
0171     $gedges->{$from} ||= {};
0172     $gedges->{$from}->{$to} = $include
0173       if (! exists $gedges->{$from}->{$to}
0174           || $gedges->{$from}->{$to} > $include);
0175   }
0176 
0177   return if (exists $gnodes->{$to});
0178 
0179   $gnodes->{$to} = 1;
0180   foreach (keys %{$edges->{$to}}) {
0181     &do_node($gedges, $gnodes, $edges, $nodes, $rinclusive, $rrx, $xinclusive, $xrx, $to, $_,
0182              &include_edge($edges, $nodes, $rinclusive, $rrx, $xinclusive, $xrx, $to, $_));
0183   }
0184 }
0185 
0186 sub prune_node {
0187   my ($renames, $edges, $groups, $from, $node) = @_;
0188   foreach my $g (keys %{$groups->{$from}})
0189   {
0190     foreach my $d (keys %{$edges->{$g}})
0191     {
0192       my $rd = &rename_node($renames, $d);
0193       if($rd eq $node)
0194       {delete  $edges->{$g}{$d};}
0195     }
0196   }
0197 }
0198 
0199 sub prune_edge {
0200   my ($renames, $groups, $cache1, $cache, $edges, $from) = @_;
0201   my $rfrom = &rename_node($renames, $from);
0202   my $edge = $edges->{$from};
0203   $cache->{$rfrom} ||={};
0204   $cache->{$from}{$from}=1;
0205   my $pcache=$cache->{$rfrom};
0206   foreach my $to (keys %{$edge}) {
0207     my $rto = &rename_node($renames, $to);
0208     if ($rto eq $rfrom)
0209     {delete $edge->{$to};next;}
0210     if ($pcache->{$rto})
0211     {delete $edge->{$to};next;}
0212     else{$pcache->{$rto}=1;}
0213     if (!exists $cache->{$to})
0214     {&prune_edge ($renames, $groups, $cache1, $cache, $edges, $to);}
0215     foreach my $rdep (keys %{$cache->{$rto}}) {
0216       if ($rdep eq $rfrom){next;}
0217       if ($rdep eq $rto){next;}
0218       if (exists $pcache->{$rdep} && (!defined $cache1 || !exists $cache1->{$rto}{$rfrom}))
0219       {&prune_node ($renames, $edges, $groups, $rfrom, $rdep);}
0220       else{$pcache->{$rdep}=1;}
0221     }
0222     if  (defined $cache1 && !exists $cache1->{$rto}{$rfrom}) {
0223       my $xcache = $cache->{$rto};
0224       foreach my $rdep (keys %{$cache1->{$rto}}) {
0225         if(exists $xcache->{$rdep}){next;}
0226         if ($rdep eq $rfrom){next;}
0227         if (exists $pcache->{$rdep})
0228         {&prune_node ($renames, $edges, $groups, $rfrom, $rdep);}
0229         else{$pcache->{$rdep}=1;}
0230       }
0231     }
0232   }
0233   $groups->{$rfrom}{$from}=1;
0234 }
0235 
0236 sub prune_edges1 {
0237   my ($renames, $edges) = @_;
0238   my $cache= {};
0239   my $cache1 = undef;
0240   my $groups = {};
0241   my @ed=keys %$edges;
0242   for(my $i=0; $i<2; $i++) {
0243     foreach my $from (@ed) {
0244       if (!exists $cache->{$from}) {
0245         &prune_edge($renames, $groups, $cache1, $cache, $edges, $from);
0246       }
0247     }
0248     foreach my $from (keys %$cache){delete $cache->{$from}{$from};}
0249     $cache1 = $cache;
0250     $cache = {};
0251   }
0252 }
0253 
0254 sub has_edge {
0255   my ($edges, $from, $to, $intermed, @seen) = @_;
0256   push (@seen, $from);
0257   return 0 if grep($intermed eq $_, @seen);
0258 
0259   foreach my $x (keys %{$edges->{$intermed}}) {
0260     return 1 if ($x eq $to || &has_edge($edges, $intermed, $to, $x, @seen));
0261   }
0262   return 0;
0263 }
0264 
0265 sub prune_edges {
0266   my ($edges) = @_;
0267   my $dirty;
0268   do {
0269     $dirty = 0;
0270     foreach my $from (keys %$edges) {
0271       foreach my $to (keys %{$edges->{$from}}) {
0272         foreach my $x (keys %{$edges->{$from}}) {
0273           if ($x ne $to && &has_edge($edges, $from, $to, $x)) {
0274             delete $edges->{$from}{$to};
0275             $dirty = 1;
0276           }
0277         }
0278       }
0279     }
0280   } while ($dirty);
0281 }
0282 
0283 sub group_node {
0284   my ($grouping, $groups, $node, $default) = @_;
0285   foreach my $group (@$groups) {
0286     my $i = 0;
0287     foreach (@{$group->[2]}) {
0288       if ($node =~ /$_/) {
0289         $grouping->{$node} = [ $group, $i ];
0290         return;
0291       }
0292       $i++;
0293     }
0294   }
0295   $grouping->{$node} = [ $default, 0 ];
0296 }
0297 
0298 my %renames_results = ();
0299 sub rename_node {
0300   my ($renames, $node) = @_;
0301   if (exists $renames_results{$node}){return $renames_results{$node};}
0302   for (@$renames){
0303     if ($node =~ /$_->[0]/){
0304       my $tmp=eval $_->[1];
0305       $renames_results{$node}=$tmp;
0306       $renames_results{$tmp}=$tmp;
0307       return $tmp;
0308     }
0309   }
0310   $renames_results{$node}=$node;
0311   return $node;
0312 }
0313 
0314 sub version_node {
0315   my ($versions, $node,$d) = @_;
0316   foreach $d (@$versions){
0317     if($node =~ /$d->[0]/){ return $d->[1]; }
0318   }
0319   return "";
0320 }
0321 
0322 sub dep_count {
0323   my ($table, $i, $j, $row) = @_;
0324   my $x = $table->[$i]->[$j];
0325   my $y = $table->[$j]->[$i];
0326 
0327   my ($libs, $incs);
0328   if ($row->[3] eq 'libraries') {
0329     $libs = $row->[2];
0330     $incs = $row->[5];
0331   } else {
0332     $libs = $row->[5];
0333     $incs = $row->[2];
0334   }
0335 
0336   $x->[$CELL_LIB_OUT_COUNT] += $libs;
0337   $y->[$CELL_LIB_IN_COUNT] += $libs;
0338 
0339   $x->[$CELL_INC_OUT_COUNT] += $incs;
0340   $y->[$CELL_INC_IN_COUNT] += $incs;
0341 }
0342 
0343 sub dep_xcount {
0344   my ($edges, $rinclusive, $rrx, $xinclusive, $xrx, $labels, $table, $i, $j) = @_;
0345   my $source = $labels->[$i];
0346   my $target = $labels->[$j];
0347   my $matches = &check_xedge($edges, $rinclusive, $rrx, $xinclusive, $xrx, $source, $target);
0348   if ($matches ne 0) {
0349     $table->[$i]->[$j]->[$CELL_LOG_OUT_EDGES] += $matches;
0350     $table->[$j]->[$i]->[$CELL_LOG_IN_EDGES]  += $matches;
0351   }
0352 }
0353 
0354 sub dep_summarise {
0355   my ($summary, $labels, $table, $i) = @_;
0356 
0357   foreach my $cell (@{$table->[$i]}) {
0358     $summary->[$i]->[$SUM_INC_OUT_EDGES]++ if ($cell->[$CELL_INC_OUT_COUNT]);
0359     $summary->[$i]->[$SUM_INC_OUT_COUNT]   += $cell->[$CELL_INC_OUT_COUNT];
0360     $summary->[$i]->[$SUM_INC_IN_EDGES]++  if ($cell->[$CELL_INC_IN_COUNT]);
0361     $summary->[$i]->[$SUM_INC_IN_COUNT]    += $cell->[$CELL_INC_IN_COUNT];
0362 
0363     $summary->[$i]->[$SUM_LIB_OUT_EDGES]++ if ($cell->[$CELL_LIB_OUT_COUNT]);
0364     $summary->[$i]->[$SUM_LIB_OUT_COUNT]   += $cell->[$CELL_LIB_OUT_COUNT];
0365     $summary->[$i]->[$SUM_LIB_IN_EDGES]++  if ($cell->[$CELL_LIB_IN_COUNT]);
0366     $summary->[$i]->[$SUM_LIB_IN_COUNT]    += $cell->[$CELL_LIB_IN_COUNT];
0367 
0368     $summary->[$i]->[$SUM_LOG_OUT_EDGES]   += $cell->[$CELL_LOG_OUT_EDGES];
0369     $summary->[$i]->[$SUM_LOG_IN_EDGES]    += $cell->[$CELL_LOG_IN_EDGES];
0370 
0371     $summary->[$i]->[$SUM_ALL_IN_EDGES]++
0372       if ($cell->[$CELL_INC_IN_COUNT]
0373           || $cell->[$CELL_LIB_IN_COUNT]
0374           || $cell->[$CELL_LOG_IN_EDGES]);
0375 
0376     $summary->[$i]->[$SUM_ALL_OUT_EDGES]++
0377       if ($cell->[$CELL_INC_OUT_COUNT]
0378           || $cell->[$CELL_LIB_OUT_COUNT]
0379           || $cell->[$CELL_LOG_OUT_EDGES]);
0380   }
0381 }
0382 
0383 sub dep_metrics_mark {
0384   my ($edges, $tedges, $n,$m) = @_;
0385   return if defined $m && exists $tedges->{$n}->{$m};
0386   if (defined $m) {
0387     $tedges->{$n}->{$m} = 1;
0388     map { &dep_metrics_mark($edges, $tedges, $n, $_) } keys %{$edges->{$m}};
0389   }
0390   map { &dep_metrics_mark($edges, $tedges, $n, $_) } keys %{$edges->{$n}};
0391 }
0392 
0393 sub dep_metrics {
0394   # see ftp://ftp.aw.com/cp/lakos/idep_ldep.c
0395   my ($edges, $nodes, $metrics) = @_;
0396 
0397   my @nodes     = keys %$nodes;
0398   my $n         = scalar @nodes;
0399   my @levels    = (0) x $n;
0400   my $nlevels   = 1;
0401   my @levelnums = (0) x $n;
0402   my @mapping   = (0) x $n;
0403   my $nmapping  = 0;
0404 
0405   my @lowerthan = map { [ (0) x $n ] } @nodes, 'foo', 'bar';
0406    
0407   # calculate transitive closure
0408   my %tedges = map { $_ => {} } keys %$edges;
0409   map { &dep_metrics_mark($edges, \%tedges, $_, undef) } keys %$edges;
0410 
0411   # determine and label all members of all cycles
0412   my @cycles    = (-1) x $n;
0413   my @weights   = (0) x $n;
0414   my @cindices  = (-1) x $n;
0415   my $ncycles   = 0;
0416   my $nmembers  = 0;
0417   for (my $i = 0; $i < $n; ++$i) {
0418     next if $cycles[$i] >= 0;
0419     my $found = 0;
0420     $cycles[$i] = $i;
0421     for (my $j = $i + 1; $j < $n; ++$j) {
0422       next if $cycles[$j] >= 0;
0423       if ($tedges{$nodes[$i]}->{$nodes[$j]} && $tedges{$nodes[$j]}->{$nodes[$i]}) {
0424         $found = 1;
0425         $cycles[$j] = $i;
0426       }
0427     }
0428     if ($found) {
0429       my $weight = 0;
0430       for (my $j = $i; $j < $n; ++$j) {
0431         ++$weight if $cycles[$j] == $i;
0432       }
0433       for (my $j = $i; $j < $n; ++$j) {
0434         $weights[$j] = $weight if $cycles[$j] == $i;
0435       }
0436       $nmembers += $weight;
0437       $ncycles++;
0438     } else {
0439       $cycles[$i] = -1;
0440     }
0441   }
0442 
0443   # sort packages into levelized order; strip principal cycle
0444   # members from their dependencies on other cycle members
0445   for (my $i = 0; $i < $n; ++$i) {
0446     next if $cycles[$i] != $i;
0447     for (my $j = $i + 1; $j < $n; ++$j) {
0448       next if $cycles[$j] != $i;
0449       delete $tedges{$nodes[$i]}->{$nodes[$j]};
0450       $lowerthan[1]->[$j] = 1;
0451     }
0452   }
0453 
0454   # construct levelized array of package indices
0455   while ($nmapping < $n) {
0456     my $count = 0;
0457     my @current = (0) x $n;
0458     for (my $i = 0; $i < $n; ++$i) {
0459       next if $cycles[$i] >= 0 && $cycles[$i] != $i;
0460       next if $lowerthan[$nlevels]->[$i];
0461 
0462       my $weight = 1;
0463       if ($cycles[$i] == $i) {
0464         next if $weights[$i] > $nlevels + 1;
0465         $weight = $weights[$i];
0466       }
0467 
0468       my $level = $nlevels + 1 - $weight;
0469       my $j;
0470       for ($j = 0; $j < $n; ++$j) {
0471         next if $i == $j;
0472         last if $tedges{$nodes[$i]}->{$nodes[$j]} && !$lowerthan[$level]->[$j];
0473       }
0474       next if $j < $n;
0475 
0476       $current[$i] = 1;
0477       $mapping[$nmapping++] = $i;
0478       $count++;
0479       if ($cycles[$i] == $i) {
0480         for ($j = $i + 1; $j < $n; ++$j) {
0481           next if $cycles[$j] != $i;
0482           $mapping[$nmapping++] = $j;
0483           $count++;
0484           $tedges{$nodes[$i]}->{$nodes[$j]} = 1;
0485         }
0486       }
0487     }
0488     for (my $i = 0; $i < $n; ++$i) {
0489       $current[$i] ||= $lowerthan[$nlevels][$i];
0490     }
0491     $levels[$nlevels++] = $count;
0492     @{$lowerthan[$nlevels]} = @current;
0493     @current = (0) x $n;
0494   }
0495   die "internal error" if $nmapping != $n;
0496 
0497   # start loading level number array
0498   my $start = 0;
0499   for (my $i = 1; $i < $nlevels; ++$i) {
0500     my $top = $start + $levels[$i];
0501     for (my $j = $start; $j < $top; ++$j) {
0502       $levelnums[$mapping[$j]] = $i;
0503     }
0504     $start = $top;
0505   }
0506 
0507   # sort packages on each level lexicographically
0508   $start = 0;
0509   for (my $k = 1; $k < $nlevels; ++$k) {
0510     my $top = $start + $levels[$k];
0511     for (my $i = $start + 1; $i < $top; ++$i) {
0512       for (my $j = $start; $j < $i; ++$j) {
0513         if (($nodes[$mapping[$i]] cmp $nodes[$mapping[$j]]) < 0) {
0514           my $tmp = $mapping[$i];
0515           $mapping[$i] = $mapping[$j];
0516           $mapping[$j] = $tmp;
0517         }
0518       }
0519     }
0520     $start = $top;
0521   }
0522 
0523   # create @cindices from cycles array and the level map
0524   my $ncycle = 0;
0525   for (my $i = 0; $i < $n; ++$i) {
0526     my $label = $cycles[$mapping[$i]];
0527     next if $label < 0;
0528     my $index = $cindices[$mapping[$i]];
0529     next if $index >= 0 && $index < $ncycle;
0530     for (my $j = $i; $j < $n; ++$j) {
0531       $cindices[$mapping[$j]] = $ncycle if $label == $cycles[$mapping[$j]];
0532     }
0533     $ncycle++;
0534   }
0535   die "internal error" if $ncycle != $ncycles;
0536 
0537   # sort packages on each level again but now grouping
0538   # cyclically dependent packages together
0539   $start = 0;
0540   for (my $k = 1; $k < $nlevels; ++$k) {
0541     my $top = $start + $levels[$k];
0542     for (my $i = $start + 1; $i < $top; ++$i) {
0543       for (my $j = $start; $j < $i; ++$j) {
0544         my $ci = $cindices[$mapping[$i]];
0545         my $cj = $cindices[$mapping[$j]];
0546         if ($ci < $cj || ($ci == $cj && ($nodes[$mapping[$i]] cmp $nodes[$mapping[$j]]) < 0)) {
0547           my $tmp = $mapping[$i];
0548           $mapping[$i] = $mapping[$j];
0549           $mapping[$j] = $tmp;
0550         }
0551       }
0552     }
0553     $start = $top;
0554   }
0555 
0556   # calculate ccd
0557   my %tmp = map { $_ => {} } keys %tedges;
0558   foreach my $n (keys %tedges) {
0559     map { $tmp{$n}->{$_} = 1 } keys %{$tedges{$n}};
0560   }
0561 
0562   for (my $i = 0; $i < $n; ++$i) {
0563     if ($levelnums[$i] == 0) {
0564       for (my $j = 0; $j < $n; ++$j) {
0565         delete $tmp{$nodes[$j]}->{$nodes[$i]};
0566       }
0567     } else {
0568       $tmp{$nodes[$i]}->{$nodes[$i]} = 1;
0569     }
0570   }
0571 
0572   my $ccd = 0;
0573   map { $ccd += scalar keys %{$tmp{$_}} } keys %tmp;
0574 
0575   if (0) {
0576     # make canonical representation: remove all redundant (transitive0edges
0577     my @tmp = map { [ (0) x $n ] } @nodes;
0578     for (my $i = 0; $i < $n; ++$i) {
0579       my $u = $mapping[$i];
0580       for (my $j = 0; $j < $n; ++$j) {
0581         my $v = $mapping[$j];
0582         my $bit = $tedges{$nodes[$u]}->{$nodes[$v]};
0583         $tmp[$i]->[$j] = $bit;
0584       }
0585     }
0586     # FIXME;
0587   }
0588 
0589   $metrics->{NODES}     = \@nodes;
0590   $metrics->{N}         = $n;
0591   $metrics->{LEVELS}    = \@levels;
0592   $metrics->{NLEVELS}   = $nlevels;
0593   $metrics->{LEVELNUMS} = \@levelnums;
0594   $metrics->{MAPPING}   = \@mapping;
0595   $metrics->{NMAPPINGS} = $nmapping;
0596    
0597   $metrics->{TEDGES}    = \%tedges;
0598 
0599   $metrics->{CYCLES}    = \@cycles;
0600   $metrics->{WEIGHTS}   = \@weights;
0601   $metrics->{CINDICES}  = \@cindices;
0602   $metrics->{NCYCLES}   = $ncycles;
0603   $metrics->{NMEMBERS}  = $nmembers;
0604 
0605   $metrics->{CCD}       = $ccd;
0606 
0607   return $nmembers;
0608 }
0609 
0610 my %url_results = ();
0611 sub node_url {
0612   my ($urls, $name) = @_;
0613   if (exists $url_results{$name}){return $url_results{$name};}
0614   foreach my $mapping (@$urls) {
0615     if ($name =~ /$mapping->[0]/) {
0616       my $N = $name;
0617       my $tmp = eval $mapping->[1];
0618       $url_results{$name} = $tmp;
0619       return $tmp;
0620     }
0621   }
0622   $url_results{$name} = undef;
0623   return undef;
0624 }
0625 
0626 sub read_report {
0627   my ($edges, $nodes, $file, $part) = @_;
0628   my $node = undef;
0629 
0630   open(FILE, $file) || die "$file: error: cannot open: $!\n";
0631   while (<FILE>) {
0632     last if /^\# $part/;
0633   }
0634 
0635   while (<FILE>) {
0636     chomp;
0637     last if (/^\#\#\#\#\#/);
0638     next if (/^\s*$/);
0639 
0640     if (/^\S/) {
0641       chop;
0642       s% ([^/]+)$%%;
0643       $node = $_;
0644       $nodes->{$_} = 1;
0645       $edges->{$_} ||= {};
0646     } elsif (/^\s+\S/) {
0647       print STDERR "$file: warning: no current node for a dependency\n"
0648         if ! $node;
0649 
0650       s/\s//g;
0651       $nodes->{$_} = 1;
0652       $edges->{$_} ||= {}; 
0653       $edges->{$node}->{$_} ||= [];
0654       push(@{$edges->{$node}->{$_}}, ':hard');
0655     } else {
0656       print STDERR "$file: warning: unrecognized line: `$_'\n";
0657     }
0658   }
0659   close(FILE);
0660 }
0661 
0662 sub keep_nodes {
0663   my ($kept_nodes, $edges, $node) = @_;
0664   $kept_nodes->{$node} = 1;
0665   foreach my $edge (keys %{$edges->{$node}}) {
0666     if (!exists $kept_nodes->{$edge}){
0667       &keep_nodes ($kept_nodes, $edges, $edge);
0668     }
0669   }
0670 }
0671 
0672 sub remove_nodes {
0673   my ($edges, $nodes, $spart) = @_;
0674   my %kept_nodes = ();
0675   my $node;
0676   foreach $node (keys %$nodes) {
0677     if (($node=~/$spart/) && (!exists $kept_nodes{$node})){
0678       &keep_nodes (\%kept_nodes, $edges, $node);
0679     }
0680   }
0681   foreach $node (keys %$nodes) {
0682     if (!exists $kept_nodes{$node}){
0683       delete $nodes->{$node};
0684       delete $edges->{$node};
0685     }
0686   }
0687 }
0688 
0689 sub read_counts {
0690   my ($edges, $nodes, $vector, $note, $file) = @_;
0691   open(FILE, $file) || die "$file: error: cannot open: $!\n";
0692   while (<FILE>) {
0693     chomp;
0694     if (! /^\s*(\d+)\s+(\S+)\s+(\S+(,\s+\S+)?)(\s+via\s+(\S+))?$/) {
0695       print STDERR "$file: error: bad line `$_'\n";
0696       next;
0697     }
0698     next if $2 eq $3;
0699     $nodes->{$2} = 1; $edges->{$2} ||= {};
0700     $nodes->{$3} = 1; $edges->{$3} ||= {};
0701     $edges->{$2}->{$3} ||= [];
0702     push(@{$edges->{$2}->{$3}}, ':hard');
0703     push(@$vector, [ $2, $3, $1, $note, $5 || '', -1]);
0704   }
0705   close(FILE);
0706 }
0707 
0708 sub read_loglinks {
0709   my ($edges, $nodes, $file) = @_;
0710   my @xtargets = ();
0711   my @xreasons = ();
0712 
0713   open(FILE, $file) || die "$file: error: cannot open: $!\n";
0714   while (<FILE>) {
0715     chomp; s/\#.*//; s/\s*$//;
0716     next if (/^\s*$/);
0717 
0718     if (/^(\S+(\s*,\s*\S+)*)(\s*:\s*->\s*(\S+(\s*,\s*\S+)*))?$/) {
0719       @xreasons = split(/\s*,\s*/, $1);
0720       @xtargets = split(/\s*,\s*/, $4 || '');
0721     } elsif (/^\s+(\S+(\s*,\s*\S+)*)(\s*->\s*(\S+(\s*,\s*\S+)*))?$/) {
0722       my @sources = split(/\s*,\s*/, $1);
0723       my @targets = ($4 ? split(/\s*,\s*/, $4) : @xtargets);
0724       foreach my $source (grep(exists $nodes->{$_}, @sources)) {
0725         $edges->{$source} ||= {};
0726         map { $edges->{$source}->{$_} ||= [];
0727               push(@{$edges->{$source}->{$_}}, @xreasons); } @targets;
0728       }
0729     } else {
0730       print STDERR "$file: warning: unrecognized line: `$_'\n";
0731     }
0732   }
0733   close(FILE);
0734 }
0735 
0736 sub read_groups {
0737   my ($groups, $file) = @_;
0738   my $i = 0;
0739 
0740   open(FILE, $file) || die "$file: error: cannot open: $!\n";
0741   while (<FILE>) {
0742     chomp;
0743     while (/\\$/ && !eof(FILE)) {
0744       chop; $_ .= <FILE>; chomp;
0745     }
0746     if (/\\$/) {
0747       print STDERR "$file: warning: incomplete last line\n";
0748       chop;
0749     }
0750     s/\#.*//; s/^\s*//; s/\s*$//;
0751     next if (/^$/);
0752 
0753     if (/^group\s+(\S+)\s+(.*)\s+--\s+(.*)$/) {
0754       push(@$groups, [ $i++, $1, [ split(/\s+/, $2) ], $3 ]);
0755     } else {
0756       print STDERR "$file: warning: unrecognized line: `$_'\n";
0757     }
0758   }
0759   close(FILE);
0760 }
0761 
0762 sub read_renames {
0763   my ($renames, $file) = @_;
0764   my $i = 0;
0765 
0766   open(FILE, $file) || die "$file: error: cannot open: $!\n";
0767   while (<FILE>) {
0768     chomp;
0769     while (/\\$/ && !eof(FILE)) {
0770       chop; $_ .= <FILE>; chomp;
0771     }
0772     if (/\\$/) {
0773       print STDERR "$file: warning: incomplete last line\n";
0774       chop;
0775     }
0776     s/\#.*//; s/^\s*//; s/\s*$//;
0777     next if (/^$/);
0778 
0779     if (/^rename\s+([^:]+):(.*)$/) {
0780       push(@$renames, [ $1, $2 ]);
0781     } else {
0782       print STDERR "$file: warning: unrecognized line: `$_'\n";
0783     }
0784   }
0785   close(FILE);
0786 }
0787 
0788 sub read_versions {
0789   my ($versions, $file) = @_;
0790   my $i = 0;
0791 
0792   open(FILE, $file) || die "$file: error: cannot open: $!\n";
0793   while (<FILE>) {  
0794     chomp;
0795     while (/\\$/ && !eof(FILE)) {
0796       chop; $_ .= <FILE>; chomp;
0797     }    
0798     if (/\\$/) {
0799       print STDERR "$file: warning: incomplete last line\n";
0800       chop;
0801     }
0802     s/\#.*//; s/^\s*//; s/\s*$//;
0803     next if (/^$/);
0804 
0805     if (/^version\s+([^:]+):(.+)$/) {
0806       push(@$versions, [ $1, $2 ]);
0807     } else {
0808       print STDERR "$file: warning: unrecognized line: `$_'\n";
0809     }
0810   }
0811   close(FILE);
0812 }
0813 
0814 sub read_urls {
0815   my ($urls, $file) = @_;
0816   my $i = 0;
0817 
0818   open(FILE, $file) || die "$file: error: cannot open: $!\n";
0819   while (<FILE>) {
0820     chomp;
0821     while (/\\$/ && !eof(FILE)) {
0822       chop; $_ .= <FILE>; chomp;
0823     }
0824     if (/\\$/) {
0825       print STDERR "$file: warning: incomplete last line\n";
0826       chop;
0827     }
0828     s/^\s*//; s/\s*$//;
0829     next if (/^$/);
0830     next if (/^#/);
0831 
0832     if (/^([^:]+):(.*)$/) {
0833       push(@$urls, [ $1, $2 ]);
0834     } else {
0835       print STDERR "$file: warning: unrecognized line: `$_'\n";
0836     }
0837   }
0838   close(FILE);
0839 }
0840 
0841 ######################################################################
0842 my $usage =
0843     "usage: $me [--group=FILE] [--rename=FILE] [--ratio=RATIO] [--concentrate={YES|NO}]\n"
0844   . "                [--version=FILE] [--long-edges={YES|NO}] [--shape=SHAPE] [--url=FILE]\n"
0845   . "                REPORT-FILE REPORT-PART REPORT-SUBPART DOT-TITLE INCLUSIVE? [RE...]\n"
0846   . "                [! RESTRICT-INCLUSIVE? RESTRICT-RE...]\n"
0847   . "                [-- EXTRA EXTRA-INCLUSIVE? EXTRA-RE...]\n\n"
0848   . "(plot with `dot' from AT&T graphviz: $me ... | dot -Tps -o output.ps)\n\n"
0849   . "for example:\n"
0850   . "  $0 dependencies.txt \"Direct combined\" \"^Ig_Modules/\" \"Cmscan Direct Dependencies\" yes Cmscan\n"
0851   . "  $0 dependencies.txt \"Direct combined\" \"^Ig_Modules/\" \"IGUANA 2.1.2 Import/LHC++ Direct Dependencies\" \\\n"
0852   . "          no Ig_Extensions Examples Ig_Applications\n"
0853   . "  $0 dependencies.txt \"Direct combined\" \"^Ig_Modules/\" \"IGUANA 2.1.2 Extensions Only Direct Dependencies\" \\\n"
0854   . "          yes Ig_Extensions ! yes Ig_Extensions\n\n"
0855   . "graphviz is part of ``Practical Reusable UNIX Software'' by AT&T, available\n"
0856   . "at http://portal.research.bell-labs.com/orgs/ssr/book/reuse/\n";
0857 
0858 my $longedges = 1;
0859 my $shape = 'ellipse';
0860 my $ratio = 'auto';
0861 my $concentrate = 1;
0862 my $groupfile = '/dev/null';
0863 my $renamefile = '/dev/null';
0864 my $versionfile = '/dev/null';
0865 my $urlfile = '/dev/null';
0866 my $default_group = [ -1, 'other', [ '.*' ], '1 1 1' ];
0867 
0868 while (scalar @ARGV && $ARGV[0] =~ /^--(.*)$/) {
0869   my $arg = $1;
0870   if ($1 =~ /^group=(.*)/) {
0871     $groupfile = $1;
0872   } elsif ($1 =~ /^rename=(.*)/) {
0873     $renamefile = $1;
0874   } elsif ($1 =~ /^version=(.*)/) {
0875     $versionfile = $1;
0876   } elsif ($1 =~ /^url=(.*)/) {
0877     $urlfile = $1;
0878   } elsif ($1 =~ /^ratio=(.*)/) {
0879     $ratio = $1;
0880   } elsif ($1 =~ /^concentrate=(.*)/) {
0881     $concentrate = &validate_flag($1, "--concentrate");
0882   } elsif ($1 =~ /^long-edges=(.*)/) {
0883     $longedges = &validate_flag($1, "--long-edges");
0884   } elsif ($1 =~ /^shape=(.*)/) {
0885     $shape = $1;
0886   } else {
0887     print STDERR "unrecognised option `$arg'\n";
0888     die $usage;
0889   }
0890   shift(@ARGV);
0891 }
0892 
0893 die $usage if (scalar @ARGV < 4);
0894 my ($file, $part, $subpart, $title, $inclusive, @restargs) = @ARGV;
0895 my ($rinclusive, $xfile, $xinclusive, @rx, @rrx, @xrx) = (1, "/dev/null", 1);
0896 $inclusive = &validate_flag($inclusive, "INCLUSIVE?");
0897 
0898 while (scalar @restargs) {
0899   my $arg = shift(@restargs);
0900   if ($arg eq '!') {
0901     die $usage if (scalar @restargs < 2);
0902     $rinclusive = &validate_flag(shift(@restargs), "RESTRICT-INCLUSIVE?");
0903     while (scalar @restargs && $restargs[0] ne '--') {
0904       push(@rrx, shift(@restargs));
0905     }
0906   } elsif ($arg eq '--') {
0907     die $usage if (scalar @restargs < 2);
0908     $xfile = shift(@restargs);
0909     $xinclusive = &validate_flag(shift(@restargs), "EXTRA-INCLUSIVE?");
0910     @xrx = @restargs;
0911     $xrx[0] ||= '.*';
0912     last;
0913   } else {
0914     push(@rx, $arg);
0915   }
0916 }
0917 $rx[0] ||= '.*';
0918 $rrx[0] ||= '.*';
0919 
0920 ######################################################################
0921 my %nodes = ();
0922 my %gnodes = ();
0923 my %edges = ();
0924 my %gedges = ();
0925 my @roots = ();
0926 my @all = ();
0927 my @groups = ();
0928 my %grouping = ();
0929 my @renames = ();
0930 my @versions = ();
0931 my @urls = ();
0932 
0933 # Read in the report.
0934 &read_report(\%edges, \%nodes, $file, $part);
0935 
0936 # Read in the extra-deps file.
0937 &read_loglinks(\%edges, \%nodes, $xfile);
0938 
0939 # Remove the unwanted nodes
0940 &remove_nodes(\%edges, \%nodes, $subpart) if ($subpart ne "");
0941 
0942 # Read in grouping.
0943 &read_groups(\@groups, $groupfile);
0944 
0945 # Read in renames.
0946 &read_renames(\@renames, $renamefile);
0947 
0948 # Read in versions.
0949 &read_versions(\@versions, $versionfile);
0950 
0951 # Read node-to-url mappings
0952 &read_urls(\@urls, $urlfile);
0953 
0954 # Calculate which nodes can be reached.
0955 foreach my $node (keys %nodes) {
0956   push(@roots, $node) if (grep($node =~ /$_/, @rx) && 1) eq $inclusive;
0957 }
0958 
0959 foreach (@roots) {
0960   &do_node(\%gedges,\%gnodes, \%edges,\%nodes, $rinclusive,\@rrx, $xinclusive,\@xrx, undef,$_,1);
0961 }
0962 @all = keys %gnodes;
0963 
0964 # If requested to suppress edges where a longer variant exists,
0965 # filter shorter ones out
0966 
0967 &prune_edges1 (\@renames, \%gedges, \%gnodes) if ! $longedges;
0968 
0969 # Group nodes
0970 map { &group_node(\%grouping, \@groups, $_, $default_group) } keys %nodes;
0971 
0972 ######################################################################
0973 # Make the graph
0974 print "digraph Dependencies {\n",
0975       "  fontname=\"Helvetica\"; fontsize=12; center=true; ratio=$ratio;",
0976       ($concentrate ? "concentrate=true;\n" : "\n"),
0977       "  label=\"\\n$title\\n\"\n\n",
0978       "  node [shape=$shape, fontname=\"Helvetica-Bold\", fontsize=12 ]\n",
0979       "  edge [fontname=\"Helvetica\", fontsize=12 ]\n\n";
0980 
0981 my %diag_done=();
0982 foreach (sort keys %gnodes) {
0983   my @hsvcolor = split(/\s+/, $grouping{$_}->[0]->[3]);
0984   my $y = (&rgb2yiq(&hsv2rgb(@hsvcolor)))[0];
0985   my $textcolor = ($y < .5 ? "0 0 1" : "0 0 0");
0986   my $url = &node_url(\@urls, $_);
0987   my $renamed=&rename_node(\@renames, $_);
0988   my $v=&version_node(\@versions, $renamed);
0989   if($v ne ""){$renamed="$renamed\\n$v";}
0990   if(exists $diag_done{$renamed}){next;}
0991   $diag_done{$renamed}=1;
0992   print "  \"" . $renamed . "\" [ "
0993     . ($url ? "URL=\"$url\", " : "")
0994     . ($hsvcolor[2] eq 1 && $hsvcolor[1] eq 0 ? ""
0995        : "style=filled, color=\"" . join(" ", @hsvcolor) . "\", ")
0996     . "fontcolor=\"$textcolor\" ]\n";
0997 }
0998 print "\n";
0999 
1000 %diag_done=();
1001 my %done = ();
1002 foreach my $n (sort keys %gedges) {
1003   my $rename_n=&rename_node(\@renames, $n);
1004   my $v=&version_node(\@versions, $rename_n);
1005   if($v ne ""){$rename_n="$rename_n\\n$v";}
1006   foreach my $m (sort keys %{$gedges{$n}}) {
1007     my $rename_m=&rename_node(\@renames, $m);
1008     $v=&version_node(\@versions, $rename_m);
1009     if($v ne ""){$rename_m="$rename_m\\n$v";}
1010     if($rename_m eq $rename_n){next;}
1011     my $outlevel = $gedges{$n}->{$m};
1012     my $inlevel  = exists $gedges{$m} && $gedges{$m}->{$n} || 0;
1013     my @options  = ();
1014     $done{"$n -> $m"} = $outlevel;
1015     if (exists $diag_done{"$rename_n -> $rename_m"}){next;}
1016     $diag_done{"$rename_n -> $rename_m"}=1;
1017     if ($concentrate) {
1018       next if (exists $done{"$m -> $n"} && $done{"$m -> $n"});
1019       my $style = (qw(dotted dashed dotted))[$inlevel];
1020       push(@options, "style=$style") if ($outlevel eq 2);
1021       push(@options, "dir=both")     if ($inlevel);
1022     } else {
1023       next if (((exists $done{"$m -> $n"} && $done{"$m -> $n"}) || 0) eq $outlevel);
1024 
1025       push(@options, "style=dotted") if ($outlevel eq 2);
1026       push(@options, "dir=both")     if ($inlevel eq $outlevel);
1027     }
1028     print "  \"" . $rename_n . "\" -> \""
1029       . $rename_m . "\" [" . join(", ", @options) . "]\n";
1030   }
1031   print "\n";
1032 }
1033 
1034 print "}\n";