|
Packit Service |
c5cf8c |
#!/usr/bin/env perl
|
|
Packit Service |
c5cf8c |
# (C) 2011 by Argonne National Laboratory.
|
|
Packit Service |
c5cf8c |
# See COPYRIGHT in top-level directory.
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# This script scavenges the MPICH source tree looking for "subconfigure.m4"
|
|
Packit Service |
c5cf8c |
# files. It then analyzes the dependencies between them and emits a
|
|
Packit Service |
c5cf8c |
# "subsys_include.m4" file that "m4_include"s them all in the correct order and
|
|
Packit Service |
c5cf8c |
# writes an autoconf macro that is suitable for expanding subsys macros in the
|
|
Packit Service |
c5cf8c |
# correct order.
|
|
Packit Service |
c5cf8c |
#
|
|
Packit Service |
c5cf8c |
# Finding a "dnl MPICH_SUBCFG_BEFORE=BAR" statement inside of
|
|
Packit Service |
c5cf8c |
# FOO/subconfigure.m4 means that FOO depends on BAR and that FOO's macros should
|
|
Packit Service |
c5cf8c |
# be emitted *before* BAR's macros so that FOO can influence BAR's
|
|
Packit Service |
c5cf8c |
# configuration.
|
|
Packit Service |
c5cf8c |
#
|
|
Packit Service |
c5cf8c |
# Finding a "dnl MPICH_SUBCFG_AFTER=QUUX" statement inside of
|
|
Packit Service |
c5cf8c |
# BAZ/subconfigure.m4 means that BAZ uses QUUX and that BAZ's macros be emitted
|
|
Packit Service |
c5cf8c |
# *after* QUUX's macros so that BAZ can utilize information exported by QUUX's
|
|
Packit Service |
c5cf8c |
# configuration macros.
|
|
Packit Service |
c5cf8c |
#
|
|
Packit Service |
c5cf8c |
# We have both forms of macros because some subsystems know their consumers
|
|
Packit Service |
c5cf8c |
# explicitly and some packages only know what they consume explicitly, and some
|
|
Packit Service |
c5cf8c |
# packages are a blend. For example, ch3 depends on the mpid/common/sched code
|
|
Packit Service |
c5cf8c |
# in order to support NBC ops so a BEFORE statement is used in the
|
|
Packit Service |
c5cf8c |
# ch3/subconfigure.m4 in order to "enable_mpid_common_sched=yes". The nemesis
|
|
Packit Service |
c5cf8c |
# netmods all depend on the core nemesis configuration happening first, so they
|
|
Packit Service |
c5cf8c |
# use an AFTER statement in each of their subconfigure.m4 files.
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
################################################################################
|
|
Packit Service |
c5cf8c |
use strict;
|
|
Packit Service |
c5cf8c |
use warnings;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
use Getopt::Long;
|
|
Packit Service |
c5cf8c |
use Data::Dumper;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
################################################################################
|
|
Packit Service |
c5cf8c |
# Default global settings and constants
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# unlikely to change, since this name is assumed in lots of other pieces of the
|
|
Packit Service |
c5cf8c |
# build system
|
|
Packit Service |
c5cf8c |
my $SUBCFG_NAME = "subconfigure.m4";
|
|
Packit Service |
c5cf8c |
my $OUTPUT_FILE = "subsys_include.m4";
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# if true, add a dependency (ancestor-->child) when $SUBCFG_NAME files are
|
|
Packit Service |
c5cf8c |
# encountered and they have an ancestor $SUBCFG_NAME file in an enclosing
|
|
Packit Service |
c5cf8c |
# directory
|
|
Packit Service |
c5cf8c |
my $USE_IMPLICIT_EDGES = 1;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# the existence of this file means we should stop recursing down
|
|
Packit Service |
c5cf8c |
# the enclosing directory tree
|
|
Packit Service |
c5cf8c |
my $stop_sentinel = ".no_subcfg_recursion";
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# which directories should be recursively searched for "subconfigure.m4" files
|
|
Packit Service |
c5cf8c |
my @root_dirs = qw( src );
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# coloring constants for the graph algorithms later on
|
|
Packit Service |
c5cf8c |
my ($WHITE, $GRAY, $BLACK) = (1, 2, 3);
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
################################################################################
|
|
Packit Service |
c5cf8c |
# Option processing: here's a great place to permit overriding the default
|
|
Packit Service |
c5cf8c |
# global settings from above if we ever need to in the future.
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
my $do_bist = 0;
|
|
Packit Service |
c5cf8c |
GetOptions(
|
|
Packit Service |
c5cf8c |
"--help" => \&print_usage,
|
|
Packit Service |
c5cf8c |
"--bist" => \$do_bist,
|
|
Packit Service |
c5cf8c |
) or die "unable to process options, stopped";
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
sub print_usage {
|
|
Packit Service |
c5cf8c |
print <
|
|
Packit Service |
c5cf8c |
This script builds '$OUTPUT_FILE' from '$SUBCFG_NAME' files.
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
Usage: $0 [--help] [--bist]
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
--bist - Run simple self tests on this script.
|
|
Packit Service |
c5cf8c |
--help - This message.
|
|
Packit Service |
c5cf8c |
EOT
|
|
Packit Service |
c5cf8c |
exit 0;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
if ($do_bist) {
|
|
Packit Service |
c5cf8c |
bist();
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
################################################################################
|
|
Packit Service |
c5cf8c |
# preorder traverse the root dirs looking for files named $SUBCFG_NAME
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# stack for recursion, contains dirs that must yet be visited
|
|
Packit Service |
c5cf8c |
my @dirstack = ( @root_dirs );
|
|
Packit Service |
c5cf8c |
# Parallel stack that keeps track of the nearest ancestor with a subconfigure.m4
|
|
Packit Service |
c5cf8c |
# file. All root dirs have no ancestors.
|
|
Packit Service |
c5cf8c |
my @anc_stack = map { '' } @root_dirs;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# keys are the full path to the found file, value is the nearest ancestor (in
|
|
Packit Service |
c5cf8c |
# the directory hierarchy sense) subconfigure.m4 file, or '' if none exists.
|
|
Packit Service |
c5cf8c |
my %found_files = ();
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
while (my $dir = pop @dirstack) {
|
|
Packit Service |
c5cf8c |
my $anc = pop @anc_stack;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# check for a $SUBCFG_NAME before recursing in order to correctly propagate
|
|
Packit Service |
c5cf8c |
# ancestor information for subdirectories
|
|
Packit Service |
c5cf8c |
my $fp = "$dir/$SUBCFG_NAME";
|
|
Packit Service |
c5cf8c |
if (-e $fp) {
|
|
Packit Service |
c5cf8c |
# found a subconfigure.m4 file
|
|
Packit Service |
c5cf8c |
$found_files{$fp} = $anc;
|
|
Packit Service |
c5cf8c |
# override our parent's ancestor for all of our descendants
|
|
Packit Service |
c5cf8c |
$anc = $fp;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
if (-e "$dir/$stop_sentinel") {
|
|
Packit Service |
c5cf8c |
# the existence of this file means we should stop recursing down
|
|
Packit Service |
c5cf8c |
# this particular directory tree
|
|
Packit Service |
c5cf8c |
next;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# now that we've visited the current vertex, push all of our child dirs onto
|
|
Packit Service |
c5cf8c |
# the stack to continue the traversal
|
|
Packit Service |
c5cf8c |
opendir DH, $dir
|
|
Packit Service |
c5cf8c |
or die "unable to open dir='$dir', stopped";
|
|
Packit Service |
c5cf8c |
my @contents = sort readdir DH;
|
|
Packit Service |
c5cf8c |
foreach my $f (@contents) {
|
|
Packit Service |
c5cf8c |
# avoid endless recursion
|
|
Packit Service |
c5cf8c |
next if $f eq "." || $f eq "..";
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
if (-d "$dir/$f") {
|
|
Packit Service |
c5cf8c |
push @dirstack, "$dir/$f";
|
|
Packit Service |
c5cf8c |
push @anc_stack, $anc;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
closedir DH;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
################################################################################
|
|
Packit Service |
c5cf8c |
# We now have a list of all $SUBCFG_NAME files in @found_files. Process each of
|
|
Packit Service |
c5cf8c |
# the files and build a DAG.
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# A DAG where the vertices are full path filenames for $SUBCFG_NAME files and the
|
|
Packit Service |
c5cf8c |
# edges are dependencies between the $SUBCFG_NAME files (A-->B indicates that
|
|
Packit Service |
c5cf8c |
# A should come before B in a topo sort). We concretely express this DAG as an
|
|
Packit Service |
c5cf8c |
# adjacency list stored in a hash. Keys of the hash are filenames, values are
|
|
Packit Service |
c5cf8c |
# refs to hashes whose keys are outbound edge filenames.
|
|
Packit Service |
c5cf8c |
# IOW:
|
|
Packit Service |
c5cf8c |
# ( a => {b=>1,c=>1}, b => {c=>1}, c => {} )
|
|
Packit Service |
c5cf8c |
# represents the following (crudely drawn) graph:
|
|
Packit Service |
c5cf8c |
# a-->b-->c
|
|
Packit Service |
c5cf8c |
# \------^
|
|
Packit Service |
c5cf8c |
my %dag = ();
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# Helper routine, adds a new edge to the given dag hash (passed by reference),
|
|
Packit Service |
c5cf8c |
# automatically creating src or dst vertices as necessary. A cute bit of calling
|
|
Packit Service |
c5cf8c |
# syntax is that the src and dst args can be separated by "=>" because it's just
|
|
Packit Service |
c5cf8c |
# a fancy comma in perl.
|
|
Packit Service |
c5cf8c |
sub add_edge {
|
|
Packit Service |
c5cf8c |
my $dag_ref = shift;
|
|
Packit Service |
c5cf8c |
my $src = shift;
|
|
Packit Service |
c5cf8c |
my $dst = shift;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
die "\$dag_ref is invalid, stopped" unless ref($dag_ref) eq "HASH";
|
|
Packit Service |
c5cf8c |
die "\$src is invalid, stopped" unless $src;
|
|
Packit Service |
c5cf8c |
die "\$dst is invalid, stopped" unless $dst;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
$dag_ref->{$src} = {} unless exists $dag_ref->{$src};
|
|
Packit Service |
c5cf8c |
$dag_ref->{$src}->{$dst} = 1;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
foreach my $k (keys %found_files) {
|
|
Packit Service |
c5cf8c |
# add the vertex to the graph with no edges to start
|
|
Packit Service |
c5cf8c |
$dag{$k} = {} unless exists $dag{$k};
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
my $anc = $found_files{$k};
|
|
Packit Service |
c5cf8c |
if ($anc and $USE_IMPLICIT_EDGES) {
|
|
Packit Service |
c5cf8c |
# need to add the implicit edge from the ancestor to $k
|
|
Packit Service |
c5cf8c |
add_edge(\%dag, $anc => $k);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# now process the file and add any explicit edges
|
|
Packit Service |
c5cf8c |
open FILE, '<', $k;
|
|
Packit Service |
c5cf8c |
while (my $line = <FILE>) {
|
|
Packit Service |
c5cf8c |
if ($line =~ m/^\s*dnl +MPICH_SUBCFG_([A-Z_]*)=(.*)\s*$/) {
|
|
Packit Service |
c5cf8c |
my $bef_aft = $1;
|
|
Packit Service |
c5cf8c |
my $arg = $2;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# users can set GEN_SUBCFG_NO_ERROR=1 in the environment to prevent
|
|
Packit Service |
c5cf8c |
# this script from complaining about missing '/' chars
|
|
Packit Service |
c5cf8c |
if ($arg !~ m|/| and not $ENV{GEN_SUBCFG_NO_ERROR}) {
|
|
Packit Service |
c5cf8c |
print "ERROR: no '/' characters detected in '$arg', possible old-style structured comment still present\n";
|
|
Packit Service |
c5cf8c |
exit 1;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# normalize the $arg to match our DAG
|
|
Packit Service |
c5cf8c |
$arg .= "/$SUBCFG_NAME";
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
if ($bef_aft eq "BEFORE") {
|
|
Packit Service |
c5cf8c |
add_edge(\%dag, $k => $arg);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
elsif ($bef_aft eq "AFTER") {
|
|
Packit Service |
c5cf8c |
add_edge(\%dag, $arg => $k);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
else {
|
|
Packit Service |
c5cf8c |
die "unrecognized structured comment ('MPICH_SUBCFG_${bef_aft}')\n".
|
|
Packit Service |
c5cf8c |
"at $k:$., possible typo? Stopped";
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
close FILE;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
################################################################################
|
|
Packit Service |
c5cf8c |
# We now have a DAG expressing the dependency information between the various
|
|
Packit Service |
c5cf8c |
# subconfigure.m4 files. Now we need to topologically sort it.
|
|
Packit Service |
c5cf8c |
#
|
|
Packit Service |
c5cf8c |
# We use the topo sort algorithm given in "Introduction to Algorithms" (1st
|
|
Packit Service |
c5cf8c |
# ed.), page 486 with a small modification to detect cycles in the digraph. We
|
|
Packit Service |
c5cf8c |
# perform a DFS on the DAG, coloring vertices as we go. We could compute
|
|
Packit Service |
c5cf8c |
# discovery and finishing times, as well as predecessors, but we don't need that
|
|
Packit Service |
c5cf8c |
# information for topological sorting or cycle detection. As each vertex is
|
|
Packit Service |
c5cf8c |
# finished (colored BLACK) we prepend it to an array. The resulting array is
|
|
Packit Service |
c5cf8c |
# sorted in "ascending" topological order (a,b,c in our previous example).
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# the output array in which the sorted results will be stored
|
|
Packit Service |
c5cf8c |
my @tsorted = ();
|
|
Packit Service |
c5cf8c |
topo_sort(\%dag, \@tsorted);
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
################################################################################
|
|
Packit Service |
c5cf8c |
# Now just emit the $OUTPUT_FILE in the correct format.
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
open OUTFD, '>', $OUTPUT_FILE;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
my $datestamp = scalar(localtime);
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
print OUTFD <
|
|
Packit Service |
c5cf8c |
dnl generated by $0 at $datestamp
|
|
Packit Service |
c5cf8c |
dnl DO NOT EDIT BY HAND!!!
|
|
Packit Service |
c5cf8c |
dnl re-run ./maint/updatefiles instead
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
EOT
|
|
Packit Service |
c5cf8c |
foreach my $file (@tsorted) {
|
|
Packit Service |
c5cf8c |
print OUTFD "m4_include([$file])\n";
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
print OUTFD <
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
dnl a macro suitable for use as m4_map([some_unary_macro],[PAC_SUBCFG_MODULE_LIST])
|
|
Packit Service |
c5cf8c |
m4_define([PAC_SUBCFG_MODULE_LIST],
|
|
Packit Service |
c5cf8c |
m4_dquote(
|
|
Packit Service |
c5cf8c |
EOT
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
foreach my $file (@tsorted[0 .. $#tsorted-1]) {
|
|
Packit Service |
c5cf8c |
my $mod_name = $file;
|
|
Packit Service |
c5cf8c |
$mod_name =~ s+/$SUBCFG_NAME$++;
|
|
Packit Service |
c5cf8c |
$mod_name =~ tr+/+_+;
|
|
Packit Service |
c5cf8c |
print OUTFD "[$mod_name],\n";
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
my $mod_name = $tsorted[-1];
|
|
Packit Service |
c5cf8c |
$mod_name =~ s+/$SUBCFG_NAME$++g;
|
|
Packit Service |
c5cf8c |
$mod_name =~ tr+/+_+;
|
|
Packit Service |
c5cf8c |
print OUTFD "[$mod_name]dnl <--- this dnl is important\n";
|
|
Packit Service |
c5cf8c |
print OUTFD "))\n\n";
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
close OUTFD;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
################################################################################
|
|
Packit Service |
c5cf8c |
# SUBROUTINES
|
|
Packit Service |
c5cf8c |
################################################################################
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# The DFS-Visit(u) algorithm specialized for topo sorting. Currently a
|
|
Packit Service |
c5cf8c |
# subroutine to permit recursive invocation, but could be converted to use an
|
|
Packit Service |
c5cf8c |
# explicit stack and the subroutine could be eliminated.
|
|
Packit Service |
c5cf8c |
#
|
|
Packit Service |
c5cf8c |
# takes four arguments: a ref to the DAG hash, a ref to a colors hash, the
|
|
Packit Service |
c5cf8c |
# vertex $u, and an output array reference to be populated as vertices are
|
|
Packit Service |
c5cf8c |
# finished.
|
|
Packit Service |
c5cf8c |
sub dfs_visit {
|
|
Packit Service |
c5cf8c |
my $dag_ref = shift;
|
|
Packit Service |
c5cf8c |
my $colors_ref = shift;
|
|
Packit Service |
c5cf8c |
my $u = shift;
|
|
Packit Service |
c5cf8c |
my $out_arr_ref = shift;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
$colors_ref->{$u} = $GRAY;
|
|
Packit Service |
c5cf8c |
foreach my $v (keys %{$dag_ref->{$u}}) {
|
|
Packit Service |
c5cf8c |
# detect cycles in the graph at this point, see ("Classification of edges"
|
|
Packit Service |
c5cf8c |
# in CLR)
|
|
Packit Service |
c5cf8c |
if ((exists $colors_ref->{$v}) && $colors_ref->{$v} == $GRAY) {
|
|
Packit Service |
c5cf8c |
# We are already exploring the tree from $v, so this is a "back edge",
|
|
Packit Service |
c5cf8c |
# indicating a cycle is present in the digraph. This is erroneous in
|
|
Packit Service |
c5cf8c |
# our usage, since we cannot topologically sort a cyclic graph.
|
|
Packit Service |
c5cf8c |
die "A back edge was found in the digraph but a DAG is required.\n".
|
|
Packit Service |
c5cf8c |
"The back edge was from\n".
|
|
Packit Service |
c5cf8c |
" $u\n".
|
|
Packit Service |
c5cf8c |
"to\n".
|
|
Packit Service |
c5cf8c |
" $v\n".
|
|
Packit Service |
c5cf8c |
"Stopped";
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
elsif ((exists $colors_ref->{$v}) && $colors_ref->{$v} == $WHITE) {
|
|
Packit Service |
c5cf8c |
dfs_visit($dag_ref, $colors_ref, $v, $out_arr_ref);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
$colors_ref->{$u} = $BLACK;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# append $u to the output
|
|
Packit Service |
c5cf8c |
unshift @$out_arr_ref, $u;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# Takes two arguments, a ref to a DAG hash and a reference to an output array.
|
|
Packit Service |
c5cf8c |
# Returns in the output array a valid topological sort of the given DAG.
|
|
Packit Service |
c5cf8c |
sub topo_sort {
|
|
Packit Service |
c5cf8c |
my $dag_ref = shift;
|
|
Packit Service |
c5cf8c |
my $out_arr_ref = shift;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# helper hash that is indexed by vertex name in order to avoid building a
|
|
Packit Service |
c5cf8c |
# complicated set of nested structures inside the main DAG
|
|
Packit Service |
c5cf8c |
my $colors_ref = {}; # values are one of $WHITE, $GRAY, or $BLACK
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# a simplified version of the DFS(G) algorithm
|
|
Packit Service |
c5cf8c |
foreach my $u (keys %$dag_ref) {
|
|
Packit Service |
c5cf8c |
$colors_ref->{$u} = $WHITE;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
foreach my $u (keys %$dag_ref) {
|
|
Packit Service |
c5cf8c |
if ($colors_ref->{$u} == $WHITE) {
|
|
Packit Service |
c5cf8c |
dfs_visit($dag_ref, $colors_ref, $u, $out_arr_ref);
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
################################################################################
|
|
Packit Service |
c5cf8c |
# self tests
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
# run this subroutine to self-test portions of this script
|
|
Packit Service |
c5cf8c |
sub bist {
|
|
Packit Service |
c5cf8c |
bist_topo_sort();
|
|
Packit Service |
c5cf8c |
print "all self-tests PASSED\n";
|
|
Packit Service |
c5cf8c |
exit 0;
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
sub bist_topo_sort {
|
|
Packit Service |
c5cf8c |
my $dag;
|
|
Packit Service |
c5cf8c |
my $out_arr;
|
|
Packit Service |
c5cf8c |
my $expected;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
$dag = { a => {b=>1,c=>1}, b => {c=>1}, c => {} };
|
|
Packit Service |
c5cf8c |
$out_arr = [];
|
|
Packit Service |
c5cf8c |
$expected = [ qw(a b c) ];
|
|
Packit Service |
c5cf8c |
topo_sort($dag, $out_arr);
|
|
Packit Service |
c5cf8c |
cmp_arrays($out_arr, $expected);
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
$dag = { a => {}, b => {}, c => {} };
|
|
Packit Service |
c5cf8c |
$out_arr = [];
|
|
Packit Service |
c5cf8c |
topo_sort($dag, $out_arr);
|
|
Packit Service |
c5cf8c |
# this DAG has no single expected result, so just check lengths
|
|
Packit Service |
c5cf8c |
unless (scalar @$out_arr eq scalar @$expected) {
|
|
Packit Service |
c5cf8c |
die "\$out_arr and \$expected differ in length, stopped\n";
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
my $routine = (caller(0))[3];
|
|
Packit Service |
c5cf8c |
print "$routine PASSED\n";
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
sub cmp_arrays {
|
|
Packit Service |
c5cf8c |
my $out_arr = shift;
|
|
Packit Service |
c5cf8c |
my $expected = shift;
|
|
Packit Service |
c5cf8c |
|
|
Packit Service |
c5cf8c |
#print "out_arr=".Dumper($out_arr)."\n";
|
|
Packit Service |
c5cf8c |
#print "expected=".Dumper($expected)."\n";
|
|
Packit Service |
c5cf8c |
unless (scalar @$out_arr eq scalar @$expected) {
|
|
Packit Service |
c5cf8c |
die "\$out_arr and \$expected differ in length, stopped\n";
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
for (my $i = 0; $i < @$out_arr; ++$i) {
|
|
Packit Service |
c5cf8c |
unless ($out_arr->[$i] eq $expected->[$i]) {
|
|
Packit Service |
c5cf8c |
die "element $i of \$out_arr differs from the expected value (".
|
|
Packit Service |
c5cf8c |
$out_arr->[$i]." ne ".$expected->[$i]."), stopped\n";
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|
|
Packit Service |
c5cf8c |
}
|