f77tof90.in 15.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#! @PERL@ -w
# -*- Mode: perl; -*-
#
# f77tof90 indir outdir [ Makefile-template [Make-Append] ]
# For each file in indir/*.[fF], create a corresponding file in outdir
# with .f90/.F90, and with any include "mpif.h" replaced with use mpi
# It also changes to the new comment style, because some compilers
# use the comment style to choose other features of the language
#
# We also allow the file name to be modified to help out Windows, since
# programs in a project need to have distinct names
#
$indir = $ARGV[0];
$outdir = $ARGV[1];
$makeTemplate = $ARGV[2];
$makeAppend   = $ARGV[3];
$convertToFreeForm    = 1;
$convertToNewComments = 1;
# Including a newline variable allows us to handle Unix and DOS source files
$newline = "\n";

%replaceInclude = ( 'iodisp' => 'integer (kind=MPI_OFFSET_KIND) disp',
		    'ioaint' => 'integer (kind=MPI_ADDRESS_KIND) aint',
		    'iooffset' => 'integer (kind=MPI_OFFSET_KIND) offset',
		    'type1aint' => 'integer (kind=MPI_ADDRESS_KIND) aint', 
		    'typeaints' => 'integer (kind=MPI_ADDRESS_KIND) aint, aintv(max_asizev)', 
		    'attr1aints' => 'integer (kind=MPI_ADDRESS_KIND) extrastate, valin, valout, val',
		    'attraints' => 'integer (kind=MPI_ADDRESS_KIND) extrastate, valin, valout, val',
		    'addsize' => 'integer (kind=MPI_ADDRESS_KIND) asize',
                    'add1size' => 'integer (kind=MPI_ADDRESS_KIND) asize',
	    );

%excludePrograms = ();
$debugReplace = 0;
35
$reportSkipped = 1;
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# --------------------------------------------------------------------------
# Check the input arguments
if ($indir eq "" || $outdir eq "") {
    print STDERR "Usage: f77tof90 indir outdir [ makefile-template ]\n";
    exit 1;
}
if ( ! -d $indir) {
    print STDERR "Input directory $indir does not exist\n";
    exit 1;
}
if (! -d $outdir) {
    print STDERR "Output directory $outdir does not exist\n";
    exit 1;
}
# --------------------------------------------------------------------------


# --------------------------------------------------------------------------
opendir( DIR, "$indir" );
my @filelist = ();
while ($file = readdir(DIR)) {
    # Extract the extension
    if ($file =~ /^(.*)\.([^\.]*)$/) {
	$name = $1;
	$ext  = $2;
	# Special handling for C files, if any
	if ($ext eq "c") {
	    my $name90 = $name;
64
65
66
67
            # don't transform "ctypesfromc" to "ctypesf90romc"
            if ($name ne "ctypesfromc") {
                $name90 =~ s/f/f90/g;
            }
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
	    &ConvertCFile( "$indir/$file", "$outdir/$name90.c.new" );
	    &ReplaceIfDifferent( "$outdir/$name90.c", 
				 "$outdir/$name90.c.new" );
	    next;
	}
	# Skip if the file isn't a Fortran source file
	if ($ext ne "f" && $ext ne "F") { next; }
	&ConvertToF90( "$indir/$file", "$outdir/${name}90.${ext}90.new" );
	&ReplaceIfDifferent( "$outdir/${name}90.${ext}90", 
			     "$outdir/${name}90.${ext}90.new" );
	$filelist[$#filelist+1] = $file;
    }
}
closedir( DIR );

# &CreateMakefile( "filelist", $outdir );
if (defined($makeTemplate) && $makeTemplate ne "" &&
    -s "$indir/$makeTemplate") {
    &ConvertMakefile( $indir, $outdir, $makeTemplate );
    if (defined($makeAppend) && -s "$outdir/$makeAppend") {
	# If there is a makeAppend in the output directory, then 
	# append that to the generated makefile
	&AppendFile( "$outdir/$makeAppend", "$outdir/$makeTemplate.new" );
    }
    &ReplaceIfDifferent( "$outdir/$makeTemplate", 
			 "$outdir/$makeTemplate.new" );
94
95
96
97
98
99

    # unconditionally update the stamp file to assist the maintainer rebuild
    # rules
    open(STAMP, '>', "$outdir/Makefile.am-stamp");
    print STAMP localtime()."\n";
    close(STAMP);
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
}
if (-s "$indir/testlist" || -s "$indir/testlist.in") {
    # We allow both testlist.in and testlist as source files;
    # testlist.in gets priority
    my $filename = "testlist";
    if (-s "$indir/testlist.in") {
	$filename = "testlist.in";
    }
    &ConvertTestlist( $indir, $outdir, $filename );
    
    if (-s "$outdir/testlist.ap") {
	&AppendFile( "$outdir/testlist.ap", "$outdir/$filename.new" );
    }
    &ReplaceIfDifferent( "$outdir/$filename", "$outdir/$filename.new" );
}

exit 0;

# -----------------------------------------------------------------------------

sub ConvertToF90 {
    my $infile = $_[0];
    my $outfile = $_[1];
    
    open (INF, "<$infile" ) || die "Could not open $infile\n";
    open (OUTF, ">$outfile" ) || die "Could not open $outfile\n";

    print OUTF "! This file created from $infile with f77tof90\n";
    my $lastLine = "";
    my $firstline = 1;
    while (<INF>) {
	if (/\r/) { $newline = "\r\n"; }
	# Remove any end-of-line characters
	s/[\r\n]*//g;
        # The implicit none must not come before the use mpi statement,
	# but in F77, it must come before the include mpif.h statement.
	# Rather than try and fix this, rely on the F77 versions to 
	# catch undeclared variables
        if (/[Ii][Mm][Pp][Ll][Ii][Cc][Ii][Tt]\s+[Nn][Oo][Nn][Ee]/) { next; }
	if (/^(\s*)include\s+[\'\"]mpif\.h/) {
	    $_ = "$1use mpi";
	}
	# Allow the insertion of Fortran 90 only statements, such as
	# interface definitions
        if (/^CF90/) {
	    s/^CF90/    /;
	}
	# Since we use interface statements for the error handlers,
	# remove their external declaration
	if (/^\s+external myerrhanfunc/) {
	    s/^\s/!/;
	}
	if ($convertToNewComments) {
	    s/^C/!/;
	    s/^c/!/;
	}	
	# Update the special includes that are used to provide 
	# address or offset sized types with ones the use the
	# Fortran90 KIND style
	if (/^(\s*)include\s+[\'\"]([\/\.\w]+)\.h[\"\']/) {
	    my $leading     = $1;
	    my $includename = $2;
	    if (defined($replaceInclude{$includename})) {
		$_ = $leading . $replaceInclude{$includename} . "\n";
	    }
	}

	# We need to handle the special case of the program
	# name in spawn commands
	if (/(.*)\"([\.\/\w]*spawn[^\"]*)\"(.*)/) {
	    my $before = $1;
	    my $name = $2;
	    my $after = $3;
	    $_ = $before . "\"" . $name . "90" . "\"" . $after;
	}

	# We could also detect continuations in column six and 
	# convert to free-form input by holding one line back.
	if ($convertToFreeForm) {
	    if (/^     \S(.*)/) {
		$leftover = $1;
		# This line contains a continuation marker
		# Add a continuation marker to the previous line if
		# it doesn't already have one
		if (! ($lastline =~ /\&\s*$/) ) {
	  	    $lastline .= " &";	   
		}
		$_ = "      \&$leftover";
	    }
	}
	print OUTF "$lastline$newline" if (! $firstline);
	$firstline = 0;
	$lastline = $_;
    }
    print OUTF "$lastline$newline";

    close (INF);
    close (OUTF);
}

#
# A very simple routine for creating a version of a C file that refers
# to F90 instead of F77.
sub ConvertCFile {
    my $infile = $_[0];
    my $outfile = $_[1];
    
    open (INF, "<$infile" ) || die "Could not open $infile\n";
    open (OUTF, ">$outfile" ) || die "Could not open $outfile\n";

    print OUTF "/* This file created from $infile with f77tof90 */\n";
    while (<INF>) {
	if (/\r/) { $newline = "\r\n"; }
	# Remove any end-of-line characters
	s/[\r\n]*//g;
	# replace F77 with F90, mostly for CPP tests, except for name
	# mapping
	if (! /F77_NAME/) {
	    s/F77/F90/g;
	}
	print OUTF "$_$newline";
    }

    close (INF);
    close (OUTF);
}

# Create a makefile from a template.  Replace @EXECS@ with the programs
# in the filelist.
# CreateMakefile( "filelist", $outdir )
sub CreateMakefile {
    my $filelist = $_[0];
    my $outdir   = $_[1];

    print STDERR "This function is not implemented\n";
    return 0;
}

#
# Take an existing makefile and perform the following transformations:
# .f -> .f90, .F -> .F90
# Others as necessary
# ConvertMakefile( indir, outdir, filename )
243
# By providing the filename, we can accept Makefile, Makefile.in, Makefile.ap,
244
245
246
247
# Makefile.sm, or even nonstandard names such as buildscript.
sub ConvertMakefile {
    my ($indir, $outdir, $filename) = @_;
    %excludePrograms = ();
248
249
    my $saw_maintainercleanfiles = 0;
    my $saw_extra_dist = 0;
250
251
252
253
254
255

    open( INF, "<$indir/$filename" ) || die "Cannot open $indir/$filename\n";
    open( OUTF, ">$outdir/$filename.new" ) || die "Cannot open $outdir/$filename.new\n";
    print OUTF "# This $filename generated automatically by f77tof90\n";
    print OUTF "# from $indir/$filename.  DO NOT EDIT\n"; 
    while (<INF>) {
256
257
258
259
260
261
262
        if (not m/^\s*#/ and m/MAINTAINERCLEANFILES/) {
            $saw_maintainercleanfiles = 1;
        }
        if (not m/^\s*#/ and m/EXTRA_DIST/) {
            $saw_extra_dist = 1;
        }

263
264
        # First, check for sources that are not present.  These
	# may be derived files (see f77/io for an example).  For now,
265
266
267
268
269
270
271
	# we'll skip these unless they are explicitly expected to not be present
        # (by the presence of a "nodist_" prefix.
	if (/^(nodist_)?(\w+)_SOURCES\s*=\s*(\w+\.f)/) {
            my $nodist     = $1;
	    my $sourcebase = $2;
	    my $sourcename = $3;
	    if (not $nodist and ! -s "$indir/$sourcename") {
272
273
274
275
276
		print "Skipping source file $indir/$sourcename because it is not present\n" if $reportSkipped;
		$excludePrograms{$sourcebase} = 1;
		next; 
	    }
	}
277
278
279
280
281
282
283
284
285
286
287

        # Headers like f77/spawn/type1aints.h are not used in the f90 tests.
        # They are instead replaced with appropriate "kind=" type declarations
        # in a different part of this script.  So we drop any lines that add
        # dependencies on a header.
        if (m/^\w+f\.\$\(OBJEXT\):.*\.h/) {
            my $chomped = $_;
            chomp $_;
            next;
        }

288
289
        # convert program names from foof.f to foof90.f90
        s/f_SOURCES/f90_SOURCES/g;
290
        s/f_DEPENDENCIES/f90_DEPENDENCIES/g;
291
292
293
294
295
296
297
298
299
300
	if (/f\.f/) {
	    s/f\.f/f90.f90/g;
	}
	else {
	    # Move files to f90
	    s/\.f/.f90/g;
	}
	s/mtestf\.o/mtestf90.o/;
	s/\.F/.F90/g;
	s/f77/f90/g;
301
	s/F77/FC/g;
302
303
304
305
306
307
308
309
310
	# Update any per-program LDADD values
        s/f_LDADD/f90_LDADD/g;
	#
	# Handle special cases:  
	# Force the c2f2c test to use the f90 compiler
	s/c2f2cf90_SOURCES.*/c2f2cf90_SOURCES = c2f2cf90.f90 c2f902c.c/;
	s/c2f2ciof90_SOURCES.*/c2f2ciof90_SOURCES = c2f2ciof90.f90 c2f902cio.c/;
#	s/c2f2ciof90_LDADD/c2f2cfio90_LDADD/g;
	s/c2f2cwinf90_SOURCES.*/c2f2cwinf90_SOURCES = c2f2cwinf90.f90 c2f902cwin.c/;
311
        s/c2fmult/c2f90mult/g;
312
313
314
315

	if (/EXTRA_PROGRAMS/) {
	    s/allocmemf/allocmemf90/;   # allocmemf test is special 
	}
316
317
	# Handle the special case of C programs (used for f2c/c2f testing)
	if (/^(\w+)_SOURCES(\s*=\s*)(\w+)\.c\s*$/) {
318
319
320
	    my $progname = $1;
	    my $spacing  = $2;
	    my $name     = $3;
321
322
323
324
325
            if ($name !~ m/ctypesfromc/) {
                $name =~ s/f(?!90)/f90/;
                $progname =~ s/f(?!90)/f90/;
                $_ = "$progname" . "_SOURCES" . $spacing . $name . ".c\n";
            }
326
327
	}

328
329
330
        # Catch any bare "Xf" tests listed in noinst_PROGRAMS or
        # EXTRA_PROGRAMS.  Check for "Xf.f" to avoid substituting any random
        # word that ends in "f".  We also cheat a bit and look into the
331
        # ioharness.defn in the indir directory for valid file names.
332
333
334
335
336
        if (not m/^\s*#/) {
            while (m/\b(\w+f)\b/g) {
                my $word = $1;
                next if $word eq "if" or $word eq "rf"; # filter out some noise
                if (-e "$indir/${word}.f" or
337
                    0 == system(qq(grep 'TESTDEFN filename="${word}\\.f"' '$indir/ioharness.defn' >/dev/null 2>&1)))
338
339
340
341
342
343
344
345
346
347
348
349
350
                {
                    s/\b${word}\b/${word}90/g;
                }
                else
                {
                    print "Skipping word '${word}' because file $indir/${word}.f is not present\n" if $reportSkipped;
                }
            }
        }

        # f90 makefiles should use the corresponding common automake fragment
        s/Makefile_f77.mtest/Makefile_f90.mtest/g;

351
352
353
354
355
	# Eventually need some way to update directory paths (particularly
	# relative ones) and add F90 compile rules when not present.
	print OUTF $_;
    }

356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
    my $mcf_plus = '';
    if ($saw_maintainercleanfiles) {
        $mcf_plus = '+';
    }
    my $ed_plus = '';
    if ($saw_extra_dist) {
        $ed_plus = '+';
    }

    my $testlist_ap = '';
    if (-f "${outdir}/testlist.ap") {
        $testlist_ap = 'testlist.ap';
    }
    my $makefile_ap = '';
    if (-f "${outdir}/Makefile.ap") {
        $makefile_ap = 'Makefile.ap';
    }

    # append a rule to help remake the f90 Makefile.am if the f77 Makefile.am is
    # updated
    print OUTF <<EOT;

if MAINTAINER_MODE

# f77tof90 only replaces output files if the contents differ, so we must use a
# separate timestamp file in order avoid constantly trying to remake the f90
# copy of the Makefile when the f77 copy is updated in a way that does not
# change its contents
\$(srcdir)/Makefile.am: \$(srcdir)/Makefile.am-stamp

\$(srcdir)/Makefile.am-stamp: \$(master_top_srcdir)/${indir}/Makefile.am \$(master_top_srcdir)/maint/f77tof90
\t( cd \$(master_top_srcdir) && ./maint/f77tof90 ${indir} ${outdir} Makefile.am Makefile.ap )

MAINTAINERCLEANFILES ${mcf_plus}= Makefile.am-stamp
EXTRA_DIST ${ed_plus}= Makefile.am-stamp ${testlist_ap} ${makefile_ap}

endif MAINTAINER_MODE

EOT

396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
    close( INF );
    close( OUTF );
    # The check on a file change is handled in the routine that calls this
    # because we may append to this file first.
}

# Append infile to the end of inout file
#( infile, inoutfile )
sub AppendFile { 
    my $infile = $_[0];
    my $outfile = $_[1];
    
    open( INA, "<$infile" ) || die "Cannot open $infile\n";
    open( OUTA, ">>$outfile" ) || die "Cannot open $outfile\n";
    while (<INA>) {
        print OUTA $_;
    }
    close(INA);
    close(OUTA);
}

#
# Replace old file with new file only if new file is different
# Otherwise, remove new filename 
sub ReplaceIfDifferent {
    my ($oldfilename,$newfilename) = @_;
    my $rc = 1;
    if (-s $oldfilename) { 
	$rc = system "cmp -s $newfilename $oldfilename";
	$rc >>= 8;   # Shift right to get exit status
    }
    if ($rc != 0) {
	print STDERR "Replacing $oldfilename\n";
	if ($debugReplace && -s $oldfilename) {
	    print STDERR "Differences are:";
	    system "diff $newfilename $oldfilename";
	}
	# The files differ.  Replace the old file 
	# with the new one
	if (-s $oldfilename) {
	    unlink $oldfilename;
	}
	rename $newfilename, $oldfilename || 
	    die "Could not replace $oldfilename";
    }
    else {
	unlink $newfilename;
    }
}

# Change the names of the tests.  Remove any that were skipped from the 
# Makefile.  Check for a  testlist.in before testlist
sub ConvertTestlist {
    my ($indir, $outdir, $filename) = @_;

    open( INF, "<$indir/$filename" ) || die "Cannot open $indir/$filename\n";
    open( OUTF, ">$outdir/$filename.new" ) || die "Cannot open $outdir/$filename.new\n";
    print OUTF "# This file generated by f77tof90\n";
    while (<INF>) {
	if (/^(\w+)\s/) {
	    my $sourcebase = $1;
	    if (defined($excludePrograms{$sourcebase})) { next; }
	}
	if (/^(\w+f)\s+(.*)/) {
	    $_ = $1 . "90 "  . $2 . "\n";
	}
	elsif (/^c2fmult(\w*)\s+(.*)/) {
	    # This is a special case for programs that are not Fortran
	    # programs but are part of the Fortran tests; principly, these
	    # are the tests of MPI handle conversion
	    # note the \w* instead of \w+; this allows us to match both
	    # c2fmult.c and c2fmultio.c
	    $_ = "c2f90mult$1 $2\n";
	}
	elsif (/^\@ALLOCMEMF\@/) {
	    # This is a special case for an optional feature (using
	    # Cray-style pointers for MPI_Alloc_mem).
473
	    $_ = "\@ALLOCMEMFC\@\n";
474
	}
475
	elsif (/^(\@\w+\@)(\s*\w+f)\s+(.*)/) {
476
477
478
479
	    # This handles the case where an autoconf variable in the
            # testlist.in file is used to optionally comment out a test
	    $_ = $1 . $2 . "90 "  . $3 . "\n";
	}
480
481
482
483
484
485
	print OUTF $_;
    }
    close INF;
    close OUTF;

}