# `utopya_build` module

The `utopya_build` module provides classes to copy a source code to a work directory and build (compile) an executable. All classes are accesible through the top level `utopya` module.

- The `UtopyaBuild` class creates an instance of a top level object that, based on rcfile settings, performs all necessary tasks to build an executable. Instances from the following classes are used to perform these tasks.

  - The `UtopyaCopy` class is used to collect source code files and copy them into a work directory.

  - The `UtopyaConfigure` is used to configure the source code and create make files. This is done using instances from two other classes defined in this module:

    - The `UtopyaCompilerSettings` class is used to provide information on the compiler to be used, for example the compiler executable name(s) and useful compiler flags.

    - The `UtopyaLibSettings` class is used to provide library names and paths on the target machine.

- The `UtopyaMake` class compiles the source code and creates an executable.

The configuration of the objects is done using rcfile settings.

## Example script and settings

A script is provided to show how to build an executable from a Fortran source. The example builds the `SLM` (Simple Linear Model) provided with UTOPyA. The script uses UTOPyA Python classes from the `utopya_build` module to first copy the source code to a work directory, and then to configure and compile the code.

The exampe script is available as:

> bin/run-example-build

The example settings to build SLM are provided in:

> rc/example-build-slm.rc

These settings are loaded by default if the script is run without arguments:

    ./bin/run-example-build

where an example with explicit arguments would be:

    ./bin/run-example-build rc/example-build-slm.rc --rcbase=slm

By changing the name of the rcfile and the base key for the settings the script could be used to build another souce code too.

## Class hierarchy

The classes provided by this module have been derived with the following hierchy:

- `UtopyaRc`

  - `UtopyaBuild`

  - `UtopyaCopy`

  - `UtopyaConfigure`

  - `UtopyaCompilerSettings`

    - `UtopyaCompilerSettingsFortran`

    - `UtopyaCompilerSettingsF2py`

  - `UtopyaLibSettings`

  - `UtopyaDependencies`

  - `UtopyaMake`

## Classes

class utopya_build.UtopyaBuild(rcfile, rcbase='', env={}, new=None, njob=1)

  Bases: `UtopyaRc`

  Base class for objects that build (compile) an executable or library from a source code.

  For initialization, provide the name of the rcfile with the settings that define the tasks to be done. Optionally, provide an ‘rcbase’ as prefix for the settings to be used, and an environment dictionairy ‘env’ for variable substitutions; see the initialization of the `UtopyaRc` class for details and the necessary configuration of the logging system.

  A number of tasks could be performed common for compiling a source code. Each of these taskes is performed if a dedicated rcfile flag is enabled.

  - The following flag defines if a source code should be copied, for example to a temporary build directory in order not to polute the source with object files:

        [<rcbase>.]build.copy              :  True

    If enabled, an instance of the `UtopyaCopy` class is created. At initialization, the optional argument ‘new’ is passed to allow a forced cleanup of an existing build by the calling method.

  - The following flag defines if a source code should be configured, e.g. creation of Makefiles. If enabled, an instance of the `UtopyaConfigure` class is created:

        [<rcbase>.]build.configure         :  True

  - Define if an executable should be made:

        [<rcbase>.]build.make              :  True

    If enabled, an instance of the `UtopyaMake` class is created. At initialization, the optional argument ‘njob’ is passed to allow making in parallel by multiple jobs.

class utopya_build.UtopyaCopy(rcfile, rcbase='', env={}, new=None)

  Bases: `UtopyaRc`

  Base class for objects that collect and copy source codes into a work directory.

  For initialization, provide the name of the rcfile with the settings that define the tasks to be done. Optionally, provide an ‘rcbase’ as prefix for the settings to be used, and an environment dictionairy ‘env’ for variable substitutions; see the initialization of the `UtopyaRc` class for details and the necessary configuration of the logging system.

  If the ‘new’ flag is present it overwrites the ‘*.copy.new’ flag; set to ‘True’ to remove an existing build directory if present.

  The following example shows the required settings for an rcbase ‘optm’. The content of the directories:

      sources/model/base/src
                        /py
      sources/model/special/src
                           /py

  should be copied into:

      ${SCRATCH}/model/build/src
                            /py

  In addition, also the files in:

      sources/other

  should be copied into:

      ${SCRATCH}/model/build/src

  The comment should describe the purpose of each setting:

      ! Sub directories to be maintained ;
      ! leave empty if no subdirs are defined:
      optm.copy.subdirs             :  src py

      ! List of source directories to be copied;
      ! files in later directories overwrite previously copied versions:
      optm.copy.dirs                :  sources/model/base  sources/model/special

      ! extra source directories to be copied into "src":
      optm.copy.src.dirs            :  sources/other

      ! directories to be inlcuded in copy,
      ! otherwise only files are copied:
      optm.copy.incdirs             :  include

      ! Skip files matching these filename patterns
      ! (tempoary editor files, compiled modules, etc)
      optm.copy.skip                :  .#* *~ *.pyc

      ! An optional flag is present to remove particular extensions
      ! from the copied filename. These extensions start with '__',
      ! and are used in certain application to distinguish different
      ! source files from eachother. With this flage enabled,
      ! the source file "work__special.F90" will be copied to "work.F90":
      optm.copy.remove__ext         :  True

      ! Prefix for destination of source and script files
      ! (base path for subdirectories src, py, etc) ;
      ! this should be an absolute path,
      ! use ${PWD} for the present dir if necessary:
      optm.copy.prefix              :  ${SCRATCH}/model/build

      ! The above defined refix could be extended with extra keywords,
      ! fore example 'build_optim-all_mpi' instead of just 'build'.
      ! While develloping it is sometimes useful to have different
      ! builds next to eachother to avoid a complete rebuild when
      ! a simple switch from optimal to debug compiler flags is needed,
      ! or one would like to test compilation with and without MPI.
      ! Provide here a space separated list of extension keywords,
      ! usually these are copied from other settings sush as compile options:
      optm.copy.prefix.extensions   :  optim-all mpi

      ! remove existing build directory if present ?
      optm.copy.new                 :  False

      ! optionally define a filename to which the evaluated rcfile settings will be written:
      optm.copy.rcwrite             :  ${SCRATCH}/model/build/optm-runtime.rc

class utopya_build.UtopyaConfigure(rcfile, rcbase='', env={})

  Bases: `UtopyaRc`

  Base class to perform tasks that configure a source code, which is mainly the creation of a suitable Makefile. The tasks are partly performed by the following methods:

  - `ConfigureCompilerFlags()`

  - `ConfigureMacros()`

  - `ConfigureChecks()`

  - `ConfigureLibs()`

  - `ConfigureMakeDependencies()`

  Derived classes might re-define some of these if the standard versions are not sufficient. The following overview shows the rcfile settings needed for the configuration, and the methods that perform parts of the taks.

  - First specify the location of the source directory; typically this is the place to which `UtopyaCopy` has copied the source files:

        ! where to configure ?
        [<rcbase>.]configure.dir            :  /work/model/src

  - Specify the name of the class that should provide compiler settings such as the compiler executable name(s) and the compilation flags. This class should be derived from UTOPyA’s `UtopyaCompilerSettings` class, for example the `UtopyaCompilerSettingsFortran` class. Also specify the arguments for initialization; for the base class this is a rcfile name and keyword prefix:

        ! class for compiler settings, and arguments for initialization:
        [<rcbase>.]configure.compiler.class     :  utopya.UtopyaCompilerSettingsFortran
        [<rcbase>.]configure.compiler.args      :  rcfile='configure.compiler.rc', rcbase=''

    An instance of the specified class is created, and the `UtopyaCompilerSettingsFortran.GetCompilers()` method of the instance is called to obtain the compiler(s) and linker names. The instance is also passed as argument to the `ConfigureCompilerFlags()` methode.

  - The following flags specify if a source should be configured for running in parallel in an MPI or OpenMP environment. Their value is for example passed as arguments to the `UtopyaCompilerSettingsFortran.GetCompilers()` method of (class derived from) the `UtopyaCompilerSettings` class and to the `ConfigureCompilerFlags()` method:

        ! enable parallel computing?
        [<rcbase>.]configure.enable.mpi     :  False
        [<rcbase>.]configure.enable.openmp  :  False

  - See the description of the `ConfigureCompilerFlags()` method for the setings needed to select the appropriate compiler flags.

  - See the `ConfigureMacros()` method for the settings needed for definition of preprocessing macro’s.

  - If the macro definitions should be included in the compiler flags, e.g.:

        f90 .. -Dwith_test -D__VALUE__=12 ..

    then enable the following flag in the rcfile:

        [<rcbase>.]configure.macro.define_flags  :  True

    A line with the compiler flags is obtained from a call to the `UtopyaCompilerSettings.GetMacroDefinitionFlags()` method of the `UtopyaCompilerSettings` class (or one of its derivatives), which takes the the list of macro’s returned by the `ConfigureMacros()` method as argument.

  - Optionally specify a list of depricated or un-used files that will be removed from the build directory:

        [<rcbase>.]configure.remove  :  file1.F90 file2.F90 ...

    If files should only be removed if a certain macro is defined, use:

        [<rcbase>.]configure.remove.ifdef.with_test  :  file3.F90 file4.F90 ...

    If files should be removed if a certain macro is not defined, use:

    > [<rcbase>.]configure.remove.ifndef.with_test : test1.F90 test2.F90 …

  - See the `ConfigureChecks()` method for the settings needed to perform some quick checks on the source code.

  - The `ConfigureLibs()` method is called to obtain the compiler and linker flags related to use of external libraries.

  The methode will write two textfiles that could be included in the final Makefile. It is up to the user to write a suitable Makefile that actually does this. The name of the included files should be spefied in the settings:

  - The compiler flags will be written to a textfile with a name specified by:

        configure.flags.includefile   :  Makefile_compiler_and_flags

    The written file might look like:

        #
        # include file with compiler names and flags for Makefile.
        #

        # compiler and linker:
        FC = f90
        F77 = f77
        LINKER = f90

        # compile flags:
        FFLAGS = --implicit-none -O2 -g -Dwith_test -I/opt/netcdf4/include

        # linker flags:
        LDFLAGS = -L/opt/netcdf4/lib -lnetcdf -L/usr/lib -ljasper

  - Optionally, explicit compilation rules are written to a file specified by:

        configure.rules.includefile   :  Makefile_rules

    If this file is defined, it will be created and filled with compilation rule(s) that differ from the default rule(s). This could be used to specify that for some files implicit typing is allowed while the default is to not allow that, or to disable optimizations if that causes failues for some files. The created include file might look like:

        #
        # include file with explicit compile rules for Makefile.
        #

        oldstuff.o:
          $(FC) -c -o $@ $(FFLAGS) --implicit-yes $<

        olderstuff.o:
          $(FC) -c -o $@ $(FFLAGS) --implicit-yes -O0 $<

    In this example, the ‘–implicit-yes’ and ‘-O0’ flags are inserted after the expansion of the default ‘$(FFLAGS)’, which usually means that similar setting in ‘$(FFLAGS)’ are over-rulled.

    To specify which objects require an explicit rule, and which flags should be added, use the following configuration:

        ! specify a list with objects and additional flag groups:
        !
        !    objectfile1 : flaggroup1 ;  objectfile2 : flaggroup2a flaggroup2b ; ...
        !
        configure.rules.explicit : \
             oldstuff.o    :  implicit \
             olderstuff.o  :  implict optim-none

    See the description of the `ConfigureCompilerFlags()` method for how the flag group keywords ‘implicit’ etc are expanded to actual flags ‘–implicit-yes’ etc.

  - Makefile dependencies might be created automatically if the following flag is enabled:

        ! create depencencies (True|False) ?
        configure.makedep        :  True

    If enabled, the method `ConfigureMakeDependencies()` will be called. Depending on the settings and the sources found, this might for example write a file ‘Makefile_deps’ that will be included in the main ‘Makefile’.

  The user defined Makefile could now look like:

      #
      # Makefile
      #

      # compiler name and flags:
      include "Makefile_compiler_and_flags"

      # default rule for how to form object files from f90 source:
      %.o: %.f90
        $(FC) -c -o $@ $(FFLAGS) $<

      # explicit rules for some files:
      include "Makefile_rules"

      # dependencies:
      #   myprog.o: mytool.o
      # here include dependencies from a file (created by configure?)
      include "Makefile_deps"

      # objects:
      OBJS = myprog.o mytool.o

      # target executable:
      myprog.x: myprog.o
        $(LINKER)  -o $@ $(OBJS) $(LDFLAGS)

  ConfigureCompilerFlags(compiler, mpi=False, openmp=False)

    Return compiler flags to be used. Arguments:

    - compiler : instance of the `UtopyaCompilerSettings` class

    Optional arguments:

    - mpi : bool flags to enable compilation with MPI compilers;.

    - openmp : bool flags to enable compilation with OpenMP support.

    Return values:

    - fflags : str object with Fortran compiler flags, for example ‘-O2 -g’ ;

    - ldflags : str object with linker flags

    In the settings, specify a space seperated list of what is called ‘compiler flags groups’:

        [<rcbase>.]configure.flags.groups     :  optim-none check-all

    Each group name is passed as argument to the `UtopyaCompilerSettings.GetFlags()` method, which should then return the the actual flags to be passed to the compiler as defined in the compiler specific rcfile settings.

    The following group names are added automatically:

    - ‘default’ : to insert default flags, for example to disable implicit typing;

    - ‘mpi’ if MPI is enabled;

    - ‘openmp’ if OpenMP is enabled.

  ConfigureMacros()

    The source files might contain pre-processing statements such as:

        #ifdef with_test
        print *, 'this is test output'
        #endif

        #if __VERSION__ == 12
        call Work_v12()
        #else
        call Work()
        #endif

    In this example, the print statement is only included in the actual code if the pre-processing macro ‘with_test’ is defined, and which version of the work routine is called depends on the value of the ‘__VERSION__’ macro (if defined). Pre-processing macros could be defined in the following ways:

    - The compiler might automatically define certain macro’s. Typically some macro’s are defined to hold the compiler version, and if OpenMP is enabled then the macro ‘_OPENMP’ is defined.

    - A macro definition flag could be added to the compiler flags:

          f90 .. -Dwith_test -D__VALUE__=12 ..

    - A macro definition statement could be included in the source file:

          #define with_test
          #define __VALUE__ 12

      Since preprocessing macro’s are often used in more than one source file, it is common practice to collect all macro definitions in a header file (in Fortran called an ‘include’ file), and incorporate this in each source file:

          #include macros.inc

    The method using header (include) files is prefered since it ensures that a source code could be compiled later on again using exactly the same macro definitions. UTOPyA therefore provides a mechanism to create macro definition include files.

    First specify a list of macro group names, for example one name for each sub-program in a source:

        [<rcbase>.]configure.macro.groups       :  model optimizer

    For each of the groups, specify the following settings:

    - A list with all macro’s in the group.

    - Optionally the name of a header file; if provided, this file is created in the source directory and filled with appropriate ‘#define’ commands. The corresponding ‘#include’ statement should be added to each source file that uses the macro’s in the group.

    An example for the settings for the above macro groups

        [<rcbase>.]configure.macro.model.all         :  with_test without_error
        [<rcbase>.]configure.macro.model.hfile       :  model.inc

        [<rcbase>.]configure.macro.opimizer.all      :  DEBUG __VERSION__
        [<rcbase>.]configure.macro.optimizer.hfile   :  optim.inc

    Finally specify a list of the macro’s that actually need to be defined in a certain application. The macro’s defined should appear in at least one of the lists with all macro’s for a group:

        [<rcbase>.]configure.macro.define       :  with_test __VERSION__=12

    With the above settings, this method will:

    - check if the requested macro’s are supported;

    - write header files if necessary.

    Return values:

    - list with character strings holding the defined macro’s (and values):

          [ 'with_test', '__VERSION__=12' ]

    - list with character strings holding the supported macro’s:

      [ ‘with_test’, ‘with_fix’, ‘__VERSION__’ ]

  ConfigureChecks()

    Apply checks on source code files.

    Provide a space seperated list with keywords for the checks to be applied:

        ! list with keywords for checks to be applied:
        [<rcbase>.]configure.checks      :  go_inc unknown_macro strange_file

    For each keyword, provide settings with:

    - a short message to describe the test;

    - the filename pattern to select the files on which the test should be applied;

    - a filename pattern to exclude some files;

    - a python boolean expression applied to each line of the file; the line itself is stored in a string variable named ‘line’;

    - a help text to be displayed in case a warning is issued; include ‘`\n`’ for newlines.

    Example for check if all `go*.F90` files do include `go.inc`:

        [<rcbase>.]configure.check.go_inc.msg       :  Test on GO files that do no not include "go.inc" ...
        [<rcbase>.]configure.check.go_inc.files     :  go*.F90
        [<rcbase>.]configure.check.go_inc.skip      :
        [<rcbase>.]configure.check.go_inc.test      :  not line.startswith('#include "go.inc"')
        [<rcbase>.]configure.check.go_inc.help      :  \n\
              All GO files should include "go.inc" in the header.\n\
              This include file is filled by the scripting with '#define' pre-processing macros.

    If for one of the lines in a source file the test evaluates to ‘True’, a warning is issued. If the test expression starts with `not`, a warning is issued if none of the lines evaluates to `True` for the test after the `not`.

    The following check will warn for unexpected file names:

        ! source directories should not be polluted with strang files
        [<rcbase>.]configure.check.strange_file.msg       :  Test on strange file names ...
        [<rcbase>.]configure.check.strange_file.files     :  *
        [<rcbase>.]configure.check.strange_file.skip      :  *.inc *.f *.F *.f90 *.F90 Makefile* *.o *.mod *.x
        [<rcbase>.]configure.check.strange_file.test      :  True
        [<rcbase>.]configure.check.strange_file.help      :  \n\
              Source directories should not be polutted with strange files\
              such as backup files created by editors.

    Some checks are too complicated to be defined with the above settings. The following special test is used to check the macro definitions:

        ! check on unknown macros used in '#if[n]def' lines,
        ! but not listed in the configuration settings:
        [<rcbase>.]configure.check.unknown_macro.msg :  Test for unsupported macros ...

    Another special test could be used to check on files that occure more than once but with different extension:

        ! check on files with same basename but different extensions,
        ! for example 'afile.f90' and 'afile.F90'
        [<rcbase>.]configure.check.multiple_ext.msg  :  Test on multiple extensions ..

    Use the following flag to raise an error instead of a warning:

        [<rcbase>.]configure.check.error   :  True

  ConfigureLibs(macros=[])

    Return compiler flags related to external libraries.

    Optional arguments:

    - macros : list object holding character strings with defined macros as returned by the `ConfigureMacros()` methode.

    Return values:

    - fflags : str object with Fortran compiler flags, for example:

          '-I/opt/netcdf4/include'

    - ldflags : str object with linker flags, for example:

          '-L/opt/netcdf4/lib -lnetcdf -L/usr/lib -ljasper'

    In the settings, first specify the name of the class that should provide library settings such as the compile and link flags. This class should be derived from UTOPyA’s `UtopyaLibSettings` class. Also specify the arguments for initialization, for the base class this is an rcfile name and keyword prefix. See the documentation of the class for the settings that need to be present in this rcfile; these are usually machine dependend:

        ! class for library settings, and arguments for initialization:
        [<rcbase>.]configure.libs.class     :  utopya.UtopyaLibSettings
        [<rcbase>.]configure.libs.args      :  rcfile='machine.rc', rcbase=''

    An instance of the specified class is created, and the `UtopyaLibSettings.GetFlags()` method of the instance is called to obtain the compile and link flags.

    Then specify a list with names (keywords) for all supported external libraries:

        configure.libs.all    :  netcdf4 jasper hdf5 sz z

    The order of the names should be from ‘high’ level to ‘low’ level (a high level library might depend on a low level library). This order is important for linking, and should therefore be specified.

    Finaly specify a list with the names of the libraries that should actually be linked; the order is not important here:

        [<rcbase>.]configure.libs      :  jasper netcdf4

    If the optional ‘macros’ list with macro definitions is passed as argument, the settings are scanned for a specification of libraries that should be linked if a certain macro is defined:

        [<rcbase>.]configure.libs.ifdef.with_lapack   :  lapack blas
        [<rcbase>.]configure.libs.ifdef.with_mkl      :  mkl

    This feature could be used for codes that depend on libraries that are not always available in the same way on different machines. For example, when using Intel compilers, optimizer versions of BLAS and LAPACK are available through the Math Kernel Library (MKL).

  ConfigureMakeDependencies(define_flags=False, macros_defined=[])

    Create makefile dependencies based on settings and sources that are found. The dependencies will be written to file that should be included in the main Makefile.

    The following (optional) arguments could be set by the calling method:

    - Set the ‘define_flags’ bool to ‘True’ if source code contains pre-processing macro’s that should be defined by compiler flags, e.g.:

          f90 .. -Dwith_test -D__VALUE__=12

      The depency generator needs to be aware of this to ensure that dependencies are defined for the final pre-processed source.

    - The ‘macros_defined’ list should contain the macro definitions that are needed when the ‘define_flags’ argument is ‘True’.

    The following rcfile setting defines the name of the file that will contain the dependencies:

        ! include file to be written for Makefile:
        configure.makedep.includefile     :  Makefile_deps

    An ojbect derived rom the `UtopyaDependencies` class is used to create the dependency lines. The name of the class and the initilization arguments are read from the settings:

        ! class for library settings, and arguments for initialization:
        [<rcbase>.]configure.makedep.class     :  utopya.UtopyaDependencies
        [<rcbase>.]configure.makedep.args      :  rcfile='build.rc', rcbase=''

    An instance of the specified class is created, and the `UtopyaDependencies.GetLines()` method of the instance is called to obtain the dependency lines. The arguments for this method are read from the following settings:

        ! dependencies are created for files matching the pattern:
        configure.makedep.files           :  *.[Ff] *.[Ff]90

        ! define explicit dependencies for some objects,
        ! in case these are not recoqnized by 'makedep',
        ! for example dependencies on object files that are no modules;
        ! specifiy ';' seperated lines of target and object list seperated by ':' :
        !    target-object1 : object1 object2 ... ;
        !    target-object2 : object3 object4 ...
        configure.makedep.explicit        :

    After the dependency lines are obtained, the lines that define the linking of the executable(s) can be formed. The name(s) of the executables are read from the following setting:

        ! Which executables to be build ?
        ! Provide ';' seperated list of executable names,
        ! eventually followed by the basename of the source file
        ! with the main program if this is different from the
        ! basename of the executable. For example, the following
        ! tells to make executables 'test.x' and 'boe.x',
        ! where the first is to be compiled from a 'main.F90' or so,
        ! and the seccond is assumed to be compiled from 'boe.F90':
        configure.makedep.exec            : test.x main ; boe.x

    With this information, the make-includefile is written, which could look like:

        boe.o: boe.F90 tools.o
        driver.o: driver.F90 tools.o
        main.o: main.F90 driver.o tools.o
        tools.o: tools.F90

        test.x: main.o
                $(LINKER) -o $@ main.o driver.o tools.o $(LDFLAGS)

        boe.x: boe.o
                $(LINKER) -o $@ boe.o tools.o $(LDFLAGS)

class utopya_build.UtopyaCompilerSettings(rcfile=None, rcbase='', env={}, urc=None)

  Bases: `UtopyaRc`

  Base class for objects that should return compiler specific settings defined in a rcfile.

  GetCompilers(mpi=False, openmp=False)

    Returns a dictionairy with compiler names that will be used in the Makefile and their value:

        { 'FC' : 'f90', 'F77' : 'f77', 'LINKER' : 'f90' }

    The names of the compilers/linkers might be defined based on the `mpi` and/or `openmp` flags.

    This template returns some default settings.

  GetFlags(name)

    Return compiler flags for specified name. Empty for this base class.

    Returns two str objects:

    - fflags : flags passed to fortran compiler

    - ldflags : flags passed to linker

  GetMacroDefinitionFlags(macros)

    Return a line with command line arguments to define preprocessing macro’s. Such a line probably looks like:

        -Dwith_this_flag -Dwith_that_flag ...

    Pass a list with macro’s to be defined as argument.

    The following setting should contain the macro definition flag of the compiler:

        [<rcbase>.]configure.compiler.defineflag    :  -D

class utopya_build.UtopyaCompilerSettingsFortran(rcfile=None, rcbase='', env={}, urc=None)

  Bases: `UtopyaCompilerSettings`

  Class with methods that return Fortran compiler settings.

  GetCompilers(mpi=False, openmp=False)

    Return actual names of compiler and linker to be used in make file. These names might depend on the flags that enable MPI and/or OpenMP enabled. The dictionairy that is returned has elements for the Fortran compiler, the linker (probably the same value), and for backwards compatability a Fortran77 specific version:

        { 'FC' : 'f90', 'F77' : 'f77', 'LINKER' : 'f90' }

    The names for compiler and linker are assumed to be the same, but his might be changed by derived classes.

    The name of the compiler/linker with MPI enabled is often defined by the MPI wrapper (MPICH, OpenMPI), but some compiler manufactures provide their own wrappers.

    OpenMP is either enabled by passing a flag to the standard compiler command (–openmp), but some compiler families use an alias instead.

    To support all these different styles, this method requires that the rcfile contains explicit names for all four possible combinations with/without MPI/OpenMP:

        ! fortran compiler:
        [<rcbase>.]configure.compiler.fc                       :  gfortran
        [<rcbase>.]configure.compiler.fc.openmp                :  gfortran

        ! compilers for MPI programs:
        [<rcbase>.]configure.compiler.fc.mpi                   :  mpif90
        [<rcbase>.]configure.compiler.fc.mpi.openmp            :  mpif90

        ! optional: f77 compiler (default same as fc):
        [<rcbase>.]configure.compiler.f77                      :  g77

  GetFlags(name)

    Return compiler flags for specified name. Example settings for name `optim-fast`:

        [<rcbase>.]configure.compiler.optim-fast.fflags        :  -O3
        [<rcbase>.]configure.compiler.optim-fast.ldflags       :

    Returns two str objects:

    - fflags : flags passed to fortran compiler

    - ldflags : flags passed to linker

  GetMacroDefinitionFlags(macros)

    Return a line with command line arguments to define preprocessing macro’s. Such a line probably looks like:

        -Dwith_this_flag -Dwith_that_flag ...

    Pass a list with macro’s to be defined as argument.

    The following setting should contain the macro definition flag of the compiler:

        [<rcbase>.]configure.compiler.defineflag    :  -D

class utopya_build.UtopyaCompilerSettingsF2Py(rcfile=None, rcbase='', env={}, urc=None)

  Bases: `UtopyaCompilerSettings`

  Class with methods that return F2Py compiler settings. The purpose of the `F2Py` (Fortran to Python interface generator) is to compile Fortran programs and create a Python interface to them. `F2Py` is an element of the Python’s `Numpy` module, and therefore by default available in most distributions.

  GetCompilers(mpi=False, openmp=False)

    Return actual names of compiler and linker to be used in make file. The dictionairy that is returned has only one element:

        { 'F2PY' : 'f2py' }

    The name of f2py compiler is read from the following rcfile setting:

        ! f2py compiler:
        [<rcbase>.]configure.compiler.f2py   :  f2py

    Flags for compilation with MPI and/or OpenMP are ignored.

  GetFlags(name)

    Return compiler flags for specified name. Example settings for name `optim-fast`:

        [<rcbase>.]configure.compiler.optim-fast.fflags        :  -O3
        [<rcbase>.]configure.compiler.optim-fast.ldflags       :

    Returns two str objects:

    - fflags : flags passed to fortran compiler

    - ldflags : flags passed to linker

  GetMacroDefinitionFlags(macros)

    Return a line with command line arguments to define preprocessing macro’s. Such a line probably looks like:

        -Dwith_this_flag -Dwith_that_flag ...

    Pass a list with macro’s to be defined as argument.

    The following setting should contain the macro definition flag of the compiler:

        [<rcbase>.]configure.compiler.defineflag    :  -D

class utopya_build.UtopyaLibSettings(rcfile=None, rcbase='', env={}, urc=None)

  Bases: `UtopyaRc`

  Base class for objects that should return library settings defined in a rcfile.

  GetFlags(name)

    Return library flags for specified name. Example settings for name ‘netcdf’:

        [<rcbase>.]configure.lib.netcdf.fflags     : -I${NETCDF_PREFIX}/include
        [<rcbase>.]configure.lib.netcdf.ldflags    : -L${NETCDF_PREFIX}/lib -lnetcdff -lnetcdf

    Return values:

    - fflags : flags passed to Fortran compiler

    - ldflags : flags passed to linker

class utopya_build.UtopyaDependencies(rcfile=None, rcbase='', env={}, urc=None)

  Bases: `UtopyaRc`

  Base class for automatic generation of Fortran source file dependencies.

  GetLines(flags='', files='*.[Ff]*', expl={})

    Return list with dependency lines for Fortran source.

    This method is actually an interface to the ‘makedepf90’ tool:

    - MakeDepF90 repository

    To check if the tool is available, and for help on it’s usage, try:

        makedepf90 --help

    The location of the tool could be specified (optionally) in the rcfile:

        makedepf90     :   /opt/makedepf90/bin/makedepf90

    Optional arguments:

    - ‘flags’: String with flags passed to ‘makedepf90’ tool,

        for example macro definition:

            -Dwith_some_library -D__VALUE__=12 ...

    - ‘files’: String with (list of) filename filters for source files for which dependecies should be made. Default is to use all Fortran files in the current directory.

    - ‘expl’: Dictionairy with explicit dependencies that are otherwise not recoqnized by ‘makedepf90’, for example files that are no modules. Example:

          { 'main.o' : ['tools.o','other.o',..], ... }

    Returns a list with dependency lines for a Makefile. Each line has the form:

        'file.o : dependency.o ...'

  GetObjects(main)

    Return character string of objects on which a main program depends.

    Arguments:

    - ‘main’: Basename of the main program, e.g. ‘myprog’ . The corresponding objectfile ‘myprog.o’ is the first value on the returned list. Other object names are added given the dependency information.

class utopya_build.UtopyaMake(rcfile, rcbase='', env={}, njob=1)

  Bases: `UtopyaRc`

  Base class to perform tasks to make an executable:

  - Change to source directory.

  - Make target.

  Example of rcfile settings:

      ! where to make?
      [<rcbase>.]make.dir             :  appl/model/src/

      ! targets to be build:
      [<rcbase>.]make.targets         :  model.x install

      ! Make command to be used for each of the targets;
      ! template '%{target}' is filled with the target name,
      ! and '%{njob}' is filled from the initialization argument:
      [<rcbase>.]make.target.model.x  :  make --file=Makefile --jobs=%{njob} %{target}
      [<rcbase>.]make.target.install  :  make --file=Makefile %{target}