#!/usr/bin/env perl #----------------------------------------------------------------------------------------------- # # configure # # # This utility allows the CICE user to specify compile-time configuration # options via a commandline interface. The output from configure is a # Makefile and a cache file that contains all configuration parameters # required to produce the Makefile. A subsequent invocation of configure # can use the cache file as input (via the -defaults argument) to reproduce # the CICE configuration contained in it. Note that when a cache file is # used to set default values only the model parameters are used. The # parameters that are platform dependent (e.g., compiler options, library # locations, etc) are ignored. # # As the build time configurable options of CICE are changed, this script # must also be changed. Thus configure is maintained under revision # control in the CICE source tree and it is assumed that only the version of # configure in the source tree will be used to build CICE. Thus we assume # that the root of the source tree can be derived from the location of this # script. # # configure has an optional test mode to check that the Fortran90 compiler # works and that external references to the netCDF and MPI libraries can be # resolved at link time. # # Date Contributor Modification # ----------------------------------------------------------------------------------------------------- # 2009-01-18 Mariana Vertenstein Original version #----------------------------------------------------------------------------------------------- use strict; #use warnings; #use diagnostics; use Cwd; use English; use Getopt::Long; use IO::File; use IO::Handle; #----------------------------------------------------------------------------------------------- sub usage { die <). Any value that contains white-space must be quoted. Long option names may be supplied with either single or double leading dashes. A consequence of this is that single letter options may NOT be bundled. ***************************************************** To obtain the cice decompsition, can do any of the following Set all of -bsizex, -bsizey, -maxblocks, -decomptype explicitly sets the cice decomposition OR -ntasks, -nthreads used to determine defaults for the cice decomposition. OR -nodecomp the cice decompsition is determined external to configure ****************************************************** -cache Name of output cache file (default: config_cache.xml). -cachedir Name of directory where output cache file is written (default: CICE build directory). -cice_mode Mode [prognostic | thermo_only | prescribed ]. (default: prognostic) -comp_intf Component interface to use (ESMF, MCT) (default MCT) -bsizex Size of cice block in first horizontal dimension. -bsizey Size of cice block in second horizontal dimension. -maxblocks Max number of cice blocks per processor. -decomptype Decomposition type ( valid values are [cartesian | spacecurve]) -help [or -h] Print usage to STDOUT. -hgrid Specify horizontal grid. Use nlatxnlon for spectral grids; dlatxdlon for fv grids (dlat and dlon are the grid cell size in degrees for latitude and longitude respectively); -ntasks Number of MPI tasks. -nthreads Number of OMP threads per process. Setting nthreads > 0 implies smp. -nodecomp Turns off the generation of cice decomposition (default: not defined - decomp is generated) -ntr_aero Turns on aerosal tracers (valid values are 0,1,2,3,4,5,6, default is 3) -silent [or -s] Turns on silent mode - only fatal messages issued. -verbose [or -v] Turn on verbose echoing of settings made by configure. -version Echo the CVS tag name used to check out this CICE distribution. EOF } #----------------------------------------------------------------------------------------------- # Setting autoflush (an IO::Handle method) on STDOUT helps in debugging. It forces the test # descriptions to be printed to STDOUT before the error messages start. *STDOUT->autoflush(); #----------------------------------------------------------------------------------------------- # Set the directory that contains the CICE configuration scripts. If the configure command was # issued using a relative or absolute path, that path is in $ProgDir. Otherwise assume the # command was issued from the current working directory. (my $ProgName = $0) =~ s!(.*)/!!; # name of this script my $ProgDir = $1; # name of directory containing this script -- may be a # relative or absolute path, or null if the script is in # the user's PATH my $cwd = getcwd(); # current working directory my $cfgdir; # absolute pathname of directory that contains this script if ($ProgDir) { $cfgdir = absolute_path($ProgDir); } else { $cfgdir = $cwd; } #----------------------------------------------------------------------------------------------- # Save commandline my $commandline = "$cfgdir/configure @ARGV"; #----------------------------------------------------------------------------------------------- # Parse command-line options. my %opts = ( cache => "config_cache.xml", ); GetOptions( "cache=s" => \$opts{'cache'}, "cachedir=s" => \$opts{'cachedir'}, "cice_mode=s" => \$opts{'cice_mode'}, "comp_intf=s" => \$opts{'comp_intf'}, "bsizex=s" => \$opts{'bsizex'}, "bsizey=s" => \$opts{'bsizey'}, "maxblocks=s" => \$opts{'maxblocks'}, "decomptype=s" => \$opts{'decomptype'}, "h|help" => \$opts{'help'}, "hgrid=s" => \$opts{'hgrid'}, "ntr_aero=s" => \$opts{'ntr_aero'}, "ntasks=s" => \$opts{'ntasks'}, "nthreads=s" => \$opts{'nthreads'}, "nodecomp!" => \$opts{'nodecomp'}, "s|silent" => \$opts{'silent'}, "v|verbose" => \$opts{'verbose'}, "version" => \$opts{'version'}, ) or usage(); # Give usage message. usage() if $opts{'help'}; # Echo version info. version($cfgdir) if $opts{'version'}; # Check for unparsed argumentss if (@ARGV) { print "ERROR: unrecognized arguments: @ARGV\n"; usage(); } # Define 3 print levels: # 0 - only issue fatal error messages # 1 - only informs what files are created (default) # 2 - verbose my $print = 1; if ($opts{'silent'}) { $print = 0; } if ($opts{'verbose'}) { $print = 2; } my $eol = "\n"; my %cfg = (); # build configuration #----------------------------------------------------------------------------------------------- # Make sure we can find required perl modules and configuration files. # Look for them in the directory that contains the configure script. # Check for the configuration definition file. my $config_def_file = "config_files/definition.xml"; (-f "$cfgdir/$config_def_file") or die <<"EOF"; ** Cannot find configuration definition file \"$config_def_file\" in directory \"$cfgdir\" ** EOF # The configuration definition file specifies generic defaults. # # Horizontal grid. my $horiz_grid_file = 'config_grid.xml'; (-f "$cfgdir/../../../../scripts/ccsm_utils/Case.template/$horiz_grid_file") or die <<"EOF"; ** Cannot find horizonal grid parameters file \"$horiz_grid_file\" in directory \"$cfgdir/../../../../scripts/ccsm_utils/Case.template/\" ** EOF # The XML::Lite module is required to parse the XML configuration files. (-f "$cfgdir/../../../../scripts/ccsm_utils/Tools/perl5lib/XML/Lite.pm") or die <<"EOF"; ** Cannot find perl module \"XML/Lite.pm\" in directory \"$cfgdir/../../../../scripts/ccsm_utils/Tools/perl5lib\" ** EOF # The Build::Config module provides utilities to store and manipulate the configuration. (-f "$cfgdir/../../../../scripts/ccsm_utils/Tools/perl5lib/Build/Config.pm") or die <<"EOF"; ** Cannot find perl module \"Build/Config.pm\" in directory \"$cfgdir/../../../../scripts/ccsm_utils/Tools/perl5lib\" ** EOF if ($print>=2) { print "Setting CICE configuration script directory to $cfgdir$eol"; } #----------------------------------------------------------------------------------------------- # Add $cfgdir/perl5lib to the list of paths that Perl searches for modules unshift @INC, "$cfgdir/../../../../scripts/ccsm_utils/Tools/perl5lib"; unshift @INC, "$cfgdir"; require XML::Lite; require Build::Config; # Initialize the configuration. The $config_def_file provides the definition of a CICE # configuration. $cfg_ref is a reference to the new configuration object. my $cfg_ref = Build::Config->new("$cfgdir/$config_def_file"); #----------------------------------------------------------------------------------------------- # CICE root directory. my $cice_root = absolute_path("$cfgdir/../../../.."); if (-d "$cice_root/models/ice/cice/src") { $cfg_ref->set('cice_root', $cice_root); } else { die <<"EOF"; ** Invalid CICE root directory: $cice_root ** ** The CICE root directory must contain the subdirectory models/ice/cice/src/. ** It is derived from "config_dir/../../../.." where config_dir is the ** directory in the CICE distribution that contains the configuration ** scripts. EOF } if ($print>=2) { print "Setting CICE root directory to $cice_root$eol"; } #----------------------------------------------------------------------------------------------- # CICE build directory. my $cice_bld; if (defined $opts{'cice_bld'}) { $cice_bld = absolute_path($opts{'cice_bld'}); } else { # use default value $cice_bld = absolute_path($cfg_ref->get('cice_bld')); } if (-d $cice_bld or mkdirp($cice_bld)) { # If the build directory exists or can be made then set the value... $cfg_ref->set('cice_bld', $cice_bld); } else { die <<"EOF"; ** Could not create the specified CICE build directory: $cice_bld EOF } if ($print>=2) { print "Setting CICE build directory to $cice_bld$eol"; } #----------------------------------------------------------------------------------------------- # configuration cache directory and file. my $config_cache_dir; my $config_cache_file; if (defined $opts{'cachedir'}) { $config_cache_dir = absolute_path($opts{'cachedir'}); } else { $config_cache_dir = $cfg_ref->get('cice_bld'); } if (-d $config_cache_dir or mkdirp($config_cache_dir)) { $config_cache_file = "$config_cache_dir/$opts{'cache'}"; } else { die <<"EOF"; ** Could not create the specified directory for configuration cache file: $config_cache_dir EOF } if ($print>=2) { print "The configuration cache file will be created in $config_cache_file$eol"; } #----------------------------------------------------------------------------------------------- # Comp_intf option if (defined $opts{'comp_intf'}) { $cfg_ref->set('comp_intf', $opts{'comp_intf'}); } my $comp_intf = $cfg_ref->get('comp_intf'); if ($print>=2) { print "Using $comp_intf for comp_intf.$eol"; } #----------------------------------------------------------------------------------------------- # Horizontal grid parameters if (defined $opts{'hgrid'}) { $cfg_ref->set('hgrid', $opts{'hgrid'}); } my $hgrid = $cfg_ref->get('hgrid'); # set_horiz_grid sets the parameters for specific hgrid combinations. set_horiz_grid("$cfgdir/../../../../scripts/ccsm_utils/Case.template/$horiz_grid_file", $cfg_ref); if ($print>=2) { print "Horizontal grid specifier: $hgrid.$eol"; } #----------------------------------------------------------------------------------------------- # Set the cice mode my $cice_mode; if ($opts{'cice_mode'}) { $cice_mode = $opts{'cice_mode'}; $cfg_ref->set('cice_mode', $cice_mode); } else { $cice_mode = $cfg_ref->get('cice_mode'); } if ($print>=2) { print "cice : mode is $cice_mode $eol";} #----------------------------------------------------------------------------------------------- # Set the number of ice categories my $ncat; if (($cice_mode eq 'prognostic') || ($cice_mode eq 'thermo_only')) { $ncat = 5; } elsif ($cice_mode eq 'prescribed') { $ncat = 1; } else { die <<"EOF"; ** ERROR: following mode is not supported: $cice_mode EOF } if ($print>=2) { print "cice : ncat is $ncat $eol";} # Tracers if (defined $opts{'ntr_aero'}) { $cfg_ref->set('ntr_aero', $opts{'ntr_aero'}); } else { if ($cice_mode eq 'prescribed') { $cfg_ref->set('ntr_aero', 0); } else { $cfg_ref->set('ntr_aero', 3); } } #----------------------------------------------------------------------------------------------- # Get the CICE grid my $hgrid = $cfg_ref->get('hgrid'); my $nlon = $cfg_ref->get('nlon'); my $nlat = $cfg_ref->get('nlat'); if ($print>=2) { print "cice : grid is $hgrid $eol";} if ($print>=2) { print "cice : nlon is $nlon $eol";} if ($print>=2) { print "cice : nlat is $nlat $eol";} #----------------------------------------------------------------------------------------------- # If the cice decomposition is not to be specified, then set the cpp variables and stop my $ntr_aero = $cfg_ref->get('ntr_aero'); if (defined $opts{'ntr_aero'}) {$ntr_aero = $opts{'ntr_aero'}}; if ($ntr_aero gt "6"){ die <<"EOF"; ** ERROR: ntr_aero value of $ntr_aero is invalid, only values less than 6 are supported EOF } my $cice_cppdefs = " -DCCSMCOUPLED -Dcoupled -Dncdf -DNCAT=$ncat -DNXGLOB=$nlon -DNYGLOB=$nlat -DNTR_AERO=$ntr_aero"; #----------------------------------------------------------------------------------------------- # Determine if will set the cice decomposition my $set_decomp = (defined $opts{'nodecomp'}) ? 0 : 1; # Set the CICE decomposition my $bsizex; my $bsizey; my $maxblocks; my $decomptype; if ($set_decomp) { # First, check to see if user has specified it. my $cice_decomp_params = 0; if (defined $opts{'bsizex'}) { $bsizex = $opts{'bsizex'}; ++$cice_decomp_params; } if (defined $opts{'bsizey'}) { $bsizey = $opts{'bsizey'}; ++$cice_decomp_params; } if (defined $opts{'maxblocks'}) { $maxblocks = $opts{'maxblocks'}; ++$cice_decomp_params; } if (defined $opts{'decomptype'}) { $decomptype = $opts{'decomptype'}; ++$cice_decomp_params; } # Check that if the user has specified any of the decomp parameters, # they must all be specified. if ($cice_decomp_params) { unless ($cice_decomp_params == 4) { die <<"EOF"; ** ERROR: If any of the CICE decomposition parameters are specified, then they ** must all be specified. The settings were: ** bsizex=$bsizex bsizey=$bsizey maxblocks=$maxblocks decomptype=$decomptype EOF } unless (defined $opts{'decomptype'}) { die <<"EOF"; ** ERROR: Whend the CICE decomposition parameters are specified, the ** cice decomptype must also be specified as ant input argument. EOF } $cfg_ref->set('cice_decomptype', $decomptype); } if ($cice_decomp_params == 0) { # Check whether ntasks is specified. my $ntasks; if (defined $opts{'ntasks'}) { $cfg_ref->set('ntasks', $opts{'ntasks'}); $ntasks = $opts{'ntasks'}; # Check for legal ntasks value if ($ntasks < 1) { die "ERROR: ntasks value < 1: ntasks= $ntasks $eol"; } } # Check whether nthreads is specified. my $nthreads; if (defined $opts{'nthreads'}) { $cfg_ref->set('nthreads', $opts{'nthreads'}); $nthreads = $opts{'nthreads'}; # Check for legal nthreads value if ($nthreads < 1) { die "ERROR: nthreads value < 1: nthreads= $nthreads $eol"; } } # User hasn't specified CICE decomp so get default values. # The defaults depend on the total number of execution threads available. These must # be specified by the user. my $cice_ntasks; my $cice_nthreads; # User must define both ntasks and nthreads if (defined $ntasks and defined $nthreads) { $cice_ntasks = $ntasks; $cice_nthreads = $nthreads; } else { die <<"EOF"; ** ERROR: If CICE decomposition parameters are not specified, then either ** -ntasks and -nthreads must be specified to determine a default decomposition ** OR -nodecomp must be set as an argument. EOF } if ($cice_ntasks == 1 && $cice_nthreads ==1 ) { # serial case: $bsizex = $nlon; $bsizey = $nlat; $maxblocks = 1; $decomptype = 'cartesian'; $cfg_ref->set('cice_decomptype', $decomptype); } else { # If not serial, use the script provided by CICE to generate the default decomposition my $cice_decomp_gen = "$cice_root/models/ice/cice/bld/generate_cice_decomp.pl"; my $cice_decomp = `$cice_decomp_gen -res $hgrid -nproc $cice_ntasks -thrds $cice_nthreads -output all`; # check for error if ($cice_decomp =~ /No Decomp Created/) { die <<"EOF"; ** ERROR: CICE decomposition generator returns: $cice_decomp ** May need to explicitly specify the decomposition using the arguments -bsizex, -bsizey, and -maxblocks EOF } else { # output is blank separated values for nlons, nlats, bsizex, bsizey, maxblocks, decomptype my @tokens = split " ", $cice_decomp; $bsizex = $tokens[2]; $bsizey = $tokens[3]; $maxblocks = $tokens[4]; $decomptype = $tokens[5]; $cfg_ref->set('cice_decomptype', $decomptype); } } } } if ($print>=2) { print "cice : bsizex is $bsizex $eol";} if ($print>=2) { print "cice : bsizey is $bsizey $eol";} if ($print>=2) { print "cice : mxblcks is $maxblocks $eol";} if ($print>=2) { print "cice : decomp type is $decomptype $eol";} if ($set_decomp) { $cice_cppdefs = "$cice_cppdefs -DBLCKX=$bsizex -DBLCKY=$bsizey -DMXBLCKS=$maxblocks"; } #----------------------------------------------------------------------------------------------- # Makefile configuration ####################################################################### #----------------------------------------------------------------------------------------------- # the CPP definitions that were explicitly set in the defaults my $cfg_cppdefs = ' '; # CICE ice model $cfg_cppdefs .= $cice_cppdefs; # CPP defines to put on Makefile my $make_cppdefs = "$cfg_cppdefs"; if ($print>=2) { print "CPP definitions set by configure: \'$cfg_cppdefs\'$eol"; } #----------------------------------------------------------------------------------------------- # Write configuration files #################################################################### #----------------------------------------------------------------------------------------------- my $fp_filename = 'Filepath'; # name of output filepath file my $cpp_filename = 'CICE_cppdefs'; # name of output file for cice's cppdefs in ccsm # Write the filepath file for ccsm. write_filepath("$cice_bld/$fp_filename", $cfg_ref); if ($print>=2) { print "creating $cice_bld/$fp_filename\n"; } # Write the file for cice's cppdefs needed in ccsm. write_cppdefs("$cice_bld/$cpp_filename", $make_cppdefs); if ($print>=2) { print "creating $cice_bld/$cpp_filename\n"; } # Write the configuration file. $cfg_ref->write_file("$config_cache_file", $commandline); if ($print>=2) { print "creating $cice_bld/$config_cache_file\n"; } #----------------------------------------------------------------------------------------------- # Finished unless testing requested ############################################################ #----------------------------------------------------------------------------------------------- # Done testing. chdir( $cwd ) || die <<"EOF"; ** Trouble changing directory back to $cwd ** EOF if ($print) { print "CICE configure done.\n"; } exit; #----------------------------------------------------------------------------------------------- # REALLY FINNISHED ############################################################################# #----------------------------------------------------------------------------------------------- sub write_filepath { my ($file, $cfg_ref) = @_; my $fh = new IO::File; my $comp_intf = $cfg_ref->get('comp_intf'); my $cpl_dir; if ( $comp_intf eq "MCT" ) { $cpl_dir = "cpl_mct"; } elsif ( $comp_intf eq "ESMF" ) { $cpl_dir = "cpl_esmf"; } else { die "** comp_intf interface must by MCT or ESMF \n"; } my $CASEROOT = "$ENV{'CASEROOT'}"; my $CODEROOT = "$ENV{'CODEROOT'}"; $fh->open(">$file") or die "** can't open filepath file: $file\n"; print $fh "$CASEROOT/SourceMods/src.cice\n"; print $fh "$CODEROOT/ice/cice/src/drivers/$cpl_dir\n"; print $fh "$CODEROOT/ice/cice/src/drivers/cpl_share\n"; print $fh "$CODEROOT/ice/cice/src/mpi\n"; print $fh "$CODEROOT/ice/cice/src/source\n"; $fh->close; } #------------------------------------------------------------------------------- sub write_cppdefs { my ($file, $make_cppdefs) = @_; my $fh = new IO::File; $fh->open(">$file") or die "** can't open cpp defs file: $file\n"; print $fh "$make_cppdefs\n"; $fh->close; } #------------------------------------------------------------------------------- sub set_horiz_grid { # Set the parameters for the specified horizontal grid. The # parameters are read from an input file, and if no grid matches are # found then issue error message. # This routine uses the configuration defined at the package level ($cfg_ref). my ($hgrid_file, $cfg_ref) = @_; my $xml = XML::Lite->new( $hgrid_file ); my $root = $xml->root_element(); # Check for valid root node my $name = $root->get_name(); $name eq "config_horiz_grid" or die "file $hgrid_file is not a horizontal grid parameters file\n"; # Get grid from the package's configuration my $hgrid = $cfg_ref->get('hgrid'); # Read the grid parameters from $hgrid_file. my @e = $xml->elements_by_name( "horiz_grid" ); my %a = (); # Search for matching grid. my $found = 0; HGRID: while ( my $e = shift @e ) { %a = $e->get_attributes(); if ( $hgrid eq $a{'GLOB_GRID'} ) { $found = 1; last HGRID; } } # Die unless search was successful. unless ($found) { die "set_horiz_grid: no match for hgrid $hgrid\n"; } # Set parameter values $cfg_ref->set('nlat', $a{'ny'}); $cfg_ref->set('nlon', $a{'nx'}); # Need to override for extended grid configurations if ($hgrid =~ 'ar9v2') { $cfg_ref->set('nlat', 720); $cfg_ref->set('nlon', 1280); } # Need to override for extended grid configurations if ($hgrid =~ 'ar9v4') { $cfg_ref->set('nlat', 720); $cfg_ref->set('nlon', 1280); } # Override resolution settings to configure for SCAM mode. The override is needed # because in SCAM mode the -hgrid option is used to specify the resolution of default # datasets from which single data columns are extracted. my $scam = $cfg_ref->get('scam'); if ($scam) { $cfg_ref->set('nlat', 1); $cfg_ref->set('nlon', 1); } } #------------------------------------------------------------------------------- sub absolute_path { # # Convert a pathname into an absolute pathname, expanding any . or .. characters. # Assumes pathnames refer to a local filesystem. # Assumes the directory separator is "/". # my $path = shift; my $cwd = getcwd(); # current working directory my $abspath; # resulting absolute pathname # Strip off any leading or trailing whitespace. (This pattern won't match if # there's embedded whitespace. $path =~ s!^\s*(\S*)\s*$!$1!; # Convert relative to absolute path. if ($path =~ m!^\.$!) { # path is "." return $cwd; } elsif ($path =~ m!^\./!) { # path starts with "./" $path =~ s!^\.!$cwd!; } elsif ($path =~ m!^\.\.$!) { # path is ".." $path = "$cwd/.."; } elsif ($path =~ m!^\.\./!) { # path starts with "../" $path = "$cwd/$path"; } elsif ($path =~ m!^[^/]!) { # path starts with non-slash character $path = "$cwd/$path"; } my ($dir, @dirs2); my @dirs = split "/", $path, -1; # The -1 prevents split from stripping trailing nulls # This enables correct processing of the input "/". # Remove any "" that are not leading. for (my $i=0; $i<=$#dirs; ++$i) { if ($i == 0 or $dirs[$i] ne "") { push @dirs2, $dirs[$i]; } } @dirs = (); # Remove any "." foreach $dir (@dirs2) { unless ($dir eq ".") { push @dirs, $dir; } } @dirs2 = (); # Remove the "subdir/.." parts. foreach $dir (@dirs) { if ( $dir !~ /^\.\.$/ ) { push @dirs2, $dir; } else { pop @dirs2; # remove previous dir when current dir is .. } } if ($#dirs2 == 0 and $dirs2[0] eq "") { return "/"; } $abspath = join '/', @dirs2; return( $abspath ); } #------------------------------------------------------------------------------- sub subst_env_path { # # Substitute for any environment variables contained in a pathname. # Assumes the directory separator is "/". # my $path = shift; my $newpath; # resulting pathname # Strip off any leading or trailing whitespace. (This pattern won't match if # there's embedded whitespace. $path =~ s!^\s*(\S*)\s*$!$1!; my ($dir, @dirs2); my @dirs = split "/", $path, -1; # The -1 prevents split from stripping trailing nulls # This enables correct processing of the input "/". foreach $dir (@dirs) { if ( $dir =~ /^\$(.+)$/ ) { push @dirs2, $ENV{$1}; } else { push @dirs2, $dir; } } $newpath = join '/', @dirs2; return( $newpath ); } #------------------------------------------------------------------------------- sub mkdirp { my ($dir) = @_; my (@dirs) = split /\//, $dir; my (@subdirs, $path); # if $dir is absolute pathname then @dirs will start with "" if ($dirs[0] eq "") { push @subdirs, shift @dirs; } while ( @dirs ) { # check that each subdir exists and mkdir if it doesn't push @subdirs, shift @dirs; $path = join '/', @subdirs; unless (-d $path or mkdir($path, 0777)) { return 0; } } return 1; } #------------------------------------------------------------------------------- sub get_option { my ($mes, @expect) = @_; my ($ans, $expect, $max_tries); $max_tries = 5; print $mes; while ($max_tries) { $ans = <>; chomp $ans; --$max_tries; $ans =~ s/^\s+//; $ans =~ s/\s+$//; # Check for null response which indicates that default is accepted. unless ($ans) { return ""; } foreach $expect (@expect) { if ($ans =~ /^$expect$/i) { return $expect; } } if ($max_tries > 1) { print "$ans does not match any of the expected values: @expect\n"; print "Please try again: "; } elsif ($max_tries == 1) { print "$ans does not match any of the expected values: @expect\n"; print "Last chance! "; } } die "Failed to get answer to question: $mes\n"; } #------------------------------------------------------------------------------- sub write_tests_filepath { my ($test_dir) = @_; my $fh = new IO::File; $fh->open(">Filepath") or die "** can't open file: $test_dir/Filepath\n"; print $fh "$test_dir\n"; $fh->close; } #------------------------------------------------------------------------------- sub version { # The version is found in CICE's ChangeLog file. # $cfgdir is set by the configure script to the name of its directory. my ($cfgdir) = @_; my $logfile = "$cfgdir/../doc/ChangeLog"; my $fh = IO::File->new($logfile, '<') or die "** can't open ChangeLog file: $logfile\n"; while (my $line = <$fh>) { if ($line =~ /^Tag name:\s*(\w+)/ ) { print "$1\n"; exit; } } } #------------------------------------------------------------------------------- sub print_hash { my %h = @_; my ($k, $v); while ( ($k,$v) = each %h ) { print "$k => $v\n"; } }