Skip to content

Commit 4c9e8ec

Browse files
authored
Merge pull request #4923 from boegel/easyblock_hook
add support for pre/post `easyblock` hook
2 parents 87b3d9d + 86b5bb3 commit 4c9e8ec

File tree

5 files changed

+55
-7
lines changed

5 files changed

+55
-7
lines changed

easybuild/framework/easyblock.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@
9898
from easybuild.tools.filetools import is_sha256_checksum, mkdir, move_file, move_logs, read_file, remove_dir
9999
from easybuild.tools.filetools import remove_file, remove_lock, symlink, verify_checksum, weld_paths, write_file
100100
from easybuild.tools.hooks import (
101-
BUILD_STEP, CLEANUP_STEP, CONFIGURE_STEP, EXTENSIONS_STEP, EXTRACT_STEP, FETCH_STEP, INSTALL_STEP, MODULE_STEP,
102-
MODULE_WRITE, PACKAGE_STEP, PATCH_STEP, PERMISSIONS_STEP, POSTITER_STEP, POSTPROC_STEP, PREPARE_STEP, READY_STEP,
103-
SANITYCHECK_STEP, SINGLE_EXTENSION, TEST_STEP, TESTCASES_STEP, load_hooks, run_hook,
101+
BUILD_STEP, CLEANUP_STEP, CONFIGURE_STEP, EASYBLOCK, EXTENSIONS_STEP, EXTRACT_STEP, FETCH_STEP, INSTALL_STEP,
102+
MODULE_STEP, MODULE_WRITE, PACKAGE_STEP, PATCH_STEP, PERMISSIONS_STEP, POSTITER_STEP, POSTPROC_STEP, PREPARE_STEP,
103+
READY_STEP, SANITYCHECK_STEP, SINGLE_EXTENSION, TEST_STEP, TESTCASES_STEP, load_hooks, run_hook,
104104
)
105105
from easybuild.tools.run import RunShellCmdError, raise_run_shell_cmd_error, run_shell_cmd
106106
from easybuild.tools.jenkins import write_to_xml
@@ -5001,6 +5001,8 @@ def build_and_install_one(ecdict, init_env):
50015001
_log.debug("Skip set to %s" % skip)
50025002
app.cfg['skip'] = skip
50035003

5004+
hooks = load_hooks(build_option('hooks'))
5005+
50045006
# build easyconfig
50055007
error_msg = '(no error)'
50065008
exit_code = None
@@ -5022,6 +5024,8 @@ def build_and_install_one(ecdict, init_env):
50225024
else:
50235025
enabled_write_permissions = False
50245026

5027+
run_hook(EASYBLOCK, hooks, pre_step_hook=True, args=[app])
5028+
50255029
result = app.run_all_steps(run_test_cases=run_test_cases)
50265030

50275031
if not dry_run:
@@ -5127,6 +5131,8 @@ def ensure_writable_log_dir(log_dir):
51275131
except EasyBuildError as err:
51285132
_log.warning("Unable to commit easyconfig to repository: %s", err)
51295133

5134+
run_hook(EASYBLOCK, hooks, post_step_hook=True, args=[app])
5135+
51305136
# cleanup logs
51315137
app.close_log()
51325138

easybuild/tools/hooks.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
START = 'start'
6363
PARSE = 'parse'
6464
BUILD_AND_INSTALL_LOOP = 'build_and_install_loop'
65+
EASYBLOCK = 'easyblock'
6566
SINGLE_EXTENSION = 'single_extension'
6667
MODULE_WRITE = 'module_write'
6768
END = 'end'
@@ -86,6 +87,7 @@
8687
START,
8788
PARSE,
8889
PRE_PREF + BUILD_AND_INSTALL_LOOP,
90+
PRE_PREF + EASYBLOCK,
8991
] + [p + x for x in STEP_NAMES[:STEP_NAMES.index(EXTENSIONS_STEP)]
9092
for p in [PRE_PREF, POST_PREF]] + [
9193
# pre-extensions hook is triggered before starting installation of extensions,
@@ -105,6 +107,7 @@
105107
POST_PREF + MODULE_STEP,
106108
] + [p + x for x in STEP_NAMES[STEP_NAMES.index(MODULE_STEP)+1:]
107109
for p in [PRE_PREF, POST_PREF]] + [
110+
POST_PREF + EASYBLOCK,
108111
POST_PREF + BUILD_AND_INSTALL_LOOP,
109112
END,
110113
CANCEL,

test/framework/hooks.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ def setUp(self):
5757
'def pre_build_and_install_loop_hook(ecs):',
5858
' print("About to start looping for %d easyconfigs!" % len(ecs))',
5959
'',
60+
'def pre_easyblock_hook(self):',
61+
' print(f"Starting installation of {self.name} {self.version}")',
62+
'',
6063
'def foo():',
6164
' print("running foo helper method")',
6265
'',
@@ -78,6 +81,9 @@ def setUp(self):
7881
' if cmd == "make install":',
7982
' return "sudo " + cmd',
8083
'',
84+
'def post_easyblock_hook(self):',
85+
' print(f"Done with installation of {self.name} {self.version}")',
86+
'',
8187
'def fail_hook(err):',
8288
' print("EasyBuild FAIL: %s" % err)',
8389
'',
@@ -101,13 +107,15 @@ def test_load_hooks(self):
101107

102108
hooks = load_hooks(self.test_hooks_pymod)
103109

104-
self.assertEqual(len(hooks), 9)
110+
self.assertEqual(len(hooks), 11)
105111
expected = [
106112
'crash_hook',
107113
'fail_hook',
108114
'parse_hook',
109115
'post_configure_hook',
116+
'post_easyblock_hook',
110117
'pre_build_and_install_loop_hook',
118+
'pre_easyblock_hook',
111119
'pre_install_hook',
112120
'pre_run_shell_cmd_hook',
113121
'pre_single_extension_hook',
@@ -197,12 +205,18 @@ def test_run_hook(self):
197205

198206
init_config(build_options={'debug': True})
199207

208+
class FakeEasyBlock():
209+
def __init__(self, *args, **kwargs):
210+
self.name = 'fake'
211+
self.version = '1.2.3'
212+
200213
def run_hooks():
201214
self.mock_stdout(True)
202215
self.mock_stderr(True)
203216
run_hook('start', hooks)
204217
run_hook('parse', hooks, args=['<EasyConfig instance>'], msg="Running parse hook for example.eb...")
205218
run_hook('build_and_install_loop', hooks, args=[['ec1', 'ec2']], pre_step_hook=True)
219+
run_hook('easyblock', hooks, args=[FakeEasyBlock()], pre_step_hook=True)
206220
run_hook('configure', hooks, pre_step_hook=True, args=[None])
207221
run_hook('run_shell_cmd', hooks, pre_step_hook=True, args=["configure.sh"], kwargs={'interactive': True})
208222
run_hook('configure', hooks, post_step_hook=True, args=[None])
@@ -220,6 +234,7 @@ def run_hooks():
220234
run_hook('extensions', hooks, post_step_hook=True, args=[None])
221235
run_hook('fail', hooks, args=[EasyBuildError('oops')])
222236
run_hook('crash', hooks, args=[RuntimeError('boom!')])
237+
run_hook('easyblock', hooks, args=[FakeEasyBlock()], post_step_hook=True)
223238
stdout = self.get_stdout()
224239
stderr = self.get_stderr()
225240
self.mock_stdout(False)
@@ -236,6 +251,8 @@ def run_hooks():
236251
"Parse hook with argument <EasyConfig instance>",
237252
"== Running pre-build_and_install_loop hook...",
238253
"About to start looping for 2 easyconfigs!",
254+
"== Running pre-easyblock hook...",
255+
"Starting installation of fake 1.2.3",
239256
"== Running pre-run_shell_cmd hook...",
240257
"this is run before running interactive command 'configure.sh'",
241258
"== Running post-configure hook...",
@@ -257,6 +274,8 @@ def run_hooks():
257274
"EasyBuild FAIL: 'oops'",
258275
"== Running crash hook...",
259276
"EasyBuild CRASHED, oh no! => boom!",
277+
"== Running post-easyblock hook...",
278+
"Done with installation of fake 1.2.3",
260279
]
261280
expected_stdout = '\n'.join(expected_stdout_lines)
262281

test/framework/options.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,7 @@ def test_avail_hooks(self):
797797
" start_hook",
798798
" parse_hook",
799799
" pre_build_and_install_loop_hook",
800+
" pre_easyblock_hook",
800801
" pre_fetch_hook",
801802
" post_fetch_hook",
802803
" pre_ready_hook",
@@ -836,6 +837,7 @@ def test_avail_hooks(self):
836837
" post_package_hook",
837838
" pre_testcases_hook",
838839
" post_testcases_hook",
840+
" post_easyblock_hook",
839841
" post_build_and_install_loop_hook",
840842
" end_hook",
841843
" cancel_hook",

test/framework/toy_build.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3654,13 +3654,20 @@ def start_hook():
36543654
36553655
def parse_hook(ec):
36563656
print('%s %s' % (ec.name, ec.version))
3657-
# print sources value to check that raw untemplated strings are exposed in parse_hook
3657+
# print sources value to check that raw untemplated strings are exposed in parse_hook
36583658
print(ec['sources'])
3659-
# try appending to postinstallcmd to see whether the modification is actually picked up
3660-
# (required templating to be disabled before parse_hook is called)
3659+
# try appending to postinstallcmd to see whether the modification is actually picked up
3660+
# (required templating to be disabled before parse_hook is called)
36613661
ec['postinstallcmds'].append('echo toy')
36623662
print(ec['postinstallcmds'][-1])
36633663
3664+
def pre_build_and_install_loop_hook(ecs):
3665+
mod_names = ' '.join(ec['full_mod_name'] for ec in ecs)
3666+
print(f"installing {len(ecs)} easyconfigs: {mod_names}")
3667+
3668+
def pre_easyblock_hook(self):
3669+
print(f'starting installation of {self.name} {self.version}')
3670+
36643671
def pre_configure_hook(self):
36653672
print('pre-configure: toy.source: %s' % os.path.exists('toy.source'))
36663673
@@ -3704,6 +3711,13 @@ def post_run_shell_cmd_hook(cmd, *args, **kwargs):
37043711
copy_file('toy', 'copy_of_toy')
37053712
change_dir(cwd)
37063713
print("'%s' command failed (exit code %s), but I fixed it!" % (cmd, exit_code))
3714+
3715+
def post_easyblock_hook(self):
3716+
print(f'done with installation of {self.name} {self.version}')
3717+
3718+
def post_build_and_install_loop_hook(ecs):
3719+
mod_names = ' '.join(ec[0]['full_mod_name'] for ec in ecs)
3720+
print(f"done with installing {len(ecs)} easyconfigs: {mod_names}")
37073721
""")
37083722
write_file(hooks_file, hooks_file_txt)
37093723

@@ -3741,6 +3755,8 @@ def post_run_shell_cmd_hook(cmd, *args, **kwargs):
37413755
toy 0.0
37423756
['%(name)s-%(version)s.tar.gz']
37433757
echo toy
3758+
installing 1 easyconfigs: toy/0.0
3759+
starting installation of toy 0.0
37443760
pre-configure: toy.source: True
37453761
post-configure: toy.source: False
37463762
pre_run_shell_cmd_hook triggered for ' gcc toy.c -o toy '
@@ -3764,6 +3780,8 @@ def post_run_shell_cmd_hook(cmd, *args, **kwargs):
37643780
in module-write hook hook for {mod_name}
37653781
in module-write hook hook for {mod_name}
37663782
in module-write hook hook for {mod_name}
3783+
done with installation of toy 0.0
3784+
done with installing 1 easyconfigs: toy/0.0
37673785
end hook triggered, all done!
37683786
""").strip().format(mod_name=os.path.basename(toy_mod_file))
37693787
self.assertEqual(stdout.strip(), expected_output)

0 commit comments

Comments
 (0)