Skip to content

Commit 882177c

Browse files
committed
Make matching component sources in Bundle easyblock more reliable
Store where sources have been added to get the corresponding filled structure without relying on partially resolved templates.
1 parent bacc8b3 commit 882177c

File tree

1 file changed

+39
-47
lines changed

1 file changed

+39
-47
lines changed

easybuild/easyblocks/generic/bundle.py

Lines changed: 39 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,10 @@ def __init__(self, *args, **kwargs):
9292
# (like adding component sources to top-level sources easyconfig parameter)
9393
self.cfg = self.cfg.copy()
9494

95+
# Keep track of sources for each component to restore them:
96+
# (Component instance, start idx [in self.src], end idx)
97+
self.comp_to_src_idxs = []
98+
9599
# disable templating to avoid premature resolving of template values
96100
# Note that self.cfg.update also resolves templates!
97101
with self.cfg.disable_templating():
@@ -166,6 +170,10 @@ def __init__(self, *args, **kwargs):
166170
for key in comp_specs:
167171
comp_cfg[key] = comp_specs[key]
168172

173+
# Combine all component sources into the top-level sources parameter
174+
# This allows reusing top-level source_urls and unpacking them all in the extract_step
175+
176+
old_num_srcs = len(self.cfg.get('sources', resolve=False))
169177
# Don't require that all template values can be resolved at this point but still resolve them.
170178
# This is important to ensure that template values like %(name)s and %(version)s
171179
# are correctly resolved with the component name/version before values are copied over to self.cfg
@@ -195,19 +203,15 @@ def __init__(self, *args, **kwargs):
195203
comp_checksums = comp_cfg['checksums']
196204
if comp_checksums:
197205
src_cnt = len(comp_sources)
198-
199206
# add per-component checksums for sources to list of checksums
200207
self.cfg.update('checksums', comp_checksums[:src_cnt])
201208

202-
# add per-component checksums for patches to list of checksums for patches
203-
checksums_patches.extend(comp_checksums[src_cnt:])
204-
205-
with comp_cfg.allow_unresolved_templates():
206-
comp_patches = comp_cfg['patches']
207-
if comp_patches:
208-
self.cfg.update('patches', comp_patches)
209-
210209
self.comp_instances.append((comp_cfg, comp_cfg.easyblock(comp_cfg, logfile=self.logfile)))
210+
new_num_srcs = len(self.cfg.get('sources', resolve=False))
211+
self.comp_to_src_idxs.append((self.comp_instances[-1], old_num_srcs, new_num_srcs))
212+
# check if sanity checks are enabled for the component
213+
if self.cfg['sanity_check_all_components'] or comp_cfg['name'] in self.cfg['sanity_check_components']:
214+
self.comp_cfgs_sanity_check.append(self.comp_instances[-1])
211215

212216
self.cfg.update('checksums', checksums_patches)
213217

@@ -229,6 +233,26 @@ def check_checksums(self):
229233

230234
return checksum_issues
231235

236+
def fetch_step(self):
237+
"""Fetch sources of all extensions"""
238+
super().fetch_step()
239+
# Init src attribute as usually done by fetch_step
240+
for (_, comp), start_idx, end_idx in self.comp_to_src_idxs:
241+
comp.src = self.src[start_idx:end_idx]
242+
# need to run fetch_patches to ensure per-component patches are gathered
243+
comp.fetch_patches()
244+
245+
def prepare_step(self, *args, **kwargs):
246+
"""Init required properties for components"""
247+
super().prepare_step(*args, **kwargs)
248+
for _, comp in self.comp_instances:
249+
# correct build/install dirs
250+
comp.builddir = self.builddir
251+
comp.install_subdir, comp.installdir = self.install_subdir, self.installdir
252+
253+
# make sure we can build in parallel
254+
comp.set_parallel()
255+
232256
def patch_step(self):
233257
"""Patch step must be a no-op for bundle, since there are no top-level sources/patches."""
234258
pass
@@ -260,47 +284,15 @@ def install_step(self):
260284
(cfg['name'], cfg['version'], idx + 1, comp_cnt))
261285
self.log.info("Installing component %s v%s using easyblock %s", cfg['name'], cfg['version'], cfg.easyblock)
262286

263-
# correct build/install dirs
264-
comp.builddir = self.builddir
265-
comp.install_subdir, comp.installdir = self.install_subdir, self.installdir
266-
267-
# make sure we can build in parallel
268-
comp.set_parallel()
269-
270287
# figure out correct start directory
271-
comp.guess_start_dir()
272-
273-
# need to run fetch_patches to ensure per-component patches are applied
274-
comp.fetch_patches()
275-
288+
# Compatibility with ECs expecting the previous behavior where src wasn't populated at this point
289+
tmp_src = comp.src
276290
comp.src = []
291+
comp.guess_start_dir()
292+
comp.src = tmp_src
277293

278-
# find matching entries in self.src for this component
279-
with comp.cfg.allow_unresolved_templates():
280-
comp_sources = comp.cfg['sources']
281-
for source in comp_sources:
282-
if isinstance(source, str):
283-
comp_src_fn = source
284-
elif isinstance(source, dict):
285-
if 'filename' in source:
286-
comp_src_fn = source['filename']
287-
else:
288-
raise EasyBuildError("Encountered source file specified as dict without 'filename': %s", source)
289-
else:
290-
raise EasyBuildError("Specification of unknown type for source file: %s", source)
291-
292-
found = False
293-
for src in self.src:
294-
if src['name'] == comp_src_fn:
295-
self.log.info("Found spec for source %s for component %s: %s", comp_src_fn, comp.name, src)
296-
comp.src.append(src)
297-
found = True
298-
break
299-
if not found:
300-
raise EasyBuildError("Failed to find spec for source %s for component %s", comp_src_fn, comp.name)
301-
302-
# location of first unpacked source is used to determine where to apply patch(es)
303-
comp.src[-1]['finalpath'] = comp.cfg['start_dir']
294+
# location of first unpacked source is used to determine where to apply patch(es)
295+
comp.src[0]['finalpath'] = comp.cfg['start_dir']
304296

305297
# check if sanity checks are enabled for the component
306298
if self.cfg['sanity_check_all_components'] or comp.cfg['name'] in self.cfg['sanity_check_components']:

0 commit comments

Comments
 (0)