diff --git a/docs/configure.rst b/docs/configure.rst index 6a0bc4b674..5cb3abc119 100644 --- a/docs/configure.rst +++ b/docs/configure.rst @@ -104,17 +104,18 @@ The valid attributes of a system are the following: * ``descr``: A detailed description of the system (default is the system name). * ``hostnames``: This is a list of hostname patterns that will be used by ReFrame when it tries to `auto-detect <#system-auto-detection>`__ the current system (default ``[]``). -* ``modules_system``: The modules system that should be used for loading environment modules on this system (default :class:`None`). +* ``modules_system``: *[new in 2.8]* The modules system that should be used for loading environment modules on this system (default :class:`None`). Three types of modules systems are currently supported: - - ``tmod``: The classic Tcl implementation of the `environment modules `__ (versions older than 3.2 are not supported). + - ``tmod`` or ``tmod32``: The classic Tcl implementation of the `environment modules `__ (version 3.2). + - ``tmod31``: *[new in 2.21]* The classic Tcl implementation of the `environment modules `__ (version 3.1). - ``tmod4``: The version 4 of the Tcl implementation of the `environment modules `__ (versions older than 4.1 are not supported). - ``lmod``: The Lua implementation of the `environment modules `__. -* ``modules``: Modules to be loaded always when running on this system. +* ``modules``: *[new in 2.19]* Modules to be loaded always when running on this system. These modules modify the ReFrame environment. This is useful when for example a particular module is needed to submit jobs on a specific system. -* ``variables``: Environment variables to be set always when running on this system. +* ``variables``: *[new in 2.19]* Environment variables to be set always when running on this system. * ``prefix``: Default regression prefix for this system (default ``.``). * ``stagedir``: Default stage directory for this system (default :class:`None`). * ``outputdir``: Default output directory for this system (default :class:`None`). @@ -126,13 +127,7 @@ The valid attributes of a system are the following: For a more detailed description of the ``prefix``, ``stagedir``, ``outputdir`` and ``perflogdir`` directories, please refer to the `"Configuring ReFrame Directories" `__ and `"Performance Logging" `__ sections. .. note:: - .. versionadded:: 2.8 - The ``modules_system`` key was introduced for specifying custom modules systems for different systems. - -.. note:: - .. versionadded:: 2.19 - The ``modules`` and ``variables`` configuration parameters were introduced at the system level. - + A different backend is used for Tmod 3.1, due to its different Python bindings. .. warning:: .. versionchanged:: 2.18 diff --git a/reframe/core/modules.py b/reframe/core/modules.py index 3e30d96550..c66488ea22 100644 --- a/reframe/core/modules.py +++ b/reframe/core/modules.py @@ -86,8 +86,12 @@ class ModulesSystem: def create(cls, modules_kind=None): if modules_kind is None: return ModulesSystem(NoModImpl()) + elif modules_kind == 'tmod31': + return ModulesSystem(TMod31Impl()) elif modules_kind == 'tmod': return ModulesSystem(TModImpl()) + elif modules_kind == 'tmod32': + return ModulesSystem(TModImpl()) elif modules_kind == 'tmod4': return ModulesSystem(TMod4Impl()) elif modules_kind == 'lmod': @@ -378,7 +382,7 @@ def __str__(self): class TModImpl(ModulesSystemImpl): - '''Module system for TMod (Tcl).''' + '''Base class for TMod Module system (Tcl).''' MIN_VERSION = (3, 2) @@ -499,6 +503,74 @@ def emit_unload_instr(self, module): return 'module unload %s' % module +class TMod31Impl(TModImpl): + '''Module system for TMod (Tcl).''' + + MIN_VERSION = (3, 1) + + def __init__(self): + # Try to figure out if we are indeed using the TCL version + try: + modulecmd = os.getenv('MODULESHOME') + modulecmd = os.path.join(modulecmd, 'modulecmd.tcl') + completed = os_ext.run_command(modulecmd) + except OSError as e: + raise ConfigError( + 'could not find a sane TMod31 installation: %s' % e) from e + + version_match = re.search(r'Release Tcl (\S+)', completed.stderr, + re.MULTILINE) + tcl_version_match = version_match + + if version_match is None or tcl_version_match is None: + raise ConfigError('could not find a sane TMod31 installation') + + version = version_match.group(1) + try: + ver_major, ver_minor, *_ = [int(v) for v in version.split('.')] + except ValueError: + raise ConfigError( + 'could not parse TMod31 version string: ' + version) from None + + if (ver_major, ver_minor) < self.MIN_VERSION: + raise ConfigError( + 'unsupported TMod version: %s (required >= %s)' % + (version, self.MIN_VERSION)) + + self._version = version + self._command = '%s python' % modulecmd + + try: + # Try the Python bindings now + completed = os_ext.run_command(self._command) + except OSError as e: + raise ConfigError( + 'could not get the Python bindings for TMod31: ' % e) from e + + if re.search(r'Unknown shell type', completed.stderr): + raise ConfigError( + 'Python is not supported by this TMod installation') + + def name(self): + return 'tmod31' + + def _exec_module_command(self, *args, msg=None): + completed = self._run_module_command(*args, msg=msg) + exec_match = re.search(r'^exec\s\'', completed.stdout) + if exec_match is None: + raise ConfigError('could not use the python bindings') + else: + cmd = completed.stdout + exec_match = re.search(r'^exec\s\'(\S+)\'', cmd, + re.MULTILINE) + if exec_match is None: + raise ConfigError('could not use the python bindings') + with open(exec_match.group(1), 'r') as content_file: + cmd = content_file.read() + + exec(cmd) + + class TMod4Impl(TModImpl): '''Module system for TMod 4.'''