|
1 | 1 | # Hooks to customize how EasyBuild installs software in EESSI |
2 | 2 | # see https://docs.easybuild.io/en/latest/Hooks.html |
| 3 | +import datetime |
3 | 4 | import glob |
4 | 5 | import os |
5 | 6 | import re |
6 | 7 |
|
7 | 8 | import easybuild.tools.environment as env |
8 | 9 | from easybuild.easyblocks.generic.configuremake import obtain_config_guess |
9 | 10 | from easybuild.framework.easyconfig.constants import EASYCONFIG_CONSTANTS |
| 11 | +from easybuild.tools import config |
10 | 12 | from easybuild.tools.build_log import EasyBuildError, print_msg |
11 | | -from easybuild.tools.config import build_option, update_build_option |
12 | | -from easybuild.tools.filetools import apply_regex_substitutions, copy_file, remove_file, symlink, which |
| 13 | +from easybuild.tools.config import build_option, install_path, update_build_option |
| 14 | +from easybuild.tools.filetools import apply_regex_substitutions, copy_dir, copy_file, remove_file, symlink, which |
13 | 15 | from easybuild.tools.run import run_cmd |
14 | 16 | from easybuild.tools.systemtools import AARCH64, POWER, X86_64, get_cpu_architecture, get_cpu_features |
15 | 17 | from easybuild.tools.toolchain.compiler import OPTARCH_GENERIC |
|
46 | 48 | # Make sure a single environment variable name is used for this throughout the hooks |
47 | 49 | EESSI_IGNORE_ZEN4_GCC1220_ENVVAR="EESSI_IGNORE_LMOD_ERROR_ZEN4_GCC1220" |
48 | 50 |
|
| 51 | +STACK_REPROD_SUBDIR = 'reprod' |
| 52 | + |
49 | 53 | def is_gcccore_1220_based(**kwargs): |
50 | 54 | # ecname, ecversion, tcname, tcversion): |
51 | 55 | """ |
@@ -516,6 +520,26 @@ def post_module_hook_zen4_gcccore1220(self, *args, **kwargs): |
516 | 520 | del self.initial_environ[EESSI_IGNORE_ZEN4_GCC1220_ENVVAR] |
517 | 521 |
|
518 | 522 |
|
| 523 | +def post_easyblock_hook_copy_exts_patches_to_easybuild_subdir(self, *args, **kwargs): |
| 524 | + """Copy the patches of extensions to the easybuild subdir of the installation directory.""" |
| 525 | + |
| 526 | + # Temporary fix for https://github.com/easybuilders/easybuild-framework/issues/4864: |
| 527 | + # make sure that the patches of extensions are copied to the easybuild subdirectory |
| 528 | + for ext in self.exts: |
| 529 | + for patch in ext.get('patches', []): |
| 530 | + new_log_dir = os.path.join(self.installdir, config.log_path(ec=self.cfg)) |
| 531 | + target = os.path.join(new_log_dir, os.path.basename(patch['path'])) |
| 532 | + copy_file(patch['path'], target) |
| 533 | + |
| 534 | + # Now we simply copy the entire "easybuild" subdirectory of the installed application |
| 535 | + # to a timestamped subdirectory of the stack's central reprod directory, e.g.: |
| 536 | + # |
| 537 | + stack_reprod_dir = os.path.join(os.path.dirname(install_path()), STACK_REPROD_SUBDIR) |
| 538 | + now_utc_timestamp = datetime.datetime.now(datetime.UTC).isoformat('T', 'seconds').replace('+00:00', 'Z') |
| 539 | + app_reprod_dir = os.path.join(stack_reprod_dir, self.install_subdir, now_utc_timestamp) |
| 540 | + copy_dir(new_log_dir, app_reprod_dir) |
| 541 | + |
| 542 | + |
519 | 543 | # Modules for dependencies are loaded in the prepare step. Thus, that's where we need this variable to be set |
520 | 544 | # so that the modules can be succesfully loaded without printing the error (so that we can create a module |
521 | 545 | # _with_ the warning for the current software being installed) |
@@ -1297,6 +1321,31 @@ def post_module_hook(self, *args, **kwargs): |
1297 | 1321 | post_module_hook_zen4_gcccore1220(self, *args, **kwargs) |
1298 | 1322 |
|
1299 | 1323 |
|
| 1324 | +def post_easyblock_hook(self, *args, **kwargs): |
| 1325 | + """Main post easyblock hook: trigger custom functions based on software name.""" |
| 1326 | + if self.name in POST_EASYBLOCK_HOOKS: |
| 1327 | + POST_EASYBLOCK_HOOKS[self.name](self, *args, **kwargs) |
| 1328 | + |
| 1329 | + # Always trigger this one, regardless of self.name |
| 1330 | + post_easyblock_hook_copy_exts_patches_to_easybuild_subdir(self, *args, **kwargs) |
| 1331 | + |
| 1332 | + |
| 1333 | +def post_build_and_install_loop_hook(ecs, *args, **kwargs): |
| 1334 | + """ |
| 1335 | + Post build and install loop hook that copies the easybuild subdirectory of every installed application |
| 1336 | + to a central location in the root of the software stack, e.g.: |
| 1337 | + /path/to/stack/reprod/20250102T12:34:56Z/MyApp/1.2-foss-2025a |
| 1338 | + """ |
| 1339 | + |
| 1340 | + stack_reprod_dir = os.path.join(os.path.dirname(install_path()), STACK_REPROD_SUBDIR) |
| 1341 | + for (ec, status) in ecs: |
| 1342 | + if status['success']: |
| 1343 | + now_timestamp = datetime.datetime.now(datetime.UTC).isoformat('T', 'seconds').replace('+00:00', 'Z') |
| 1344 | + app_easybuild_dir = os.path.dirname(status['log_file']) |
| 1345 | + app_reprod_dir = os.path.join(stack_reprod_dir, ec['short_mod_name'], now_timestamp, 'easybuild') |
| 1346 | + copy_dir(app_easybuild_dir, app_reprod_dir) |
| 1347 | + |
| 1348 | + |
1300 | 1349 | PARSE_HOOKS = { |
1301 | 1350 | 'casacore': parse_hook_casacore_disable_vectorize, |
1302 | 1351 | 'CGAL': parse_hook_cgal_toolchainopts_precise, |
@@ -1365,6 +1414,8 @@ def post_module_hook(self, *args, **kwargs): |
1365 | 1414 |
|
1366 | 1415 | POST_MODULE_HOOKS = {} |
1367 | 1416 |
|
| 1417 | +POST_EASYBLOCK_HOOKS = {} |
| 1418 | + |
1368 | 1419 | # Define parallelism limit operations |
1369 | 1420 | def divide_by_factor(parallel, factor): |
1370 | 1421 | """Divide parallelism by given factor""" |
|
0 commit comments