From aec2ca1693d6b241bd6358997f401499bcc4b275 Mon Sep 17 00:00:00 2001 From: Javier Otero Date: Fri, 23 Jul 2021 17:41:55 +0200 Subject: [PATCH] Use RegressionTestMeta in BuildSystem --- reframe/core/buildsystems.py | 133 ++++++++++------------------------- 1 file changed, 39 insertions(+), 94 deletions(-) diff --git a/reframe/core/buildsystems.py b/reframe/core/buildsystems.py index d1fa3102ca..07c09f73c2 100644 --- a/reframe/core/buildsystems.py +++ b/reframe/core/buildsystems.py @@ -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 @@ -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`, @@ -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`, @@ -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`, @@ -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`, @@ -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`, @@ -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`, @@ -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`, @@ -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`, @@ -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): @@ -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. #: @@ -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``. @@ -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'] @@ -374,7 +349,7 @@ 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. #: @@ -382,7 +357,7 @@ class SingleSource(BuildSystem): #: :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. #: @@ -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 @@ -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] @@ -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): @@ -715,20 +675,20 @@ 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 @@ -736,7 +696,7 @@ class EasyBuild(BuildSystem): #: #: :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. #: @@ -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): @@ -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. @@ -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()