Skip to content

Commit d6ac47a

Browse files
author
ocaisa
authored
Merge pull request #2575 from boegel/modulerc
add modulerc method to ModuleGenerator class
2 parents 43ab583 + e317816 commit d6ac47a

4 files changed

Lines changed: 95 additions & 6 deletions

File tree

.travis.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ python: 2.6
33
env:
44
matrix:
55
# purposely specifying slowest builds first, to gain time overall
6-
- LMOD_VERSION=5.8
7-
- LMOD_VERSION=5.8 TEST_EASYBUILD_MODULE_SYNTAX=Tcl
6+
- LMOD_VERSION=6.6.3
7+
- LMOD_VERSION=6.6.3 TEST_EASYBUILD_MODULE_SYNTAX=Tcl
88
- LMOD_VERSION=7.7.16
99
- LMOD_VERSION=7.7.16 TEST_EASYBUILD_MODULE_SYNTAX=Tcl
1010
- ENV_MOD_VERSION=3.2.10 TEST_EASYBUILD_MODULES_TOOL=EnvironmentModulesC TEST_EASYBUILD_MODULE_SYNTAX=Tcl
@@ -19,7 +19,7 @@ matrix:
1919
include:
2020
# also test default configuration with Python 2.7
2121
- python: 2.7
22-
env: LMOD_VERSION=5.8
22+
env: LMOD_VERSION=6.6.3
2323
addons:
2424
apt:
2525
packages:

easybuild/tools/module_generator.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
:author: Fotis Georgatos (Uni.Lu, NTUA)
3434
:author: Damian Alvarez (Forschungszentrum Juelich GmbH)
3535
"""
36+
import copy
3637
import os
3738
import re
3839
import sys
@@ -45,7 +46,7 @@
4546
from easybuild.tools.build_log import EasyBuildError
4647
from easybuild.tools.config import build_option, get_module_syntax, install_path
4748
from easybuild.tools.filetools import convert_name, mkdir, read_file, remove_file, resolve_path, symlink, write_file
48-
from easybuild.tools.modules import ROOT_ENV_VAR_NAME_PREFIX, modules_tool
49+
from easybuild.tools.modules import ROOT_ENV_VAR_NAME_PREFIX, EnvironmentModulesC, modules_tool
4950
from easybuild.tools.utilities import quote_str
5051

5152

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

201+
def modulerc(self, module_version=None):
202+
"""
203+
Generate contents of .modulerc file, in Tcl syntax (compatible with all module tools, incl. Lmod)
204+
205+
:param module_version: specs for module-version statement (dict with 'modname', 'sym_version' & 'version' keys)
206+
"""
207+
modulerc = [ModuleGeneratorTcl.MODULE_SHEBANG]
208+
209+
if module_version:
210+
if isinstance(module_version, dict):
211+
expected_keys = ['modname', 'sym_version', 'version']
212+
if sorted(module_version.keys()) == expected_keys:
213+
214+
module_version_statement = "module-version %(modname)s %(sym_version)s"
215+
216+
# for Environment Modules we need to guard the module-version statement,
217+
# to avoid "Duplicate version symbol" warning messages where EasyBuild trips over,
218+
# which occur because the .modulerc is parsed twice
219+
# "module-info version <arg>" returns its argument if that argument is not a symbolic version (yet),
220+
# and returns the corresponding real version in case the argument is an existing symbolic version
221+
# cfr. https://sourceforge.net/p/modules/mailman/message/33399425/
222+
if modules_tool().__class__ == EnvironmentModulesC:
223+
224+
modname, sym_version, version = [module_version[key] for key in expected_keys]
225+
226+
# determine module name with symbolic version
227+
if version in modname:
228+
# take a copy so we don't modify original value
229+
module_version = copy.copy(module_version)
230+
module_version['sym_modname'] = modname.replace(version, sym_version)
231+
else:
232+
raise EasyBuildError("Version '%s' does not appear in module name '%s'", version, modname)
233+
234+
module_version_statement = '\n'.join([
235+
'if {"%(sym_modname)s" eq [module-info version %(sym_modname)s]} {',
236+
' ' * 4 + module_version_statement,
237+
"}",
238+
])
239+
240+
modulerc.append(module_version_statement % module_version)
241+
else:
242+
raise EasyBuildError("Incorrect module_version spec, expected keys: %s", expected_keys)
243+
else:
244+
raise EasyBuildError("Incorrect module_version value type: %s", type(module_version))
245+
246+
return '\n'.join(modulerc)
247+
200248
# From this point on just not implemented methods
201249

202250
def check_group(self, group, error_msg=None):

easybuild/tools/modules.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
[^\s\(]*[^:/] # module name must not have '(' or whitespace in it, must not end with ':' or '/'
112112
) # end named group for module name
113113
(?P<default>\(default\))? # optional '(default)' that's not part of module name
114+
(\([^()]+\))? # ignore '(...)' that is not part of module name (e.g. for symbolic versions)
114115
\s*$ # ignore whitespace at the end of the line
115116
""", re.VERBOSE),
116117
}
@@ -1106,7 +1107,7 @@ class Lmod(ModulesTool):
11061107
"""Interface to Lmod."""
11071108
COMMAND = 'lmod'
11081109
COMMAND_ENVIRONMENT = 'LMOD_CMD'
1109-
REQ_VERSION = '5.8'
1110+
REQ_VERSION = '6.6.3'
11101111
REQ_VERSION_DEPENDS_ON = '7.6.1'
11111112
VERSION_REGEXP = r"^Modules\s+based\s+on\s+Lua:\s+Version\s+(?P<version>\d\S*)\s"
11121113
USER_CACHE_DIR = os.path.join(os.path.expanduser('~'), '.lmod.d', '.cache')

test/framework/module_generator.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
from easybuild.framework.easyblock import EasyBlock
4646
from easybuild.framework.easyconfig.easyconfig import EasyConfig, ActiveMNS
4747
from easybuild.tools.build_log import EasyBuildError
48-
from easybuild.tools.modules import Lmod
48+
from easybuild.tools.modules import EnvironmentModulesC, Lmod
4949
from easybuild.tools.utilities import quote_str
5050
from test.framework.utilities import EnhancedTestCase, TestLoaderFiltered, find_full_path, init_config
5151

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

318+
def test_modulerc(self):
319+
"""Test modulerc method."""
320+
self.assertErrorRegex(EasyBuildError, "Incorrect module_version value type", self.modgen.modulerc, 'foo')
321+
322+
arg = {'foo': 'bar'}
323+
error_pattern = "Incorrect module_version spec, expected keys"
324+
self.assertErrorRegex(EasyBuildError, error_pattern, self.modgen.modulerc, arg)
325+
326+
modulerc = self.modgen.modulerc({'modname': 'test/1.2.3.4.5', 'sym_version': '1.2.3', 'version': '1.2.3.4.5'})
327+
328+
if self.modtool.__class__ == EnvironmentModulesC:
329+
expected = '\n'.join([
330+
'#%Module',
331+
'if {"test/1.2.3" eq [module-info version test/1.2.3]} {',
332+
' module-version test/1.2.3.4.5 1.2.3',
333+
'}',
334+
])
335+
else:
336+
expected = '\n'.join([
337+
'#%Module',
338+
"module-version test/1.2.3.4.5 1.2.3",
339+
])
340+
341+
self.assertEqual(modulerc, expected)
342+
343+
write_file(os.path.join(self.test_prefix, 'test', '1.2.3.4.5'), '#%Module')
344+
write_file(os.path.join(self.test_prefix, 'test', '.modulerc'), modulerc)
345+
346+
self.modtool.use(self.test_prefix)
347+
348+
# 'show' picks up on symbolic versions, regardless of modules tool being used
349+
self.assertEqual(self.modtool.exist(['test/1.2.3.4.5', 'test/1.2.3.4', 'test/1.2.3']), [True, False, True])
350+
351+
# loading of module with symbolic version works
352+
self.modtool.load(['test/1.2.3'])
353+
# test/1.2.3.4.5 is actually loaded (rather than test/1.2.3)
354+
res = self.modtool.list()
355+
self.assertEqual(len(res), 1)
356+
self.assertEqual(res[0]['mod_name'], 'test/1.2.3.4.5')
357+
318358
def test_unload(self):
319359
"""Test unload part in generated module file."""
320360

0 commit comments

Comments
 (0)