Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use one argument 'module swap' statements in Tcl modulefiles (required by Modules 4.2.3+) #3397

Merged
merged 6 commits into from
Aug 27, 2020
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
7 changes: 6 additions & 1 deletion easybuild/tools/module_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -962,7 +962,12 @@ def swap_module(self, mod_name_out, mod_name_in, guarded=True):
:param mod_name_in: name of module to load (swap in)
:param guarded: guard 'swap' statement, fall back to 'load' if module being swapped out is not loaded
"""
body = "module swap %s %s" % (mod_name_out, mod_name_in)
# In Modules 4.2.3+ a 2-argument swap 'module swap foo foo/X.Y.Z' will fail as the unloaded 'foo'
# means all 'foo' modules conflict and 'foo/X.Y.Z' will not load. A 1-argument swap like
# 'module swap foo/X.Y.Z' will unload any currently loaded 'foo' without it becoming conflicting
# and successfully load the new module.
# See: https://modules.readthedocs.io/en/latest/NEWS.html#modules-4-2-3-2019-03-23
body = "module swap %s" % (mod_name_in)
Copy link
Member

Choose a reason for hiding this comment

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

@welucas2 It's probably worth adding a comment above here to explain why we use single argument swap commands.

Before we didn't have a good reason to use swap foo foo/<version> rather than swap foo/<version> I think, but now we clearly do, so it's worth mentioning that I think...

I'll also check with the other module tools we support whether single argument swap works for those, I'm not sure that's covered by the test suite right now (we just do a text comparison of the generated module file in the tests currently).

if guarded:
alt_body = self.LOAD_TEMPLATE % {'mod_name': mod_name_in}
swap_statement = [self.conditional_statement(self.is_loaded(mod_name_out), body, else_body=alt_body)]
Expand Down
24 changes: 21 additions & 3 deletions test/framework/module_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,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 EnvironmentModulesC, Lmod
from easybuild.tools.modules import EnvironmentModulesC, EnvironmentModulesTcl, Lmod
from easybuild.tools.utilities import quote_str
from test.framework.utilities import EnhancedTestCase, TestLoaderFiltered, find_full_path, init_config

Expand Down Expand Up @@ -610,7 +610,7 @@ def test_swap(self):
if self.MODULE_GENERATOR_CLASS == ModuleGeneratorTcl:
expected = '\n'.join([
'',
"module swap foo bar",
"module swap bar",
'',
])
else:
Expand All @@ -627,7 +627,7 @@ def test_swap(self):
expected = '\n'.join([
'',
"if { [ is-loaded foo ] } {",
" module swap foo bar",
" module swap bar",
'} else {',
" module load bar",
'}',
Expand All @@ -647,6 +647,24 @@ def test_swap(self):
self.assertEqual(expected, self.modgen.swap_module('foo', 'bar', guarded=True))
self.assertEqual(expected, self.modgen.swap_module('foo', 'bar'))

# create tiny test Tcl module to make sure that tested modules tools support single-argument swap
# see https://github.com/easybuilders/easybuild-framework/issues/3396;
# this is known to fail with the ancient Tcl-only implementation of environment modules,
# but that's considered to be a non-issue (since this is mostly relevant for Cray systems,
# which are either using EnvironmentModulesC (3.2.10), EnvironmentModules (4.x) or Lmod...
if self.MODULE_GENERATOR_CLASS == ModuleGeneratorTcl and self.modtool.__class__ != EnvironmentModulesTcl:
test_mod_txt = "#%Module\nmodule swap GCC/7.3.0-2.30"

test_mod_fn = 'test_single_arg_swap/1.2.3'
write_file(os.path.join(self.test_prefix, test_mod_fn), test_mod_txt)

self.modtool.load(['GCC/4.6.3'])
self.modtool.use(self.test_prefix)
self.modtool.load(['test_single_arg_swap/1.2.3'])

expected = ['GCC/7.3.0-2.30', 'test_single_arg_swap/1.2.3']
self.assertEqual(sorted([m['mod_name'] for m in self.modtool.list()]), expected)

def test_append_paths(self):
"""Test generating append-paths statements."""
# test append_paths
Expand Down