Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Fortran 90 makedepend

2 views
Skip to first unread message

Kate Hedstrom

unread,
Nov 8, 1994, 11:15:18 AM11/8/94
to
Several people have been asking about makedepend scripts so I
include mine below. A friend pointed out to me that Fortran 90
modules represent some new dependencies so that my old makedepend
script needed to be updated. I used it as an excuse to try out some
perl 5 features in the process.

Kate Hedstrom
Institute of Marine and Coastal Sciences

#!/usr/bin/perl
#
# Usage: sfmakedepend [-s] [-e ext] [-f file] [-I incdir]
# [-m mod_ext] file ...
#
# This is a makedepend script for Fortran, including Fortran 90.
# It searches for Fortran style includes, C preprocessor includes,
# and module dependencies to the extent that I understand them.
#
# Your files must have an extension listed in the @suffixes list
# below.
#
# The switch [-s] is for stupid Fortran compilers that don't know
# how to automatically send things through the C preprocessor.
# It is possible to force 'make' to invoke 'cpp' to create a .f
# file from a .F file (which has cpp directives), but make won't
# know that a .f file will depend on the files that the .F file
# included. This option will provide those dependencies.
#
# The [-e ext] switch is used with the [-s] switch for compilers
# which expect an extension other than .f on source files. For
# instance, one would use "-e fcm" for the Connection Machine.
#
# The [-f file] switch is used to change the name of the current
# Makefile.
#
# The [-I incdir] option tells sfmakedepend to look in alternate
# directories for the include files. There can be several "-I dir"
# options used at once. The current directory is still searched
# first.
#
# The [-m mod_ext] option tells sfmakedepend what extension to
# use for Fortran 90 module files. The default for "use My_Mod"
# is to list the dependency as "my_mod.mod" since this is what
# NAG f90 and IBM xlf both use. Let me know if other compilers
# use a different filename for the module information.
#
# NOTES
# This makedepend script is my first attempt at using perl 5
# objects. Therefore, it may not be the best example of how
# to do this. Also, it requires perl 5 and will die if you
# to use it with an older perl.
#
# Fortran 90 introduces some interesting dependencies. Both
# compilers I have access to (NAG f90 and IBM xlf) produce a
# private "mod_name.mod" file if you define "module mod_name"
# in your code. This file is used by the compiler when you
# use the module as a consistency check (type-safe). I have
# no idea what other compiler vendors do to store the module
# information.
#
# I would still make sure that my modules were recompiled
# before the rest of the code in my makefile:
#
# all: modules rest
# modules: mod1.o mod2.o mod3.o
# rest: modules main.o ...
#
# I sometimes include non-existent files as a compile time
# consistency check:
#
# #ifndef PLOTS
# #include "must_define_PLOTS" /* bogus include */
# #endif
#
# This program warns about include files it can't find, but
# not if there is a "bogus" on the same line.
#
# BUGS
# It can sometimes produce duplicate dependencies.
#
# Please let me know if you find any others.
# Kate Hedstrom
# ka...@ahab.rutgers.edu
#

package source_file;

# Ref to anonymous hash containing names of included files
$inc_ref = {};

# Constructor
sub new {
my $type = shift;
my $filename = shift;
my $path = shift;
my $self = {};
$self->{'source_file'} = $filename;
$self->{'filepath'} = $path;
$self->{'includes'} = {};
$self->{'any_incs'} = 0;
$self->{'modules'} = {};
bless $self;
}

sub find_includes {
my $self = shift;
my $mod_ext = shift;
my $file = $self->{'filepath'};
my($after, $filepath, $ref, $included, $use, $modname, $seen);
local(*FILE);
local($_);

if (-f $file) {
open(FILE, $file) || warn "Can't open $file: $!\n";
} elsif (-f "RCS/$file,v" || -f "$file,v" ) {
system("co $file");
open(FILE, $file) || warn "Can't open $file: $!\n";
$main::rcs{$file} = 1;
} else {
return;
}
while (<FILE>) {
$included = "";
$use = 0;
# look for Fortran style includes
if (/^\s*include\s*['"]([^"']*)["']/i) {
$included = $1;
$after = $';
# C preprocessor style includes
} elsif (/^#\s*include\s*["<]([^">]*)[">]/) {
$included = $1;
$after = $';
# Fortran 90 "use"
} elsif (/^\s*use\s+(\w+)/i) {
$included = $1;
$use = 1;
$after = $';
# Make the module name lowercase and add filename extension
# May be compiler dependent!!
$included =~ y/A-Z/a-z/;
$included .= "." . $mod_ext;
# Fortran 90 module
} elsif (/^\s*module\s+(\w+)/i) {
$modname = $1;
$modname =~ y/A-Z/a-z/;
$modname .= "." . $mod_ext;
$self->{'modules'}{$modname} = 1;
}
if ($included) {
if ( $inc_ref->{$included} ) {
$filepath = $inc_ref->{$included}{'filepath'};
} else {
$filepath = &main::findfile($included);
if ( $use && ! $filepath ) {
$filepath = $included;
}
$ref = new source_file($included, $filepath);
$inc_ref->{$included} = $ref;
if ( ! $use ) { # don't search module.mod files
$ref->find_includes($mod_ext);
}
}
if ( $filepath ) {
$self->{'includes'}{$included} = 1;
$self->{'any_incs'} = 1;
} else {
if ($after !~ /bogus/i) {
warn "Can't find file: $included\n";
}
}
}
}
close FILE;
}

sub print_includes {
my $self = shift;
my($i, $file, $ref);

foreach $file (keys %{$self->{'includes'}}) {
$ref = $$inc_ref{$file};
print " " . $ref->{'filepath'};
$ref->print_includes();
}
}

sub print {
my $self = shift;
my $ext = shift;
my $stupid = shift;
my $target = $self->{'source_file'};
my @suffixes = qw( .c .C .cc .cxx .cpp .f .F .f90);
my($base, $i, $name, $path, $suffix, $object, $modname);

# print out "include" and "use" dependencies
if ($self->{'any_incs'}) {
$base = &main::basename($target, @suffixes);
$target = $base . ".o";
print "$target:";
$self->print_includes();
print "\n";
if ($stupid) {
$target = $base . "." . $ext;
print "$target:";
$self->print_includes();
print "\n";
}
}
# print out module dependencies
($name, $path, $suffix) =
&main::fileparse($self->{'filepath'}, @suffixes);
foreach $modname (keys %{$self->{'modules'}}) {
$object = $path . "/" . $name . ".o";
print "$modname: $object\n";
}
}


# Start of main program
package main;

require 5;
use File::Basename;
use Getopt::Long;

GetOptions("s", "e=s", "f=s", "I=s@", "m=s")
|| die "problem in GetOptions";

# For compilers that don't invoke cpp for you
if ($opt_s) {
$stupid = 1;
}
if ($opt_e) {
$ext = $opt_e;
} else {
$ext = "f";
}

# list of directories to search, starting with current directory
@incdirs = (".", @opt_I);

if ($opt_f) {
$mf = $opt_f;
} elsif (-f "makefile") {
$mf = 'makefile';
} else {
$mf = 'Makefile';
}

# extension used for compiler's private module information
if ($opt_m) {
$mod_ext = $opt_m;
} else {
$mod_ext = 'mod';
}

$mystring = '# DO NOT DELETE THIS LINE - used by make depend';


# Search for the includes in all the files
foreach $file (@ARGV) {
$sources{$file} = new source_file($file, $file);
$sources{$file}->find_includes($mod_ext);
}

# Create new Makefile with new dependencies.

open(MFILE, $mf) || die "can't read Makefile $mf: $!\n";
open(NMFILE, "> Makefile.new") || die "can't write Makefile.new: $!\n";
select(NMFILE);

while (<MFILE>) {
if (!/$mystring/) {
print;
} else {
last;
}
}

print $mystring, "\n";

# Now print out dependencies in sorted order.


foreach $target (sort keys(%sources)) {
$sources{$target}->print($ext, $stupid);
}

# Sort out the Makefiles

rename($mf, "$mf.old") || warn "can't overwrite $mf.old: $!\n";
rename('Makefile.new', $mf) ||
warn "can't move Makefile.new to $mf: $!\n";

# Delete those RCS files we checked out
foreach $file (keys %rcs) {
unlink($file);
}

#
# End of main
#

sub findfile {
# Let's see if we can find the included file. Look in current
# directory first, then in directories from -I arguments. Finally,
# look for RCS files in current directory.
my $file = shift;
my($found, $i, $filepath);

$found = 0;
DIR:
foreach $i (0 .. $#incdirs) {
$filepath = $incdirs[$i]."/".$file;
if ( -f $filepath ) {
$found = 1;
$filepath =~ s#^\./##; # convert ./foo.h to foo.h
return $filepath;
}
}
#see if it is a checked-in RCS file
if (-f "RCS/$file,v" || -f "$file,v" ) {
$found = 1;
system("co $file");
$filepath = $file;
$rcs{$file} = 1;
}
if ( ! $found ) {
$filepath = "";
}
$filepath;
}

0 new messages