JiscMail Logo
Email discussion lists for the UK Education and Research communities

Help for COMP-FORTRAN-90 Archives


COMP-FORTRAN-90 Archives

COMP-FORTRAN-90 Archives


COMP-FORTRAN-90@JISCMAIL.AC.UK


View:

Message:

[

First

|

Previous

|

Next

|

Last

]

By Topic:

[

First

|

Previous

|

Next

|

Last

]

By Author:

[

First

|

Previous

|

Next

|

Last

]

Font:

Proportional Font

LISTSERV Archives

LISTSERV Archives

COMP-FORTRAN-90 Home

COMP-FORTRAN-90 Home

COMP-FORTRAN-90  2001

COMP-FORTRAN-90 2001

Options

Subscribe or Unsubscribe

Subscribe or Unsubscribe

Log In

Log In

Get Password

Get Password

Subject:

Re: Makefiles and cascade recompilation, yet again

From:

Ted Stern <[log in to unmask]>

Reply-To:

Fortran 90 List <[log in to unmask]>

Date:

Tue, 11 Dec 2001 10:02:56 -0800

Content-Type:

multipart/mixed

Parts/Attachments:

Parts/Attachments

text/plain (128 lines) , IncrBuild.mk (65 lines) , Makefile (82 lines) , mkdepf90.perl (346 lines)

Hi Malcolm,

>>>>> "Malcolm" == Malcolm Cohen <[log in to unmask]> writes:

    Malcolm> Ted Stern said:
    >> The issue of chain recompilation has been hashed around in this group
    >> for years.  Many solutions have been offered but as John Bray has
    >> pointed out, no really satisfactory solution has come forward.  You
    >> either have a bunch of "touch" files scattered around or you have to
    >> call MAKE twice to get everything updated.

I'm attaching updated versions of my examples.  I should clarify that I am
thinking about an extreme case --- you might have a huge directory tree of
.f90 files and want to build in a similar tree of object files.  I routinely
deal with builds of around 8K f77 sources, and I want to address how one would
handle this sort of situation when f90 module dependencies are considered.

    Malcolm> Despite believing this myself for a long time, a little while ago
    Malcolm> someone pointed out to me a simple scheme which avoids these
    Malcolm> problems.  An example of how to do it is attached.

    Malcolm> But first, the explanation:

    Malcolm> (1) Just do the Makefile as per usual, with the .o files
    Malcolm> depending on the USEd .mod files, however the rules for making
    Malcolm> the .mod files should not have any dependencies - instead, they
    Malcolm> just (delete and then) make the relevant .o files.  This is the
    Malcolm> crucial point - if the .mod files have dependencies they will be
    Malcolm> regenerated unnecessarily.

This means that a particular object file could result from more than one
target rule.  But MAKE would not know that.  You would be lying to MAKE.  This
is a Bad Thing and will eventually get you into trouble ;-).

    Malcolm> (2) Files which contain modules should use a script
    Malcolm> e.g. "checkf95" which does a comparison of the .mod files.

I suggested using "cmp -s".  Essentially the same thing.  See

           http://www.theochem.uwa.edu.au/fortran/recompile/

for a discussion of this point.

For my proposed method, there are several simplifications possible:  if you
discipline the programmer and have a compiler that ensures that

   - there is only one module per file,

   - the module name has exactly the same stem as the object file,

   - you are compiling in the object directory so no paths are required,

you can use $*.o in your compilation rules to designate the resulting object
file.  The main new thing I've contributed is a special GNU Make function to
either do the compilation or touch the results, using an implicit pattern rule:

        compile_f90 = $(FC) -c $(FFLAGS) $< -o $*.o

        %.o %.mod: %.f90
                 @$(call INCR_BUILD,$(compile_f90),$*.o,$(objmods),%.mod)

My perl script can be simplified dramatically to generate output like

        filename.o \
        filename.mod:  objmods := filename.mod
        filename.o:    another.mod andanother.mod

But regarding those coding guidelines: in general, how does a .mod MAKE target
rule know what .o file to generate?  What if the resulting .mod file has a
completely different name than the associated object file or source file?
What if the source file has mixed lower and upper case?  While you can direct
object output to a file with the same stem as the source, generated modules
are usually lowercased or uppercased versions of the module name.  And what if
there is more than one module per file?

It is possible that for one or more compilers or platforms, you may not be
able to enforce the requirements.

    Malcolm> No special tools are needed, though the checkf95 script I've
    Malcolm> provided exploits the NAG compiler's ability to suppress .mod
    Malcolm> file output or to only produce .mod file output.

I was thinking of compiler/platform independence ;-).

    Malcolm> Nor have I supplied a clever perl script to generate Makefiles
    Malcolm> like this.  I'm sure someone else could throw one together
    Malcolm> though...

The perl script I provided doesn't generate Makefiles --- the Makefile
generates the Makefile ;-).  The perl script generates a dependency list and
the target rule for a particular object and its side-effect modules.  In the
special case I describe above, an explicit target rule is not necessary --
only the dependency list is required, and a special target-specific variable
to reference the resulting module file.

    Malcolm> Aside: There is one slight deficiency of the current NAG f95 with
    Malcolm> this scheme, which I'll explain with reference to the example: if
    Malcolm> a.f90 is changed in a way that does not affect a.mod, and then
    Malcolm> b.f90 is changed, all subsequent compilations of files that USE B
    Malcolm> will complain about a.mod being out of date (it's just a warning,
    Malcolm> but irritating nonetheless).

That was precisely the problem I was addressing.  I handle it in such a way
that MAKE knows exactly what is going on.

    Malcolm>   We've added an option "-nocheck_modtime" to suppress this
    Malcolm> message - the message is really only there for people who are not
    Malcolm> using "make" or whose Makefiles are broken.

IMO, the Makefile is "broken" if your dependencies are perpetually out of date
;-).  The technique I proposed (based on contibutions of *many* others)
ensures that your dependency graph is always complete and always updated.

For many people with simple projects, a simple technique is adequate, I agree.
For them, a simplified version of my mkdepf90.perl script would be sufficient,
and I can provide one if you like.

Ted
--
 ============================================================
 Ted Stern                           Engineering Applications
 Cray Inc.                                http://www.cray.com
 411 First Avenue South, Suite 600               206-701-2182
 Seattle, WA 98104-2860                     FAX: 206-701-2500
 ============================================================




# # Some utility functions used to handle incremental recompilations: # # Single argument function that echoes argument before running it: ECHO_AND_RUN = \         /bin/echo "\n$(1)\n" ; $(1); # # Single argument function to create "old" files for dependency checks: # $1 = list of results # MAKE_BACKUPS = \         for file in $(1) ; do \            test -s $${file} && \               cp -p $${file} $${file}~ || \               touch $${file}~ ; \         done; # # 3 argument incremental recompile utility function that does comparison. # If the old files differ, the compile command is run, otherwise # the results are touched only. # $1 = compile command # $2 = list of dependencies # $3 = list of results # TOUCH_OR_BUILD = \         i=0 ; \         for file in $(2) ; do \            cmp -s $${file} $${file}~ || i=1 ; \         done ; \         if test $${i} -eq 1 ; then \            $(call ECHO_AND_RUN,$(1)) \         else \            $(call ECHO_AND_RUN,touch $(3)) \         fi; # INCR_BUILD: # Four argument general purpose incremental build function # $1 = build command # $2 = main build target # $3 = side results of build # $4 = pattern to look for in dependencies # # The first $(if ...) generates backup side-results files # The second $(if ...) checks whether there are auto-generated dependencies # that are out of date. If not, a regular compilation is done. # If there are out-of-date auto-generated dependencies, and there are # other dependencies, normal compilation is done, otherwise the special # TOUCH_OR_BUILD utility is called. # define INCR_BUILD         $(if $(3),\            $(call MAKE_BACKUPS,$(3)))\         $(if $(filter $(4),$?),\            $(if $(filter-out $(4),$?),\               $(call ECHO_AND_RUN,$(1)),\               $(call TOUCH_OR_BUILD,$(1),$(filter $(4),$?),$(2) $(3))),\            $(call ECHO_AND_RUN,$(1))) endef #EOF
#!/usr/bin/perl # # $Id: mkdepf90.perl,v 1.13 2001/12/07 20:08:14 stern Exp $ # Author: Ted Stern <[log in to unmask]> # # Usage: # # mkdepf90.perl -src srcfile [-objdir objdir] # # Produces two files as output. If `basename srcfile .f90` is srcbase, # the output goes to two files # # objdir/srcbase.rule and objdir/srcbase.deps # # - Intended for use with IncrBuild.mk in GNU Make versions 3.79.1 and higher. # # - Generates macros, target rules, and dependency listings for f90 files # that USE or define modules. # # - Intended for one source file at a time. For example, use in a Makefile # with the following pattern rule and include statement: # # $(OBJDIR)/%.rule $(OBJDIR)/%.deps: $(SRCDIR)/%.f90 # mkdepf90.perl -src $< -objdir $(@D) # # # include variable definitions and target rules: # include $(F90_objs:.o=.rule) # # # include extra dependencies: # include $(F90_objs:.o=.deps) # # - It doesn't matter if the specified object directory has a trailing # slash or not. # # Acknowledgements: # Cribbed basic dependency text-processing logic from makemake.perl[1], # with some argument parsing from fmkmf[4]. # # References: # [1] http://www.fortran.com/makemake.perl # [2] http://www.theochem.uwa.edu.au/fortran/recompile/ # [3] http://www.helsinki.fi/~eedelman/makedepf90.html # [4] http://www.met.ed.ac.uk/~hcp/fmkmf.html # [5] http://www.gfdl.gov/~vb/mkmf.html # [6] http://www.canb.auug.org.au/~millerp/rmch/recu-make-cons-harm.html # [7] http://www.paulandlesley.org/gmake/ # #----------------------------------------------------------------------- # Implementation comments: # # *** NOTE *** # Modify the &ModCase function for your particular compiler! # It is currently set up for Cray UNICOS compilers, which convert # module names into uppercase; e.g., "module a_def" causes the creation # of A_DEF.mod when the -em compile option is specified. On many other # systems, module files are created with lowercased names. # *** NOTE *** # # Target-specific variables are defined so that both object and module # targets can compile to the same target, $(objpath) (a MAKE variable # reference, not a perl variable!). # # Example of simple output for no OBJDIR and no modules created: # # mkdepf90.perl -src filename.f90 # # output in filename.rule: # # # Variable containing the full path to the object: # filename.o := filename.o # # # Target-specific variable: # # objpath = the full path to the f90 obj file: # $(filename.o): objpath := $(filename.o) # # # Target-specific variable: # # objbase = the f90 obj file stripped of directory component: # $(filename.o): objbase := filename.o # # # The main target rule: # $(filename.o): filename.f90 # $(COMPILE_F90) # # output in filename.deps: # # # Extra dependencies of the obj file: # $(filename.o): $(A_DEF.mod) file1.h file2.inc # # Example of complex case with -objdir specified, multiple modules created, # and multiple module and include dependencies: # # mkdepf90.perl -src complex_def.f90 -objdir $(OBJDIR)/indep/c/ # # output in $(OBJDIR)/indep/c/complex_def.rule: # # # Using objdir $(OBJDIR)/indep/c/ from cmd line # # Variable containing the full path to the object: # complex_def.o := $(OBJDIR)/indep/c/complex_def.o # # # For each defined module, define the dir-stripped module name # # as the full path to the generated module: # A_DEF.mod := $(OBJDIR)/indep/c/A_DEF.mod # # B_DEF.mod := $(OBJDIR)/indep/c/B_DEF.mod # # # Variable containing the complete list of generated modules: # complex_def.o.mods := $(A_DEF.mod) $(B_DEF.mod) # # # Target-specific variable: # # objmods = the modules associated with the target list: # $(complex_def.o) \ # $(complex_def.o.mods): objmods := $(complex_def.o.mods) # # # Target-specific variable: # # objpath = the full path to the f90 obj file: # $(complex_def.o) \ # $(complex_def.o.mods): objpath := $(complex_def.o) # # # Target-specific variable: # # objbase = the f90 obj file stripped of directory component: # $(complex_def.o) \ # $(complex_def.o.mods): objbase := complex_def.o # # # The main target rule: # $(complex_def.o) \ # $(complex_def.o.mods): complex_def.f90 # $(COMPILE_F90) # # output in $(OBJDIR)/indep/c/complex_def.deps: # # # Extra dependencies of the obj file: # $(complex_def.o): $(C_DEF.mod) $(D_DEF.mod) $(E_DEF.mod) \ # $(F_DEF.mod) file1.h file2.inc file3.inc # #----------------------------------------------------------------------- # Here's what does the work (argument parsing while (@ARGV){     $arg=shift;     if ($arg =~ /^-src$/){         $filename=shift;     }     if ($arg =~ /^-objdir$/){         $objdir=shift;     } } if ( not $filename ) {     die(         "Expecting 1 to 2 arguments\n",         "Usage:\n",         "\t\tmkdepf90.perl -src srcfile [-objdir objdir]\n",         "srcfile is required, objdir is optional. objdir may have\n",         "a trailing slash -- it doesn't matter. If objdir is not\n",         "entered, the current working directory is used for output.\n",         "Output:\n",         "\tobjdir/srcbase.rule \& objdir/srcbase.deps\n"); } if ( $objdir ) {     $objdir =~ s/\/*$//; # remove trailing slashes     $objdir =~ s/\.//; # ignore "." directory     # $prefix stores the object directory without trailing slashes     if ( $objdir ) {         ($prefix=$objdir) =~ s/$/\//;     } } # Get the base part of the filename (remove everything after last .): ($base = $filename) =~ s/\.[^\.]+$//; # Strip everything up to last slash: $base =~ s/^.*\///; # Prepend the object path onto the objfile name, add .o extension ($objfile = $base) =~ s/.*/$prefix$&.o/; # Make the *.rule and *.deps filenames: ($rulefile = $base) =~ s/.*/$prefix$&.rule/; ($depsfile = $base) =~ s/.*/$prefix$&.deps/; # This variable contains the directory-stripped object file name with # a .o extension: ($objbase = $base) =~ s/$/.o/; undef @incs; undef @modules; undef @dependencies; undef @moddefs; undef @modlist; open(FILE, $filename) or die "Cannot open $filename: $!\n"; unlink $rulefile; open(OUTFILE,">" . $rulefile); # Search for includes and USE calls: while (<FILE>) {     /^\s*(\#|\?\?)*\s*include\s+[\"\']([^\"\']+)[\"\']/i && push(@incs, $2);     /^\s*use\s+([^\s,!]+)/i && push(@modules, &ModCase($1));     /^\s*module\s+([^\s,!]+)/i && push(@moddefs, &ModCase($1)); } # Define the MAKE macro $($objbase) as the full path to # the object. E.g., 'foo.o = /path/to/foo.o' print OUTFILE "# Variable containing the full path to the object:\n"; print OUTFILE "$objbase := $objfile\n\n"; $targlist = "\$($objbase)"; # If modules are defined, create macros for modules and # a combined object+module target rule: if (defined @moddefs) {     # @modlist stores module names with .mod on the end     foreach $module (&uniq(sort(@moddefs))) {         ($name = $module) =~ s/$/.mod/;         push(@modlist, $name);     }     # Redefine @moddefs to the new @modlist     @moddefs = @modlist;     # Redfine @modlist with each element of @moddefs surrounded by     # MAKE parens:     undef @modlist;     foreach $module (@moddefs) {       ($name = $module) =~ s/.*/\$\($&\)/;       push(@modlist, $name);     }     print OUTFILE "# For each defined module, define the dir-stripped module name\n";     print OUTFILE "# as the full path to the generated module:\n";     foreach $module (@moddefs) {       # Define the MAKE macro $(modnamebase.mod) as the full path to the       # module       print OUTFILE "$module := $prefix$module\n\n";     }     # This defines the macro $(objbase.o.mods) as the list of     # modules dependent on the object.     print OUTFILE "# Variable containing the complete list of generated modules:\n";     print OUTFILE "$objbase.mods := ";     $j = length($objbase) + 9;     &PrintWords($j, 0, @modlist);     print OUTFILE "\n\n";     $targlist = "\$($objbase) \\\n\$($objbase.mods)";     print OUTFILE "# Target-specific variable:\n";     print OUTFILE "# objmods = modules associated with the target list:\n";     print OUTFILE "$targlist: objmods := \$($objbase.mods)\n\n"; } print OUTFILE "# Target-specific variable:\n"; print OUTFILE "# objpath = full path to the f90 obj file:\n"; print OUTFILE "$targlist: objpath := \$($objbase)\n\n"; print OUTFILE "# Target-specific variable:\n"; print OUTFILE "# objbase = f90 obj file stripped of directory component:\n"; print OUTFILE "$targlist: objbase := $objbase\n\n"; print OUTFILE "# The main target rule:\n"; print OUTFILE "$targlist: $filename\n\t\$(COMPILE_F90)\n\n"; # Stop writing to .rule file, start writing to .deps file: close(OUTFILE); unlink $depsfile; open(OUTFILE,">" . $depsfile); # Print out extra dependencies for the object file beyond the default: if (defined @incs || defined @modules) {     foreach $module (&uniq(sort(@modules))) {         ($name = $module) =~ s/.*/\$\($&.mod\)/;         push(@dependencies, $name);     }     print OUTFILE "# Extra dependencies of the obj file:\n";     print OUTFILE "\$($objbase): ";     $j = length($objbase) + 5;     &PrintWords($j, 0, @dependencies, &uniq(sort(@incs))); } print OUTFILE "\n"; # Add newline to .deps file close(OUTFILE); # End of processing. Below are utility routines: #----------------------------------------------------------------------- # Internal text formatting functions, copied from makemake.perl. # Note that &ModCase should be modified to handle the module-name # generation format of your compiler. #----------------------------------------------------------------------- # &PrintWords(current output column, extra tab?, word list); --- print words # nicely # sub PrintWords {     local($columns) = 78 - shift(@_);     local($extratab) = shift(@_);     local($wordlength);     #     print OUTFILE "$_[0]";     $columns -= length(shift(@_));     foreach $word (@_) {         $wordlength = length($word);         if ($wordlength + 1 < $columns) {             print OUTFILE " $word";             $columns -= $wordlength + 1;         }         else {             #             # Continue onto a new line             #             if ($extratab) {                 print OUTFILE " \\\n\t\t$word";                 $columns = 62 - $wordlength;             }             else {                 print OUTFILE " \\\n\t$word";                 $columns = 70 - $wordlength;             }         }     } } #----------------------------------------------------------------------- # &ModCase(string); --- convert string into correct case for module names # sub ModCase {     local($string) = $_[0];     # Lowercase on non-UNICOS:     # $string =~ tr/A-Z/a-z/;     ## Uppercase on Cray UNICOS     $string =~ tr/a-z/A-Z/;     $string; } #----------------------------------------------------------------------- # &uniq(sorted word list); --- remove adjacent duplicate words # sub uniq {     local(@words);     foreach $word (@_) {         if ($word ne $words[$#words]) {             push(@words, $word);         }     }     @words; }

Top of Message | Previous Page | Permalink

JiscMail Tools


RSS Feeds and Sharing


Advanced Options


Archives

December 2023
February 2023
November 2022
September 2022
February 2022
January 2022
June 2021
November 2020
September 2020
June 2020
May 2020
April 2020
December 2019
October 2019
September 2019
March 2019
February 2019
January 2019
November 2018
October 2018
September 2018
August 2018
July 2018
May 2018
April 2018
March 2018
February 2018
January 2018
December 2017
November 2017
October 2017
September 2017
August 2017
July 2017
June 2017
May 2017
April 2017
March 2017
January 2017
December 2016
November 2016
October 2016
September 2016
August 2016
July 2016
June 2016
May 2016
April 2016
March 2016
February 2016
December 2015
November 2015
October 2015
September 2015
August 2015
June 2015
April 2015
March 2015
January 2015
December 2014
November 2014
October 2014
August 2014
July 2014
May 2014
April 2014
March 2014
February 2014
January 2014
December 2013
November 2013
July 2013
June 2013
May 2013
April 2013
February 2013
January 2013
December 2012
November 2012
October 2012
September 2012
August 2012
July 2012
June 2012
April 2012
March 2012
February 2012
January 2012
December 2011
November 2011
October 2011
September 2011
August 2011
July 2011
June 2011
May 2011
April 2011
March 2011
February 2011
January 2011
December 2010
November 2010
October 2010
August 2010
July 2010
June 2010
March 2010
February 2010
January 2010
December 2009
October 2009
August 2009
July 2009
June 2009
March 2009
February 2009
January 2009
December 2008
November 2008
October 2008
September 2008
August 2008
July 2008
June 2008
May 2008
April 2008
March 2008
February 2008
December 2007
November 2007
October 2007
September 2007
August 2007
July 2007
June 2007
May 2007
April 2007
March 2007
January 2007
2006
2005
2004
2003
2002
2001
2000
1999
1998
1997


JiscMail is a Jisc service.

View our service policies at https://www.jiscmail.ac.uk/policyandsecurity/ and Jisc's privacy policy at https://www.jisc.ac.uk/website/privacy-notice

For help and support help@jisc.ac.uk

Secured by F-Secure Anti-Virus CataList Email List Search Powered by the LISTSERV Email List Manager