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

add support for post-install patches #3974

Merged
merged 2 commits into from
Mar 2, 2022
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
28 changes: 24 additions & 4 deletions easybuild/framework/easyblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,13 +454,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:
patch_specs = self.cfg['patches']
# 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

patches = []
for index, patch_spec in enumerate(patch_specs):

patch_info = create_patch_info(patch_spec)
patch_info['postinstall'] = patch_spec in post_install_patches
lexming marked this conversation as resolved.
Show resolved Hide resolved

force_download = build_option('force_download') in [FORCE_DOWNLOAD_ALL, FORCE_DOWNLOAD_PATCHES]
path = self.obtain_file(patch_info['name'], extension=extension, force_download=force_download)
Expand Down Expand Up @@ -2230,7 +2234,7 @@ def fetch_step(self, skip_checksums=False):
self.dry_run_msg("\nList of patches:")

# fetch patches
if self.cfg['patches']:
if self.cfg['patches'] + self.cfg['postinstallpatches']:
if isinstance(self.cfg['checksums'], (list, tuple)):
# if checksums are provided as a list, first entries are assumed to be for sources
patches_checksums = self.cfg['checksums'][len(self.cfg['sources']):]
Expand Down Expand Up @@ -2402,11 +2406,15 @@ def extract_step(self):
else:
raise EasyBuildError("Unpacking source %s failed", src['name'])

def patch_step(self, beginpath=None):
def patch_step(self, beginpath=None, patches=None):
"""
Apply the patches
"""
for patch in self.patches:
if patches is None:
# if no patches are specified, use all non-post-install patches
patches = [p for p in self.patches if not p['postinstall']]

for patch in patches:
self.log.info("Applying patch %s" % patch['name'])
trace_msg("applying patch %s" % patch['name'])

Expand Down Expand Up @@ -2821,13 +2829,25 @@ def run_post_install_commands(self, commands=None):
raise EasyBuildError("Invalid element in 'postinstallcmds', not a string: %s", cmd)
run_cmd(cmd, simple=True, log_ok=True, log_all=True)

def apply_post_install_patches(self, patches=None):
"""
Apply post-install patch files that are specified via the 'postinstallpatches' easyconfig parameter.
"""
if patches is None:
patches = [p for p in self.patches if p['postinstall']]

self.log.debug("Post-install patches to apply: %s", patches)
if patches:
self.patch_step(beginpath=self.installdir, patches=patches)

def post_install_step(self):
"""
Do some postprocessing
- run post install commands if any were specified
"""

self.run_post_install_commands()
self.apply_post_install_patches()

self.fix_shebang()

Expand Down
1 change: 1 addition & 0 deletions easybuild/framework/easyconfig/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
'preinstallopts': ['', 'Extra prefix options for installation.', BUILD],
'pretestopts': ['', 'Extra prefix options for test.', BUILD],
'postinstallcmds': [[], 'Commands to run after the install step.', BUILD],
'postinstallpatches': [[], 'Patch files to apply after running the install step.', BUILD],
'required_linked_shared_libs': [[], "List of shared libraries (names, file names, or paths) which must be "
"linked in all installed binaries/libraries", BUILD],
'runtest': [None, ('Indicates if a test should be run after make; should specify argument '
Expand Down
5 changes: 5 additions & 0 deletions test/framework/sandbox/sources/toy/toy-0.0_fix-README.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
--- README.old 2022-03-01 14:28:43.000000000 +0100
+++ README 2022-03-01 14:29:01.000000000 +0100
@@ -1 +1 @@
-TOY
+toy 0.0, a toy test program
19 changes: 19 additions & 0 deletions test/framework/toy_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -3721,6 +3721,25 @@ def test_toy_ignore_test_failure(self):
self.assertTrue("Build succeeded (with --ignore-test-failure) for 1 out of 1" in stdout)
self.assertFalse(stderr)

def test_toy_post_install_patches(self):
"""
Test use of post-install patches
"""
test_ecs = os.path.join(os.path.dirname(__file__), 'easyconfigs', 'test_ecs')
toy_ec = os.path.join(test_ecs, 't', 'toy', 'toy-0.0.eb')

test_ec_txt = read_file(toy_ec)
test_ec_txt += "\npostinstallpatches = ['toy-0.0_fix-README.patch']"
test_ec = os.path.join(self.test_prefix, 'test.eb')
write_file(test_ec, test_ec_txt)

self.test_toy_build(ec_file=test_ec)

toy_installdir = os.path.join(self.test_installpath, 'software', 'toy', '0.0')
toy_readme_txt = read_file(os.path.join(toy_installdir, 'README'))
# verify whether patch indeed got applied
self.assertEqual(toy_readme_txt, "toy 0.0, a toy test program\n")


def suite():
""" return all the tests in this file """
Expand Down