Blame test/mpi/maint/testmerge.in

Packit Service c5cf8c
#! @PERL@
Packit Service c5cf8c
# -*- Mode: Perl; -*-
Packit Service c5cf8c
#
Packit Service c5cf8c
#  (C) 2004 by Argonne National Laboratory.
Packit Service c5cf8c
#      See COPYRIGHT in top-level directory.
Packit Service c5cf8c
#
Packit Service c5cf8c
#
Packit Service c5cf8c
# This file provides a way to merge a template file with a set of
Packit Service c5cf8c
# code fragments.  This makes it simple to generate many related tests
Packit Service c5cf8c
# from a single test harness, while ending up with relatively simple code
Packit Service c5cf8c
# in case it is necessary to debug the code.
Packit Service c5cf8c
# The template file uses an XML-like notation to mark off places for code.
Packit Service c5cf8c
# Specifically, lines of the form
Packit Service c5cf8c
#    <name/>
Packit Service c5cf8c
# are replaced from a definition file.  The template is XML-like because
Packit Service c5cf8c
# angle brackets and ampersands can be freely used as long as they don't
Packit Service c5cf8c
# match the perl pattern <\w*\/> (which no valid C, C++, or Fortran code will)
Packit Service c5cf8c
#
Packit Service c5cf8c
# The file of definitions has the following form, also in an XML-like 
Packit Service c5cf8c
# format (for simple parsing)
Packit Service c5cf8c
#    <TESTDEFN filename="fname">
Packit Service c5cf8c
#    <blockname>
Packit Service c5cf8c
#      definition
Packit Service c5cf8c
#    </blockname>
Packit Service c5cf8c
#    ...
Packit Service c5cf8c
#    </TESTDEFN>
Packit Service c5cf8c
# where "blockname" is an arbitrary name (matching the perl expression \w*)
Packit Service c5cf8c
# that matches the names in the template file.  
Packit Service c5cf8c
# TESTDEFN is a required field
Packit Service c5cf8c
#
Packit Service c5cf8c
# Possible extensions:
Packit Service c5cf8c
#    Common definitions for all files (allows a common template for
Packit Service c5cf8c
#      multiple sets of merges)
Packit Service c5cf8c
#    Allow the *template* to define some names (eg, value)
Packit Service c5cf8c
#      that are replaced in the generated file.
Packit Service c5cf8c
#    
Packit Service c5cf8c
# ----------------------------------------------------------------------------
Packit Service c5cf8c
# Global variables
Packit Service c5cf8c
$debug = 0;
Packit Service c5cf8c
Packit Service c5cf8c
$lang = "Fortran";   
Packit Service c5cf8c
%knownLang = ( "Fortran" => 1, "C" => 1, "C++" => 1 );
Packit Service c5cf8c
#$lang = "C";
Packit Service c5cf8c
#$lang = "C++";
Packit Service c5cf8c
Packit Service c5cf8c
# Definitions
Packit Service c5cf8c
%Definitions = ();
Packit Service c5cf8c
# Global definitions are for all files, and can contain standard comments,
Packit Service c5cf8c
# initializations, and other data
Packit Service c5cf8c
%GlobalDefinitions = ();
Packit Service c5cf8c
# Read a definition file
Packit Service c5cf8c
# ReadDefinition( filename )
Packit Service c5cf8c
# Places the definitions into %Definitions{name} => content
Packit Service c5cf8c
sub ReadDefinition {
Packit Service c5cf8c
    my $DFD = $_[0];
Packit Service c5cf8c
Packit Service c5cf8c
    while (<$DFD>) {
Packit Service c5cf8c
	# Check for end-of-description
Packit Service c5cf8c
	if (/<\/TESTDEFN>/) { last; }
Packit Service c5cf8c
Packit Service c5cf8c
	# match definition name
Packit Service c5cf8c
	if (/<(\w*)>/) {
Packit Service c5cf8c
	    my $name = $1;
Packit Service c5cf8c
	    my $defn = "";
Packit Service c5cf8c
	    my $found = 0;
Packit Service c5cf8c
	    while (<$DFD>) {
Packit Service c5cf8c
		if (/<\/$name>/) { $found = 1; last; }
Packit Service c5cf8c
		s/\r//g;   # Remove any extraneous characters
Packit Service c5cf8c
		$defn .= $_;
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    # If we didn't close the definition, generate an error message
Packit Service c5cf8c
	    if (! $found) {
Packit Service c5cf8c
		print STDERR "Read to end-of-file while looking for </$name>\n";
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    $Definitions{$name} = $defn;
Packit Service c5cf8c
	}
Packit Service c5cf8c
	else {
Packit Service c5cf8c
	    # Skip (blank space, comment, etc)
Packit Service c5cf8c
	    next;
Packit Service c5cf8c
	}
Packit Service c5cf8c
    }
Packit Service c5cf8c
    
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
# ReadGlobalDefinitions( filename )
Packit Service c5cf8c
sub ReadGlobalDefinitions {
Packit Service c5cf8c
    my $filename = $_[0];
Packit Service c5cf8c
    # Save Definitions, if any
Packit Service c5cf8c
    my %saveDefinitions = %Definitions;
Packit Service c5cf8c
    # Reset Definitions to hold the current global set
Packit Service c5cf8c
    %Definitions = %GlobalDefinitions;
Packit Service c5cf8c
Packit Service c5cf8c
    my $DFD = "DFD";
Packit Service c5cf8c
    open $DFD, "<$filename" || die "Could not open global definition file $filename\n";
Packit Service c5cf8c
    print "Opening $filename..\n";
Packit Service c5cf8c
    &ReadDefinition( $DFD );
Packit Service c5cf8c
    close $DFD;
Packit Service c5cf8c
    %GlobalDefinitions = %Definitions;
Packit Service c5cf8c
    %Definitions = %saveDefinitions;
Packit Service c5cf8c
    # Print the new definitions if requested.
Packit Service c5cf8c
    if ($debug) {
Packit Service c5cf8c
	foreach my $key (keys(%GlobalDefinitions)) {
Packit Service c5cf8c
	    my $val = $GlobalDefinitions{$key};
Packit Service c5cf8c
	    print "$key => $val\n";
Packit Service c5cf8c
	}
Packit Service c5cf8c
    }
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
# ---------------------------------------------------------------------------
Packit Service c5cf8c
# MergeTemplate
Packit Service c5cf8c
# Read a template and merge the output
Packit Service c5cf8c
# MergeTemplate( template file, output file )
Packit Service c5cf8c
# Preserve indentation
Packit Service c5cf8c
sub MergeTemplate {
Packit Service c5cf8c
    my $templateFilename = $_[0];
Packit Service c5cf8c
    my $outputFilename   = $_[1];
Packit Service c5cf8c
Packit Service c5cf8c
    open IFD, "<$templateFilename" || die "Cannot open $templateFilename\n";
Packit Service c5cf8c
    open OFD, ">$outputFilename" || die "Cannot open $outputFilename\n";
Packit Service c5cf8c
Packit Service c5cf8c
    while (<IFD>) {
Packit Service c5cf8c
	s/\r//;
Packit Service c5cf8c
	my $loopLimit = 20;
Packit Service c5cf8c
	while (/(\s*)<(\w*)\/>/) {
Packit Service c5cf8c
	    my $indent = $1;
Packit Service c5cf8c
	    my $name = $2;
Packit Service c5cf8c
	    $indent =~ s/\s*\n//g;
Packit Service c5cf8c
	    if ($loopLimit-- <= 0) {
Packit Service c5cf8c
		print STDERR "Exceeded loop limit while writing $outputFilename\n";
Packit Service c5cf8c
		print STDERR "Searching for $name in $_";
Packit Service c5cf8c
		last;
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    if (defined($Definitions{$name})) {
Packit Service c5cf8c
		my $defn = $Definitions{$name};
Packit Service c5cf8c
		my $newdefn = "";
Packit Service c5cf8c
		# Add indentation to definition; substitute any defintions
Packit Service c5cf8c
		foreach my $line (split(/\n/,$defn)) {
Packit Service c5cf8c
		    print "Looking at |$line|\n" if $debug;
Packit Service c5cf8c
		    $newdefn .= $indent . $line . "\n";
Packit Service c5cf8c
		}
Packit Service c5cf8c
		chop $newdefn;
Packit Service c5cf8c
		print "Replacing <$name> with |$newdefn|\n" if $debug;
Packit Service c5cf8c
		s/$indent<$name\/>/$newdefn/;   # Only do one at a time
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    elsif (defined($GlobalDefinitions{$name})) {
Packit Service c5cf8c
		# local definitions can override any global definitions
Packit Service c5cf8c
		my $defn = $GlobalDefinitions{$name};
Packit Service c5cf8c
		my $newdefn = "";
Packit Service c5cf8c
		# Add indentation to definition
Packit Service c5cf8c
		foreach my $line (split(/\n/,$defn)) {
Packit Service c5cf8c
		    $newdefn .= $indent . $line . "\n";
Packit Service c5cf8c
		}
Packit Service c5cf8c
		print "Replacing <$name> with |$newdefn|\n" if $debug;
Packit Service c5cf8c
		chop $newdefn;
Packit Service c5cf8c
		s/$indent<$name\/>/$newdefn/;   # Only do one at a time
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    else {
Packit Service c5cf8c
		# Unknown name!
Packit Service c5cf8c
		print STDERR "Unknown name $name in template file when creating $outputFilename!\n";
Packit Service c5cf8c
		last;
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	}
Packit Service c5cf8c
	&printLine( OFD, $_ );
Packit Service c5cf8c
    }
Packit Service c5cf8c
    close OFD;
Packit Service c5cf8c
    close IFD;
Packit Service c5cf8c
}
Packit Service c5cf8c
# ---------------------------------------------------------------------------
Packit Service c5cf8c
# ReadAndMerge( description file, template file )
Packit Service c5cf8c
sub ReadAndMerge {
Packit Service c5cf8c
    my $filename = $_[0];
Packit Service c5cf8c
    my $templateFile = $_[1];
Packit Service c5cf8c
Packit Service c5cf8c
    $DFD = "DFD";
Packit Service c5cf8c
    open $DFD, "<$filename" || die "Can not open $filename\n";
Packit Service c5cf8c
    
Packit Service c5cf8c
    while (<$DFD>) {
Packit Service c5cf8c
	s/#.*//;            # Remove comments
Packit Service c5cf8c
	# Read until a TESTDEFN line
Packit Service c5cf8c
	if (/<TESTDEFN\s+filename=\"(.*)\">/) {
Packit Service c5cf8c
	    my $outputFile = $1;
Packit Service c5cf8c
	    %Definitions = ();
Packit Service c5cf8c
	    # Read until </TESTDEFN>
Packit Service c5cf8c
	    &ReadDefinition( $DFD );
Packit Service c5cf8c
	    # Create the merged file
Packit Service c5cf8c
	    &MergeTemplate( $templateFile, $outputFile );
Packit Service c5cf8c
	}
Packit Service c5cf8c
	elsif (/<LANG>([\w\+]*)<\/LANG>/) {
Packit Service c5cf8c
	    # Special for language definition
Packit Service c5cf8c
	    $lang = $1;
Packit Service c5cf8c
	    if (!defined($knownLang{$lang})) {
Packit Service c5cf8c
		print STDERR "Unknown language $lang\n"; 
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	}
Packit Service c5cf8c
	elsif (/<(\w*)>/) {
Packit Service c5cf8c
	    my $name = $1;
Packit Service c5cf8c
	    my $defn = "";
Packit Service c5cf8c
	    # read this as a global definition
Packit Service c5cf8c
	    while (<$DFD>) {
Packit Service c5cf8c
		if (/<\/$name>/) { last; }
Packit Service c5cf8c
		s/\r//;
Packit Service c5cf8c
		$defn .= $_;
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    if (eof($DFD)) { print STDERR "found EOF before end of $name\n"; }
Packit Service c5cf8c
	    $GlobalDefinitions{$name} = $defn;
Packit Service c5cf8c
	}
Packit Service c5cf8c
    }
Packit Service c5cf8c
    close $DFD;
Packit Service c5cf8c
Packit Service c5cf8c
}
Packit Service c5cf8c
# --------------------------------------------------------------------------
Packit Service c5cf8c
# Debug
Packit Service c5cf8c
sub PrintDefinitions {
Packit Service c5cf8c
    foreach my $name (keys(%Definitions)) {
Packit Service c5cf8c
        print "<$name>\n";
Packit Service c5cf8c
        my $defn = $Definitions{$name};
Packit Service c5cf8c
        # Here we could consider doing replacement for embedded <name>...</name>,
Packit Service c5cf8c
        # for things like arguments.
Packit Service c5cf8c
        print $defn;
Packit Service c5cf8c
        print "</$name>\n";
Packit Service c5cf8c
    }
Packit Service c5cf8c
}
Packit Service c5cf8c
# --------------------------------------------------------------------------
Packit Service c5cf8c
# printLine handles any continuation conventions
Packit Service c5cf8c
# printLine ( FD, lines )
Packit Service c5cf8c
# Note that a very simple approach works for Fortran because blanks
Packit Service c5cf8c
 # are ignored.  However, we will try to make the code easier to read
Packit Service c5cf8c
sub printLine {
Packit Service c5cf8c
    my $OFD   = $_[0];
Packit Service c5cf8c
    my $lines = $_[1];
Packit Service c5cf8c
Packit Service c5cf8c
    # Make sure that we get the current conventions
Packit Service c5cf8c
    if ($lang eq "Fortran") {
Packit Service c5cf8c
	$maxPrintLine = 72;
Packit Service c5cf8c
	$postLine     = "  &";
Packit Service c5cf8c
	$preLine      = "     &";
Packit Service c5cf8c
    }
Packit Service c5cf8c
    elsif ($lang eq "C" || $lang eq "C++") {
Packit Service c5cf8c
	$maxPrintLine = 180;
Packit Service c5cf8c
	$postLine     = "";
Packit Service c5cf8c
	$preLine      = "\t";
Packit Service c5cf8c
    }
Packit Service c5cf8c
    
Packit Service c5cf8c
    foreach my $line (split(/\n/,$lines)) {
Packit Service c5cf8c
	# Compute length
Packit Service c5cf8c
	my $length = length($line);
Packit Service c5cf8c
	while ($length > $maxPrintLine) {
Packit Service c5cf8c
	    # For Fortran 90 and C/C++, lines must be
Packit Service c5cf8c
	    # broken at whitespace.  Fortran 77 ignores whitespace,
Packit Service c5cf8c
	    my $subline = substr $line, 0, $maxPrintLine;
Packit Service c5cf8c
	    # Now, break subline at the last non-letter
Packit Service c5cf8c
	    if ($subline =~ /(.*)([^\w]\S*)$/) {
Packit Service c5cf8c
		$subline = $1;
Packit Service c5cf8c
		$line    = $2 . $line;
Packit Service c5cf8c
		# Add blanks to end of the subline to match what was stripped
Packit Service c5cf8c
		# off
Packit Service c5cf8c
		my $len = length($2);
Packit Service c5cf8c
		for (my $i=0; $i < $len; $i++) { 
Packit Service c5cf8c
		    $subline .= " ";
Packit Service c5cf8c
		}
Packit Service c5cf8c
	    }
Packit Service c5cf8c
	    print $OFD $subline . $postLine . "\n";
Packit Service c5cf8c
Packit Service c5cf8c
	    $line = substr $line, $maxPrintLine;
Packit Service c5cf8c
	    $line = $preLine . $line;
Packit Service c5cf8c
	    $length = length($line);
Packit Service c5cf8c
	}
Packit Service c5cf8c
        print $OFD $line . "\n";
Packit Service c5cf8c
    }
Packit Service c5cf8c
}
Packit Service c5cf8c
# --------------------------------------------------------------------------
Packit Service c5cf8c
# Process the file
Packit Service c5cf8c
# Still to do:
Packit Service c5cf8c
#   Allow multiple definition files, to allow for common definitions
Packit Service c5cf8c
#   for such things as headers
Packit Service c5cf8c
my $defnFile = "";
Packit Service c5cf8c
my $tmplFile = "";
Packit Service c5cf8c
my $posCount = 0;
Packit Service c5cf8c
for (@ARGV) {
Packit Service c5cf8c
    if (/-defn=(.*)/) {
Packit Service c5cf8c
	my $extraFile = $1;
Packit Service c5cf8c
	&ReadGlobalDefinitions( $extraFile );
Packit Service c5cf8c
    }
Packit Service c5cf8c
    elsif (/-lang=(.*)/) {
Packit Service c5cf8c
	$lang = $1;
Packit Service c5cf8c
	if (!defined($knownLang{$lang})) {
Packit Service c5cf8c
	    print STDERR "Unknown language $lang\n";
Packit Service c5cf8c
	    exit 1;
Packit Service c5cf8c
	}
Packit Service c5cf8c
    }
Packit Service c5cf8c
    elsif (/^-/) {
Packit Service c5cf8c
	print STDERR "Unrecognized argument $_\n";
Packit Service c5cf8c
	exit 1;
Packit Service c5cf8c
    }
Packit Service c5cf8c
    else {
Packit Service c5cf8c
	if ($posCount == 0) {
Packit Service c5cf8c
	    $defnFile = $_;
Packit Service c5cf8c
	}
Packit Service c5cf8c
	elsif ($posCount == 1) {
Packit Service c5cf8c
	    $tmplFile = $_;
Packit Service c5cf8c
	}
Packit Service c5cf8c
	else {
Packit Service c5cf8c
	    print STDERR "Too many arguments ($_)\n";
Packit Service c5cf8c
	    exit 1;
Packit Service c5cf8c
	}
Packit Service c5cf8c
	$posCount ++;
Packit Service c5cf8c
    }
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
if ($defnFile eq "" || $tmplFile eq "") {
Packit Service c5cf8c
    print STDERR "testmerge [ -defn=name ] defintion-file template-file \n";
Packit Service c5cf8c
    exit 1;
Packit Service c5cf8c
}
Packit Service c5cf8c
Packit Service c5cf8c
&ReadAndMerge( $defnFile, $tmplFile );