Skip to content

Commit 5c7ef82

Browse files
author
Vasileios Karakasis
authored
Merge pull request #1719 from rsarm/feat/easybuild
[feat] Add support for building tests using EasyBuild
2 parents ca811d8 + 511c269 commit 5c7ef82

File tree

12 files changed

+416
-111
lines changed

12 files changed

+416
-111
lines changed

docs/config_reference.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1276,6 +1276,18 @@ It can either be a simple string or a JSON object with the following attributes:
12761276
A boolean value indicating whether this module refers to a module collection.
12771277
Module collections are treated differently from simple modules when loading.
12781278

1279+
.. js:attribute:: .path
1280+
1281+
:required: No
1282+
:default: ``null``
1283+
1284+
If the module is not present in the default ``MODULEPATH``, the module's location can be specified here.
1285+
ReFrame will make sure to set and restore the ``MODULEPATH`` accordingly for loading the module.
1286+
1287+
1288+
.. versionadded:: 3.5.0
1289+
1290+
12791291
.. seealso::
12801292

12811293
Module collections with `Environment Modules <https://modules.readthedocs.io/en/latest/MIGRATING.html#module-collection>`__ and `Lmod <https://lmod.readthedocs.io/en/latest/010_user.html#user-collections>`__.

reframe/core/buildsystems.py

Lines changed: 128 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import abc
77
import os
8+
import re
89

910
import reframe.core.fields as fields
1011
import reframe.utility.typecheck as typ
@@ -122,8 +123,8 @@ def __init__(self):
122123
def emit_build_commands(self, environ):
123124
'''Return the list of commands for building using this build system.
124125
125-
The build commands may always assume to be issued from the top-level
126-
directory of the code that is to be built.
126+
The build commands, as well as this function, will always be executed
127+
from the test's stage directory.
127128
128129
:arg environ: The programming environment for which to emit the build
129130
instructions.
@@ -132,8 +133,24 @@ def emit_build_commands(self, environ):
132133
:raises: :class:`BuildSystemError` in case of errors when generating
133134
the build instructions.
134135
135-
.. note::
136-
This method is relevant only to developers of new build systems.
136+
.. versionchanged:: 3.5.0
137+
This function executes from the test stage directory.
138+
139+
:meta private:
140+
141+
'''
142+
143+
def post_build(self, buildjob):
144+
'''Callback function that the framework will call when the compilation
145+
is done.
146+
147+
Build systems may use this information to do some post processing and
148+
provide additional, build system-specific, functionality to the users.
149+
150+
.. versionadded:: 3.5.0
151+
152+
:meta private:
153+
137154
'''
138155

139156
def _resolve_flags(self, flags, environ):
@@ -655,6 +672,113 @@ def emit_build_commands(self, environ):
655672
return prepare_cmd + [' '.join(configure_cmd), ' '.join(make_cmd)]
656673

657674

675+
class EasyBuild(BuildSystem):
676+
'''A build system for building test code using `EasyBuild
677+
<https://easybuild.io/>`__.
678+
679+
ReFrame will use EasyBuild to build and install the code in the test's
680+
stage directory by default. ReFrame uses environment variables to
681+
configure EasyBuild for running, so Users can pass additional options to
682+
the ``eb`` command and modify the default behaviour.
683+
684+
.. versionadded:: 3.5.0
685+
686+
'''
687+
688+
#: The list of easyconfig files to build and install.
689+
#: This field is required.
690+
#:
691+
#: :type: :class:`List[str]`
692+
#: :default: ``[]``
693+
easyconfigs = fields.TypedField(typ.List[str])
694+
695+
#: Options to pass to the ``eb`` command.
696+
#:
697+
#: :type: :class:`List[str]`
698+
#: :default: ``[]``
699+
options = fields.TypedField(typ.List[str])
700+
701+
#: Instruct EasyBuild to emit a package for the built software.
702+
#: This will essentially pass the ``--package`` option to ``eb``.
703+
#:
704+
#: :type: :class:`bool`
705+
#: :default: ``[]``
706+
emit_package = fields.TypedField(bool)
707+
708+
#: Options controlling the package creation from EasyBuild.
709+
#: For each key/value pair of this dictionary, ReFrame will pass
710+
#: ``--package-{key}={val}`` to the EasyBuild invocation.
711+
#:
712+
#: :type: :class:`Dict[str, str]`
713+
#: :default: ``{}``
714+
package_opts = fields.TypedField(typ.Dict[str, str])
715+
716+
#: Default prefix for the EasyBuild installation.
717+
#:
718+
#: Relative paths will be appended to the stage directory of the test.
719+
#: ReFrame will set the following environment variables before running
720+
#: EasyBuild.
721+
#:
722+
#: .. code-block:: bash
723+
#:
724+
#: export EASYBUILD_BUILDPATH={prefix}/build
725+
#: export EASYBUILD_INSTALLPATH={prefix}
726+
#: export EASYBUILD_PREFIX={prefix}
727+
#: export EASYBUILD_SOURCEPATH={prefix}
728+
#:
729+
#: Users can change these defaults by passing specific options to the
730+
#: ``eb`` command.
731+
#:
732+
#: :type: :class:`str`
733+
#: :default: ``easybuild``
734+
prefix = fields.TypedField(str)
735+
736+
def __init__(self):
737+
super().__init__()
738+
self.easyconfigs = []
739+
self.options = []
740+
self.emit_package = False
741+
self.package_opts = {}
742+
self.prefix = 'easybuild'
743+
self._eb_modules = []
744+
self._prefix_save = None
745+
746+
def emit_build_commands(self, environ):
747+
if not self.easyconfigs:
748+
raise BuildSystemError(f"'easyconfigs' must not be empty")
749+
750+
easyconfigs = ' '.join(self.easyconfigs)
751+
if self.emit_package:
752+
self.options.append('--package')
753+
for key, val in self.package_opts.items():
754+
self.options.append(f'--package-{key}={val}')
755+
756+
prefix = os.path.join(os.getcwd(), self.prefix)
757+
options = ' '.join(self.options)
758+
self._prefix_save = prefix
759+
return [f'export EASYBUILD_BUILDPATH={prefix}/build',
760+
f'export EASYBUILD_INSTALLPATH={prefix}',
761+
f'export EASYBUILD_PREFIX={prefix}',
762+
f'export EASYBUILD_SOURCEPATH={prefix}',
763+
f'eb {easyconfigs} {options}']
764+
765+
def post_build(self, buildjob):
766+
# Store the modules generated by EasyBuild
767+
768+
modulesdir = os.path.join(self._prefix_save, 'modules', 'all')
769+
with open(buildjob.stdout) as fp:
770+
out = fp.read()
771+
772+
self._eb_modules = [
773+
{'name': m, 'collection': False, 'path': modulesdir}
774+
for m in re.findall(r'building and installing (\S+)...', out)
775+
]
776+
777+
@property
778+
def generated_modules(self):
779+
return self._eb_modules
780+
781+
658782
class BuildSystemField(fields.TypedField):
659783
def __init__(self, fieldname, *other_types):
660784
super().__init__(fieldname, BuildSystem, *other_types)

reframe/core/environments.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def normalize_module_list(modules):
2020
ret = []
2121
for m in modules:
2222
if isinstance(m, str):
23-
ret.append({'name': m, 'collection': False})
23+
ret.append({'name': m, 'collection': False, 'path': None})
2424
else:
2525
ret.append(m)
2626

0 commit comments

Comments
 (0)