Skip to content
Merged
Show file tree
Hide file tree
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
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ python: 2.6
env:
matrix:
# purposely specifying slowest builds first, to gain time overall
- LMOD_VERSION=5.8
- LMOD_VERSION=5.8 TEST_EASYBUILD_MODULE_SYNTAX=Tcl
- LMOD_VERSION=6.6.3
- LMOD_VERSION=6.6.3 TEST_EASYBUILD_MODULE_SYNTAX=Tcl
- LMOD_VERSION=7.7.16
- LMOD_VERSION=7.7.16 TEST_EASYBUILD_MODULE_SYNTAX=Tcl
- ENV_MOD_VERSION=3.2.10 TEST_EASYBUILD_MODULES_TOOL=EnvironmentModulesC TEST_EASYBUILD_MODULE_SYNTAX=Tcl
Expand All @@ -19,7 +19,7 @@ matrix:
include:
# also test default configuration with Python 2.7
- python: 2.7
env: LMOD_VERSION=5.8
env: LMOD_VERSION=6.6.3
addons:
apt:
packages:
Expand Down
50 changes: 49 additions & 1 deletion easybuild/tools/module_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
:author: Fotis Georgatos (Uni.Lu, NTUA)
:author: Damian Alvarez (Forschungszentrum Juelich GmbH)
"""
import copy
import os
import re
import sys
Expand All @@ -45,7 +46,7 @@
from easybuild.tools.build_log import EasyBuildError
from easybuild.tools.config import build_option, get_module_syntax, install_path
from easybuild.tools.filetools import convert_name, mkdir, read_file, remove_file, resolve_path, symlink, write_file
from easybuild.tools.modules import ROOT_ENV_VAR_NAME_PREFIX, modules_tool
from easybuild.tools.modules import ROOT_ENV_VAR_NAME_PREFIX, EnvironmentModulesC, modules_tool
from easybuild.tools.utilities import quote_str


Expand Down Expand Up @@ -197,6 +198,53 @@ def prepend_paths(self, key, paths, allow_abs=False, expand_relpaths=True):
"""
return self.update_paths(key, paths, prepend=True, allow_abs=allow_abs, expand_relpaths=expand_relpaths)

def modulerc(self, module_version=None):
"""
Generate contents of .modulerc file, in Tcl syntax (compatible with all module tools, incl. Lmod)

:param module_version: specs for module-version statement (dict with 'modname', 'sym_version' & 'version' keys)
"""
modulerc = [ModuleGeneratorTcl.MODULE_SHEBANG]

if module_version:
if isinstance(module_version, dict):
expected_keys = ['modname', 'sym_version', 'version']
if sorted(module_version.keys()) == expected_keys:

module_version_statement = "module-version %(modname)s %(sym_version)s"

# for Environment Modules we need to guard the module-version statement,
# to avoid "Duplicate version symbol" warning messages where EasyBuild trips over,
# which occur because the .modulerc is parsed twice
# "module-info version <arg>" returns its argument if that argument is not a symbolic version (yet),
# and returns the corresponding real version in case the argument is an existing symbolic version
# cfr. https://sourceforge.net/p/modules/mailman/message/33399425/
if modules_tool().__class__ == EnvironmentModulesC:

modname, sym_version, version = [module_version[key] for key in expected_keys]

# determine module name with symbolic version
if version in modname:
# take a copy so we don't modify original value
module_version = copy.copy(module_version)
module_version['sym_modname'] = modname.replace(version, sym_version)
else:
raise EasyBuildError("Version '%s' does not appear in module name '%s'", version, modname)

module_version_statement = '\n'.join([
'if {"%(sym_modname)s" eq [module-info version %(sym_modname)s]} {',
' ' * 4 + module_version_statement,
"}",
])

modulerc.append(module_version_statement % module_version)
else:
raise EasyBuildError("Incorrect module_version spec, expected keys: %s", expected_keys)
else:
raise EasyBuildError("Incorrect module_version value type: %s", type(module_version))

return '\n'.join(modulerc)

# From this point on just not implemented methods

def check_group(self, group, error_msg=None):
Expand Down
3 changes: 2 additions & 1 deletion easybuild/tools/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
[^\s\(]*[^:/] # module name must not have '(' or whitespace in it, must not end with ':' or '/'
) # end named group for module name
(?P<default>\(default\))? # optional '(default)' that's not part of module name
(\([^()]+\))? # ignore '(...)' that is not part of module name (e.g. for symbolic versions)
\s*$ # ignore whitespace at the end of the line
""", re.VERBOSE),
}
Expand Down Expand Up @@ -1106,7 +1107,7 @@ class Lmod(ModulesTool):
"""Interface to Lmod."""
COMMAND = 'lmod'
COMMAND_ENVIRONMENT = 'LMOD_CMD'
REQ_VERSION = '5.8'
REQ_VERSION = '6.6.3'
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: Lmod 6.0 or newer is needed to pick up on .modulerc files in module file subdirectory.

Older Lmod 6.x versions have bugs that make it incompatible with EasyBuild in other (unrelated ways), hence the more recent 6.x being required.

Lmod 6.6.3 is the latest 6.x release, which was released Nov 13th 2016 (close to 2 years old), so I don't expect this to be a big problem.

Also, this way EasyBuild is still compatible with both Lmod 6.x & 7.x...

REQ_VERSION_DEPENDS_ON = '7.6.1'
VERSION_REGEXP = r"^Modules\s+based\s+on\s+Lua:\s+Version\s+(?P<version>\d\S*)\s"
USER_CACHE_DIR = os.path.join(os.path.expanduser('~'), '.lmod.d', '.cache')
Expand Down
42 changes: 41 additions & 1 deletion test/framework/module_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
from easybuild.framework.easyblock import EasyBlock
from easybuild.framework.easyconfig.easyconfig import EasyConfig, ActiveMNS
from easybuild.tools.build_log import EasyBuildError
from easybuild.tools.modules import Lmod
from easybuild.tools.modules import EnvironmentModulesC, Lmod
from easybuild.tools.utilities import quote_str
from test.framework.utilities import EnhancedTestCase, TestLoaderFiltered, find_full_path, init_config

Expand Down Expand Up @@ -315,6 +315,46 @@ def test_load(self):
init_config(build_options={'mod_depends_on': 'True'})
self.assertErrorRegex(EasyBuildError, expected, self.modgen.load_module, "mod_name")

def test_modulerc(self):
"""Test modulerc method."""
self.assertErrorRegex(EasyBuildError, "Incorrect module_version value type", self.modgen.modulerc, 'foo')

arg = {'foo': 'bar'}
error_pattern = "Incorrect module_version spec, expected keys"
self.assertErrorRegex(EasyBuildError, error_pattern, self.modgen.modulerc, arg)

modulerc = self.modgen.modulerc({'modname': 'test/1.2.3.4.5', 'sym_version': '1.2.3', 'version': '1.2.3.4.5'})

if self.modtool.__class__ == EnvironmentModulesC:
expected = '\n'.join([
'#%Module',
'if {"test/1.2.3" eq [module-info version test/1.2.3]} {',
' module-version test/1.2.3.4.5 1.2.3',
'}',
])
else:
expected = '\n'.join([
'#%Module',
"module-version test/1.2.3.4.5 1.2.3",
])

self.assertEqual(modulerc, expected)

write_file(os.path.join(self.test_prefix, 'test', '1.2.3.4.5'), '#%Module')
write_file(os.path.join(self.test_prefix, 'test', '.modulerc'), modulerc)

self.modtool.use(self.test_prefix)

# 'show' picks up on symbolic versions, regardless of modules tool being used
self.assertEqual(self.modtool.exist(['test/1.2.3.4.5', 'test/1.2.3.4', 'test/1.2.3']), [True, False, True])

# loading of module with symbolic version works
self.modtool.load(['test/1.2.3'])
# test/1.2.3.4.5 is actually loaded (rather than test/1.2.3)
res = self.modtool.list()
self.assertEqual(len(res), 1)
self.assertEqual(res[0]['mod_name'], 'test/1.2.3.4.5')

def test_unload(self):
"""Test unload part in generated module file."""

Expand Down