From 7cb196a126ea383f4b12fc6e57210c2448350cef Mon Sep 17 00:00:00 2001 From: Michael H Kelsey Date: Tue, 5 May 2020 09:54:01 -0500 Subject: [PATCH 1/5] New variable 'moddependpaths' to resolve dependencies at load time. --- easybuild/framework/easyblock.py | 20 +++++++++++ easybuild/framework/easyconfig/default.py | 1 + test/framework/easyblock.py | 42 +++++++++++++++++++++++ 3 files changed, 63 insertions(+) diff --git a/easybuild/framework/easyblock.py b/easybuild/framework/easyblock.py index 1d4e06247d..e169b70da8 100644 --- a/easybuild/framework/easyblock.py +++ b/easybuild/framework/easyblock.py @@ -1014,6 +1014,25 @@ def make_devel_module(self, create_in_builddir=False): # cleanup: unload fake module, remove fake module dir self.clean_up_fake_module(fake_mod_data) + def make_module_deppaths(self): + """ + Add specific 'module use' actions to module file, in order to find + dependencies outside the end user's MODULEPATH. + """ + deppaths = self.cfg['moddependpaths'] + if not deppaths: + return '' + elif not isinstance(deppaths, (str, tuple, list)): + raise EasyBuildError("moddependpaths value %s (type: %s) is not a string or collection", + deppaths, type(deppaths)) + + if isinstance(deppaths, str): + txt = self.module_generator.use([deppaths], guarded=True) + else: + txt = self.module_generator.use(deppaths, guarded=True) + + return txt + def make_module_dep(self, unload_info=None): """ Make the dependencies for the module file. @@ -2771,6 +2790,7 @@ def make_module_step(self, fake=False): txt += self.make_module_description() txt += self.make_module_group_check() + txt += self.make_module_deppaths() txt += self.make_module_dep() txt += self.make_module_extend_modpath() txt += self.make_module_req() diff --git a/easybuild/framework/easyconfig/default.py b/easybuild/framework/easyconfig/default.py index 1fe7c705b6..796879d592 100644 --- a/easybuild/framework/easyconfig/default.py +++ b/easybuild/framework/easyconfig/default.py @@ -154,6 +154,7 @@ 'multi_deps': [{}, "Dict of lists of dependency versions over which to iterate", DEPENDENCIES], 'multi_deps_load_default': [True, "Load module for first version listed in multi_deps by default", DEPENDENCIES], 'osdependencies': [[], "OS dependencies that should be present on the system", DEPENDENCIES], + 'moddependpaths': [None, "Absolute path or paths that should be searched for dependencies", DEPENDENCIES], # LICENSE easyconfig parameters 'group': [None, "Name of the user group for which the software should be available; " diff --git a/test/framework/easyblock.py b/test/framework/easyblock.py index 8139e326ae..6802391ce8 100644 --- a/test/framework/easyblock.py +++ b/test/framework/easyblock.py @@ -516,6 +516,48 @@ def test_make_module_extra(self): for pattern in patterns: self.assertTrue(re.search(pattern, txt, re.M), "Pattern '%s' found in: %s" % (pattern, txt)) + def test_make_module_deppaths(self): + """Test for make_module_deppaths""" + init_config(build_options={'silent': True}) + + self.contents = '\n'.join([ + 'easyblock = "ConfigureMake"', + 'name = "pi"', + 'version = "3.14"', + 'homepage = "http://example.com"', + 'description = "test easyconfig"', + "toolchain = {'name': 'gompi', 'version': '2018a'}", + 'moddependpaths = "/path/to/mods"', + 'dependencies = [', + " ('FFTW', '3.3.7'),", + ']', + ]) + self.writeEC() + eb = EasyBlock(EasyConfig(self.eb_file)) + + eb.installdir = os.path.join(config.install_path(), 'pi', '3.14') + eb.check_readiness_step() + eb.make_builddir() + eb.prepare_step() + + if get_module_syntax() == 'Tcl': + use_load = '\n'.join([ + "if { [ file isdirectory /path/to/mods ] } {", + " module use /path/to/mods", + "}", + ]) + elif get_module_syntax() == 'Lua': + use_load = '\n'.join([ + 'if isDir("/path/to/mods") then', + ' prepend_path("MODULEPATH", "/path/to/mods")', + 'end', + ]) + else: + self.assertTrue(False, "Unknown module syntax: %s" % get_module_syntax()) + + expected = use_load + self.assertEqual(eb.make_module_deppaths().strip(), expected) + def test_make_module_dep(self): """Test for make_module_dep""" init_config(build_options={'silent': True}) From 1eb9a71a60526cb2fa6298960f0a06e5a8cdaeca Mon Sep 17 00:00:00 2001 From: Michael H Kelsey Date: Tue, 5 May 2020 10:06:00 -0500 Subject: [PATCH 2/5] Appease the Hound, removing extraneous whitespace and tab characters. --- easybuild/framework/easyblock.py | 2 +- test/framework/easyblock.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/easybuild/framework/easyblock.py b/easybuild/framework/easyblock.py index e169b70da8..ab0fec65af 100644 --- a/easybuild/framework/easyblock.py +++ b/easybuild/framework/easyblock.py @@ -1030,7 +1030,7 @@ def make_module_deppaths(self): txt = self.module_generator.use([deppaths], guarded=True) else: txt = self.module_generator.use(deppaths, guarded=True) - + return txt def make_module_dep(self, unload_info=None): diff --git a/test/framework/easyblock.py b/test/framework/easyblock.py index 6802391ce8..2df1e8adde 100644 --- a/test/framework/easyblock.py +++ b/test/framework/easyblock.py @@ -543,7 +543,7 @@ def test_make_module_deppaths(self): if get_module_syntax() == 'Tcl': use_load = '\n'.join([ "if { [ file isdirectory /path/to/mods ] } {", - " module use /path/to/mods", + " module use /path/to/mods", "}", ]) elif get_module_syntax() == 'Lua': @@ -557,7 +557,7 @@ def test_make_module_deppaths(self): expected = use_load self.assertEqual(eb.make_module_deppaths().strip(), expected) - + def test_make_module_dep(self): """Test for make_module_dep""" init_config(build_options={'silent': True}) From 82e3ee39c03f53271e094222419c755838a1ba4a Mon Sep 17 00:00:00 2001 From: Michael H Kelsey Date: Tue, 5 May 2020 10:48:39 -0500 Subject: [PATCH 3/5] Fixing quoting in expected Tcl output in deppaths test. --- test/framework/easyblock.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/framework/easyblock.py b/test/framework/easyblock.py index 2df1e8adde..53eeb4c331 100644 --- a/test/framework/easyblock.py +++ b/test/framework/easyblock.py @@ -542,9 +542,9 @@ def test_make_module_deppaths(self): if get_module_syntax() == 'Tcl': use_load = '\n'.join([ - "if { [ file isdirectory /path/to/mods ] } {", - " module use /path/to/mods", - "}", + 'if { [ file isdirectory "/path/to/mods" ] } {', + ' module use "/path/to/mods"', + '}', ]) elif get_module_syntax() == 'Lua': use_load = '\n'.join([ From 1510de545a599019d6ebfe0e67f9707bbd460808 Mon Sep 17 00:00:00 2001 From: Michael H Kelsey Date: Wed, 6 May 2020 09:49:41 -0500 Subject: [PATCH 4/5] Improve phrasing of new moddependpaths descriptions. --- easybuild/framework/easyblock.py | 4 ++-- easybuild/framework/easyconfig/default.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/easybuild/framework/easyblock.py b/easybuild/framework/easyblock.py index ab0fec65af..5cdfbae2f5 100644 --- a/easybuild/framework/easyblock.py +++ b/easybuild/framework/easyblock.py @@ -1022,8 +1022,8 @@ def make_module_deppaths(self): deppaths = self.cfg['moddependpaths'] if not deppaths: return '' - elif not isinstance(deppaths, (str, tuple, list)): - raise EasyBuildError("moddependpaths value %s (type: %s) is not a string or collection", + elif not isinstance(deppaths, (str, list, tuple)): + raise EasyBuildError("moddependpaths value %s (type: %s) is not a string, list or tuple", deppaths, type(deppaths)) if isinstance(deppaths, str): diff --git a/easybuild/framework/easyconfig/default.py b/easybuild/framework/easyconfig/default.py index 796879d592..e164294717 100644 --- a/easybuild/framework/easyconfig/default.py +++ b/easybuild/framework/easyconfig/default.py @@ -154,7 +154,7 @@ 'multi_deps': [{}, "Dict of lists of dependency versions over which to iterate", DEPENDENCIES], 'multi_deps_load_default': [True, "Load module for first version listed in multi_deps by default", DEPENDENCIES], 'osdependencies': [[], "OS dependencies that should be present on the system", DEPENDENCIES], - 'moddependpaths': [None, "Absolute path or paths that should be searched for dependencies", DEPENDENCIES], + 'moddependpaths': [None, "Absolute path or paths to prepend to MODULEPATH before loading the module's dependencies", DEPENDENCIES], # LICENSE easyconfig parameters 'group': [None, "Name of the user group for which the software should be available; " From 3f61876796f1bc75172ed857266bf7d8978a1362 Mon Sep 17 00:00:00 2001 From: Michael H Kelsey Date: Wed, 6 May 2020 09:52:30 -0500 Subject: [PATCH 5/5] Appease the Hound after improving phrasing of new moddependpaths descriptions. --- easybuild/framework/easyconfig/default.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/framework/easyconfig/default.py b/easybuild/framework/easyconfig/default.py index e164294717..904a7d7bd7 100644 --- a/easybuild/framework/easyconfig/default.py +++ b/easybuild/framework/easyconfig/default.py @@ -154,7 +154,7 @@ 'multi_deps': [{}, "Dict of lists of dependency versions over which to iterate", DEPENDENCIES], 'multi_deps_load_default': [True, "Load module for first version listed in multi_deps by default", DEPENDENCIES], 'osdependencies': [[], "OS dependencies that should be present on the system", DEPENDENCIES], - 'moddependpaths': [None, "Absolute path or paths to prepend to MODULEPATH before loading the module's dependencies", DEPENDENCIES], + 'moddependpaths': [None, "Absolute path(s) to prepend to MODULEPATH before loading dependencies", DEPENDENCIES], # LICENSE easyconfig parameters 'group': [None, "Name of the user group for which the software should be available; "