Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 39 additions & 94 deletions reframe/core/buildsystems.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,14 @@
import reframe.core.fields as fields
import reframe.utility.typecheck as typ
from reframe.core.exceptions import BuildSystemError
from reframe.core.meta import RegressionTestMeta


class _UndefinedType:
'''Used as an initial value for undefined values instead of None.'''
__slots__ = ()
class BuildSystemMeta(RegressionTestMeta, abc.ABCMeta):
'''Build systems metaclass.'''

def __deepcopy__(self, memo):
return self


_Undefined = _UndefinedType()


class BuildSystem(abc.ABC):
class BuildSystem(metaclass=BuildSystemMeta):
'''The abstract base class of any build system.

Concrete build systems inherit from this class and must override the
Expand All @@ -37,7 +31,7 @@ class BuildSystem(abc.ABC):
#:
#: :type: :class:`str`
#: :default: ``''``
cc = fields.TypedField(str)
cc = variable(str, value='')

#: The C++ compiler to be used.
#: If empty and :attr:`flags_from_environ` is :class:`True`,
Expand All @@ -46,7 +40,7 @@ class BuildSystem(abc.ABC):
#:
#: :type: :class:`str`
#: :default: ``''``
cxx = fields.TypedField(str)
cxx = variable(str, value='')

#: The Fortran compiler to be used.
#: If empty and :attr:`flags_from_environ` is :class:`True`,
Expand All @@ -55,7 +49,7 @@ class BuildSystem(abc.ABC):
#:
#: :type: :class:`str`
#: :default: ``''``
ftn = fields.TypedField(str)
ftn = variable(str, value='')

#: The CUDA compiler to be used.
#: If empty and :attr:`flags_from_environ` is :class:`True`,
Expand All @@ -64,7 +58,7 @@ class BuildSystem(abc.ABC):
#:
#: :type: :class:`str`
#: :default: ``''``
nvcc = fields.TypedField(str)
nvcc = variable(str, value='')

#: The C compiler flags to be used.
#: If empty and :attr:`flags_from_environ` is :class:`True`,
Expand All @@ -73,7 +67,7 @@ class BuildSystem(abc.ABC):
#:
#: :type: :class:`List[str]`
#: :default: ``[]``
cflags = fields.TypedField(typ.List[str])
cflags = variable(typ.List[str], value=[])

#: The preprocessor flags to be used.
#: If empty and :attr:`flags_from_environ` is :class:`True`,
Expand All @@ -82,7 +76,7 @@ class BuildSystem(abc.ABC):
#:
#: :type: :class:`List[str]`
#: :default: ``[]``
cppflags = fields.TypedField(typ.List[str])
cppflags = variable(typ.List[str], value=[])

#: The C++ compiler flags to be used.
#: If empty and :attr:`flags_from_environ` is :class:`True`,
Expand All @@ -91,7 +85,7 @@ class BuildSystem(abc.ABC):
#:
#: :type: :class:`List[str]`
#: :default: ``[]``
cxxflags = fields.TypedField(typ.List[str])
cxxflags = variable(typ.List[str], value=[])

#: The Fortran compiler flags to be used.
#: If empty and :attr:`flags_from_environ` is :class:`True`,
Expand All @@ -100,7 +94,7 @@ class BuildSystem(abc.ABC):
#:
#: :type: :class:`List[str]`
#: :default: ``[]``
fflags = fields.TypedField(typ.List[str])
fflags = variable(typ.List[str], value=[])

#: The linker flags to be used.
#: If empty and :attr:`flags_from_environ` is :class:`True`,
Expand All @@ -109,26 +103,14 @@ class BuildSystem(abc.ABC):
#:
#: :type: :class:`List[str]`
#: :default: ``[]``
ldflags = fields.TypedField(typ.List[str])
ldflags = variable(typ.List[str], value=[])

#: Set compiler and compiler flags from the current programming environment
#: if not specified otherwise.
#:
#: :type: :class:`bool`
#: :default: :class:`True`
flags_from_environ = fields.TypedField(bool)

def __init__(self):
self.cc = ''
self.cxx = ''
self.ftn = ''
self.nvcc = ''
self.cflags = []
self.cxxflags = []
self.cppflags = []
self.fflags = []
self.ldflags = []
self.flags_from_environ = True
flags_from_environ = variable(bool, value=True)

@abc.abstractmethod
def emit_build_commands(self, environ):
Expand Down Expand Up @@ -247,14 +229,14 @@ class Make(BuildSystem):
#:
#: :type: :class:`List[str]`
#: :default: ``[]``
options = fields.TypedField(typ.List[str])
options = variable(typ.List[str], value=[])

#: Instruct build system to use this Makefile.
#: This option is useful when having non-standard Makefile names.
#:
#: :type: :class:`str`
#: :default: :class:`None`
makefile = fields.TypedField(str, type(None))
makefile = variable(str, type(None), value=None)

#: The top-level directory of the code.
#:
Expand All @@ -263,7 +245,7 @@ class Make(BuildSystem):
#:
#: :type: :class:`str`
#: :default: :class:`None`
srcdir = fields.TypedField(str, type(None))
srcdir = variable(str, type(None), value=None)

#: Limit concurrency for ``make`` jobs.
#: This attribute controls the ``-j`` option passed to ``make``.
Expand All @@ -277,14 +259,7 @@ class Make(BuildSystem):
#: .. note::
#: .. versionchanged:: 2.19
#: The default value is now ``1``
max_concurrency = fields.TypedField(int, type(None))

def __init__(self):
super().__init__()
self.options = []
self.makefile = None
self.srcdir = None
self.max_concurrency = 1
max_concurrency = variable(int, type(None), value=1)

def emit_build_commands(self, environ):
cmd_parts = ['make']
Expand Down Expand Up @@ -374,15 +349,15 @@ class SingleSource(BuildSystem):
#: :attr:`reframe.core.pipeline.RegressionTest.sourcepath` attribute.
#:
#: :type: :class:`str` or :class:`None`
srcfile = fields.TypedField(str, type(None))
srcfile = variable(str, type(None), value=None)

#: The executable file to be generated.
#:
#: This is set automatically by the framework based on the
#: :attr:`reframe.core.pipeline.RegressionTest.executable` attribute.
#:
#: :type: :class:`str` or :class:`None`
executable = fields.TypedField(str, type(None))
executable = variable(str, type(None), value=None)

#: The include path to be used for this compilation.
#:
Expand All @@ -392,7 +367,7 @@ class SingleSource(BuildSystem):
#:
#: :type: :class:`List[str]`
#: :default: ``[]``
include_path = fields.TypedField(typ.List[str])
include_path = variable(typ.List[str], value=[])

#: The programming language of the file that needs to be compiled.
#: If not specified, the build system will try to figure it out
Expand All @@ -406,14 +381,7 @@ class SingleSource(BuildSystem):
#: - CUDA: `.cu`.
#:
#: :type: :class:`str` or :class:`None`
lang = fields.TypedField(str, type(None))

def __init__(self):
super().__init__()
self.srcfile = None
self.executable = None
self.include_path = []
self.lang = None
lang = variable(str, type(None), value=None)

def _auto_exec_name(self):
return '%s.x' % os.path.splitext(self.srcfile)[0]
Expand Down Expand Up @@ -507,39 +475,31 @@ class ConfigureBasedBuildSystem(BuildSystem):
#:
#: :type: :class:`str`
#: :default: :class:`None`
srcdir = fields.TypedField(str, type(None))
srcdir = variable(str, type(None), value=None)

#: The CMake build directory, where all the generated files will be placed.
#:
#: :type: :class:`str`
#: :default: :class:`None`
builddir = fields.TypedField(str, type(None))
builddir = variable(str, type(None), value=None)

#: Additional configuration options to be passed to the CMake invocation.
#:
#: :type: :class:`List[str]`
#: :default: ``[]``
config_opts = fields.TypedField(typ.List[str])
config_opts = variable(typ.List[str], value=[])

#: Options to be passed to the subsequent ``make`` invocation.
#:
#: :type: :class:`List[str]`
#: :default: ``[]``
make_opts = fields.TypedField(typ.List[str])
make_opts = variable(typ.List[str], value=[])

#: Same as for the :attr:`Make` build system.
#:
#: :type: integer
#: :default: ``1``
max_concurrency = fields.TypedField(int, type(None))

def __init__(self):
super().__init__()
self.srcdir = None
self.builddir = None
self.config_opts = []
self.make_opts = []
self.max_concurrency = 1
max_concurrency = variable(int, type(None), value=1)


class CMake(ConfigureBasedBuildSystem):
Expand Down Expand Up @@ -715,28 +675,28 @@ class EasyBuild(BuildSystem):
#:
#: :type: :class:`List[str]`
#: :default: ``[]``
easyconfigs = fields.TypedField(typ.List[str])
easyconfigs = variable(typ.List[str], value=[])

#: Options to pass to the ``eb`` command.
#:
#: :type: :class:`List[str]`
#: :default: ``[]``
options = fields.TypedField(typ.List[str])
options = variable(typ.List[str], value=[])

#: Instruct EasyBuild to emit a package for the built software.
#: This will essentially pass the ``--package`` option to ``eb``.
#:
#: :type: :class:`bool`
#: :default: ``[]``
emit_package = fields.TypedField(bool)
#: :default: ``False``
emit_package = variable(bool, value=False)

#: Options controlling the package creation from EasyBuild.
#: For each key/value pair of this dictionary, ReFrame will pass
#: ``--package-{key}={val}`` to the EasyBuild invocation.
#:
#: :type: :class:`Dict[str, str]`
#: :default: ``{}``
package_opts = fields.TypedField(typ.Dict[str, str])
package_opts = variable(typ.Dict[str, str], value={})

#: Default prefix for the EasyBuild installation.
#:
Expand All @@ -756,15 +716,9 @@ class EasyBuild(BuildSystem):
#:
#: :type: :class:`str`
#: :default: ``easybuild``
prefix = fields.TypedField(str)
prefix = variable(str, value='easybuild')

def __init__(self):
super().__init__()
self.easyconfigs = []
self.options = []
self.emit_package = False
self.package_opts = {}
self.prefix = 'easybuild'
self._eb_modules = []

def emit_build_commands(self, environ):
Expand Down Expand Up @@ -829,8 +783,8 @@ class Spack(BuildSystem):
#: This field is required.
#:
#: :type: :class:`str` or :class:`None`
#: :default: :class:`None`
environment = fields.TypedField(typ.Str[r'\S+'], _UndefinedType)
#: :default: :class:`required`
environment = variable(typ.Str[r'\S+'])

#: A list of additional specs to build and install within the given
#: environment.
Expand All @@ -847,31 +801,22 @@ class Spack(BuildSystem):
#:
#: :type: :class:`List[str]`
#: :default: ``[]``
specs = fields.TypedField(typ.List[str])
specs = variable(typ.List[str], value=[])

#: Emit the necessary ``spack load`` commands before running the test.
#:
#: :type: :class:`bool`
#: :default: :obj:`True`
emit_load_cmds = fields.TypedField(bool)
emit_load_cmds = variable(bool, value=True)

#: Options to pass to ``spack install``
#:
#: :type: :class:`List[str]`
#: :default: ``[]``
install_opts = fields.TypedField(typ.List[str])

def __init__(self):
super().__init__()
self.specs = []
self.environment = _Undefined
self.emit_load_cmds = True
self.install_opts = []
self._prefix_save = None
install_opts = variable(typ.List[str], value=[])

def emit_build_commands(self, environ):
self._prefix_save = os.getcwd()
if self.environment is _Undefined:
if not hasattr(self, 'environment'):
raise BuildSystemError(f'no Spack environment is defined')

ret = self._env_activate_cmds()
Expand Down