Warning, /Utilities/ReleaseScripts/ignominy/deps2metrics is written in an unsupported language. File is not indexed.
0001 #!/usr/bin/env perl
0002 use strict;
0003 no warnings qw(recursion);
0004 $|=1;
0005 my $me = $0;
0006 $me =~ s|.*/||;
0007
0008 ######################################################################
0009 my $SUM_INC_OUT_EDGES = 0;
0010 my $SUM_INC_OUT_COUNT = 1;
0011 my $SUM_INC_IN_EDGES = 2;
0012 my $SUM_INC_IN_COUNT = 3;
0013 my $SUM_LIB_OUT_EDGES = 4;
0014 my $SUM_LIB_OUT_COUNT = 5;
0015 my $SUM_LIB_IN_EDGES = 6;
0016 my $SUM_LIB_IN_COUNT = 7;
0017 my $SUM_LOG_OUT_EDGES = 8;
0018 my $SUM_LOG_IN_EDGES = 9;
0019 my $SUM_ALL_IN_EDGES = 10;
0020 my $SUM_ALL_OUT_EDGES = 11;
0021 my $SUM_N = 12;
0022
0023 my $CELL_INC_OUT_COUNT = 0;
0024 my $CELL_INC_IN_COUNT = 1;
0025 my $CELL_LIB_OUT_COUNT = 2;
0026 my $CELL_LIB_IN_COUNT = 3;
0027 my $CELL_LOG_OUT_EDGES = 4;
0028 my $CELL_LOG_IN_EDGES = 5;
0029 my $CELL_N = 6;
0030
0031 my @SUM_LABELS = ("Number of outgoing edges from includes",
0032 "Total count of outgoing includes",
0033 "Number of incoming edges from includes",
0034 "Total count of incoming includes",
0035 "Number of outgoing edges from symbols",
0036 "Total count of outgoing symbols",
0037 "Number of incoming edges from symbols",
0038 "Total count of incoming symbols",
0039 "Number of (possibly overlapping) outgoing logical edges",
0040 "Number of (possibly overlapping) incoming logical edges",
0041 "Total count of incoming edges",
0042 "Total count of outgoing edges");
0043
0044 sub min {
0045 my $min = shift;
0046 foreach my $val (@_) {
0047 $min = $min > $val ? $val : $min;
0048 }
0049 return $min;
0050 }
0051
0052 sub max {
0053 my $max = shift;
0054 foreach my $val (@_) {
0055 $max = $max > $val ? $max : $val;
0056 }
0057 return $max;
0058 }
0059
0060 sub rgb2hsv {
0061 my ($r, $g, $b) = @_;
0062 my ($h, $s, $v);
0063 my $max = &max($r, $g, $b);
0064 my $min = &min($r, $g, $b);
0065
0066 $v = $max;
0067 $s = ($max ? ($max - $min)/$max : 0.0);
0068 if (! $s) {
0069 $h = 0;
0070 } else {
0071 my $d = $max - $min;
0072 if ($r == $max) {
0073 $h = 0. + ($g - $b)/$d;
0074 } elsif ($g == $max) {
0075 $h = 2. + ($b - $r)/$d;
0076 } elsif ($b == $max) {
0077 $h = 4. + ($r - $g)/$d;
0078 }
0079 $h /= 6.;
0080 $h += 1. if ($h < 0.);
0081 }
0082
0083 $h = int($h * 1000 + .5)/1000;
0084 $s = int($s * 1000 + .5)/1000;
0085 $v = int($v * 1000 + .5)/1000;
0086
0087 return ($h, $s, $v);
0088 }
0089
0090 sub rgb2yiq {
0091 my ($r, $g, $b) = @_;
0092 return (.299 * $r + .587 * $g + .115 * $b,
0093 .596 * $r - .275 * $g - .321 * $b,
0094 .212 * $r - .523 * $g + .311 * $b);
0095 }
0096
0097 sub hsv2rgb {
0098 my ($h, $s, $v) = @_;
0099 if ($s eq 0.) {
0100 die "bad HSV H value: $h -- should be 0\n" if $h;
0101 return ($v, $v, $v);
0102 } else {
0103 $h = 0. if $h eq 1.;
0104 $h *= 6.;
0105 my $i = int($h);
0106 my $f = $h - $i;
0107 my $p = $v * (1. - $s);
0108 my $q = $v * (1. - $s * $f);
0109 my $t = $v * (1. - $s * (1. - $f));
0110
0111 return ($v, $t, $p) if $i == 0;
0112 return ($q, $v, $p) if $i == 1;
0113 return ($p, $v, $t) if $i == 2;
0114 return ($p, $q, $v) if $i == 3;
0115 return ($t, $p, $v) if $i == 4;
0116 return ($v, $p, $q) if $i == 5;
0117 die;
0118 }
0119 }
0120
0121 sub validate_flag {
0122 my ($value, $opt) = @_;
0123
0124 return 1 if (grep(lc($value) eq $_, qw(1 y yes)));
0125 return 0 if (grep(lc($value) eq $_, qw(0 n no)));
0126 die "$opt must be (1, y, yes) or (0, n, no)\n";
0127 }
0128
0129 sub file_part {
0130 my $name = shift;
0131 $name =~ s|.*/||;
0132 return $name;
0133 }
0134
0135 sub dir_part {
0136 my $name = shift;
0137 return '' if $name !~ m|/|;
0138 $name =~ s|/[^/]+$||;
0139 return $name;
0140 }
0141
0142 sub check_xedge {
0143 my ($edges, $rinclusive, $rrx, $xinclusive, $xrx, $source, $target) = @_;
0144 return 0 if (grep($target =~ /$_/, @$rrx) && 1) ne $rinclusive;
0145
0146 my $matches = 0;
0147 foreach my $xrx (@$xrx) {
0148 $matches += scalar grep ($_ ne ':hard'
0149 && ($xinclusive ? $_ =~ /$xrx/ : $_ !~ /$xrx/),
0150 @{$edges->{$source}->{$target}});
0151 }
0152 return $matches;
0153 }
0154
0155 sub include_edge {
0156 my ($edges, $nodes, $rinclusive, $rrx, $xinclusive, $xrx, $source, $target) = @_;
0157 return 0 if !exists $nodes->{$target};
0158 return 0 if (grep($target =~ /$_/, @$rrx) && 1) ne $rinclusive;
0159 return 1 if grep($_ eq ':hard', @{$edges->{$source}->{$target}});
0160 return 2 if &check_xedge($edges,$rinclusive,$rrx,$xinclusive,$xrx,$source,$target) ne 0;
0161 return 0;
0162 }
0163
0164 sub do_node {
0165 my ($gedges,$gnodes, $edges,$nodes, $rinclusive,$rrx, $xinclusive,$xrx, $from,$to,$include)=@_;
0166 return if ! $include;
0167
0168 if (defined $from) {
0169 $gedges->{$from} ||= {};
0170 $gedges->{$from}->{$to} = $include
0171 if (! exists $gedges->{$from}->{$to}
0172 || $gedges->{$from}->{$to} > $include);
0173 }
0174
0175 return if (exists $gnodes->{$to});
0176
0177 $gnodes->{$to} = 1;
0178 foreach (keys %{$edges->{$to}}) {
0179 &do_node($gedges, $gnodes, $edges, $nodes, $rinclusive, $rrx, $xinclusive, $xrx, $to, $_,
0180 &include_edge($edges, $nodes, $rinclusive, $rrx, $xinclusive, $xrx, $to, $_));
0181 }
0182 }
0183
0184 sub group_node {
0185 my ($grouping, $groups, $node, $default) = @_;
0186 foreach my $group (@$groups) {
0187 my $i = 0;
0188 foreach (@{$group->[2]}) {
0189 if ($node =~ /$_/) {
0190 $grouping->{$node} = [ $group, $i ];
0191 return;
0192 }
0193 $i++;
0194 }
0195 }
0196 $grouping->{$node} = [ $default, 0 ];
0197 }
0198
0199 my %renames_results = ();
0200 sub rename_node {
0201 my ($renames, $node) = @_;
0202 if (exists $renames_results{$node}){return $renames_results{$node};}
0203 for (@$renames){
0204 if ($node =~ /$_->[0]/){
0205 my $tmp=eval $_->[1];
0206 $renames_results{$node}=$tmp;
0207 $renames_results{$tmp}=$tmp;
0208 return $tmp;
0209 }
0210 }
0211 $renames_results{$node}=$node;
0212 return $node;
0213 }
0214
0215 sub dep_count {
0216 my ($table, $i, $j, $row) = @_;
0217 my $x = $table->[$i]->[$j];
0218 my $y = $table->[$j]->[$i];
0219
0220 my ($libs, $incs);
0221 if ($row->[3] eq 'libraries') {
0222 $libs = $row->[2];
0223 $incs = $row->[5];
0224 } else {
0225 $libs = $row->[5];
0226 $incs = $row->[2];
0227 }
0228
0229 $x->[$CELL_LIB_OUT_COUNT] += $libs;
0230 $y->[$CELL_LIB_IN_COUNT] += $libs;
0231
0232 $x->[$CELL_INC_OUT_COUNT] += $incs;
0233 $y->[$CELL_INC_IN_COUNT] += $incs;
0234 }
0235
0236 sub dep_xcount {
0237 my ($edges, $rinclusive, $rrx, $xinclusive, $xrx, $labels, $table, $i, $j) = @_;
0238 my $source = $labels->[$i];
0239 my $target = $labels->[$j];
0240 my $matches = &check_xedge($edges, $rinclusive, $rrx, $xinclusive, $xrx, $source, $target);
0241 if ($matches ne 0) {
0242 $table->[$i]->[$j]->[$CELL_LOG_OUT_EDGES] += $matches;
0243 $table->[$j]->[$i]->[$CELL_LOG_IN_EDGES] += $matches;
0244 }
0245 }
0246
0247 sub dep_summarise {
0248 my ($summary, $labels, $table, $i) = @_;
0249
0250 foreach my $cell (@{$table->[$i]}) {
0251 $summary->[$i]->[$SUM_INC_OUT_EDGES]++ if ($cell->[$CELL_INC_OUT_COUNT]);
0252 $summary->[$i]->[$SUM_INC_OUT_COUNT] += $cell->[$CELL_INC_OUT_COUNT];
0253 $summary->[$i]->[$SUM_INC_IN_EDGES]++ if ($cell->[$CELL_INC_IN_COUNT]);
0254 $summary->[$i]->[$SUM_INC_IN_COUNT] += $cell->[$CELL_INC_IN_COUNT];
0255
0256 $summary->[$i]->[$SUM_LIB_OUT_EDGES]++ if ($cell->[$CELL_LIB_OUT_COUNT]);
0257 $summary->[$i]->[$SUM_LIB_OUT_COUNT] += $cell->[$CELL_LIB_OUT_COUNT];
0258 $summary->[$i]->[$SUM_LIB_IN_EDGES]++ if ($cell->[$CELL_LIB_IN_COUNT]);
0259 $summary->[$i]->[$SUM_LIB_IN_COUNT] += $cell->[$CELL_LIB_IN_COUNT];
0260
0261 $summary->[$i]->[$SUM_LOG_OUT_EDGES] += $cell->[$CELL_LOG_OUT_EDGES];
0262 $summary->[$i]->[$SUM_LOG_IN_EDGES] += $cell->[$CELL_LOG_IN_EDGES];
0263
0264 $summary->[$i]->[$SUM_ALL_IN_EDGES]++
0265 if ($cell->[$CELL_INC_IN_COUNT]
0266 || $cell->[$CELL_LIB_IN_COUNT]
0267 || $cell->[$CELL_LOG_IN_EDGES]);
0268
0269 $summary->[$i]->[$SUM_ALL_OUT_EDGES]++
0270 if ($cell->[$CELL_INC_OUT_COUNT]
0271 || $cell->[$CELL_LIB_OUT_COUNT]
0272 || $cell->[$CELL_LOG_OUT_EDGES]);
0273 }
0274 }
0275
0276 sub dep_metrics_mark {
0277 my ($edges, $tedges, $n,$m) = @_;
0278 return if defined $m && exists $tedges->{$n}->{$m};
0279 if (defined $m) {
0280 $tedges->{$n}->{$m} = 1;
0281 map { &dep_metrics_mark($edges, $tedges, $n, $_) } keys %{$edges->{$m}};
0282 }
0283 map { &dep_metrics_mark($edges, $tedges, $n, $_) } keys %{$edges->{$n}};
0284 }
0285
0286 sub dep_metrics {
0287 # see ftp://ftp.aw.com/cp/lakos/idep_ldep.c
0288 my ($edges, $nodes, $metrics) = @_;
0289
0290 my @nodes = keys %$nodes;
0291 my $n = scalar @nodes;
0292 my @levels = (0) x $n;
0293 my $nlevels = 1;
0294 my @levelnums = (0) x $n;
0295 my @mapping = (0) x $n;
0296 my $nmapping = 0;
0297
0298 my @lowerthan = map { [ (0) x $n ] } @nodes, 'foo', 'bar';
0299
0300 # calculate transitive closure
0301 my %tedges = map { $_ => {} } keys %$edges;
0302 map { &dep_metrics_mark($edges, \%tedges, $_, undef) } keys %$edges;
0303
0304 # determine and label all members of all cycles
0305 my @cycles = (-1) x $n;
0306 my @weights = (0) x $n;
0307 my @cindices = (-1) x $n;
0308 my $ncycles = 0;
0309 my $nmembers = 0;
0310 for (my $i = 0; $i < $n; ++$i) {
0311 next if $cycles[$i] >= 0;
0312 my $found = 0;
0313 $cycles[$i] = $i;
0314 for (my $j = $i + 1; $j < $n; ++$j) {
0315 next if $cycles[$j] >= 0;
0316 if ($tedges{$nodes[$i]}->{$nodes[$j]} && $tedges{$nodes[$j]}->{$nodes[$i]}) {
0317 $found = 1;
0318 $cycles[$j] = $i;
0319 }
0320 }
0321 if ($found) {
0322 my $weight = 0;
0323 for (my $j = $i; $j < $n; ++$j) {
0324 ++$weight if $cycles[$j] == $i;
0325 }
0326 for (my $j = $i; $j < $n; ++$j) {
0327 $weights[$j] = $weight if $cycles[$j] == $i;
0328 }
0329 $nmembers += $weight;
0330 $ncycles++;
0331 } else {
0332 $cycles[$i] = -1;
0333 }
0334 }
0335
0336 # sort packages into levelized order; strip principal cycle
0337 # members from their dependencies on other cycle members
0338 for (my $i = 0; $i < $n; ++$i) {
0339 next if $cycles[$i] != $i;
0340 for (my $j = $i + 1; $j < $n; ++$j) {
0341 next if $cycles[$j] != $i;
0342 delete $tedges{$nodes[$i]}->{$nodes[$j]};
0343 $lowerthan[1]->[$j] = 1;
0344 }
0345 }
0346
0347 # construct levelized array of package indices
0348 while ($nmapping < $n) {
0349 my $count = 0;
0350 my @current = (0) x $n;
0351 for (my $i = 0; $i < $n; ++$i) {
0352 next if $cycles[$i] >= 0 && $cycles[$i] != $i;
0353 next if $lowerthan[$nlevels]->[$i];
0354
0355 my $weight = 1;
0356 if ($cycles[$i] == $i) {
0357 next if $weights[$i] > $nlevels + 1;
0358 $weight = $weights[$i];
0359 }
0360
0361 my $level = $nlevels + 1 - $weight;
0362 my $j;
0363 for ($j = 0; $j < $n; ++$j) {
0364 next if $i == $j;
0365 last if $tedges{$nodes[$i]}->{$nodes[$j]} && !$lowerthan[$level]->[$j];
0366 }
0367 next if $j < $n;
0368
0369 $current[$i] = 1;
0370 $mapping[$nmapping++] = $i;
0371 $count++;
0372 if ($cycles[$i] == $i) {
0373 for ($j = $i + 1; $j < $n; ++$j) {
0374 next if $cycles[$j] != $i;
0375 $mapping[$nmapping++] = $j;
0376 $count++;
0377 $tedges{$nodes[$i]}->{$nodes[$j]} = 1;
0378 }
0379 }
0380 }
0381 for (my $i = 0; $i < $n; ++$i) {
0382 $current[$i] ||= $lowerthan[$nlevels][$i];
0383 }
0384 $levels[$nlevels++] = $count;
0385 @{$lowerthan[$nlevels]} = @current;
0386 @current = (0) x $n;
0387 }
0388 die "internal error" if $nmapping != $n;
0389
0390 # start loading level number array
0391 my $start = 0;
0392 for (my $i = 1; $i < $nlevels; ++$i) {
0393 my $top = $start + $levels[$i];
0394 for (my $j = $start; $j < $top; ++$j) {
0395 $levelnums[$mapping[$j]] = $i;
0396 }
0397 $start = $top;
0398 }
0399
0400 # sort packages on each level lexicographically
0401 $start = 0;
0402 for (my $k = 1; $k < $nlevels; ++$k) {
0403 my $top = $start + $levels[$k];
0404 for (my $i = $start + 1; $i < $top; ++$i) {
0405 for (my $j = $start; $j < $i; ++$j) {
0406 if (($nodes[$mapping[$i]] cmp $nodes[$mapping[$j]]) < 0) {
0407 my $tmp = $mapping[$i];
0408 $mapping[$i] = $mapping[$j];
0409 $mapping[$j] = $tmp;
0410 }
0411 }
0412 }
0413 $start = $top;
0414 }
0415
0416 # create @cindices from cycles array and the level map
0417 my $ncycle = 0;
0418 for (my $i = 0; $i < $n; ++$i) {
0419 my $label = $cycles[$mapping[$i]];
0420 next if $label < 0;
0421 my $index = $cindices[$mapping[$i]];
0422 next if $index >= 0 && $index < $ncycle;
0423 for (my $j = $i; $j < $n; ++$j) {
0424 $cindices[$mapping[$j]] = $ncycle if $label == $cycles[$mapping[$j]];
0425 }
0426 $ncycle++;
0427 }
0428 die "internal error" if $ncycle != $ncycles;
0429
0430 # sort packages on each level again but now grouping
0431 # cyclically dependent packages together
0432 $start = 0;
0433 for (my $k = 1; $k < $nlevels; ++$k) {
0434 my $top = $start + $levels[$k];
0435 for (my $i = $start + 1; $i < $top; ++$i) {
0436 for (my $j = $start; $j < $i; ++$j) {
0437 my $ci = $cindices[$mapping[$i]];
0438 my $cj = $cindices[$mapping[$j]];
0439 if ($ci < $cj || ($ci == $cj && ($nodes[$mapping[$i]] cmp $nodes[$mapping[$j]]) < 0)) {
0440 my $tmp = $mapping[$i];
0441 $mapping[$i] = $mapping[$j];
0442 $mapping[$j] = $tmp;
0443 }
0444 }
0445 }
0446 $start = $top;
0447 }
0448
0449 # calculate ccd
0450 my %tmp = map { $_ => {} } keys %tedges;
0451 foreach my $n (keys %tedges) {
0452 map { $tmp{$n}->{$_} = 1 } keys %{$tedges{$n}};
0453 }
0454
0455 for (my $i = 0; $i < $n; ++$i) {
0456 if ($levelnums[$i] == 0) {
0457 for (my $j = 0; $j < $n; ++$j) {
0458 delete $tmp{$nodes[$j]}->{$nodes[$i]};
0459 }
0460 } else {
0461 $tmp{$nodes[$i]}->{$nodes[$i]} = 1;
0462 }
0463 }
0464
0465 my $ccd = 0;
0466 map { $ccd += scalar keys %{$tmp{$_}} } keys %tmp;
0467
0468 if (0) {
0469 # make canonical representation: remove all redundant (transitive0edges
0470 my @tmp = map { [ (0) x $n ] } @nodes;
0471 for (my $i = 0; $i < $n; ++$i) {
0472 my $u = $mapping[$i];
0473 for (my $j = 0; $j < $n; ++$j) {
0474 my $v = $mapping[$j];
0475 my $bit = $tedges{$nodes[$u]}->{$nodes[$v]};
0476 $tmp[$i]->[$j] = $bit;
0477 }
0478 }
0479 # FIXME;
0480 }
0481
0482 $metrics->{NODES} = \@nodes;
0483 $metrics->{N} = $n;
0484 $metrics->{LEVELS} = \@levels;
0485 $metrics->{NLEVELS} = $nlevels;
0486 $metrics->{LEVELNUMS} = \@levelnums;
0487 $metrics->{MAPPING} = \@mapping;
0488 $metrics->{NMAPPINGS} = $nmapping;
0489
0490 $metrics->{TEDGES} = \%tedges;
0491
0492 $metrics->{CYCLES} = \@cycles;
0493 $metrics->{WEIGHTS} = \@weights;
0494 $metrics->{CINDICES} = \@cindices;
0495 $metrics->{NCYCLES} = $ncycles;
0496 $metrics->{NMEMBERS} = $nmembers;
0497
0498 $metrics->{CCD} = $ccd;
0499
0500 return $nmembers;
0501 }
0502
0503 my %url_results = ();
0504 sub node_url {
0505 my ($urls, $name) = @_;
0506 if (exists $url_results{$name}){return $url_results{$name};}
0507 foreach my $mapping (@$urls) {
0508 if ($name =~ /$mapping->[0]/) {
0509 my $N = $name;
0510 my $tmp = eval $mapping->[1];
0511 $url_results{$name} = $tmp;
0512 return $tmp;
0513 }
0514 }
0515 $url_results{$name} = undef;
0516 return undef;
0517 }
0518
0519 sub read_report {
0520 my ($edges, $nodes, $file, $part) = @_;
0521 my $node = undef;
0522
0523 open(FILE, $file) || die "$file: error: cannot open: $!\n";
0524 while (<FILE>) {
0525 last if /^\# $part/;
0526 }
0527
0528 while (<FILE>) {
0529 chomp;
0530 last if (/^\#\#\#\#\#/);
0531 next if (/^\s*$/);
0532
0533 if (/^\S/) {
0534 chop;
0535 s% ([^/]+)$%%;
0536 $node = $_;
0537 $nodes->{$_} = 1;
0538 $edges->{$_} ||= {};
0539 } elsif (/^\s+\S/) {
0540 print STDERR "$file: warning: no current node for a dependency\n"
0541 if ! $node;
0542
0543 s/\s//g;
0544 $nodes->{$_} = 1;
0545 $edges->{$_} ||= {};
0546 $edges->{$node}->{$_} ||= [];
0547 push(@{$edges->{$node}->{$_}}, ':hard');
0548 } else {
0549 print STDERR "$file: warning: unrecognized line: `$_'\n";
0550 }
0551 }
0552 close(FILE);
0553 }
0554
0555 sub keep_subpartonly_nodes {
0556 my ($kept_nodes, $edges, $node, $spart) = @_;
0557 $kept_nodes->{$node} = 1;
0558 foreach my $edge (keys %{$edges->{$node}}) {
0559 if (!exists $kept_nodes->{$edge} && ($edge=~/$spart/)){
0560 &keep_subpartonly_nodes ($kept_nodes, $edges, $edge, $spart);
0561 }
0562 }
0563 }
0564
0565 sub keep_nodes {
0566 my ($kept_nodes, $edges, $node) = @_;
0567 $kept_nodes->{$node} = 1;
0568 foreach my $edge (keys %{$edges->{$node}}) {
0569 if (!exists $kept_nodes->{$edge}){
0570 &keep_nodes ($kept_nodes, $edges, $edge);
0571 }
0572 }
0573 }
0574
0575 sub remove_nodes {
0576 my ($edges, $nodes, $spart, $spartonly) = @_;
0577 my %kept_nodes = ();
0578 my $node;
0579 foreach $node (keys %$nodes) {
0580 if (($node=~/$spart/) && (!exists $kept_nodes{$node})){
0581 if ($spartonly){
0582 &keep_subpartonly_nodes (\%kept_nodes, $edges, $node, $spart);
0583 }
0584 else{
0585 &keep_nodes (\%kept_nodes, $edges, $node);
0586 }
0587 }
0588 }
0589 foreach $node (keys %$nodes) {
0590 if (!exists $kept_nodes{$node}){
0591 delete $nodes->{$node};
0592 delete $edges->{$node};
0593 }
0594 }
0595 }
0596
0597 sub read_counts {
0598 my ($edges, $nodes, $vector, $note, $file) = @_;
0599 open(FILE, $file) || die "$file: error: cannot open: $!\n";
0600 while (<FILE>) {
0601 chomp;
0602 if (! /^\s*(\d+)\s+(\S+)\s+(\S+(,\s+\S+)?)(\s+via\s+(\S+))?$/) {
0603 print STDERR "$file: error: bad line `$_'\n";
0604 next;
0605 }
0606 next if $2 eq $3;
0607 $nodes->{$2} = 1; $edges->{$2} ||= {};
0608 $nodes->{$3} = 1; $edges->{$3} ||= {};
0609 $edges->{$2}->{$3} ||= [];
0610 push(@{$edges->{$2}->{$3}}, ':hard');
0611 push(@$vector, [ $2, $3, $1, $note, $5 || '', -1]);
0612 }
0613 close(FILE);
0614 }
0615
0616 sub read_loglinks {
0617 my ($edges, $nodes, $file) = @_;
0618 my @xtargets = ();
0619 my @xreasons = ();
0620
0621 open(FILE, $file) || die "$file: error: cannot open: $!\n";
0622 while (<FILE>) {
0623 chomp; s/\#.*//; s/\s*$//;
0624 next if (/^\s*$/);
0625
0626 if (/^(\S+(\s*,\s*\S+)*)(\s*:\s*->\s*(\S+(\s*,\s*\S+)*))?$/) {
0627 @xreasons = split(/\s*,\s*/, $1);
0628 @xtargets = split(/\s*,\s*/, $4 || '');
0629 } elsif (/^\s+(\S+(\s*,\s*\S+)*)(\s*->\s*(\S+(\s*,\s*\S+)*))?$/) {
0630 my @sources = split(/\s*,\s*/, $1);
0631 my @targets = ($4 ? split(/\s*,\s*/, $4) : @xtargets);
0632 foreach my $source (grep(exists $nodes->{$_}, @sources)) {
0633 $edges->{$source} ||= {};
0634 map { $edges->{$source}->{$_} ||= [];
0635 push(@{$edges->{$source}->{$_}}, @xreasons); } @targets;
0636 }
0637 } else {
0638 print STDERR "$file: warning: unrecognized line: `$_'\n";
0639 }
0640 }
0641 close(FILE);
0642 }
0643
0644 sub read_groups {
0645 my ($groups, $file) = @_;
0646 my $i = 0;
0647
0648 open(FILE, $file) || die "$file: error: cannot open: $!\n";
0649 while (<FILE>) {
0650 chomp;
0651 while (/\\$/ && !eof(FILE)) {
0652 chop; $_ .= <FILE>; chomp;
0653 }
0654 if (/\\$/) {
0655 print STDERR "$file: warning: incomplete last line\n";
0656 chop;
0657 }
0658 s/\#.*//; s/^\s*//; s/\s*$//;
0659 next if (/^$/);
0660
0661 if (/^group\s+(\S+)\s+(.*)\s+--\s+(.*)$/) {
0662 push(@$groups, [ $i++, $1, [ split(/\s+/, $2) ], $3 ]);
0663 } else {
0664 print STDERR "$file: warning: unrecognized line: `$_'\n";
0665 }
0666 }
0667 close(FILE);
0668 }
0669
0670 sub read_renames {
0671 my ($renames, $file) = @_;
0672 my $i = 0;
0673
0674 open(FILE, $file) || die "$file: error: cannot open: $!\n";
0675 while (<FILE>) {
0676 chomp;
0677 while (/\\$/ && !eof(FILE)) {
0678 chop; $_ .= <FILE>; chomp;
0679 }
0680 if (/\\$/) {
0681 print STDERR "$file: warning: incomplete last line\n";
0682 chop;
0683 }
0684 s/\#.*//; s/^\s*//; s/\s*$//;
0685 next if (/^$/);
0686
0687 if (/^rename\s+([^:]+):(.*)$/) {
0688 push(@$renames, [ $1, $2 ]);
0689 } else {
0690 print STDERR "$file: warning: unrecognized line: `$_'\n";
0691 }
0692 }
0693 close(FILE);
0694 }
0695
0696 sub read_urls {
0697 my ($urls, $file) = @_;
0698 my $i = 0;
0699
0700 open(FILE, $file) || die "$file: error: cannot open: $!\n";
0701 while (<FILE>) {
0702 chomp;
0703 while (/\\$/ && !eof(FILE)) {
0704 chop; $_ .= <FILE>; chomp;
0705 }
0706 if (/\\$/) {
0707 print STDERR "$file: warning: incomplete last line\n";
0708 chop;
0709 }
0710 s/^\s*//; s/\s*$//;
0711 next if (/^$/);
0712 next if (/^#/);
0713
0714 if (/^([^:]+):(.*)$/) {
0715 push(@$urls, [ $1, $2 ]);
0716 } else {
0717 print STDERR "$file: warning: unrecognized line: `$_'\n";
0718 }
0719 }
0720 close(FILE);
0721 }
0722
0723 ######################################################################
0724 my $usage =
0725 "usage: $me REPORT-FILE REPORT-PART REPORT-SUBPART SUBPARTONLY INCLUSIVE? [RE...]\n"
0726 . " [! RESTRICT-INCLUSIVE? RESTRICT-RE...]\n"
0727 . " [-- EXTRA EXTRA-INCLUSIVE? EXTRA-RE...]\n";
0728
0729 die $usage if (scalar @ARGV < 3);
0730 my ($file, $part, $subpart, $subpartonly, $inclusive, @restargs) = @ARGV;
0731 my ($rinclusive, $xfile, $xinclusive, @rx, @rrx, @xrx) = (1, "/dev/null", 1);
0732 $inclusive = &validate_flag($inclusive, "INCLUSIVE?");
0733
0734 while (scalar @restargs) {
0735 my $arg = shift(@restargs);
0736 if ($arg eq '!') {
0737 die $usage if (scalar @restargs < 2);
0738 $rinclusive = &validate_flag(shift(@restargs), "RESTRICT-INCLUSIVE?");
0739 while (scalar @restargs && $restargs[0] ne '--') {
0740 push(@rrx, shift(@restargs));
0741 }
0742 } elsif ($arg eq '--') {
0743 die $usage if (scalar @restargs < 2);
0744 $xfile = shift(@restargs);
0745 $xinclusive = &validate_flag(shift(@restargs), "EXTRA-INCLUSIVE?");
0746 @xrx = @restargs;
0747 $xrx[0] ||= '.*';
0748 last;
0749 } else {
0750 push(@rx, $arg);
0751 }
0752 }
0753 $rx[0] ||= '.*';
0754 $rrx[0] ||= '.*';
0755
0756 ######################################################################
0757 my %nodes = ();
0758 my %gnodes = ();
0759 my %edges = ();
0760 my %gedges = ();
0761 my @roots = ();
0762 my %metrics = ();
0763
0764 # Read in the report.
0765 &read_report(\%edges, \%nodes, $file, $part);
0766
0767 # Read in the extra-deps file.
0768 &read_loglinks(\%edges, \%nodes, $xfile);
0769
0770 # Remove the unwanted nodes
0771 &remove_nodes(\%edges, \%nodes, $subpart, $subpartonly) if ($subpart ne "");
0772
0773 # Calculate which nodes can be reached.
0774 foreach my $node (keys %nodes) {
0775 push(@roots, $node) if (grep($node =~ /$_/, @rx) && 1) eq $inclusive;
0776 }
0777
0778 foreach (@roots) {
0779 &do_node(\%gedges,\%gnodes, \%edges,\%nodes, $rinclusive,\@rrx, $xinclusive,\@xrx, undef,$_,1);
0780 }
0781
0782 # Calculate metrics
0783 &dep_metrics(\%gedges,\%gnodes, \%metrics);
0784
0785 ######################################################################
0786 # Print results
0787 sub plural {
0788 my $arg = shift;
0789 return $arg == 1 ? "" : "s";
0790 }
0791 sub ccdbalanced {
0792 my $n = shift;
0793 return ($n + 1) * (log($n + 1)/log(2) - 1) + 1;
0794 }
0795
0796 sub ccd {
0797 return $metrics{CCD};
0798 }
0799 sub acd {
0800 return $metrics{N} ? &ccd / $metrics{N} : 0.0;
0801 }
0802 sub nccd {
0803 return $metrics{N} ? &ccd / &ccdbalanced($metrics{N}) : 0.0;
0804 }
0805 sub minnccd {
0806 return $metrics{N} ? $metrics{N}/&ccdbalanced($metrics{N}) : 0.0;
0807 }
0808 sub maxnccd {
0809 return $metrics{N} ? $metrics{N}*$metrics{N} / &ccdbalanced($metrics{N}) : 0.0;
0810 }
0811
0812 print "# Summary\n";
0813 if ($metrics{NCYCLES} > 0) {
0814 print sprintf "%-11s %d\n", "Cycles", $metrics{NCYCLES};
0815 print sprintf "%-11s %d\n", "Members", $metrics{NMEMBERS};
0816 }
0817
0818 print sprintf "%-11s %d\n", "Packages", $metrics{N};
0819 print sprintf "%-11s %d\n", "Levels", $metrics{NLEVELS}-1;
0820
0821 print sprintf "%-11s %f\n", "CCD", &ccd;
0822 print sprintf "%-11s %f\n", "ACD", ∿
0823 print sprintf "%-11s %f [%f, %f]\n", "NCCD", &nccd, &minnccd, &maxnccd;
0824 print "\n";
0825 print "* CCD: Cumulative Component Dependency measures the cumulative testing cost\n";
0826 print " across the system.\n";
0827 print "* ACD: Average Component Dependency indicates the number of other packages\n";
0828 print " an average package depends on.\n";
0829 print "* NCCD: Normalised Cumulative Component Dependency measures how the structure\n";
0830 print " differs from a balanced binary tree of comparable size. If NCCD is one,\n";
0831 print " the structure resembles a binary tree; if much less than one, the\n";
0832 print " packages are mostly independent; if much greater than one, the system\n";
0833 print " is fairly strongly coupled. The only universal NCCD target is to\n";
0834 print " minimise for any given software system--a high value indicates a\n";
0835 print " strongly coupled system and less coupling is better.\n";
0836 print "\n";
0837
0838 for (my $i = 0; $i < $metrics{NCYCLES}; ++$i) {
0839 print "# Cycles\n" if $i == 0;
0840 print "Cycle ", $i + 1, "\n";
0841 my $j = -1;
0842 while ($j < $metrics{N}) {
0843 do { ++$j } while ($j < $metrics{N}
0844 && $metrics{CINDICES}->[$metrics{MAPPING}->[$j]] != $i);
0845 last if $j >= $metrics{N};
0846 print " " . $metrics{NODES}->[$metrics{MAPPING}->[$j]] . "\n";
0847 }
0848 }
0849
0850 print "\n" if $metrics{NCYCLES};
0851 print "# Levels\n";
0852
0853 my $start = 0;
0854 my $skiped_levels = 0;
0855 for (my ($i, $start) = (1, 0); $i < $metrics{NLEVELS}; $start += $metrics{LEVELS}->[$i++]) {
0856 my $first = 1;
0857 for (my ($j, $top) = ($start, $start + $metrics{LEVELS}->[$i]); $j < $top; ++$j) {
0858 my $name = $metrics{NODES}->[$metrics{MAPPING}->[$j]];
0859 my $cycle = $metrics{CINDICES}->[$metrics{MAPPING}->[$j]] + 1;
0860
0861 if ($first) {
0862 $first = 0;
0863 print sprintf "%-4s ", $i-$skiped_levels.".";
0864 } else {
0865 print " ";
0866 }
0867
0868 print $name;
0869 if ($metrics{NCYCLES} > 0 && $cycle) {
0870 print "<$cycle>";
0871 }
0872 map { print "\n " . $_ } keys %{$metrics{EDGES}->{$metrics{MAPPING}->[$i]}};
0873 print "\n";
0874 }
0875 if ($first){
0876 $skiped_levels++;
0877 }
0878 else{
0879 print "\n";
0880 }
0881 }