6. BUILDING PMODULES

6.1. Introduction

In the simplest case adding a Pmodule can be done by adding a modulefile to a certain directory. Below is an example of a "do nothing" modulefile in the group Sandbox.

Example: /opt/psi/Sandbox/modulefiles/null/1.0.0
#%Pmodule

module-whatis           "does absolutely nothing"
module-maintainer       "Achim Gsell <achim.gsell@psi.ch>"
module-license          "MIT"

module-help     "
This is a do nothing module.
"

In addition to the modulefile a corresponding configuration file - defining for example the release stage - should be installed in the same directory as the modulefile. See next section for a detailed documentation of configuration files.

Example: configuration file /opt/psi/Sandbox/modulefiles/null/.release-1.0.0 for the null/1.0.0 module
stable

Modulefiles are written in the Tool Command Language (Tcl). See section [modulefiles] for a detailed documentation. In Pmodules version 1.1.16 and newer modulefiles written in Lua are also supported but with some limitations.

The above example doesn’t provide any software. In the following example we show a simple 'HelloWorld' module with a 'Hello, world!' program.

Example: modulefile /opt/psi/Sandbox/modulefiles/HelloWorld/1.0.0:
#%Pmodule

module-whatis           "'Hello, world!' module"
module-maintainer       "Achim Gsell <achim.gsell@psi.ch>"
module-license          "MIT"
module-url              "http://pmodules.gitpages.psi.ch"

module-help     "
The module provides a 'Hello, world!' program.
"
Example: configuration file /opt/psi/Sandbox/modulefiles/HelloWorld/.release-1.0.0 for the HelloWorld/1.0.0 module
unstable
Example: /opt/psi/Sandbox/HelloWorld/1.0.0/bin/hello:
#!/usr/bin/env python3

print("Hello, world!")

In principal, it is possible to install modules 'by hand' by installing the software, module- and configuration files at the right place. For reproducibility, documentation and re-usability it is highly recommended to write build-recipes for each module or - at least - a README and host everything on a PSI Gitlab server.

Overall the main cases for building modules are:

  • Building a module from source. This is the most common case. Examples: git, gnuplot, openmpi, hdf5, …​
    In this case it is highly recommended to code each step - usually download, configure, compile and install - in a script, write a modulefile and a configuration file.

  • Building a module with a binary package. Examples: Intel compiler, Matlab, ANSYS, Mathematica, …​
    In this case best practice is to install the package by hand and write a "dummy" build script, a modulefile and a configuration file. If it is possible and simple to script the installation, code this in the build script. If you cannot easily script the installation, document each step in a README file.

In any case: keep everything in a Git repository on either gitlab.psi.ch or git.psi.ch. The Git repository for the groups Tool, Programming, Compiler, HDF5, HDF_serial and System is Pmodules/buildblock. Other groups can be hosted anywhere. But to keep everything together a project in Pmodules might be a good choice.

Many modules for the Pmodules environment have to be build from source. The building plan of a module is coded in a so called build-block. A build-block consists at least of a build-script with instruction how to download, compile and install a dedicated piece of software, a modulefile and a configuration file.

To avoid problems with dependencies to system libraries or installed software it is best practice to build modules on dedicated systems. At PSI:

  • For modules which should be able to run on all RHEL7 and newer systems, use the system pmod7.psi.ch. If you need access to the system please contact achim.gsell@psi.ch.

  • Merlin6 specific modules - like openmpi, mpich and modules depending on them - must be compiled on a Merlin6 login node.

  • Modules specific for a beamline should be compiled on a Ra login node with RHEL8 or a dedicated RHEL8 beamline system.

If you build a module on RHEL7, it might not run on RHEL8 due to newer versions of system libraries. In must cases this can be solved by installing these system libraries in the module itself and setting the RPATH. The recommended path in this case is $PREFIX/lib/system whereby $PREFIX is the installation prefix of the software.

In the next section we describe how to write build recipes.

6.2. Predefined variables

Since we use BASH for our build-scripts and Tcl for modulefiles, we use BASH/Tcl syntax for variables in this documentation. So, if VARIABLE is the name of a variable, $VARIABLE the value of it.

In modulefiles and build-scripts the following variables are predefined:

P

the name of the module

V

the module version. The version number consists of a major- and a minor version number, a patch-level and a release number. All numbers but the major version number are optional. Major-, minor number and patch-level are separated by dots, the release number by a minus. Example: 1.0.3-2

V_MAJOR

the major version number. Example: 1

V_MINOR

the minor version number. Example: 0

V_PATCHLVL

the patch-level. Example: 3

V_RELEASE

the release number. Example: 2

V_PKG

version number without release.

GROUP

group the module is in.

PREFIX

installation prefix of the module.

PMODULES_ROOT

root of Pmodules installation. At PSI this is /opt/psi.

In build-scripts the following variables are predefined too:

TEMP_DIR

directory for temporary files.

SRC_DIR

directory of unpacked sources, set to $PMODULES_TMPDIR/$P-$V/src

BUILD_DIR

build directory, set to $PMODULES_TMPDIR/$P-$V/build

BUILDBLOCK_DIR

Directory where the build-script is in.

BUILD_SCRIPT

Name of the build script.

SYSTEM,OS

Can be set with the --system option. The value defaults to the string returned by uname -s. The use of OS is obsolete.

In modulefiles the following variables are predefined too:

name

Same as P (obsolete, for historical reasons).

version

Same as V (obsolete, for historical reasons).

group

The group the module is member of.

6.3. Building a Pmodule

To simplify the building of modules, Pmodules has a simple build system. The build system of Pmodules is far less powerful than the build system of Spack or NixOS, but fulfills its purpose for many modules.

Perform the following steps for a new Pmodule:

  1. Create a directory with the name of the module.

  2. Write the configuration file. See section Writing build configuration files.

  3. Write the modulefile. See section modulefiles.

  4. Code all required steps to install a software in a script. If it is not possible to script everything, code as much as possible and document additional steps in a README file. See section Writing build scripts.

The first - and sometimes also to most challenging - step in building a Pmodule from source is to learn how to download, configure, compile and install a software package and dependencies the package has. The next steps are writing a configuration file, a modulefile and a script to download, configure, compile and install it.

In general the following steps needs to be performed:

prepare

Download, unpack, apply patches (optional). Downloading the software might not be scriptable due to password protection or other measurements.

configure

(only for software distributed as source code) This step heavily depends on the software itself. In many cases autotools or CMake is used, in some cases you have to deal with Makefiles or other tools.

build

(only for software distributed as source code) compile/build everything

install

Install everything in the target directory. In case of a binary package this might not be scriptable.

6.4. Writing build configuration files

6.4.1. YAML configuration files in Pmodules 1.1 and newer

Pmodules 1.1 and newer are supporting configuration files in YAML format. For backward compatibility the build-systems supports both types of configuration files with the YAML configuration file as default.

The old format of the variants file is simple but very limited and almost impossible to extend for new features. To overcome the limitations a new format using YAML for variants files has been introduced. For the time being both format are supported. But it is highly recommended to use the YAML format for new modules and to migrate existing variants files in the old format to the new.

6.4.1.1. Path to configuration file

The path to the configuration file is

build-recipe-dir/files/config.yaml

6.4.1.2. Format
format: 1
module-name-1:
  defaults:                                       # optional
    config-block
  shasums:                                        # optional
    filename-1: sha256sum-1
    ...
  versions:
    version-keys-1:
      config:                                     # optional
        config-block
      variants:                                   # optional
        - config-block-1
        - ...
    ...
module-name-2:
  ...
Field-Name Type Description

format

unsigned int

Format version of the YAML configuration file. For now it must be 1

module-name-N

config-block

Configuration for the Pmodule module-name-N. Example: openmpi

defaults

config-block

Default configuration. This block is optional.

shasums

MAP of string

SHA256 hash sums of source files. Keys are filenames, the value the
corresponding SHA256 hash sum. This block is optional. A warning is printed if a hash-sum is missing for a required file.

versions

MAP of configurations for different version keys

A version key is a semicolon separated string of version numbers. Version numbers can be specified with shell brace expansion. Example:
5.4.{0,1,2,3,4,5,8,9};5.2.{0,4,6,7,8};5.0.0;4.6.3

config

config-block

Optional, version specific configuration block. Configurations specified here are overriding the defaults.

variants

map of config-block

Variants can be used to compile the some software with different

6.4.1.3. Configuration blocks

In configuration blocks everything is optional!

config-block
build_requires: [build-req-1, ...]
compile_in_sourcetree: bool
configure_with: auto|autotools|cmake
default_variant: variant
docfiles: [file-1, ...]
group: group
group_deps:
  compilers:
    cpmpiler-1: [version-1, ...]
  mpi:
    mpi-implemation-1: [version-1, ...]
  hdf5:
    hdf5: [version-1, ...]
  hdf5_serial:
    hdf5[_serial]: [_version-1, ...]
overlay: overlay
relstage: release-stage
runtime_deps: [rt-dep-1, ...]
suffix: suffix
systems: [system-1, ...]
urls:
  - url: link
    name: filename
  - ...
variant: [variant-1, ...]
Field-Name Type Description

build_requires

array of string

Modules required to build this module

compile_in_sourcetree

boolean

Compile in source tree or in a dedicate build directory.

configure_with

string

Choose software configuration system. Allowed values are:
auto: use autotools if available
autotools: use autotools
cmake: use cmake

default_variant

string

(opt) Specifies the default variant to build if no variant is specified

docfiles

array of string

Array with documentation files to be installed in share/doc/

group

string

Group of the module

group_deps

group-deps-object

Group/hierarchical dependencies like compiler, MPI, HDF5 modules

overlay

string

Overlay of the module

relstage

string

Release stage of the module. Allowed values are:
unstable: the module is still under development
stable: the module is ready to be used
deprecated: the module is deprecated
remove: the module is deprecated and marked to be removed
removed: the module has been removed

runtime_deps

sequence of strings

Module to be loaded at runtime

suffix

string

This string will be added to the version of the module

systems

sequence of strings

A 'system' is either a hostname or OS name like rhel8. For
hostnames shell glob pattern matching like merlin-* can be used.

urls

sequence of links and optional file-names

Software which must be downloaded to build a specific module.
url: Download link
name: optional output filename. The filename defaults the last component of the url.
Default URLs can be defined by using the variables P, V, V_PKG, V_MAJOR, V_MINOR and V_PATCHLVL. Example:
https://sourceforge.net/projects/gnuplot/files/$P/$V/$P-${V_PKG}.tar.gz

variant

sequence of strings

Sequence of synonyms for a variant.

6.4.1.4. Examples
Example: Gnuplot
format: 1
gnuplot:
  defaults:                                             (1)
    group: Tools                                        (2)
    overlay: base                                       (3)
    relstage: stable                                    (4)
    systems: [rhel8, rhel7, rhel6]                      (5)
    docfiles: [Copyright, NEWS, README]                 (6)
    urls:                                               (7)
      - url: https://sourceforge.net/projects/gnuplot/files/$P/$V/$P-${V_PKG}.tar.gz
  shasums:                                              (8)
    gnuplot-5.4.10.tar.gz: 975d8c1cc2c41c7cedc4e323aff035d977feb9a97f0296dd2a8a66d197a5b27c
    gnuplot-5.4.9.tar.gz: a328a021f53dc05459be6066020e9a71e8eab6255d3381e22696120d465c6a97
    gnuplot-5.4.8.tar.gz: 931279c7caad1aff7d46cb4766f1ff41c26d9be9daf0bcf0c79deeee3d91f5cf
    gnuplot-5.4.5.tar.gz: 66f679115dd30559e110498fc94d926949d4d370b4999a042e724b8e910ee478
    gnuplot-5.4.4.tar.gz: 372300b7867f5b3538b25fc5d0ac7734af6e3fe0d202b6db926e4369913f0902
    gnuplot-5.4.3.tar.gz: 51f89bbab90f96d3543f95235368d188eb1e26eda296912256abcd3535bd4d84
    gnuplot-5.4.2.tar.gz: e57c75e1318133951d32a83bcdc4aff17fed28722c4e71f2305cfc2ae1cae7ba
    gnuplot-5.4.1.tar.gz: 6b690485567eaeb938c26936e5e0681cf70c856d273cc2c45fabf64d8bc6590e
    gnuplot-5.4.0.tar.gz: eb4082f03a399fd1e9e2b380cf7a4f785e77023d8dcc7e17570c1b5570a49c47
    gnuplot-5.2.8.tar.gz: 60a6764ccf404a1668c140f11cc1f699290ab70daa1151bb58fed6139a28ac37
    gnuplot-5.2.7.tar.gz: 97fe503ff3b2e356fe2ae32203fc7fd2cf9cef1f46b60fe46dc501a228b9f4ed
    gnuplot-5.2.6.tar.gz: 35dd8f013139e31b3028fac280ee12d4b1346d9bb5c501586d1b5a04ae7a94ee
    gnuplot-5.2.4.tar.gz: 1515f000bd373aaa53b16183f274189d4f5e0ae47d22f434857933d16a4770cb
    gnuplot-5.0.0.tar.gz: 417d4bc5bc914a60409bb75cf18dd14f48b07f53c6ad3c4a4d3cd9a8d7370faf
    gnuplot-4.6.3.tar.gz: df5ffafa25fb32b3ecc0206a520f6bca8680e6dcc961efd30df34c0a1b7ea7f5
  versions:
    5.4.{0,1,2,3,4,5,8,9};5.2.{0,4,6,7,8};5.0.0;4.6.3:  (9)
    5.4.10:                                             (10)
      config:
        relstage: unstable
1 Configuration is for gnuplot
2 will be installed in the group Tools
3 will be installed in the base overlay (/opt/psi)
4 the default release stage is stable
5 the module is available on these systems
6 install these files to $PREFIX/share/doc/gnuplot
7 download via this link
8 SHA256 hash sums for all available versions
9 no specific configuration required for these versions
10 release stage of version 5.4.10 is still unstable, override default release stage. ---
Example: HDF5
---
# yamllint disable rule:line-length             (1)
format: 1
hdf5:                                           (2)
  defaults:
    group: MPI                                  (3)
    overlay: base                               (4)
    relstage: stable                            (5)
    systems: [rhel7, rhel8, rhel9]              (6)
    urls:                                       (7)
      - url: https://support.hdfgroup.org/ftp/HDF5/releases/$P-${V_MAJOR}.${V_MINOR}/$P-${V_PKG}/src/$P-${V_PKG}.tar.bz2
  shasums:                                      (8)
    hdf5-1.8.10-patch1.tar.bz2: 292afb3615ad9e68f4d5d18ebb11e4a73f2aece39f2da3875a457ff1e109fc41
    hdf5-1.8.12.tar.bz2: 10a369a4fc207bb09245f57c758e587420e06dfc0e445e337a58b0848b75a949
    ...
    hdf5-1.13.1.tar.bz2: e16973ec893e2d5aa9c8dc73e196db9b99a605578e7317b421c713936f8bf57d

  versions:
    1.8.12:
      config:
        relstage: deprecated                    (9)
      variants:
        -
          group_deps:
            compiler: {gcc: [4.7.4, 4.8.3, 4.8.4, 4.9.2]}
            mpi: {openmpi: [1.6.5, 1.8.2, 1.8.4]}
        -
          group_deps:
            compiler: {gcc: [4.8.2]}
            mpi: {openmpi: [1.6.5]}
        ...
        -
          group_deps:
            compiler: {gcc: [5.1.0], intel: [15.2, 15.3]}
            mpi: {openmpi: [1.8.4]}
    ...
    1.10.8_slurm:
      variants:                                (10)
        -
          group_deps:
            compiler: {gcc: [10.4.0]}
            mpi: {openmpi: [4.1.4_slurm]}
        -
          relstage: unstable
          group_deps:
            compiler: {gcc: [9.5.0, 10.4.0, 11.4.0, 12.3.0, 13.1.0]}
            mpi: {openmpi: [4.1.5_slurm]}
    ...
    1.12.0:
      variants:
        -
          group_deps:
            compiler: {gcc: [7.5.0, 8.4.0, 9.3.0, 10.2.0]}
            mpi: {openmpi: [4.0.5]}
        -
          relstage: unstable
          group_deps:
            compiler: {pgi: [21.5]}
            mpi: {pgi-mpi: [21.5]}

    1.13.1:
      variants:
        -
          suffix: _slurm                        (11)
          variant: [_slurm]
          relstage: unstable
          group_deps:
            compiler: {gcc: [11.2.0]}
            mpi: {openmpi: [4.1.3_slurm]}
1 disable rule to check line length in yamllint. Default is 80.
2 this configuration is for HDF5
3 default group is MPI.
4 default overlay is base
5 default release stage is stable
6 this build-block can be used on RHEL7, RHEL8 and RHEL9
7 download via this link
8 SHA256 hash sums for all available versions
9 all variants of version 1.8.12 are deprecated
10 hdf5/1.10.8_slurm has two variants. The first variant compiled with GCC 10.4.0 and openmpi/4.1.4_slurm. The second variant is unstable and compiled with with GCC 9.5.0, 10.4.0, 11.4.0, 12.3.0, 13.1.0 and openmpi/4.1.5_slurm.
11 TBW

6.5. Writing build scripts

Pmodules has it’s own 'interpreter' to run build-scripts written in Bash. The name of the interpreter is modbuild. Thus the first line (the so called 'shebang') of a build-script must be

#!/usr/bin/env modbuild

As already mentioned the four steps 'prepare', 'configure', 'build' and 'install' has to be performed to build a Pmodule. These steps maps to the functions:

prepare

pbuild::pre_prep()
pbuild::prep()
pbuild::post_prep()

configure

pbuild::pre_configure()
pbuild::configure()
pbuild::post_configure()

compile

pbuild::pre_compile()
pbuild::compile()
pbuild::post_compile()

install

pbuild::pre_install()
pbuild::install()
pbuild::post_install()

In many cases you don’t have to implement the the functions pbuild::prep(), pbuild::configure(), pbuild::compile() and pbuild::install() or at least not all of them. The build-system provides default implementations for these functions. In the simplest case, the build script consists only of the shebang line (only for Pmodules >= 1.1). In this case all further information for building the module is in the configuration file. For software that uses autotools or CMake for configuration and creation of Makefiles, the standard functions can practically always be used. The functions pbuild::pre_STEP and pbuild::post_STEP can be used to hook into each step before and after the default function has been called. For example pbuild::pre_configure() can be used to set arguments for autotools or CMake as in the build-script for Gnuplot:

Example: build script for Gnuplot
#!/usr/bin/env modbuild

pbuild::pre_configure() {
        pbuild::add_configure_args '--with-latex=no'
        pbuild::add_configure_args '--with-qt=no'
}

6.5.1. Overloading the default implementations

If the build-system of the software package isn’t autotools or CMake you have to overload the default implementation of pbuild::configure.

pbuild::configure(){
        # add code to configure the software package
}

In case where no configuration is required, overload the default function with

pbuild::configure(){
        : # do nothing
}

You can do the same with the other default implementation.

6.5.2. Modules from binary packages

If you have a binary package like Matlab, Mathematica, ANSYS, it might not be possible to script the installation. In this case you should use a dummy build-script like below and document the installation in a README.md:

#!/usr/bin/env modbuild

pbuild::prep() { :; }
pbuild::configure() { :; }
pbuild::compile() { :; }
pbuild::install() { :; }

If the installation can be scripted, code this in the function pbuild::install.

#!/usr/bin/env modbuild

pbuild::prep() { :; }
pbuild::configure() { :; }
pbuild::compile() { :; }
pbuild::install() {
        # add code to call the installer here
}

6.5.3. Calling the build-script

To call the build-script, cd into the directory of the script and run

./build VERSION_TO_BE_BUILD
Example with output
pmod7:~/HelloWorld$ ./build 1.0.0
Using YAML configuration file - /afs/psi.ch/user/g/gsell/HelloWorld/files/config.yaml
HelloWorld/1.0.0: building ...
HelloWorld/1.0.0:
HelloWorld/1.0.0: start building ...
HelloWorld/1.0.0: preparing sources ...
HelloWorld/1.0.0: configuring ...
HelloWorld/1.0.0: compiling ...
HelloWorld/1.0.0: installing ...
HelloWorld/1.0.0: running post-installation for Linux ...
HelloWorld/1.0.0: Installing documentation to /opt/psi/Sandbox/HelloWorld/1.0.0/share/doc/HelloWorld
HelloWorld/1.0.0: adding modulefile to overlay 'base' ...
HelloWorld/1.0.0: Cleaning up '/var/tmp/gsell/HelloWorld-1.0.0/build'...
HelloWorld/1.0.0: Cleaning up '/var/tmp/gsell/HelloWorld-1.0.0/src'...
HelloWorld/1.0.0: Done ...
* * * * *

pmod7:~/HelloWorld$

You can run

./build --help

to get a list of all option with a description. The most common option are

-f

to force a rebuild

--verbose

to get a full debug trace.

6.5.4. Functions to compare versions

6.5.4.1. Compare two versions: pbuild::version_compare
Synopsis
pbuild::version_compare VERSION1 VERSION2
pbuild::version_lt VERSION1 VERSION2
pbuild::version_le VERSION1 VERSION2
pbuild::version_gt VERSION1 VERSION2
pbuild::version_ge VERSION1 VERSION2
pbuild::version_eq VERSION1 VERSION2
Description

The function splits the version strings at the dots an compares each component. If a component is a number the comparison is numerical otherwise lexical.

pbuild::version_compare VERSION1 VERSION2 returns
0 if the versions are equal
1 if VERSION1 is higher then VERSION2
2 if VERSION1 is lower then VERSION2

pbuild::version_lt VERSION1 VERSION2 returns
0 if VERSION1 is lower then VERSION2 otherwise a values greater than zero.

pbuild::version_le VERSION1 VERSION2 returns
0 if VERSION1 is lower then or equal to VERSION2 otherwise a values greater than zero.

pbuild::version_gt VERSION1 VERSION2 returns
0 if VERSION1 is higher then VERSION2 otherwise a values greater than zero.

pbuild::version_ge VERSION1 VERSION2 returns
0 if VERSION1 is higher then or equal to VERSION2 otherwise a values greater than zero.

pbuild::version_eq VERSION1 VERSION2 returns
0 if VERSION1 is equal to VERSION2 otherwise a values greater than zero.

Example
pbuild::version_lt '1.1.0' '1.2.2'  # result is 0
pbuild::version_ge '9.1.2' '10.0.0' # result is greater zero

6.5.5. Build functions

6.5.5.1. Download and unpack: pbuild::prep
Synopsis
pbuild::pre_prep
pbuild::prep
pbuild::post_prep
Description

Functions to prepare the sources. This includes

  • downloading required files a verifying the checksums

  • unpacking

  • applying patches

Before the prep-functions are called, the build-system changes to the source directory (${SRC_DIR}).

In the most uses cases the default function pbuild::prep provided by the build-system can be used and nothing must be implemented in the build-script. In some rare cases pre- or post-hooks are required to make the default function feasible. One use case with autotools for a post-hook is to create the configure scripts, if only configure.ac is shipped with the software.

The default hooks provided by the build system are only stubs and do nothing.

If the default function cannot be used, it must be implemented in the build-script.

Example

The source distribution of the IOAPI library contains object files! These files must be removed after unpacking:

pbuild::post_prep() {
        find "${SRC_DIR}" -name "*.mod" -exec rm {} \;
        find "${SRC_DIR}" -name "*.o" -exec rm {} \;
}

6.5.5.2. Configure software: pbuild::configure
Synopsis
pbuild::pre_configure
pbuild::configure
pbuild::post_configure
Description

Configure the software for compilation.

Before these functions are called, the build-system changes to the build directory (${BUILD_DIR}).

In the most uses cases the default function pbuild::configure provided by the build-system can be used and nothing must be implemented in the build-script.

The default function first searches whether the software can be configured with autotools. If yes, configuration with autotools will be performed. Otherwise the existence of a CMake script will be checked. If found, configuration will be done via CMake. If scripts for both configuration tools exist, the to be used tool can be selected with pbuild::use_autotools or pbuild::use_cmake. Arguments to configure or cmake can be set with pbuild::add_configure_args.

A common use case for the pre-configure hooks is to define arguments passed to autotools or CMake by the default function.

If the default function cannot be used, the pbuild::configure function must be implemented in the build-script.

Examples

Excerpt from the parallel HDF5 build-script

pbuild::pre_configure() {
        pbuild::add_configure_args "CC=${MPICC}"
        pbuild::add_configure_args "CXX=${MPICXX}"

        pbuild::add_configure_args "--enable-shared"
        pbuild::add_configure_args "--enable-parallel"
        pbuild::add_configure_args "--enable-cxx"
        pbuild::add_configure_args "--enable-unsupported"
        #pbuild::add_configure_args "--enable-threadsafe"
        pbuild::add_configure_args "--with-pic"

        local enable_fortran='yes'

        case "${COMPILER}" in
                clang-macos )
                        enable_fortran='no'
                        # we do not have Fortran in Xcode
                        ;;
                pgi )
                        # PGI uses GCC's include files, some object files and
                        # the STL implementation!
                        # The PGI C pre-processor is broken and doesn't work
                        # for HDF5. We use the pre-processor of the underlying
                        # GCC...
                        # This is a bit hackish!
                        #
                        # The following eval sets GCCDIR! Which is something
                        # like:
                        # /opt/psi/Programming/gcc/7.3.0/bin/../lib/gcc/x86_64-pc-linux-gnu/7.3.0
                        #
                        eval $(pgcc -show 2>/dev/null | \
                                awk '/^GCCDIR[[:space:]]*=/{gsub(/[[:space:]]/,""); print $0}')
                        pbuild::add_configure_args "CPP=${GCCDIR%%/..*}/cpp"
                        pbuild::add_configure_args "CFLAGS=-fPIC"
                        pbuild::add_configure_args "CXXFLAGS=-fPIC"
                        pbuild::add_configure_args "FCFLAGS=-fPIC"
                        ;;
        esac

        if [[ "${enable_fortran}" ===== 'yes' ]]; then
                pbuild::add_configure_args "F77=${MPIF77}"
                pbuild::add_configure_args "F90=${MPIF90}"
                pbuild::add_configure_args "FC=${MPIFC}"
                pbuild::add_configure_args "FORTRAN=${MPIFORTRAN}"
                pbuild::add_configure_args "--enable-fortran"
        fi

Simplified excerpt from the OpenBLAS build-script

pbuild::configure() {
        case ${COMPILER} in
        gcc )
                CC='gcc'
                ;;
        intel )
                CC='icc'
                ;;
        clang-macos )
                CC='gcc'
                ;;
        * )
                die 3 "Oops: unknown compiler: ${COMPILER}"
                ;;
        esac
        cat <<EOF > "${SRC_DIR}/make.inc"
SHELL = /bin/sh
PLAT =
DRVOPTS  = \$(NOOPT)
ARCHFLAGS= -ru
EOF
        echo "USE_SIMPLE_THREADED_LEVEL3 = 1" >> "${SRC_DIR}/Makefile.rule"
        echo "NO_AVX = 1" >> "${SRC_DIR}/Makefile.rule"
        echo "NO_AVX2 = 1" >> "${SRC_DIR}/Makefile.rule"
        if pbuild::use_flag "omp"; then
                echo "USE_THREAD = 1" >> "${SRC_DIR}/Makefile.rule"
        else
                echo "USE_THREAD = 0" >> "${SRC_DIR}/Makefile.rule"
        fi
}

6.5.5.3. Compile software: pbuild::compile
Synopsis
pbuild::pre_compile
pbuild::compile
pbuild::post_compile
Description

Compile the software.

Before these functions are called, the build-system changes to the build directory (${BUILD_DIR}).

In the most uses cases the default function pbuild::compile provided by the build-system can be used and nothing must be implemented in the build-script. The pre- and post-hooks might be useful in cases where

  • to run other make targets than all

  • multiple packages must be compiled into one module

  • to compile a dependency required by the main package.

If the default function cannot be used, the pbuild::compile function must be implemented in the build-script.

Example

Excerpt from perl build-script

pbuild::post_compile() {
        make test
}

6.5.5.4. Install software: pbuild::install
Synopsis
pbuild::pre_install
pbuild::install
pbuild::post_install
Description

Compile the software.

Before these functions are called, the build-system changes to the build directory (${BUILD_DIR}).

In the many uses cases the default function pbuild::install provided by the build-system can be used and nothing must be implemented in the build-script. A typical use case for the post-install hook is to install required libraries from other modules or the system to reduce or eliminate run-time dependencies

If the default function cannot be used, the pbuild::install function must be implemented in the build-script.

Example
TBW

6.6. Runtime configuration files

Table of Contents
Pmodules 1.0 configuration file .release-version
Pmodules 1.1 and newer configuration file .config-version

The runtime configuration files are evaluated only for modules inside the Pmodules hierarchy. If you use modbuild these files are created automatically. Otherwise you have to create them by hand.

6.6.1. Pmodules 1.0: .release-version

In .release-version the release stage of a module is defined. The file must be in the same directory as the modulefile. The content is a single line defining the release stage. Allowed text is

  • unstable

  • stable

  • deprecated

If the file doesn’t exist and the module is inside the Pmodule hierarchy unstable. All modules outside the Pmodules hierarchy are considered as stable.

This configuration file is deprecated in version 1.1 and newer. Please see next section.

6.6.2. Pmodules 1.1 and newer: .config-version

In .config-version the following properties of a module can be configured:

  • the release stage

  • the systems on which a module is available

  • the systems on which a module is unavailable

The format of the file is YAML.

relstage

The release stage of the module. Allowed values are unstable, stable and deprecated.

systems

Sequence of systems on which the module is available. Systems can be hostnames with shell style glob pattern or OS names like (rhel7, rhel8).

blocklist

Sequence of systems on which the module is no available. Systems can be hostnames with shell style glob patterns or OS names.

Example:

relstage: unstable
systems: [merlin-*, ra-*]
blocklist: []