Source code for reframe.core.environments

import collections
import os

import reframe.core.fields as fields
import reframe.utility as util
import reframe.utility.os_ext as os_ext
import reframe.utility.typecheck as typ
from reframe.core.runtime import runtime


[docs]class Environment: '''This class abstracts away an environment to run regression tests. It is simply a collection of modules to be loaded and environment variables to be set when this environment is loaded by the framework. ''' name = fields.TypedField('name', typ.Str[r'(\w|-)+']) modules = fields.TypedField('modules', typ.List[str]) variables = fields.TypedField('variables', typ.Dict[str, str]) def __init__(self, name, modules=[], variables=[]): self._name = name self._modules = list(modules) self._variables = collections.OrderedDict(variables) @property def name(self): '''The name of this environment. :type: :class:`str` ''' return self._name @property def modules(self): '''The modules associated with this environment. :type: :class:`list` of :class:`str` ''' return util.SequenceView(self._modules) @property def variables(self): '''The environment variables associated with this environment. :type: dictionary of :class:`str` keys/values. ''' return util.MappingView(self._variables) @property def is_loaded(self): ''':class:`True` if this environment is loaded, :class:`False` otherwise. ''' is_module_loaded = runtime().modules_system.is_module_loaded return (all(map(is_module_loaded, self._modules)) and all(os.environ.get(k, None) == os_ext.expandvars(v) for k, v in self._variables.items()))
[docs] def details(self): '''Return a detailed description of this environment.''' variables = '\n'.join(' '*8 + '- %s=%s' % (k, v) for k, v in self.variables.items()) lines = [ self._name + ':', ' modules: ' + ', '.join(self.modules), ' variables:' + ('\n' if variables else '') + variables ] return '\n'.join(lines)
def __eq__(self, other): if not isinstance(other, type(self)): return NotImplemented return (self.name == other.name and set(self.modules) == set(other.modules) and self.variables == other.variables) def __str__(self): return self.name def __repr__(self): ret = "{0}(name='{1}', modules={2}, variables={3})" return ret.format(type(self).__name__, self.name, self.modules, self.variables)
class _EnvironmentSnapshot(Environment): def __init__(self, name='env_snapshot'): super().__init__(name, runtime().modules_system.loaded_modules(), os.environ.items()) def restore(self): '''Restore this environment snapshot.''' os.environ.clear() os.environ.update(self._variables) def __eq__(self, other): if not isinstance(other, Environment): return NotImplemented # Order of variables is not important when comparing snapshots for k, v in self.variables.items(): if other.variables[k] != v: return False return (self.name == other.name and set(self.modules) == set(other.modules))
[docs]def snapshot(): '''Create an environment snapshot''' return _EnvironmentSnapshot()
[docs]def load(*environs): '''Load environments in the current Python context. Returns a tuple containing a snapshot of the environment at entry to this function and a list of shell commands required to load ``environs``. ''' env_snapshot = snapshot() commands = [] rt = runtime() for env in environs: for m in env.modules: conflicted = rt.modules_system.load_module(m, force=True) for c in conflicted: commands += rt.modules_system.emit_unload_commands(c) commands += rt.modules_system.emit_load_commands(m) for k, v in env.variables.items(): os.environ[k] = os_ext.expandvars(v) commands.append('export %s=%s' % (k, v)) return env_snapshot, commands
def emit_load_commands(*environs): env_snapshot, commands = load(*environs) env_snapshot.restore() return commands
[docs]class temp_environment: '''Context manager to temporarily change the environment.''' def __init__(self, modules=[], variables=[]): self._modules = modules self._variables = variables def __enter__(self): new_env = Environment('_rfm_temp_env', self._modules, self._variables) self._environ_save, _ = load(new_env) return new_env def __exit__(self, exc_type, exc_value, traceback): self._environ_save.restore()
[docs]class ProgEnvironment(Environment): '''A class representing a programming environment. This type of environment adds also attributes for setting the compiler and compilation flags. If compilation flags are set to :class:`None` (the default, if not set otherwise in ReFrame's `configuration <configure.html#environments-configuration>`__), they are not passed to the ``make`` invocation. If you want to disable completely the propagation of the compilation flags to the ``make`` invocation, even if they are set, you should set the :attr:`propagate` attribute to :class:`False`. ''' _cc = fields.TypedField('_cc', str) _cxx = fields.TypedField('_cxx', str) _ftn = fields.TypedField('_ftn', str) _cppflags = fields.TypedField('_cppflags', typ.List[str], type(None)) _cflags = fields.TypedField('_cflags', typ.List[str], type(None)) _cxxflags = fields.TypedField('_cxxflags', typ.List[str], type(None)) _fflags = fields.TypedField('_fflags', typ.List[str], type(None)) _ldflags = fields.TypedField('_ldflags', typ.List[str], type(None)) def __init__(self, name, modules=[], variables={}, cc='cc', cxx='CC', ftn='ftn', nvcc='nvcc', cppflags=None, cflags=None, cxxflags=None, fflags=None, ldflags=None, **kwargs): super().__init__(name, modules, variables) self._cc = cc self._cxx = cxx self._ftn = ftn self._nvcc = nvcc self._cppflags = cppflags self._cflags = cflags self._cxxflags = cxxflags self._fflags = fflags self._ldflags = ldflags @property def cc(self): '''The C compiler of this programming environment. :type: :class:`str` ''' return self._cc @property def cxx(self): '''The C++ compiler of this programming environment. :type: :class:`str` or :class:`None` ''' return self._cxx @property def ftn(self): '''The Fortran compiler of this programming environment. :type: :class:`str` or :class:`None` ''' return self._ftn @property def cppflags(self): '''The preprocessor flags of this programming environment. :type: :class:`str` or :class:`None` ''' return self._cppflags @property def cflags(self): '''The C compiler flags of this programming environment. :type: :class:`str` or :class:`None` ''' return self._cflags @property def cxxflags(self): '''The C++ compiler flags of this programming environment. :type: :class:`str` or :class:`None` ''' return self._cxxflags @property def fflags(self): '''The Fortran compiler flags of this programming environment. :type: :class:`str` or :class:`None` ''' return self._fflags @property def ldflags(self): '''The linker flags of this programming environment. :type: :class:`str` or :class:`None` ''' return self._ldflags @property def nvcc(self): return self._nvcc
[docs] def details(self): def format_flags(flags): if flags is None: return '<None>' elif len(flags) == 0: return "''" else: return ' '.join(flags) base_details = super().details() extra_details = [ ' CC: %s' % self.cc, ' CXX: %s' % self.cxx, ' FTN: %s' % self.ftn, ' NVCC: %s' % self.nvcc, ' CFLAGS: %s' % format_flags(self.cflags), ' CXXFLAGS: %s' % format_flags(self.cxxflags), ' FFLAGS: %s' % format_flags(self.fflags), ' CPPFLAGS: %s' % format_flags(self.cppflags), ' LDFLAGS: %s' % format_flags(self.ldflags) ] return '\n'.join([base_details, '\n'.join(extra_details)])