Skip to content

Commit f8ae925

Browse files
author
Vasileios Karakasis
authored
Merge branch 'master' into code_quality/test_stage_mount_container
2 parents 55ae487 + 8bd06ea commit f8ae925

File tree

3 files changed

+164
-1
lines changed

3 files changed

+164
-1
lines changed

reframe/core/buildsystems.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,13 @@
1212
from reframe.core.exceptions import BuildSystemError
1313

1414

15+
class _UndefinedType:
16+
'''Used as an initial value for undefined values instead of None.'''
17+
18+
19+
_Undefined = _UndefinedType()
20+
21+
1522
class BuildSystem(abc.ABC):
1623
'''The abstract base class of any build system.
1724
@@ -157,6 +164,16 @@ def post_build(self, buildjob):
157164
158165
'''
159166

167+
def prepare_cmds(self):
168+
'''Callback function that the framework will call before run.
169+
170+
Build systems may use this information to add commands to the run
171+
script before anything set by the user.
172+
173+
:meta private:
174+
'''
175+
return []
176+
160177
def _resolve_flags(self, flags, environ):
161178
_flags = getattr(self, flags)
162179
if _flags:
@@ -782,6 +799,100 @@ def generated_modules(self):
782799
return self._eb_modules
783800

784801

802+
class Spack(BuildSystem):
803+
'''A build system for building test code using `Spack
804+
<https://spack.io/>`__.
805+
806+
ReFrame will use a user-provided Spack environment in order to build and
807+
test a set of specs.
808+
809+
.. versionadded:: 3.6.1
810+
811+
'''
812+
813+
#: The Spack environment to use for building this test.
814+
#:
815+
#: ReFrame will activate and install this environment.
816+
#: This environment will also be used to run the test.
817+
#:
818+
#: .. code-block:: bash
819+
#:
820+
#: spack env activate -V -d <environment directory>
821+
#:
822+
#: ReFrame looks for environments in the test's
823+
#: :attr:`~reframe.core.pipeline.RegressionTest.sourcesdir`.
824+
#:
825+
#: This field is required.
826+
#:
827+
#: :type: :class:`str` or :class:`None`
828+
#: :default: :class:`None`
829+
environment = fields.TypedField(typ.Str[r'\S+'], _UndefinedType)
830+
831+
#: The list of specs to build and install within the given environment.
832+
#:
833+
#: ReFrame will add the specs to the active environment by emititing the
834+
#: following command:
835+
#:
836+
#: .. code-block:: bash
837+
#:
838+
#: spack add spec1 spec2 ... specN
839+
#:
840+
#: If no spec is passed, ReFrame will simply install what is prescribed by
841+
#: the environment.
842+
#:
843+
#: :type: :class:`List[str]`
844+
#: :default: ``[]``
845+
specs = fields.TypedField(typ.List[str])
846+
847+
#: Emit the necessary ``spack load`` commands before running the test.
848+
#:
849+
#: :type: :class:`bool`
850+
#: :default: :obj:`True`
851+
emit_load_cmds = fields.TypedField(bool)
852+
853+
#: Options to pass to ``spack install``
854+
#:
855+
#: :type: :class:`List[str]`
856+
#: :default: ``[]``
857+
install_opts = fields.TypedField(typ.List[str])
858+
859+
def __init__(self):
860+
super().__init__()
861+
self.specs = []
862+
self.environment = _Undefined
863+
self.emit_load_cmds = True
864+
self.install_opts = []
865+
self._prefix_save = None
866+
867+
def emit_build_commands(self, environ):
868+
self._prefix_save = os.getcwd()
869+
if self.environment is _Undefined:
870+
raise BuildSystemError(f'no Spack environment is defined')
871+
872+
ret = self._env_activate_cmds()
873+
if self.specs:
874+
specs_str = ' '.join(self.specs)
875+
ret.append(f'spack add {specs_str}')
876+
877+
install_cmd = 'spack install'
878+
if self.install_opts:
879+
install_cmd += ' ' + ' '.join(self.install_opts)
880+
881+
ret.append(install_cmd)
882+
return ret
883+
884+
def _env_activate_cmds(self):
885+
return [f'. $SPACK_ROOT/share/spack/setup-env.sh',
886+
f'spack env activate -V -d {self.environment}']
887+
888+
def prepare_cmds(self):
889+
cmds = self._env_activate_cmds()
890+
if self.specs and self.emit_load_cmds:
891+
cmds.append('spack load ' + ' '.join(s for s in self.specs))
892+
893+
return cmds
894+
895+
785896
class BuildSystemField(fields.TypedField):
786897
def __init__(self, fieldname, *other_types):
787898
super().__init__(fieldname, BuildSystem, *other_types)

reframe/core/pipeline.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1339,7 +1339,18 @@ def run(self):
13391339
)
13401340
exec_cmd = [self.job.launcher.run_command(self.job),
13411341
self.executable, *self.executable_opts]
1342-
commands = [*self.prerun_cmds, ' '.join(exec_cmd), *self.postrun_cmds]
1342+
1343+
if self.build_system:
1344+
prepare_cmds = self.build_system.prepare_cmds()
1345+
else:
1346+
prepare_cmds = []
1347+
1348+
commands = [
1349+
*prepare_cmds,
1350+
*self.prerun_cmds,
1351+
' '.join(exec_cmd),
1352+
*self.postrun_cmds
1353+
]
13431354
user_environ = env.Environment(type(self).__name__,
13441355
self.modules, self.variables.items())
13451356
environs = [

unittests/test_buildsystems.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,47 @@ def test_singlesource_unknown_language():
243243
build_system.emit_build_commands(ProgEnvironment('testenv'))
244244

245245

246+
def test_spack(environ, tmp_path):
247+
build_system = bs.Spack()
248+
build_system.environment = 'spack_env'
249+
build_system.install_opts = ['-j 10']
250+
with osext.change_dir(tmp_path):
251+
assert build_system.emit_build_commands(environ) == [
252+
f'. $SPACK_ROOT/share/spack/setup-env.sh',
253+
f'spack env activate -V -d {build_system.environment}',
254+
f'spack install -j 10'
255+
]
256+
assert build_system.prepare_cmds() == [
257+
f'. $SPACK_ROOT/share/spack/setup-env.sh',
258+
f'spack env activate -V -d {build_system.environment}',
259+
]
260+
261+
262+
def test_spack_with_spec(environ, tmp_path):
263+
build_system = bs.Spack()
264+
build_system.environment = 'spack_env'
265+
build_system.specs = ['spec1@version1', 'spec2@version2']
266+
specs_str = ' '.join(build_system.specs)
267+
with osext.change_dir(tmp_path):
268+
assert build_system.emit_build_commands(environ) == [
269+
f'. $SPACK_ROOT/share/spack/setup-env.sh',
270+
f'spack env activate -V -d {build_system.environment}',
271+
f'spack add {specs_str}',
272+
f'spack install'
273+
]
274+
assert build_system.prepare_cmds() == [
275+
f'. $SPACK_ROOT/share/spack/setup-env.sh',
276+
f'spack env activate -V -d {build_system.environment}',
277+
f'spack load {specs_str}',
278+
]
279+
280+
281+
def test_spack_no_env(environ, tmp_path):
282+
build_system = bs.Spack()
283+
with pytest.raises(BuildSystemError):
284+
build_system.emit_build_commands(environ)
285+
286+
246287
def test_easybuild(environ, tmp_path):
247288
build_system = bs.EasyBuild()
248289
build_system.easyconfigs = ['ec1.eb', 'ec2.eb']

0 commit comments

Comments
 (0)