@@ -105,6 +105,10 @@ def __init__(self, *args, **kwargs):
105105 # (like adding component sources to top-level sources easyconfig parameter)
106106 self .cfg = self .cfg .copy ()
107107
108+ # Keep track of sources for each component to restore them:
109+ # (Component instance, start idx [in self.src], end idx)
110+ self .comp_to_src_idxs = []
111+
108112 # disable templating to avoid premature resolving of template values
109113 # Note that self.cfg.update also resolves templates!
110114 with self .cfg .disable_templating ():
@@ -197,6 +201,10 @@ def __init__(self, *args, **kwargs):
197201
198202 comp_cfg .generate_template_values ()
199203
204+ # Combine all component sources into the top-level sources parameter
205+ # This allows reusing top-level source_urls and unpacking them all in the extract_step
206+
207+ old_num_srcs = len (self .cfg .get ('sources' , resolve = False ))
200208 # Don't require that all template values can be resolved at this point but still resolve them.
201209 # This is important to ensure that template values like %(name)s and %(version)s
202210 # are correctly resolved with the component name/version before values are copied over to self.cfg
@@ -226,22 +234,15 @@ def __init__(self, *args, **kwargs):
226234 comp_checksums = comp_cfg ['checksums' ]
227235 if comp_checksums :
228236 src_cnt = len (comp_sources )
229-
230237 # add per-component checksums for sources to list of checksums
231238 self .cfg .update ('checksums' , comp_checksums [:src_cnt ])
232239
233- # add per-component checksums for patches to list of checksums for patches
234- checksums_patches .extend (comp_checksums [src_cnt :])
235-
236- with comp_cfg .allow_unresolved_templates ():
237- comp_patches = comp_cfg ['patches' ]
238- comp_postinstall_patches = comp_cfg ['postinstallpatches' ]
239- if comp_patches :
240- self .cfg .update ('patches' , comp_patches )
241- # Patch step is skipped so adding postinstall patches of components here is harmless
242- self .cfg .update ('patches' , comp_postinstall_patches )
243-
244240 self .comp_instances .append ((comp_cfg , comp_cfg .easyblock (comp_cfg , logfile = self .logfile )))
241+ new_num_srcs = len (self .cfg .get ('sources' , resolve = False ))
242+ self .comp_to_src_idxs .append ((self .comp_instances [- 1 ], old_num_srcs , new_num_srcs ))
243+ # check if sanity checks are enabled for the component
244+ if self .cfg ['sanity_check_all_components' ] or comp_cfg ['name' ] in self .cfg ['sanity_check_components' ]:
245+ self .comp_cfgs_sanity_check .append (self .comp_instances [- 1 ])
245246
246247 self .cfg .update ('checksums' , checksums_patches + orig_checksums )
247248
@@ -263,6 +264,26 @@ def check_checksums(self):
263264
264265 return checksum_issues
265266
267+ def fetch_step (self ):
268+ """Fetch sources of all extensions"""
269+ super ().fetch_step ()
270+ # Init src attribute as usually done by fetch_step
271+ for (_ , comp ), start_idx , end_idx in self .comp_to_src_idxs :
272+ comp .src = self .src [start_idx :end_idx ]
273+ # need to run fetch_patches to ensure per-component patches are gathered
274+ comp .fetch_patches ()
275+
276+ def prepare_step (self , * args , ** kwargs ):
277+ """Init required properties for components"""
278+ super ().prepare_step (* args , ** kwargs )
279+ for _ , comp in self .comp_instances :
280+ # correct build/install dirs
281+ comp .builddir = self .builddir
282+ comp .install_subdir , comp .installdir = self .install_subdir , self .installdir
283+
284+ # make sure we can build in parallel
285+ comp .set_parallel ()
286+
266287 def patch_step (self ):
267288 """Patch step must be a no-op for bundle, since there are no top-level sources/patches."""
268289 pass
@@ -319,47 +340,15 @@ def install_step(self):
319340 (comp .name , comp .version , idx + 1 , comp_cnt ))
320341 self .log .info ("Installing component %s v%s using easyblock %s" , comp .name , comp .version , cfg .easyblock )
321342
322- # correct build/install dirs
323- comp .builddir = self .builddir
324- comp .install_subdir , comp .installdir = self .install_subdir , self .installdir
325-
326- # make sure we can build in parallel
327- comp .set_parallel ()
328-
329343 # figure out correct start directory
330- comp .guess_start_dir ()
331-
332- # need to run fetch_patches to ensure per-component patches are applied
333- comp .fetch_patches ()
334-
344+ # Compatibility with ECs expecting the previous behavior where src wasn't populated at this point
345+ tmp_src = comp .src
335346 comp .src = []
347+ comp .guess_start_dir ()
348+ comp .src = tmp_src
336349
337- # find matching entries in self.src for this component
338- with comp .cfg .allow_unresolved_templates ():
339- comp_sources = comp .cfg ['sources' ]
340- for source in comp_sources :
341- if isinstance (source , str ):
342- comp_src_fn = source
343- elif isinstance (source , dict ):
344- if 'filename' in source :
345- comp_src_fn = source ['filename' ]
346- else :
347- raise EasyBuildError ("Encountered source file specified as dict without 'filename': %s" , source )
348- else :
349- raise EasyBuildError ("Specification of unknown type for source file: %s" , source )
350-
351- found = False
352- for src in self .src :
353- if src ['name' ] == comp_src_fn :
354- self .log .info ("Found spec for source %s for component %s: %s" , comp_src_fn , comp .name , src )
355- comp .src .append (src )
356- found = True
357- break
358- if not found :
359- raise EasyBuildError ("Failed to find spec for source %s for component %s" , comp_src_fn , comp .name )
360-
361- # location of first unpacked source is used to determine where to apply patch(es)
362- comp .src [- 1 ]['finalpath' ] = comp .cfg ['start_dir' ]
350+ # location of first unpacked source is used to determine where to apply patch(es)
351+ comp .src [0 ]['finalpath' ] = comp .cfg ['start_dir' ]
363352
364353 # check if sanity checks are enabled for the component
365354 if self .cfg ['sanity_check_all_components' ] or comp .name in self .cfg ['sanity_check_components' ]:
0 commit comments