Repository: easybuilders/easybuild-framework

Issue:
Implement hook to run after easyconfigs are parsed but before the build starts
A hook that is run which is passed all of the easyconfigs would be very helpful. It would be passed a copy of `ordered_ecs`, so it can extract information from it.

The hook would then use the knowledge about all the easyconfigs to be built for various operations such as notifying external tools about the number of easyconfigs, and what they are, which could even be used to estimate build time.

I plan to implement this feature myself


Discussion:
I am struggling to think of a name for such a hook, how do you feel about "poised hook"?

Resolving patch:
diff --git a/easybuild/main.py b/easybuild/main.py
--- a/easybuild/main.py
+++ b/easybuild/main.py
@@ -69,7 +69,7 @@
 from easybuild.tools.github import add_pr_labels, install_github_token, list_prs, merge_pr, new_branch_github, new_pr
 from easybuild.tools.github import new_pr_from_branch
 from easybuild.tools.github import sync_branch_with_develop, sync_pr_with_develop, update_branch, update_pr
-from easybuild.tools.hooks import START, END, load_hooks, run_hook
+from easybuild.tools.hooks import BUILD_AND_INSTALL_LOOP, PRE_PREF, POST_PREF, START, END, load_hooks, run_hook
 from easybuild.tools.modules import modules_tool
 from easybuild.tools.options import opts_dict_to_eb_opts, set_up_configuration, use_color
 from easybuild.tools.output import COLOR_GREEN, COLOR_RED, STATUS_BAR, colorize, print_checks, rich_live_cm
@@ -545,8 +545,10 @@ def process_eb_args(eb_args, eb_go, cfg_settings, modtool, testing, init_session
         exit_on_failure = not (options.dump_test_report or options.upload_test_report)
 
         with rich_live_cm():
+            run_hook(PRE_PREF + BUILD_AND_INSTALL_LOOP, hooks, args=[ordered_ecs])
             ecs_with_res = build_and_install_software(ordered_ecs, init_session_state,
                                                       exit_on_failure=exit_on_failure)
+            run_hook(POST_PREF + BUILD_AND_INSTALL_LOOP, hooks, args=[ecs_with_res])
     else:
         ecs_with_res = [(ec, {}) for ec in ordered_ecs]
 
diff --git a/easybuild/tools/hooks.py b/easybuild/tools/hooks.py
--- a/easybuild/tools/hooks.py
+++ b/easybuild/tools/hooks.py
@@ -61,6 +61,7 @@
 
 START = 'start'
 PARSE = 'parse'
+BUILD_AND_INSTALL_LOOP = 'build_and_install_loop'
 SINGLE_EXTENSION = 'single_extension'
 MODULE_WRITE = 'module_write'
 END = 'end'
@@ -78,6 +79,7 @@
 HOOK_NAMES = [
     START,
     PARSE,
+    PRE_PREF + BUILD_AND_INSTALL_LOOP,
 ] + [p + x for x in STEP_NAMES[:STEP_NAMES.index(EXTENSIONS_STEP)]
      for p in [PRE_PREF, POST_PREF]] + [
     # pre-extensions hook is triggered before starting installation of extensions,
@@ -97,6 +99,7 @@
     POST_PREF + MODULE_STEP,
 ] + [p + x for x in STEP_NAMES[STEP_NAMES.index(MODULE_STEP)+1:]
      for p in [PRE_PREF, POST_PREF]] + [
+    POST_PREF + BUILD_AND_INSTALL_LOOP,
     END,
 ]
 KNOWN_HOOKS = [h + HOOK_SUFF for h in HOOK_NAMES]


Test patch:
diff --git a/test/framework/options.py b/test/framework/options.py
--- a/test/framework/options.py
+++ b/test/framework/options.py
@@ -679,6 +679,7 @@ def test_avail_hooks(self):
             "List of supported hooks (in order of execution):",
             "	start_hook",
             "	parse_hook",
+            "	pre_build_and_install_loop_hook",
             "	pre_fetch_hook",
             "	post_fetch_hook",
             "	pre_ready_hook",
@@ -716,6 +717,7 @@ def test_avail_hooks(self):
             "	post_package_hook",
             "	pre_testcases_hook",
             "	post_testcases_hook",
+            "	post_build_and_install_loop_hook",
             "	end_hook",
             '',
         ])