Skip to content

Commit 2111895

Browse files
committed
Merge branch 'develop' into 20250828122810_new_pr_bundle
2 parents 3ffe837 + d9dec7f commit 2111895

File tree

5 files changed

+126
-24
lines changed

5 files changed

+126
-24
lines changed

easybuild/easyblocks/generic/bundle.py

Lines changed: 46 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"""
3636
import copy
3737
import os
38+
from datetime import datetime
3839

3940
import easybuild.tools.environment as env
4041
from easybuild.framework.easyblock import EasyBlock
@@ -45,7 +46,17 @@
4546
from easybuild.tools.config import build_option
4647
from easybuild.tools.hooks import TEST_STEP
4748
from easybuild.tools.modules import get_software_root, get_software_version
48-
from easybuild.tools.utilities import nub
49+
from easybuild.tools.utilities import nub, time2str
50+
51+
52+
# Description and step name run during component installation
53+
COMPONENT_INSTALL_STEPS = [
54+
('patching', 'patch'),
55+
('configuring', 'configure'),
56+
('building', 'build'),
57+
('testing', 'test'),
58+
('installing', 'install'),
59+
]
4960

5061

5162
class Bundle(EasyBlock):
@@ -265,14 +276,39 @@ def build_step(self):
265276
"""Do nothing."""
266277
pass
267278

279+
def _install_component(self, comp):
280+
"""Run the installation steps for a single component"""
281+
# run relevant steps
282+
for descr, step_name in COMPONENT_INSTALL_STEPS:
283+
if step_name in comp.cfg['skipsteps']:
284+
comp.log.info("Skipping '%s' step for component %s v%s", step_name, comp.name, comp.version)
285+
elif build_option('skip_test_step') and step_name == TEST_STEP:
286+
comp.log.info("Skipping %s step for component %s v%s, as requested via skip-test-step", step_name,
287+
comp.name, comp.version)
288+
else:
289+
msg = f' {descr} component {comp.name}...'
290+
if self.dry_run:
291+
self.dry_run_msg("%s [DRY RUN]\n", msg)
292+
else:
293+
print_msg(msg, log=self.log, silent=self.silent)
294+
start_time = datetime.now()
295+
try:
296+
comp.run_step(step_name, [lambda x: getattr(x, '%s_step' % step_name)])
297+
finally:
298+
if not self.dry_run:
299+
step_duration = datetime.now() - start_time
300+
if step_duration.total_seconds() >= 1:
301+
print_msg(" ... (took %s)", time2str(step_duration), log=self.log, silent=self.silent)
302+
elif self.logdebug or build_option('trace'):
303+
print_msg(" ... (took < 1 sec)", log=self.log, silent=self.silent)
304+
268305
def install_step(self):
269306
"""Install components, if specified."""
270307
comp_cnt = len(self.cfg['components'])
271308
for idx, (cfg, comp) in enumerate(self.comp_instances):
272-
273309
print_msg("installing bundle component %s v%s (%d/%d)..." %
274-
(cfg['name'], cfg['version'], idx + 1, comp_cnt))
275-
self.log.info("Installing component %s v%s using easyblock %s", cfg['name'], cfg['version'], cfg.easyblock)
310+
(comp.name, comp.version, idx + 1, comp_cnt))
311+
self.log.info("Installing component %s v%s using easyblock %s", comp.name, comp.version, cfg.easyblock)
276312

277313
# correct build/install dirs
278314
comp.builddir = self.builddir
@@ -317,18 +353,10 @@ def install_step(self):
317353
comp.src[-1]['finalpath'] = comp.cfg['start_dir']
318354

319355
# check if sanity checks are enabled for the component
320-
if self.cfg['sanity_check_all_components'] or comp.cfg['name'] in self.cfg['sanity_check_components']:
356+
if self.cfg['sanity_check_all_components'] or comp.name in self.cfg['sanity_check_components']:
321357
self.comp_cfgs_sanity_check.append(comp)
322358

323-
# run relevant steps
324-
for step_name in ['patch', 'configure', 'build', 'test', 'install']:
325-
if step_name in cfg['skipsteps']:
326-
comp.log.info("Skipping '%s' step for component %s v%s", step_name, cfg['name'], cfg['version'])
327-
elif build_option('skip_test_step') and step_name == TEST_STEP:
328-
comp.log.info("Skipping %s step for component %s v%s, as requested via skip-test-step", step_name,
329-
cfg['name'], cfg['version'])
330-
else:
331-
comp.run_step(step_name, [lambda x: getattr(x, '%s_step' % step_name)])
359+
self._install_component(comp)
332360

333361
if comp.make_module_req_guess.__qualname__ != 'EasyBlock.make_module_req_guess':
334362
depr_msg = f"Easyblock used to install component {comp.name} still uses make_module_req_guess"
@@ -381,13 +409,13 @@ def make_module_step(self, *args, **kwargs):
381409
as this is done in the generic EasyBlock while creating
382410
the module file already.
383411
"""
384-
for cfg, comp in self.comp_instances:
385-
self.log.info("Gathering module paths for component %s v%s", cfg['name'], cfg['version'])
412+
for _, comp in self.comp_instances:
413+
self.log.info("Gathering module paths for component %s v%s", comp.name, comp.version)
386414

387415
# take into account that easyblock used for component may not be migrated yet to module_load_environment
388416
if comp.make_module_req_guess.__qualname__ != 'EasyBlock.make_module_req_guess':
389417

390-
depr_msg = f"Easyblock used to install component {cfg['name']} still uses make_module_req_guess"
418+
depr_msg = f"Easyblock used to install component {comp.name} still uses make_module_req_guess"
391419
self.log.deprecated(depr_msg, '6.0')
392420

393421
reqs = comp.make_module_req_guess()
@@ -403,7 +431,7 @@ def make_module_step(self, *args, **kwargs):
403431
setattr(self.module_load_environment, key, value)
404432
except AttributeError:
405433
raise EasyBuildError("Cannot process module requirements of bundle component %s v%s",
406-
cfg['name'], cfg['version'])
434+
comp.name, comp.version)
407435
else:
408436
# Explicit call required as adding step to 'install_step' is not sufficient
409437
# for module-only build. Set fake arg to True, as module components should

easybuild/easyblocks/generic/cmdcp.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,9 @@ def build_step(self):
6767
# determine command to use
6868
# find (first) regex match, then complete matching command template
6969
cmd = None
70-
for pattern, regex_cmd in self.cfg['cmds_map']:
70+
for pattern, regex_cmd in self.cfg.get_ref('cmds_map'):
71+
pattern = pattern % self.cfg.template_values
72+
regex_cmd = regex_cmd % self.cfg.template_values
7173
try:
7274
regex = re.compile(pattern)
7375
except re.error as err:
@@ -78,6 +80,6 @@ def build_step(self):
7880
break
7981
if cmd is None:
8082
raise EasyBuildError("No match for %s in %s, don't know which command to use.",
81-
src, self.cfg['cmds_map'])
83+
src, self.cfg.get_ref('cmds_map'))
8284

8385
run_shell_cmd(cmd)

easybuild/easyblocks/h/hpl.py

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,16 @@
3030
@author: Kenneth Hoste (Ghent University)
3131
@author: Pieter De Baets (Ghent University)
3232
@author: Jens Timmerman (Ghent University)
33+
@author: Davide Grassano (CECAM - EPFL)
3334
"""
3435

36+
import re
3537
import os
3638

39+
import easybuild.tools.toolchain as toolchain
3740
from easybuild.easyblocks.generic.configuremake import ConfigureMake
3841
from easybuild.tools.build_log import EasyBuildError
42+
from easybuild.tools.config import build_option
3943
from easybuild.tools.filetools import change_dir, copy_file, mkdir, remove_file, symlink
4044
from easybuild.tools.run import run_shell_cmd
4145

@@ -104,10 +108,74 @@ def build_step(self, topdir=None):
104108
# C compilers flags
105109
extra_makeopts += "CCFLAGS='$(HPL_DEFS) %s' " % os.getenv('CFLAGS')
106110

111+
comp_fam = self.toolchain.comp_family()
112+
if comp_fam in [toolchain.INTELCOMP]:
113+
# Explicitly disable optimization, since Intel compilers apply some default
114+
# level not shown on the command line.
115+
# This breaks the result comparison, resulting in all tests failing residual checks.
116+
# See https://github.com/easybuilders/easybuild-easyconfigs/pull/23704#issuecomment-3202392904
117+
extra_makeopts += 'CCNOOPT=\'$(HPL_DEFS) -O0\' '
118+
107119
# set options and build
108120
self.cfg.update('buildopts', extra_makeopts)
109121
super().build_step()
110122

123+
def test_step(self):
124+
"""Test by running xhpl"""
125+
srcdir = os.path.join(self.cfg['start_dir'], 'bin', 'UNKNOWN')
126+
change_dir(srcdir)
127+
128+
pre_cmd = ""
129+
post_cmd = ""
130+
131+
# xhpl needs atleast 4 processes to run the test suite
132+
req_cpus = 4
133+
134+
mpi_fam = self.toolchain.mpi_family()
135+
if mpi_fam is None:
136+
self.report_test_failure("Toolchain does not include an MPI implementation, cannot run tests")
137+
138+
parallel = self.cfg.parallel
139+
if not build_option('mpi_tests'):
140+
self.log.info("MPI tests disabled from buildoption. Setting parallel to 1")
141+
parallel = 1
142+
143+
if parallel < req_cpus:
144+
self.log.info("Running tests with 1 oversubscribed process")
145+
146+
pin_str = ','.join(["0"] * req_cpus)
147+
if mpi_fam in [toolchain.INTELMPI]:
148+
pre_cmd = f"I_MPI_PIN_PROCESSOR_LIST=\"{pin_str}\" I_MPI_PIN=on "
149+
elif mpi_fam in [toolchain.OPENMPI]:
150+
post_cmd = f"--cpu-set {pin_str}"
151+
elif mpi_fam in [toolchain.MPICH]:
152+
post_cmd = f"-bind-to user:{pin_str}"
153+
else:
154+
self.report_test_failure("Don't know how to oversubscribe for `%s` MPI family" % mpi_fam)
155+
156+
cmd = self.toolchain.mpi_cmd_for(f'{post_cmd} ./xhpl', req_cpus)
157+
cmd = f'{pre_cmd} {cmd}'
158+
res = run_shell_cmd(cmd)
159+
out = res.output
160+
161+
passed_rgx = re.compile(r'(\d+) tests completed and passed')
162+
failed_rgx = re.compile(r'(\d+) tests completed and failed')
163+
164+
nfailed = 0
165+
passed_mch = passed_rgx.search(out)
166+
failed_mch = failed_rgx.search(out)
167+
if passed_mch:
168+
npassed = int(passed_mch.group(1))
169+
self.log.info("%d tests passed residual checks in xhpl output" % npassed)
170+
else:
171+
self.report_test_failure("Could not find test results in output of xhpl")
172+
173+
if failed_mch:
174+
nfailed = int(failed_mch.group(1))
175+
176+
if nfailed > 0:
177+
self.report_test_failure("%d tests failed residual checks in xhpl output" % nfailed)
178+
111179
def install_step(self):
112180
"""
113181
Install by copying files to install dir
@@ -119,14 +187,15 @@ def install_step(self):
119187
srcfile = os.path.join(srcdir, filename)
120188
copy_file(srcfile, destdir)
121189

122-
def sanity_check_step(self):
190+
def sanity_check_step(self, **kwargs):
123191
"""
124192
Custom sanity check for HPL
125193
"""
126194

127-
custom_paths = {
195+
# Allow subclasses to set own custom paths
196+
kwargs.setdefault('custom_paths', {
128197
'files': ["bin/xhpl"],
129198
'dirs': []
130-
}
199+
})
131200

132-
super().sanity_check_step(custom_paths)
201+
super().sanity_check_step(**kwargs)

easybuild/easyblocks/l/lammps.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import easybuild.tools.environment as env
4141
import easybuild.tools.toolchain as toolchain
4242
from easybuild.base import fancylogger
43+
from easybuild.easyblocks.python import set_py_env_vars
4344
from easybuild.framework.easyconfig import CUSTOM, MANDATORY
4445
from easybuild.tools.build_log import EasyBuildError, print_warning, print_msg
4546
from easybuild.tools.config import build_option
@@ -646,6 +647,7 @@ def install_step(self):
646647
'site_packages': site_packages,
647648
}
648649

650+
set_py_env_vars(self.log)
649651
run_shell_cmd(cmd)
650652

651653
def sanity_check_step(self, *args, **kwargs):

easybuild/easyblocks/w/wps.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ def configure_step(self):
231231
'DM_CC': os.getenv('MPICC'),
232232
'FC': os.getenv('MPIF90'),
233233
'CC': os.getenv('MPICC'),
234+
'CPP': 'cpp -P -traditional',
234235
}
235236
if self.toolchain.options.get('openmp', None):
236237
comps.update({'LDFLAGS': '%s %s' % (self.toolchain.get_flag('openmp'), os.environ['LDFLAGS'])})

0 commit comments

Comments
 (0)