diff --git a/easybuild/framework/easyblock.py b/easybuild/framework/easyblock.py index ce5a9663f5..7335484019 100644 --- a/easybuild/framework/easyblock.py +++ b/easybuild/framework/easyblock.py @@ -48,6 +48,7 @@ import functools import glob import inspect +import itertools import json import os import random @@ -71,7 +72,7 @@ from easybuild.framework.easyconfig.easyconfig import ITERATE_OPTIONS, EasyConfig, ActiveMNS, get_easyblock_class from easybuild.framework.easyconfig.easyconfig import get_module_path, letter_dir_for, resolve_template from easybuild.framework.easyconfig.format.format import SANITY_CHECK_PATHS_DIRS, SANITY_CHECK_PATHS_FILES -from easybuild.framework.easyconfig.parser import fetch_parameters_from_easyconfig +from easybuild.framework.easyconfig.parser import fetch_parameters_from_easyconfig, ALTERNATIVE_EASYCONFIG_PARAMETERS from easybuild.framework.easyconfig.style import MAX_LINE_LENGTH from easybuild.framework.easyconfig.tools import dump_env_easyblock, get_paths_for from easybuild.framework.easyconfig.templates import TEMPLATE_NAMES_EASYBLOCK_RUN_STEP, template_constant_dict @@ -581,11 +582,17 @@ def fetch_patches(self, patch_specs=None, extension=False, checksums=None): Add a list of patches. All patches will be checked if a file exists (or can be located) """ - post_install_patches = [] if patch_specs is None: # if no patch_specs are specified, use all pre-install and post-install patches post_install_patches = self.cfg['postinstallpatches'] patch_specs = self.cfg['patches'] + post_install_patches + elif isinstance(patch_specs, list): + post_install_patches = [] + else: + if not isinstance(patch_specs, tuple) or len(patch_specs) != 2: + raise EasyBuildError('Patch specs must be a tuple of (patches, post-install patches) or a list') + post_install_patches = patch_specs[1] + patch_specs = itertools.chain(*patch_specs) patches = [] for index, patch_spec in enumerate(patch_specs): @@ -765,11 +772,15 @@ def collect_exts_file_info(self, fetch_files=True, verify_checksums=True): ) # locate extension patches (if any), and verify checksums - ext_patches = resolve_template(ext_options.get('patches', []), template_values) + ext_patch_specs = resolve_template(( + ext_options.get('patches', []), + ext_options.get(ALTERNATIVE_EASYCONFIG_PARAMETERS['post_install_patches'], + ext_options.get('post_install_patches', [])) + ), template_values) if fetch_files: - ext_patches = self.fetch_patches(patch_specs=ext_patches, extension=True) + ext_patches = self.fetch_patches(patch_specs=ext_patch_specs, extension=True) else: - ext_patches = [create_patch_info(p) for p in ext_patches] + ext_patches = [create_patch_info(p) for p in itertools.chain(*ext_patch_specs)] if ext_patches: self.log.debug('Found patches for extension %s: %s', ext_name, ext_patches) @@ -3264,11 +3275,12 @@ def apply_post_install_patches(self, patches=None): # To allow postinstallpatches for Bundle, and derived, easyblocks we directly call EasyBlock.patch_step EasyBlock.patch_step(self, beginpath=self.installdir, patches=patches) - def print_post_install_messages(self): + def print_post_install_messages(self, msgs=None): """ Print post-install messages that are specified via the 'postinstallmsgs' easyconfig parameter. """ - msgs = self.cfg['postinstallmsgs'] or [] + if msgs is None: + msgs = self.cfg['postinstallmsgs'] or [] for msg in msgs: print_msg(msg, log=self.log) diff --git a/easybuild/framework/extension.py b/easybuild/framework/extension.py index 6e4fcc4a90..22f2b9f42e 100644 --- a/easybuild/framework/extension.py +++ b/easybuild/framework/extension.py @@ -232,6 +232,8 @@ def post_install_extension(self): Stuff to do after installing a extension. """ self.master.run_post_install_commands(commands=self.cfg.get('postinstallcmds', [])) + self.master.apply_post_install_patches(patches=[p for p in self.patches if p['postinstall']]) + self.master.print_post_install_messages(msgs=self.cfg.get('postinstallmsgs', [])) def install_extension_substep(self, substep, *args, **kwargs): """ diff --git a/test/framework/toy_build.py b/test/framework/toy_build.py index f9adda1df9..9045494bc7 100644 --- a/test/framework/toy_build.py +++ b/test/framework/toy_build.py @@ -1355,25 +1355,27 @@ def test_toy_extension_patches_postinstallcmds(self): write_file(os.path.join(self.test_prefix, 'test.txt'), 'test123') test_ec = os.path.join(self.test_prefix, 'test.eb') - test_ec_txt = '\n'.join([ - toy_ec_txt, - 'exts_defaultclass = "DummyExtension"', - 'exts_list = [', - ' ("bar", "0.0", {', - ' "buildopts": " && ls -l test.txt",', - ' "patches": [', - ' "bar-0.0_fix-silly-typo-in-printf-statement.patch",', # normal patch - ' ("bar-0.0_fix-very-silly-typo-in-printf-statement.patch", 0),', # patch with patch level - ' ("test.txt", "."),', # file to copy to build dir (not a real patch file) - ' ],', - ' "postinstallcmds": ["touch %(installdir)s/created-via-postinstallcmds.txt"],', - ' }),', - ']', - ]) + test_ec_txt = f"{toy_ec_txt}\n" + textwrap.dedent(""" + exts_defaultclass = "DummyExtension" + exts_list = [ + ("bar", "0.0", { + "buildopts": " && ls -l test.txt", + "patches": [ + "bar-0.0_fix-silly-typo-in-printf-statement.patch", # normal patch + ("bar-0.0_fix-very-silly-typo-in-printf-statement.patch", 0), # patch with patch level + ("test.txt", "."), # file to copy to build dir (not a real patch file) + ], + "post_install_cmds": ["touch %(installdir)s/created-via-postinstallcmds.txt"], + "post_install_patches": [("test.txt", "test_ext.txt")], + "post_install_msgs": ["Hello World!"], + }), + ] + """) write_file(test_ec, test_ec_txt) with self.mocked_stdout_stderr(): - self._test_toy_build(ec_file=test_ec) + self._test_toy_build(ec_file=test_ec, extra_args=['--disable-cleanup-builddir']) + self.assertIn("Hello World!", self.get_stdout()) installdir = os.path.join(self.test_installpath, 'software', 'toy', '0.0') @@ -1386,6 +1388,10 @@ def test_toy_extension_patches_postinstallcmds(self): # verify that post-install command for 'bar' extension was executed fn = 'created-via-postinstallcmds.txt' self.assertExists(os.path.join(installdir, fn)) + # Same for all patches + self.assertExists(os.path.join(self.test_buildpath, 'toy', '0.0', 'system-system', + 'bar', 'bar-0.0', 'test.txt')) + self.assertExists(os.path.join(installdir, 'test_ext.txt')) # make sure that patch file for extension was copied to 'easybuild' subdir in installation directory easybuild_subdir = os.path.join(installdir, 'easybuild')