98
98
from easybuild .tools .filetools import is_sha256_checksum , mkdir , move_file , move_logs , read_file , remove_dir
99
99
from easybuild .tools .filetools import remove_file , remove_lock , symlink , verify_checksum , weld_paths , write_file
100
100
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 ,
104
104
)
105
105
from easybuild .tools .run import RunShellCmdError , raise_run_shell_cmd_error , run_shell_cmd
106
106
from easybuild .tools .jenkins import write_to_xml
@@ -311,6 +311,9 @@ def __init__(self, ec, logfile=None):
311
311
# initialize logger
312
312
self ._init_log ()
313
313
314
+ # number of iterations
315
+ self .iter_cnt = self .det_iter_cnt ()
316
+
314
317
# try and use the specified group (if any)
315
318
group_name = build_option ('group' )
316
319
group_spec = self .cfg ['group' ]
@@ -448,7 +451,7 @@ def get_checksum_for(self, checksums, filename=None, index=None):
448
451
if checksum and chksum_input_git is not None :
449
452
# ignore any checksum for given filename due to changes in https://github.com/python/cpython/issues/90021
450
453
# tarballs made for git repos are not reproducible when created with Python < 3.9
451
- if sys .version_info [ 0 ] >= 3 and sys . version_info [ 1 ] < 9 :
454
+ if sys .version_info < ( 3 , 9 ) :
452
455
print_warning (
453
456
"Reproducible tarballs of Git repos are only possible when using Python 3.9+ to run EasyBuild. "
454
457
f"Skipping checksum verification of { chksum_input } since Python < 3.9 is used."
@@ -2387,7 +2390,8 @@ def handle_iterate_opts(self):
2387
2390
self .log .debug ("Iterating opt %s: %s" , opt , self .iter_opts [opt ])
2388
2391
2389
2392
if self .iter_opts :
2390
- print_msg ("starting iteration #%s ..." % self .iter_idx , log = self .log , silent = self .silent )
2393
+ print_msg (f"starting iteration { self .iter_idx + 1 } /{ self .iter_cnt } ..." , log = self .log ,
2394
+ silent = self .silent )
2391
2395
self .log .info ("Current iteration index: %s" , self .iter_idx )
2392
2396
2393
2397
# pop first element from all iterative easyconfig parameters as next value to use
@@ -2429,7 +2433,7 @@ def det_iter_cnt(self):
2429
2433
2430
2434
# we need to take into account that builddependencies is always a list
2431
2435
# we're only iterating over it if it's a list of lists
2432
- builddeps = self .cfg [ 'builddependencies' ]
2436
+ builddeps = self .cfg . get_ref ( 'builddependencies' )
2433
2437
if all (isinstance (x , list ) for x in builddeps ):
2434
2438
iter_opt_counts .append (len (builddeps ))
2435
2439
@@ -2830,8 +2834,6 @@ def patch_step(self, beginpath=None, patches=None):
2830
2834
self .log .info ("Applying patch %s" % patch ['name' ])
2831
2835
trace_msg ("applying patch %s" % patch ['name' ])
2832
2836
2833
- # patch source at specified index (first source if not specified)
2834
- srcind = patch .get ('source' , 0 )
2835
2837
# if patch level is specified, use that (otherwise let apply_patch derive patch level)
2836
2838
level = patch .get ('level' , None )
2837
2839
# determine suffix of source path to apply patch in (if any)
@@ -2840,16 +2842,14 @@ def patch_step(self, beginpath=None, patches=None):
2840
2842
copy_patch = 'copy' in patch and 'sourcepath' not in patch
2841
2843
options = patch .get ('opts' , None ) # Extra options for patch command
2842
2844
2843
- self .log .debug ("Source index: %s; patch level: %s; source path suffix: %s; copy patch: %s; options: %s" ,
2844
- srcind , level , srcpathsuffix , copy_patch , options )
2845
+ self .log .debug ("Patch level: %s; source path suffix: %s; copy patch: %s; options: %s" ,
2846
+ level , srcpathsuffix , copy_patch , options )
2845
2847
2846
2848
if beginpath is None :
2847
- try :
2848
- beginpath = self .src [srcind ]['finalpath' ]
2849
- self .log .debug ("Determine begin path for patch %s: %s" % (patch ['name' ], beginpath ))
2850
- except IndexError as err :
2851
- raise EasyBuildError ("Can't apply patch %s to source at index %s of list %s: %s" ,
2852
- patch ['name' ], srcind , self .src , err )
2849
+ if not self .src :
2850
+ raise EasyBuildError ("Can't apply patch %s to source if no sources are given" , patch ['name' ])
2851
+ beginpath = self .src [0 ]['finalpath' ]
2852
+ self .log .debug ("Determined begin path for patch %s: %s" % (patch ['name' ], beginpath ))
2853
2853
else :
2854
2854
self .log .debug ("Using specified begin path for patch %s: %s" % (patch ['name' ], beginpath ))
2855
2855
@@ -4662,18 +4662,19 @@ def run_step(self, step, step_methods):
4662
4662
run_hook (step , self .hooks , pre_step_hook = True , args = [self ])
4663
4663
4664
4664
for step_method in step_methods :
4665
+ # step_method is a lambda function that takes an EasyBlock instance as an argument,
4666
+ # and returns the actual method
4667
+ current_method = step_method (self )
4665
4668
# Remove leading underscore from e.g. "_test_step"
4666
- method_name = '_' . join ( step_method . __code__ . co_names ) .lstrip ('_' )
4669
+ method_name = current_method . __name__ .lstrip ('_' )
4667
4670
self .log .info ("Running method %s part of step %s" , method_name , step )
4668
4671
4669
4672
if self .dry_run :
4670
4673
self .dry_run_msg ("[%s method]" , method_name )
4671
4674
4672
4675
# if an known possible error occurs, just report it and continue
4673
4676
try :
4674
- # step_method is a lambda function that takes an EasyBlock instance as an argument,
4675
- # and returns the actual method, so use () to execute it
4676
- step_method (self )()
4677
+ current_method ()
4677
4678
except Exception as err :
4678
4679
if build_option ('extended_dry_run_ignore_errors' ):
4679
4680
dry_run_warning ("ignoring error %s" % err , silent = self .silent )
@@ -4682,9 +4683,7 @@ def run_step(self, step, step_methods):
4682
4683
raise
4683
4684
self .dry_run_msg ('' )
4684
4685
else :
4685
- # step_method is a lambda function that takes an EasyBlock instance as an argument,
4686
- # and returns the actual method, so use () to execute it
4687
- step_method (self )()
4686
+ current_method ()
4688
4687
4689
4688
run_hook (step , self .hooks , post_step_hook = True , args = [self ])
4690
4689
@@ -4794,7 +4793,7 @@ def run_all_steps(self, run_test_cases):
4794
4793
if self .cfg ['stop' ] == 'cfg' :
4795
4794
return True
4796
4795
4797
- steps = self .get_steps (run_test_cases = run_test_cases , iteration_count = self .det_iter_cnt () )
4796
+ steps = self .get_steps (run_test_cases = run_test_cases , iteration_count = self .iter_cnt )
4798
4797
4799
4798
# figure out how many steps will actually be run (not be skipped)
4800
4799
step_cnt = 0
@@ -4927,7 +4926,7 @@ def copy_build_dirs_logs_failed_install(application_log, silent, app, easyconfig
4927
4926
msg = f"Build directory of failed installation copied to { build_dirs_path } "
4928
4927
4929
4928
def operation (src , dest ):
4930
- copy_dir (src , dest , dirs_exist_ok = True )
4929
+ copy_dir (src , dest , dirs_exist_ok = True , symlinks = True )
4931
4930
4932
4931
operation_args .append ((operation , [app .builddir ], build_dirs_path , msg ))
4933
4932
@@ -5001,6 +5000,8 @@ def build_and_install_one(ecdict, init_env):
5001
5000
_log .debug ("Skip set to %s" % skip )
5002
5001
app .cfg ['skip' ] = skip
5003
5002
5003
+ hooks = load_hooks (build_option ('hooks' ))
5004
+
5004
5005
# build easyconfig
5005
5006
error_msg = '(no error)'
5006
5007
exit_code = None
@@ -5022,6 +5023,8 @@ def build_and_install_one(ecdict, init_env):
5022
5023
else :
5023
5024
enabled_write_permissions = False
5024
5025
5026
+ run_hook (EASYBLOCK , hooks , pre_step_hook = True , args = [app ])
5027
+
5025
5028
result = app .run_all_steps (run_test_cases = run_test_cases )
5026
5029
5027
5030
if not dry_run :
@@ -5033,7 +5036,9 @@ def build_and_install_one(ecdict, init_env):
5033
5036
except EasyBuildError as err :
5034
5037
_log .warning ("Failed to create build environment dump for easyconfig %s: %s" , reprod_spec , err )
5035
5038
5036
- # also add any extension easyblocks used during the build for reproducibility
5039
+ # also add any component/extension easyblocks used during the build for reproducibility
5040
+ if hasattr (app , 'comp_instances' ):
5041
+ copy_easyblocks_for_reprod ([comp for cfg , comp in app .comp_instances ], reprod_dir )
5037
5042
if app .ext_instances :
5038
5043
copy_easyblocks_for_reprod (app .ext_instances , reprod_dir )
5039
5044
# If not already done remove the granted write permissions if we did so
@@ -5120,8 +5125,12 @@ def ensure_writable_log_dir(log_dir):
5120
5125
block = det_full_ec_version (app .cfg ) + ".block"
5121
5126
repo .add_easyconfig (ecdict ['original_spec' ], app .name , block , buildstats , currentbuildstats )
5122
5127
repo .add_easyconfig (spec , app .name , det_full_ec_version (app .cfg ), buildstats , currentbuildstats )
5123
- for patch in app .patches :
5124
- repo .add_patch (patch ['path' ], app .name )
5128
+ patches = app .patches
5129
+ for ext in app .exts :
5130
+ patches += ext .get ('patches' , [])
5131
+ for patch in patches :
5132
+ if 'path' in patch :
5133
+ repo .add_patch (patch ['path' ], app .name )
5125
5134
repo .commit ("Built %s" % app .full_mod_name )
5126
5135
del repo
5127
5136
except EasyBuildError as err :
@@ -5143,10 +5152,14 @@ def ensure_writable_log_dir(log_dir):
5143
5152
_log .debug ("Copied easyconfig file %s to %s" , spec , newspec )
5144
5153
5145
5154
# copy patches
5146
- for patch in app .patches :
5147
- target = os .path .join (new_log_dir , os .path .basename (patch ['path' ]))
5148
- copy_file (patch ['path' ], target )
5149
- _log .debug ("Copied patch %s to %s" , patch ['path' ], target )
5155
+ patches = app .patches
5156
+ for ext in app .exts :
5157
+ patches += ext .get ('patches' , [])
5158
+ for patch in patches :
5159
+ if 'path' in patch :
5160
+ target = os .path .join (new_log_dir , os .path .basename (patch ['path' ]))
5161
+ copy_file (patch ['path' ], target )
5162
+ _log .debug ("Copied patch %s to %s" , patch ['path' ], target )
5150
5163
5151
5164
if build_option ('read_only_installdir' ) and not app .cfg ['stop' ]:
5152
5165
# take away user write permissions (again)
@@ -5200,6 +5213,8 @@ def ensure_writable_log_dir(log_dir):
5200
5213
if not success :
5201
5214
copy_build_dirs_logs_failed_install (application_log , silent , app , ecdict ['ec' ])
5202
5215
5216
+ run_hook (EASYBLOCK , hooks , post_step_hook = True , args = [app ])
5217
+
5203
5218
del app
5204
5219
5205
5220
return (success , application_log , error_msg , exit_code )
0 commit comments