Skip to content

Commit 2e9a757

Browse files
committed
Merge branch 'develop' of https://github.com/easybuilders/easybuild-easyblocks into empty_dir
2 parents 3f52540 + d9dec7f commit 2e9a757

24 files changed

Lines changed: 527 additions & 201 deletions

easybuild/easyblocks/a/amber.py

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -88,35 +88,37 @@ def extract_step(self):
8888
def patch_step(self, *args, **kwargs):
8989
"""Patch Amber using 'update_amber' tool, prior to applying listed patch files (if any)."""
9090

91-
# figure out which Python command to use to run the update_amber script;
92-
# by default it uses 'python', but this may not be available (on CentOS 8 for example);
93-
# note that the dependencies are not loaded yet at this point, so we're at the mercy of the OS here...
94-
python_cmd = None
95-
for cand_python_cmd in ['python', 'python3', 'python2']:
96-
if which(cand_python_cmd):
97-
python_cmd = cand_python_cmd
98-
break
99-
100-
if python_cmd is None:
101-
raise EasyBuildError("No suitable Python command found to run update_amber script!")
102-
103-
if self.cfg['patchlevels'] == "latest":
104-
cmd = "%s ./update_amber --update" % python_cmd
105-
# Run as many times as specified. It is the responsibility
106-
# of the easyconfig author to get this right, especially if
107-
# he or she selects "latest". (Note: "latest" is not
108-
# recommended for this reason and others.)
109-
for _ in range(self.cfg['patchruns']):
110-
run_shell_cmd(cmd)
111-
else:
112-
for (tree, patch_level) in zip(['AmberTools', 'Amber'], self.cfg['patchlevels']):
113-
if patch_level == 0:
114-
continue
115-
cmd = "%s ./update_amber --update-to %s/%s" % (python_cmd, tree, patch_level)
91+
# Use the update_amber script if patchlevels is defined - if not then the easyconfig should apply the patches
92+
if self.cfg['patchlevels']:
93+
# figure out which Python command to use to run the update_amber script;
94+
# by default it uses 'python', but this may not be available (on CentOS 8 for example);
95+
# note that the dependencies are not loaded yet at this point, so we're at the mercy of the OS here...
96+
python_cmd = None
97+
for cand_python_cmd in ['python', 'python3', 'python2']:
98+
if which(cand_python_cmd):
99+
python_cmd = cand_python_cmd
100+
break
101+
102+
if python_cmd is None:
103+
raise EasyBuildError("No suitable Python command found to run update_amber script!")
104+
105+
if self.cfg['patchlevels'] == "latest":
106+
cmd = "%s ./update_amber --update" % python_cmd
116107
# Run as many times as specified. It is the responsibility
117-
# of the easyconfig author to get this right.
108+
# of the easyconfig author to get this right, especially if
109+
# he or she selects "latest". (Note: "latest" is not
110+
# recommended for this reason and others.)
118111
for _ in range(self.cfg['patchruns']):
119112
run_shell_cmd(cmd)
113+
else:
114+
for (tree, patch_level) in zip(['AmberTools', 'Amber'], self.cfg['patchlevels']):
115+
if patch_level == 0:
116+
continue
117+
cmd = "%s ./update_amber --update-to %s/%s" % (python_cmd, tree, patch_level)
118+
# Run as many times as specified. It is the responsibility
119+
# of the easyconfig author to get this right.
120+
for _ in range(self.cfg['patchruns']):
121+
run_shell_cmd(cmd)
120122

121123
super().patch_step(*args, **kwargs)
122124

easybuild/easyblocks/e/elpa.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,8 @@ def configure_step(self):
206206
self.cfg.update('configopts', '--with-NVIDIA-GPU-compute-capability=sm_%s' % cuda_cc_string)
207207
self.log.info("Enabling nvidia GPU support for compute capability: %s", cuda_cc_string)
208208
# There is a dedicated kernel for sm80, but only from version 2021.11.001 onwards
209-
if float(cuda_cc) >= 8.0 and LooseVersion(self.version) >= LooseVersion('2021.11.001'):
209+
# Trying to use these kernels for GPUs newer than sm80 will fail ELPA configure
210+
if float(cuda_cc) == 8.0 and LooseVersion(self.version) >= LooseVersion('2021.11.001'):
210211
self.cfg.update('configopts', '--enable-nvidia-sm80-gpu')
211212

212213
# From v2022.05.001 onwards, the config complains if CPP is not set, resulting in non-zero exit of configure

easybuild/easyblocks/g/ghc.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class EB_GHC(ConfigureMake):
3737
Support for building and installing applications with configure/make/make install
3838
"""
3939

40-
def build_step(self, verbose=False):
40+
def build_step(self, *args, **kwargs):
4141
"""
4242
Support for a binary 6.12.x installation. Starting there,
4343
later GHC versions are build from source and thus require
@@ -46,4 +46,4 @@ def build_step(self, verbose=False):
4646
if LooseVersion(self.version) < LooseVersion("7.0"):
4747
pass
4848
else:
49-
super().build_step(verbose=verbose)
49+
super().build_step(*args, **kwargs)

easybuild/easyblocks/generic/bundle.py

Lines changed: 48 additions & 21 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
@@ -445,8 +473,7 @@ def sanity_check_step(self, *args, **kwargs):
445473
# run sanity checks for specific components
446474
cnt = len(self.comp_cfgs_sanity_check)
447475
for idx, comp in enumerate(self.comp_cfgs_sanity_check):
448-
comp_name, comp_ver = comp.cfg['name'], comp.cfg['version']
449-
print_msg("sanity checking bundle component %s v%s (%i/%i)...", comp_name, comp_ver, idx + 1, cnt)
450-
self.log.info("Starting sanity check step for component %s v%s", comp_name, comp_ver)
476+
print_msg("sanity checking bundle component %s v%s (%i/%i)...", comp.name, comp.version, idx + 1, cnt)
477+
self.log.info("Starting sanity check step for component %s v%s", comp.name, comp.version)
451478

452479
comp.run_step('sanity_check', [lambda x: x.sanity_check_step])

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/generic/mesonninja.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def configure_step(self, cmd_prefix=''):
130130
res = run_shell_cmd(cmd)
131131
return res.output
132132

133-
def build_step(self, verbose=False, path=None):
133+
def build_step(self, *args, **kwargs):
134134
"""
135135
Build with Ninja.
136136
"""

easybuild/easyblocks/generic/pythonpackage.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,6 @@ def test_step(self, return_output_ec=False):
870870
res = run_shell_cmd(cmd, fail_on_error=False)
871871
# need to retrieve ec by not failing on error
872872
(out, ec) = (res.output, res.exit_code)
873-
self.log.info("cmd '%s' exited with exit code %s and output:\n%s", cmd, ec, out)
874873
else:
875874
run_shell_cmd(cmd)
876875

easybuild/easyblocks/generic/scons.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def configure_step(self):
5151
else:
5252
self.prefix = ''
5353

54-
def build_step(self, verbose=False):
54+
def build_step(self, *args, **kwargs):
5555
"""
5656
Build with SCons
5757
"""

easybuild/easyblocks/generic/waf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def configure_step(self, cmd_prefix=''):
5252

5353
return res.output
5454

55-
def build_step(self, verbose=False, path=None):
55+
def build_step(self, *args, **kwargs):
5656
"""
5757
Build with ./waf build
5858
"""

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)

0 commit comments

Comments
 (0)