|
12 | 12 | from reframe.core.exceptions import BuildSystemError |
13 | 13 |
|
14 | 14 |
|
| 15 | +class _UndefinedType: |
| 16 | + '''Used as an initial value for undefined values instead of None.''' |
| 17 | + |
| 18 | + |
| 19 | +_Undefined = _UndefinedType() |
| 20 | + |
| 21 | + |
15 | 22 | class BuildSystem(abc.ABC): |
16 | 23 | '''The abstract base class of any build system. |
17 | 24 |
|
@@ -157,6 +164,16 @@ def post_build(self, buildjob): |
157 | 164 |
|
158 | 165 | ''' |
159 | 166 |
|
| 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 | + |
160 | 177 | def _resolve_flags(self, flags, environ): |
161 | 178 | _flags = getattr(self, flags) |
162 | 179 | if _flags: |
@@ -782,6 +799,100 @@ def generated_modules(self): |
782 | 799 | return self._eb_modules |
783 | 800 |
|
784 | 801 |
|
| 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 | + |
785 | 896 | class BuildSystemField(fields.TypedField): |
786 | 897 | def __init__(self, fieldname, *other_types): |
787 | 898 | super().__init__(fieldname, BuildSystem, *other_types) |
|
0 commit comments