Skip to content
Merged
41 changes: 33 additions & 8 deletions easybuild/framework/easyblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ def __init__(self, ec, logfile=None):
self.skip = None
self.module_extra_extensions = '' # extra stuff for module file required by extensions

# indicates whether or not this instance represents an extension or not;
# indicates whether or not this instance represents an extension
# may be set to True by ExtensionEasyBlock
self.is_extension = False

Expand Down Expand Up @@ -684,12 +684,11 @@ def collect_exts_file_info(self, fetch_files=True, verify_checksums=True):
'version': ext_version,
'options': ext_options,
'github_account': ext_options.get('github_account', orig_github_account),
# if a particular easyblock is specified, make sure it's used
# (this is picked up by init_ext_instances)
'easyblock': ext_options.get('easyblock', None),
}

# if a particular easyblock is specified, make sure it's used
# (this is picked up by init_ext_instances)
ext_src['easyblock'] = ext_options.get('easyblock', None)

# construct dictionary with template values;
# inherited from parent, except for name/version templates which are specific to this extension
template_values = copy.deepcopy(self.cfg.template_values)
Expand Down Expand Up @@ -1142,14 +1141,14 @@ def obtain_file_raise_on_failure(self, filename, extension=False, urls=None, dow
@property
def name(self):
"""
Shortcut the get the module name.
Shortcut to get the module name.
"""
return self.cfg['name']

@property
def version(self):
"""
Shortcut the get the module version.
Shortcut to get the module version.
"""
return self.cfg['version']

Expand Down Expand Up @@ -1863,7 +1862,7 @@ def inject_module_extra_paths(self):
msg += f"and paths='{env_var}'"
self.log.debug(msg)

def expand_module_search_path(self, search_path, path_type=ModEnvVarType.PATH_WITH_FILES):
def expand_module_search_path(self, *_, **__):
"""
REMOVED in EasyBuild 5.1, use EasyBlock.module_load_environment.expand_paths instead
"""
Expand Down Expand Up @@ -2395,6 +2394,10 @@ def fake_module_environment(self, extra_modules=None, with_build_deps=False):
fake_mod_data = None

if with_build_deps:
if extra_modules:
print_warning("`with_build_deps` overwrites `extra_modules` in fake_module_environment. "
"Until EasyBuild 6 add the build dependencies to `extra_modules` instead",
log=self.log)
# load modules for build dependencies as extra modules
extra_modules = [dep['short_mod_name'] for dep in self.cfg.dependencies(build_only=True)]

Expand All @@ -2407,6 +2410,28 @@ def fake_module_environment(self, extra_modules=None, with_build_deps=False):
if fake_mod_data:
self.clean_up_fake_module(fake_mod_data)

@contextmanager
def sanity_check_module_environment(self, extra_modules=None, check_loaded=True):
"""Load/Unload module for performing sanity checks"""
if self.sanity_check_module_loaded and check_loaded:
raise EasyBuildError("Sanity check module is already loaded and must not be loaded again")

if self.sanity_check_module_loaded:
unload_module = False
else:
self.sanity_check_load_module(extra_modules=extra_modules)
unload_module = True

try:
yield
finally:
# cleanup (unload fake module, remove fake module dir)
if unload_module:
if self.fake_mod_data:
self.clean_up_fake_module(self.fake_mod_data)
self.fake_mod_data = None
self.sanity_check_module_loaded = False
Comment thread
Flamefire marked this conversation as resolved.

def guess_start_dir(self):
"""
Return the directory where to start the whole configure/make/make install cycle from
Expand Down
2 changes: 1 addition & 1 deletion easybuild/framework/extensioneasyblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ def sanity_check_step(self, exts_filter=None, custom_paths=None, custom_commands
# take into account that module may already be loaded earlier in sanity check
if not (self.sanity_check_module_loaded or self.is_extension or self.dry_run):
for extra_modules in lists_of_extra_modules:
with self.fake_module_environment(extra_modules=extra_modules):
with self.sanity_check_module_environment(extra_modules=extra_modules):
if extra_modules:
info_msg = f"Running extension sanity check with extra modules: {', '.join(extra_modules)}"
self.log.info(info_msg)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def extra_options():
"""Custom easyconfig parameters for toy extensions."""
extra_vars = {
'toy_ext_param': ['', "Toy extension parameter", CUSTOM],
'toy_custom_sanity_check_cmds': [None, "Optional list of custom command to run in sanity check", CUSTOM],
}
return ExtensionEasyBlock.extra_options(extra_vars=extra_vars)

Expand Down Expand Up @@ -112,4 +113,5 @@ def sanity_check_step(self, *args, **kwargs):
}
if self.src:
custom_paths['files'].extend(['bin/%s' % self.name, 'lib/lib%s.a' % self.name])
return super().sanity_check_step(custom_paths=custom_paths)
return super().sanity_check_step(custom_paths=custom_paths,
custom_commands=self.cfg['toy_custom_sanity_check_cmds'])
60 changes: 43 additions & 17 deletions test/framework/toy_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -2494,6 +2494,32 @@ def test_toy_sanity_check_commands(self):
regex = re.compile('^.*/eb-[^/]+/eb-sanity-check-[^/]+\n[ ]*0$')
self.assertTrue(regex.match(out_txt), f"Pattern '{regex.pattern}' should match in: {out_txt}")

def test_toy_extension_sanity_check(self):
"""Check sanity check for extensions:
Custom_commands from easyblocks are run."""
test_ec_txt = TOY_EC_TXT
test_ec_txt += '\n' + textwrap.dedent("""
exts_list = [
('barbar', '0.0', {
'exts_filter': ('ls -l lib/lib%(ext_name)s.a', ''),
'toy_custom_sanity_check_cmds': ['echo "Run-Custom-Cmd for %(name)s" && PLACEHOLDER'],
'sanity_check_paths': {'dirs': [], 'files': ['lib/libbarbar.a']},
})
]
""")
test_ec = os.path.join(self.test_prefix, 'test.eb')
write_file(test_ec, test_ec_txt.replace('PLACEHOLDER', 'false'))
error_pattern = 'sanity check command echo "Run-Custom-Cmd for barbar" && false failed with exit code 1'
with self.mocked_stdout_stderr():
self.assertErrorRegex(EasyBuildError, error_pattern, self._test_toy_build, ec_file=test_ec,
raise_error=True, verbose=False)

write_file(test_ec, test_ec_txt.replace('PLACEHOLDER', 'true'))
with self.mocked_stdout_stderr(), self.log_to_testlogfile() as logfile:
self._test_toy_build(ec_file=test_ec, raise_error=True)
logtxt = read_file(logfile)
self.assertRegex(logtxt, 'sanity check command .*Run-Custom-Cmd for barbar.*ran successfully',)

def test_sanity_check_paths_lib64(self):
"""Test whether fallback in sanity check for lib64/ equivalents of library files works."""
# modify test easyconfig: move lib/libtoy.a to lib64/libtoy.a
Expand Down Expand Up @@ -2710,12 +2736,12 @@ def test_toy_build_enhanced_sanity_check(self):
stdout = self.get_stdout()
self.mock_stdout(False)

pattern_lines = [
r"^== sanity checking\.\.\.",
r" >> file 'bin/toy' found: OK",
]
regex = re.compile(r'\n'.join(pattern_lines), re.M)
self.assertTrue(regex.search(stdout), "Pattern '%s' should be found in: %s" % (regex.pattern, stdout))
expected_out = textwrap.dedent("""
== sanity checking...
>> loading modules: toy/0.0...
>> file 'bin/toy' found: OK
""")
self.assertIn(expected_out, stdout)

# no directories are checked in sanity check now, only files (since dirs is an empty list)
regex = re.compile(r"directory .* found:", re.M)
Expand Down Expand Up @@ -3497,19 +3523,19 @@ def test_toy_build_trace(self):
r'',
]),
r" >> command completed: exit 0, ran in .*",
r'^' + r'\n'.join([
r"== sanity checking\.\.\.",
r" >> file 'bin/yot' or 'bin/toy' found: OK",
r" >> \(non-empty\) directory 'bin' found: OK",
r" >> loading modules: toy/0.0\.\.\.",
r" >> running command 'toy' \.\.\.",
r" >> result for command 'toy': OK",
]) + r'$',
r"^== creating module\.\.\.\n >> generating module file @ .*/modules/all/toy/0\.0(?:\.lua)?$",
]
for pattern in patterns:
regex = re.compile(pattern, re.M)
self.assertTrue(regex.search(stdout), "Pattern '%s' found in: %s" % (regex.pattern, stdout))
self.assert_multi_regex(patterns, stdout)
expected_stdout = textwrap.dedent("""
== sanity checking...
>> loading modules: toy/0.0...
>> file 'bin/yot' or 'bin/toy' found: OK
>> (non-empty) directory 'bin' found: OK
>> loading modules: toy/0.0...
>> running command 'toy' ...
>> result for command 'toy': OK
""")
self.assertIn(expected_stdout, stdout)

def test_toy_build_hooks(self):
"""Test use of --hooks."""
Expand Down