1#! @PERL@ -w 2# -*- Mode: perl; -*- 3# 4# f77tof90 indir outdir [ Makefile-template [Make-Append] ] 5# For each file in indir/*.[fF], create a corresponding file in outdir 6# with .f90/.F90, and with any include "mpif.h" replaced with use mpi 7# It also changes to the new comment style, because some compilers 8# use the comment style to choose other features of the language 9# 10# We also allow the file name to be modified to help out Windows, since 11# programs in a project need to have distinct names 12# 13$indir = $ARGV[0]; 14$outdir = $ARGV[1]; 15$makeTemplate = $ARGV[2]; 16$makeAppend = $ARGV[3]; 17$convertToFreeForm = 1; 18$convertToNewComments = 1; 19# Including a newline variable allows us to handle Unix and DOS source files 20$newline = "\n"; 21 22%replaceInclude = ( 'iodisp' => 'integer (kind=MPI_OFFSET_KIND) disp', 23 'ioaint' => 'integer (kind=MPI_ADDRESS_KIND) aint', 24 'iooffset' => 'integer (kind=MPI_OFFSET_KIND) offset', 25 'type1aint' => 'integer (kind=MPI_ADDRESS_KIND) aint', 26 'typeaints' => 'integer (kind=MPI_ADDRESS_KIND) aint, aintv(max_asizev)', 27 'attr1aints' => 'integer (kind=MPI_ADDRESS_KIND) extrastate, valin, valout, val', 28 'attraints' => 'integer (kind=MPI_ADDRESS_KIND) extrastate, valin, valout, val', 29 'addsize' => 'integer (kind=MPI_ADDRESS_KIND) asize', 30 'add1size' => 'integer (kind=MPI_ADDRESS_KIND) asize', 31 ); 32 33%excludePrograms = (); 34$debugReplace = 0; 35$reportSkipped = 0; 36# -------------------------------------------------------------------------- 37# Check the input arguments 38if ($indir eq "" || $outdir eq "") { 39 print STDERR "Usage: f77tof90 indir outdir [ makefile-template ]\n"; 40 exit 1; 41} 42if ( ! -d $indir) { 43 print STDERR "Input directory $indir does not exist\n"; 44 exit 1; 45} 46if (! -d $outdir) { 47 print STDERR "Output directory $outdir does not exist\n"; 48 exit 1; 49} 50# -------------------------------------------------------------------------- 51 52 53# -------------------------------------------------------------------------- 54opendir( DIR, "$indir" ); 55my @filelist = (); 56while ($file = readdir(DIR)) { 57 # Extract the extension 58 if ($file =~ /^(.*)\.([^\.]*)$/) { 59 $name = $1; 60 $ext = $2; 61 # Special handling for C files, if any 62 if ($ext eq "c") { 63 my $name90 = $name; 64 $name90 =~ s/f/f90/g; 65 &ConvertCFile( "$indir/$file", "$outdir/$name90.c.new" ); 66 &ReplaceIfDifferent( "$outdir/$name90.c", 67 "$outdir/$name90.c.new" ); 68 next; 69 } 70 # Skip if the file isn't a Fortran source file 71 if ($ext ne "f" && $ext ne "F") { next; } 72 &ConvertToF90( "$indir/$file", "$outdir/${name}90.${ext}90.new" ); 73 &ReplaceIfDifferent( "$outdir/${name}90.${ext}90", 74 "$outdir/${name}90.${ext}90.new" ); 75 $filelist[$#filelist+1] = $file; 76 } 77} 78closedir( DIR ); 79 80# &CreateMakefile( "filelist", $outdir ); 81if (defined($makeTemplate) && $makeTemplate ne "" && 82 -s "$indir/$makeTemplate") { 83 &ConvertMakefile( $indir, $outdir, $makeTemplate ); 84 if (defined($makeAppend) && -s "$outdir/$makeAppend") { 85 # If there is a makeAppend in the output directory, then 86 # append that to the generated makefile 87 &AppendFile( "$outdir/$makeAppend", "$outdir/$makeTemplate.new" ); 88 } 89 &ReplaceIfDifferent( "$outdir/$makeTemplate", 90 "$outdir/$makeTemplate.new" ); 91} 92if (-s "$indir/testlist" || -s "$indir/testlist.in") { 93 # We allow both testlist.in and testlist as source files; 94 # testlist.in gets priority 95 my $filename = "testlist"; 96 if (-s "$indir/testlist.in") { 97 $filename = "testlist.in"; 98 } 99 &ConvertTestlist( $indir, $outdir, $filename ); 100 101 if (-s "$outdir/testlist.ap") { 102 &AppendFile( "$outdir/testlist.ap", "$outdir/$filename.new" ); 103 } 104 &ReplaceIfDifferent( "$outdir/$filename", "$outdir/$filename.new" ); 105} 106 107exit 0; 108 109# ----------------------------------------------------------------------------- 110 111sub ConvertToF90 { 112 my $infile = $_[0]; 113 my $outfile = $_[1]; 114 115 open (INF, "<$infile" ) || die "Could not open $infile\n"; 116 open (OUTF, ">$outfile" ) || die "Could not open $outfile\n"; 117 118 print OUTF "! This file created from $infile with f77tof90\n"; 119 my $lastLine = ""; 120 my $firstline = 1; 121 while (<INF>) { 122 if (/\r/) { $newline = "\r\n"; } 123 # Remove any end-of-line characters 124 s/[\r\n]*//g; 125 # The implicit none must not come before the use mpi statement, 126 # but in F77, it must come before the include mpif.h statement. 127 # Rather than try and fix this, rely on the F77 versions to 128 # catch undeclared variables 129 if (/[Ii][Mm][Pp][Ll][Ii][Cc][Ii][Tt]\s+[Nn][Oo][Nn][Ee]/) { next; } 130 if (/^(\s*)include\s+[\'\"]mpif\.h/) { 131 $_ = "$1use mpi"; 132 } 133 # Allow the insertion of Fortran 90 only statements, such as 134 # interface definitions 135 if (/^CF90/) { 136 s/^CF90/ /; 137 } 138 # Since we use interface statements for the error handlers, 139 # remove their external declaration 140 if (/^\s+external myerrhanfunc/) { 141 s/^\s/!/; 142 } 143 if ($convertToNewComments) { 144 s/^C/!/; 145 s/^c/!/; 146 } 147 # Update the special includes that are used to provide 148 # address or offset sized types with ones the use the 149 # Fortran90 KIND style 150 if (/^(\s*)include\s+[\'\"]([\/\.\w]+)\.h[\"\']/) { 151 my $leading = $1; 152 my $includename = $2; 153 if (defined($replaceInclude{$includename})) { 154 $_ = $leading . $replaceInclude{$includename} . "\n"; 155 } 156 } 157 158 # We need to handle the special case of the program 159 # name in spawn commands 160 if (/(.*)\"([\.\/\w]*spawn[^\"]*)\"(.*)/) { 161 my $before = $1; 162 my $name = $2; 163 my $after = $3; 164 $_ = $before . "\"" . $name . "90" . "\"" . $after; 165 } 166 167 # We could also detect continuations in column six and 168 # convert to free-form input by holding one line back. 169 if ($convertToFreeForm) { 170 if (/^ \S(.*)/) { 171 $leftover = $1; 172 # This line contains a continuation marker 173 # Add a continuation marker to the previous line if 174 # it doesn't already have one 175 if (! ($lastline =~ /\&\s*$/) ) { 176 $lastline .= " &"; 177 } 178 $_ = " \&$leftover"; 179 } 180 } 181 print OUTF "$lastline$newline" if (! $firstline); 182 $firstline = 0; 183 $lastline = $_; 184 } 185 print OUTF "$lastline$newline"; 186 187 close (INF); 188 close (OUTF); 189} 190 191# 192# A very simple routine for creating a version of a C file that refers 193# to F90 instead of F77. 194sub ConvertCFile { 195 my $infile = $_[0]; 196 my $outfile = $_[1]; 197 198 open (INF, "<$infile" ) || die "Could not open $infile\n"; 199 open (OUTF, ">$outfile" ) || die "Could not open $outfile\n"; 200 201 print OUTF "/* This file created from $infile with f77tof90 */\n"; 202 while (<INF>) { 203 if (/\r/) { $newline = "\r\n"; } 204 # Remove any end-of-line characters 205 s/[\r\n]*//g; 206 # replace F77 with F90, mostly for CPP tests, except for name 207 # mapping 208 if (! /F77_NAME/) { 209 s/F77/F90/g; 210 } 211 print OUTF "$_$newline"; 212 } 213 214 close (INF); 215 close (OUTF); 216} 217 218# Create a makefile from a template. Replace @EXECS@ with the programs 219# in the filelist. 220# CreateMakefile( "filelist", $outdir ) 221sub CreateMakefile { 222 my $filelist = $_[0]; 223 my $outdir = $_[1]; 224 225 print STDERR "This function is not implemented\n"; 226 return 0; 227} 228 229# 230# Take an existing makefile and perform the following transformations: 231# .f -> .f90, .F -> .F90 232# Others as necessary 233# ConvertMakefile( indir, outdir, filename ) 234# By providing the filename, we can accept Makefile, Makefile.in, Makefile.ap, 235# Makefile.sm, or even nonstandard names such as buildscript. 236sub ConvertMakefile { 237 my ($indir, $outdir, $filename) = @_; 238 %excludePrograms = (); 239 240 open( INF, "<$indir/$filename" ) || die "Cannot open $indir/$filename\n"; 241 open( OUTF, ">$outdir/$filename.new" ) || die "Cannot open $outdir/$filename.new\n"; 242 print OUTF "# This $filename generated automatically by f77tof90\n"; 243 print OUTF "# from $indir/$filename. DO NOT EDIT\n"; 244 while (<INF>) { 245 # First, check for sources that are not present. These 246 # may be derived files (see f77/io for an example). For now, 247 # we'll skip these 248 if (/^(\w+)_SOURCES\s*=\s*(\w+\.f)/) { 249 my $sourcebase = $1; 250 my $sourcename = $2; 251 if (! -s "$indir/$sourcename") { 252 print "Skipping source file $indir/$sourcename because it is not present\n" if $reportSkipped; 253 $excludePrograms{$sourcebase} = 1; 254 next; 255 } 256 } 257 # convert program names from foof.f to foof90.f90 258 s/f_SOURCES/f90_SOURCES/g; 259 if (/f\.f/) { 260 s/f\.f/f90.f90/g; 261 } 262 else { 263 # Move files to f90 264 s/\.f/.f90/g; 265 } 266 s/mtestf\.o/mtestf90.o/; 267 s/\.F/.F90/g; 268 s/f77/f90/g; 269 s/F77/F90/g; 270 # Update any per-program LDADD values 271 s/f_LDADD/f90_LDADD/g; 272 # 273 # Handle special cases: 274 # Force the c2f2c test to use the f90 compiler 275 s/c2f2cf90_SOURCES.*/c2f2cf90_SOURCES = c2f2cf90.f90 c2f902c.c/; 276 s/c2f2ciof90_SOURCES.*/c2f2ciof90_SOURCES = c2f2ciof90.f90 c2f902cio.c/; 277# s/c2f2ciof90_LDADD/c2f2cfio90_LDADD/g; 278 s/c2f2cwinf90_SOURCES.*/c2f2cwinf90_SOURCES = c2f2cwinf90.f90 c2f902cwin.c/; 279 280 if (/EXTRA_PROGRAMS/) { 281 s/allocmemf/allocmemf90/; # allocmemf test is special 282 } 283 # Handle the special case of C programs (used for f2d/c2f testing) 284 if (/(\w+)_SOURCES(\s*=\s*)(\w+)\.c\s*$/) { 285 my $progname = $1; 286 my $spacing = $2; 287 my $name = $3; 288 $name =~ s/f/f90/; 289 $progname =~ s/f/f90/; 290 $_ = "$progname" . "_SOURCES" . $spacing . $name . ".c\n"; 291 } 292 293 # Eventually need some way to update directory paths (particularly 294 # relative ones) and add F90 compile rules when not present. 295 print OUTF $_; 296 } 297 298 close( INF ); 299 close( OUTF ); 300 # The check on a file change is handled in the routine that calls this 301 # because we may append to this file first. 302} 303 304# Append infile to the end of inout file 305#( infile, inoutfile ) 306sub AppendFile { 307 my $infile = $_[0]; 308 my $outfile = $_[1]; 309 310 open( INA, "<$infile" ) || die "Cannot open $infile\n"; 311 open( OUTA, ">>$outfile" ) || die "Cannot open $outfile\n"; 312 while (<INA>) { 313 print OUTA $_; 314 } 315 close(INA); 316 close(OUTA); 317} 318 319# 320# Replace old file with new file only if new file is different 321# Otherwise, remove new filename 322sub ReplaceIfDifferent { 323 my ($oldfilename,$newfilename) = @_; 324 my $rc = 1; 325 if (-s $oldfilename) { 326 $rc = system "cmp -s $newfilename $oldfilename"; 327 $rc >>= 8; # Shift right to get exit status 328 } 329 if ($rc != 0) { 330 print STDERR "Replacing $oldfilename\n"; 331 if ($debugReplace && -s $oldfilename) { 332 print STDERR "Differences are:"; 333 system "diff $newfilename $oldfilename"; 334 } 335 # The files differ. Replace the old file 336 # with the new one 337 if (-s $oldfilename) { 338 unlink $oldfilename; 339 } 340 rename $newfilename, $oldfilename || 341 die "Could not replace $oldfilename"; 342 } 343 else { 344 unlink $newfilename; 345 } 346} 347 348# Change the names of the tests. Remove any that were skipped from the 349# Makefile. Check for a testlist.in before testlist 350sub ConvertTestlist { 351 my ($indir, $outdir, $filename) = @_; 352 353 open( INF, "<$indir/$filename" ) || die "Cannot open $indir/$filename\n"; 354 open( OUTF, ">$outdir/$filename.new" ) || die "Cannot open $outdir/$filename.new\n"; 355 print OUTF "# This file generated by f77tof90\n"; 356 while (<INF>) { 357 if (/^(\w+)\s/) { 358 my $sourcebase = $1; 359 if (defined($excludePrograms{$sourcebase})) { next; } 360 } 361 if (/^(\w+f)\s+(.*)/) { 362 $_ = $1 . "90 " . $2 . "\n"; 363 } 364 elsif (/^c2fmult(\w*)\s+(.*)/) { 365 # This is a special case for programs that are not Fortran 366 # programs but are part of the Fortran tests; principly, these 367 # are the tests of MPI handle conversion 368 # note the \w* instead of \w+; this allows us to match both 369 # c2fmult.c and c2fmultio.c 370 $_ = "c2f90mult$1 $2\n"; 371 } 372 elsif (/^\@ALLOCMEMF\@/) { 373 # This is a special case for an optional feature (using 374 # Cray-style pointers for MPI_Alloc_mem). 375 $_ = "\@ALLOCMEMF90\@\n"; 376 } 377 print OUTF $_; 378 } 379 close INF; 380 close OUTF; 381 382} 383