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";