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.'''