Site-specific Configuration Files#
This chapter describes the design of the site-specific configuration files. It starts with the concept, and then includes some examples.
Concepts for site-specific setup#
Fab’s base class supports site-specific setup, and it is is based on
using a site name and a platform name.
For example, The UK Met Office traditionally
uses meto
as site name, and then a different platform name, e.g.
xc40
or ex1a
. The Fab base class uses a specific setup directory
based on the concatenation of these names. In the example above, this would be
site_specific/meto_xc40
or site_specific/meto_ex1a
.
The site and platform can be specified as command line option (see
Command Line Options). All these
directories are stored under the site_specific
directories
to keep the directory structure cleaner.
If no site name is specified, default
is used as site. And
similarly, if no platform is specified, default
is used as platform
(resulting e.g. in site_specific/meto-default
etc). If neither site
nor platform is specified, the name site_specific/default
is used.
Fab comes with a template for a site_specific
setup. It only
contains setting for the default
site.
Default configuration#
It is strongly recommended for each application to have a default configuration file, which will define for example compiler profiles, and typical compiler flags. Any site-specific configuration file should then inherit from this default, but can also enhance the setup done by the default.
from default.config import Config as DefaultConfig
class Config(DefaultConfig):
'''Make intel-classic the default compiler
'''
def __init__(self):
super().__init__()
tr = ToolRepository()
tr.set_default_compiler_suite("intel-classic")
# Add a new compiler to the ToolRepository. It is
# a compiler wrapper available for ifort and gfortran
# on this site.
for ftn in ["ifort", "gfortran"]:
compiler = tr.get_tool(Category.FORTRAN_COMPILER, ftn)
tr.add_tool(Tauf90(compiler))
Callbacks in configuration files#
The base class adds several calls to the site-specific configuration file, allowing site-specific changes to the build process. These callbacks are described here.
Constructor#
The constructor receives no parameter, and happens rather early in the
processing chain (see Defining site and platform), i.e. at a stage
where not even all command line options have been defined. Besides
general setting up the object, adding new tools to Fab’s
ToolRepository
can be done here.
get_valid_profiles
#
This method is called by FabBase
when defining the command line options.
It defines the list of valid compilation profile modes. This is used
in setting up Python’s ArgumentParser
to only allow valid arguments.
- Config.get_valid_profiles()
Determines the list of all allowed compiler profiles. The first entry in this list is the default profile to be used. This method can be overwritten by site configs to add or modify the supported profiles.
A well written default configuration file will take newly defined profiles into account and set them up automatically. See Adding new compilation profiles for an extended example.
handle_command_line_options
#
This method is called immediately after calling the application-specific
handle_command_line_options
method.
- Config.handle_command_line_options(args)
Additional callback function executed once all command line options have been added. This is for example used to add Vernier profiling flags, which are site-specific.
- Parameters:
args (argparse.Namespace) – the command line options added in the site configs
- Return type:
It allows site-specific changes based on the specified command line
options. An example is that selecting a hardware target (--host
command line option) like GPU or CPU will require different
compiler options. The following example will store all command
line options of the user, and use them later when setting up the
compiler:
def handle_command_line_options(self, args: argparse.Namespace) -> None:
# Keep a copy of the args, so they can be used when
# initialising compilers
self._args = args
update_toolbox
#
The update_toolbox
method is called after the Fab ToolBox
and BuildConfig
objects have been created. All command line
options have been parsed, and selected compilers have been added to
the ToolBox
.
- Config.update_toolbox(build_config)
Set the default compiler flags for the various compiler that are supported.
- Parameters:
build_config (
BuildConfig
) – the Fab build configuration instance- Return type:
Here is an example of defining the appropriate compilation profiles for all compilers and linkers:
def update_toolbox(self, build_config: BuildConfig) -> None:
for compiler in (tr[Category.C_COMPILER] +
tr[Category.FORTRAN_COMPILER] +
tr[Category.LINKER]):
compiler.define_profile("base", inherit_from="")
for profile in self.get_valid_profiles():
compiler.define_profile(profile, inherit_from="base")
This sets up a hierarchy where each of the valid compilation profiles
inherits from a base
profile. And they are defined for all
compilers, even if they might not be available. This will make sure
that using compilation modes work in a Fab compiler wrapper, since
it is possible that the wrapped compiler is not available, i.e.
not in $PATH
, but the wrapper is. Additionally, using
get_valid_profiles
also means that any additional profiles defined
from a derived class will automatically be created. If a different
hierarchy is requested (e.g. memory-profile
might want to inherit
from full-debug
, this needs to be updated in the inheriting
class).
After the profiling modes, a default
class should setup
all compilers (including the various flags for the different
compilation profiles). To continue the example from above,
shown here is the code that uses the saved command line options
from the user to setup flags for an Nvidia compiler:
def update_toolbox(self, build_config: BuildConfig) -> None:
setup_nvidia(build_config, self.args)
def setup_nvidia(build_config: BuildConfig,
args: argparse.Namespace) -> None:
tr = ToolRepository()
nvfortran = tr.get_tool(Category.FORTRAN_COMPILER, "nvfortran")
if args.openacc or args.openmp:
host = args.host.lower()
else:
# Neither openacc nor openmp specified
host = ""
flags = []
if args.openacc:
if host == "gpu":
flags.extend(["-acc=gpu", "-gpu=managed"])
else:
# CPU
flags.extend(["-acc=cpu"])
...
nvfortran.add_flags(flags, "base")