Skip to content

Commit c073716

Browse files
committed
Merge branch 'develop' into 5.0.x
2 parents 6cde0db + 685467c commit c073716

File tree

12 files changed

+97
-22
lines changed

12 files changed

+97
-22
lines changed

.github/workflows/unit_tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,9 @@ jobs:
101101
# and are only run after the PR gets merged
102102
GITHUB_TOKEN: ${{secrets.CI_UNIT_TESTS_GITHUB_TOKEN}}
103103
run: |
104-
# don't install GitHub token when testing with Lmod 7.x or non-Lmod module tools, to avoid hitting GitHub rate limit;
104+
# only install GitHub token when testing with Lmod 8.x + Python 3.6 or 3.9, to avoid hitting GitHub rate limit;
105105
# tests that require a GitHub token are skipped automatically when no GitHub token is available
106-
if [[ ! "${{matrix.modules_tool}}" =~ 'Lmod-7' ]] && [[ ! "${{matrix.modules_tool}}" =~ 'modules-' ]]; then
106+
if [[ "${{matrix.modules_tool}}" =~ 'Lmod-8' ]] && [[ "${{matrix.python}}" =~ 3.[69] ]]; then
107107
if [ ! -z $GITHUB_TOKEN ]; then
108108
SET_KEYRING="import keyrings.alt.file; keyring.set_keyring(keyrings.alt.file.PlaintextKeyring())";
109109
python -c "import keyring; $SET_KEYRING; keyring.set_password('github_token', 'easybuild_test', '$GITHUB_TOKEN')";

RELEASE_NOTES

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,25 @@ For more detailed information, please see the git log.
44
These release notes can also be consulted at https://easybuild.readthedocs.io/en/latest/Release_notes.html.
55

66

7+
v4.8.2 (29 October 2023)
8+
------------------------
9+
10+
update/bugfix release
11+
12+
- various enhancements, including:
13+
- add support for `%(sysroot)s` template value (#4359)
14+
- add `dependency_names` method to `EasyConfig` class to get set of names of (direct) dependencies (#4360)
15+
- various bug fixes, including:
16+
- add CI workflow to run unit tests with Python 2 (again) (#4333)
17+
- fix typo in help message for `--silence-hook-trigger` (#4343)
18+
- include major version (`*majver`) templates in auto-generated documentation (#4347)
19+
- reset `tempfile.tempdir` to `None` to avoid that tmpdir path gets progressively deeper with each easystack item (#4350)
20+
- fix `findPythonDeps.py` script when called with an (absolute or relative) path to an easyconfig instead of a filename (#4365)
21+
- fix broken test for `reasons_for_closing`, which fails because commit status of easyconfigs PR is no longer available (#4366)
22+
- other changes:
23+
- reduce number of CI jobs by testing for Lua and Tcl module syntax in a single CI job (#4192)
24+
25+
726
v4.8.1 (11 September 2023)
827
--------------------------
928

easybuild/framework/easyconfig/easyconfig.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,15 @@ def dependencies(self, build_only=False):
11171117

11181118
return retained_deps
11191119

1120+
def dependency_names(self, build_only=False):
1121+
"""
1122+
Return a set of names of all (direct) dependencies after filtering.
1123+
Iterable builddependencies are flattened when not iterating.
1124+
1125+
:param build_only: only return build dependencies, discard others
1126+
"""
1127+
return {dep['name'] for dep in self.dependencies(build_only=build_only) if dep['name']}
1128+
11201129
def builddependencies(self):
11211130
"""
11221131
Return a flat list of the parsed build dependencies

easybuild/framework/easyconfig/templates.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@
8989
# template values which are only generated dynamically
9090
TEMPLATE_NAMES_DYNAMIC = [
9191
('arch', "System architecture (e.g. x86_64, aarch64, ppc64le, ...)"),
92+
('sysroot', "Location root directory of system, prefix for standard paths like /usr/lib and /usr/include"
93+
"as specify by the --sysroot configuration option"),
9294
('mpi_cmd_prefix', "Prefix command for running MPI programs (with default number of ranks)"),
9395
('cuda_compute_capabilities', "Comma-separated list of CUDA compute capabilities, as specified via "
9496
"--cuda-compute-capabilities configuration option or via cuda_compute_capabilities easyconfig parameter"),
@@ -197,6 +199,9 @@ def template_constant_dict(config, ignore=None, toolchain=None):
197199
# set 'arch' for system architecture based on 'machine' (4th) element of platform.uname() return value
198200
template_values['arch'] = platform.uname()[4]
199201

202+
# set 'sysroot' template based on 'sysroot' configuration option, using empty string as fallback
203+
template_values['sysroot'] = build_option('sysroot') or ''
204+
200205
# step 1: add TEMPLATE_NAMES_EASYCONFIG
201206
for name in TEMPLATE_NAMES_EASYCONFIG:
202207
if name in ignore:

easybuild/scripts/findPythonDeps.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,11 @@ def run_cmd(arguments, action_desc, capture_stderr=True, **kwargs):
5555
extra_args['universal_newlines'] = True
5656
stderr = subprocess.STDOUT if capture_stderr else subprocess.PIPE
5757
p = subprocess.Popen(arguments, stdout=subprocess.PIPE, stderr=stderr, **extra_args)
58-
out, _ = p.communicate()
58+
out, err = p.communicate()
5959
if p.returncode != 0:
60-
raise RuntimeError('Failed to %s: %s' % (action_desc, out))
60+
if err:
61+
err = "\nSTDERR:\n" + err
62+
raise RuntimeError('Failed to %s: %s%s' % (action_desc, out, err))
6163
return out
6264

6365

@@ -171,20 +173,23 @@ def print_deps(package, verbose):
171173
capture_stderr=False,
172174
action_desc='Get missing dependencies'
173175
)
176+
excluded_dep = '(%s)' % os.path.basename(args.ec)
174177
missing_deps = [dep for dep in missing_dep_out.split('\n')
175-
if dep.startswith('*') and '(%s)' % args.ec not in dep
178+
if dep.startswith('*') and excluded_dep not in dep
176179
]
177180
if missing_deps:
178181
print('You need to install all modules on which %s depends first!' % args.ec)
179182
print('\n\t'.join(['Missing:'] + missing_deps))
180183
sys.exit(1)
181184

185+
# If the --ec argument is a (relative) existing path make it absolute so we can find it after the chdir
186+
ec_arg = os.path.abspath(args.ec) if os.path.exists(args.ec) else args.ec
182187
with temporary_directory() as tmp_dir:
183188
old_dir = os.getcwd()
184189
os.chdir(tmp_dir)
185190
if args.verbose:
186191
print('Running EasyBuild to get build environment')
187-
run_cmd(['eb', args.ec, '--dump-env', '--force'], action_desc='Dump build environment')
192+
run_cmd(['eb', ec_arg, '--dump-env', '--force'], action_desc='Dump build environment')
188193
os.chdir(old_dir)
189194

190195
cmd = "source %s/*.env && python %s '%s'" % (tmp_dir, sys.argv[0], args.package)

easybuild/toolchains/compiler/gcc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class Gcc(Compiler):
5050

5151
COMPILER_FAMILY = TC_CONSTANT_GCC
5252
COMPILER_UNIQUE_OPTS = {
53-
'loop': (False, "Automatic loop parallellisation"),
53+
'loop': (False, "Automatic loop parallelisation"),
5454
'f2c': (False, "Generate code compatible with f2c and f77"),
5555
'lto': (False, "Enable Link Time Optimization"),
5656
}

easybuild/tools/options.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,8 @@ def basic_options(self):
275275
'only-blocks': ("Only build listed blocks", 'strlist', 'extend', None, 'b', {'metavar': 'BLOCKS'}),
276276
'rebuild': ("Rebuild software, even if module already exists (don't skip OS dependencies checks)",
277277
None, 'store_true', False),
278-
'robot': ("Enable dependency resolution, using easyconfigs in specified paths",
279-
'pathlist', 'store_or_None', [], 'r', {'metavar': 'PATH[%sPATH]' % os.pathsep}),
278+
'robot': ("Enable dependency resolution, optionally consider additional paths to search for easyconfigs",
279+
'pathlist', 'store_or_None', [], 'r', {'metavar': '[PATH[%sPATH]]' % os.pathsep}),
280280
'robot-paths': ("Additional paths to consider by robot for easyconfigs (--robot paths get priority)",
281281
'pathlist', 'add_flex', self.default_robot_paths, {'metavar': 'PATH[%sPATH]' % os.pathsep}),
282282
'search-paths': ("Additional locations to consider in --search (next to --robot and --robot-paths paths)",
@@ -467,7 +467,7 @@ def override_options(self):
467467
'output-style': ("Control output style; auto implies using Rich if available to produce rich output, "
468468
"with fallback to basic colored output",
469469
'choice', 'store', OUTPUT_STYLE_AUTO, OUTPUT_STYLES),
470-
'parallel': ("Specify (maximum) level of parallellism used during build procedure",
470+
'parallel': ("Specify (maximum) level of parallelism used during build procedure",
471471
'int', 'store', None),
472472
'parallel-extensions-install': ("Install list of extensions in parallel (if supported)",
473473
None, 'store_true', False),

easybuild/tools/systemtools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1205,7 +1205,7 @@ def get_default_parallelism():
12051205
raise EasyBuildError("Specified level of parallelism '%s' is not an integer value: %s", par, err)
12061206

12071207
if maxpar is not None and maxpar < par:
1208-
_log.info("Limiting parallellism from %s to %s", par, maxpar)
1208+
_log.info("Limiting parallelism from %s to %s", par, maxpar)
12091209
par = maxpar
12101210

12111211
return par

test/framework/easyblock.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2097,7 +2097,7 @@ def test_extensions_sanity_check(self):
20972097
eb.run_all_steps(True)
20982098

20992099
def test_parallel(self):
2100-
"""Test defining of parallellism."""
2100+
"""Test defining of parallelism."""
21012101
topdir = os.path.abspath(os.path.dirname(__file__))
21022102
toy_ec = os.path.join(topdir, 'easyconfigs', 'test_ecs', 't', 'toy', 'toy-0.0.eb')
21032103
toytxt = read_file(toy_ec)
@@ -2114,7 +2114,7 @@ def test_parallel(self):
21142114
os.close(handle)
21152115
write_file(toy_ec3, toytxt + "\nparallel = False")
21162116

2117-
# default: parallellism is derived from # available cores + ulimit
2117+
# default: parallelism is derived from # available cores + ulimit
21182118
test_eb = EasyBlock(EasyConfig(toy_ec))
21192119
test_eb.check_readiness_step()
21202120
self.assertTrue(isinstance(test_eb.cfg['parallel'], int) and test_eb.cfg['parallel'] > 0)

test/framework/easyconfig.py

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,9 @@ def test_dependency(self):
302302
self.assertEqual(det_full_ec_version(first), '1.1-GCC-4.6.3')
303303
self.assertEqual(det_full_ec_version(second), '2.2-GCC-4.6.3')
304304

305+
self.assertEqual(eb.dependency_names(), {'first', 'second', 'foo', 'bar'})
305306
# same tests for builddependencies
307+
self.assertEqual(eb.dependency_names(build_only=True), {'first', 'second'})
306308
first = eb.builddependencies()[0]
307309
second = eb.builddependencies()[1]
308310

@@ -355,6 +357,7 @@ def test_false_dep_version(self):
355357
self.assertEqual(len(deps), 2)
356358
self.assertEqual(deps[0]['name'], 'second_build')
357359
self.assertEqual(deps[1]['name'], 'first')
360+
self.assertEqual(eb.dependency_names(), {'first', 'second_build'})
358361

359362
# more realistic example: only filter dep for POWER
360363
self.contents = '\n'.join([
@@ -378,12 +381,14 @@ def test_false_dep_version(self):
378381
deps = eb.dependencies()
379382
self.assertEqual(len(deps), 1)
380383
self.assertEqual(deps[0]['name'], 'not_on_power')
384+
self.assertEqual(eb.dependency_names(), {'not_on_power'})
381385

382386
# only power, dependency gets filtered
383387
st.get_cpu_architecture = lambda: POWER
384388
eb = EasyConfig(self.eb_file)
385389
deps = eb.dependencies()
386390
self.assertEqual(deps, [])
391+
self.assertEqual(eb.dependency_names(), set())
387392

388393
def test_extra_options(self):
389394
""" extra_options should allow other variables to be stored """
@@ -1350,6 +1355,34 @@ def test_start_dir_template(self):
13501355
self.assertIn('start_dir in extension configure is %s &&' % ext_start_dir, logtxt)
13511356
self.assertIn('start_dir in extension build is %s &&' % ext_start_dir, logtxt)
13521357

1358+
def test_sysroot_template(self):
1359+
"""Test the %(sysroot)s template"""
1360+
1361+
test_easyconfigs = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'easyconfigs', 'test_ecs')
1362+
toy_ec = os.path.join(test_easyconfigs, 't', 'toy', 'toy-0.0.eb')
1363+
1364+
test_ec = os.path.join(self.test_prefix, 'test.eb')
1365+
test_ec_txt = read_file(toy_ec)
1366+
test_ec_txt += '\nconfigopts = "--some-opt=%(sysroot)s/"'
1367+
test_ec_txt += '\nbuildopts = "--some-opt=%(sysroot)s/"'
1368+
test_ec_txt += '\ninstallopts = "--some-opt=%(sysroot)s/"'
1369+
write_file(test_ec, test_ec_txt)
1370+
1371+
# Validate the value of the sysroot template if sysroot is unset (i.e. the build option is None)
1372+
ec = EasyConfig(test_ec)
1373+
self.assertEqual(ec['configopts'], "--some-opt=/")
1374+
self.assertEqual(ec['buildopts'], "--some-opt=/")
1375+
self.assertEqual(ec['installopts'], "--some-opt=/")
1376+
1377+
# Validate the value of the sysroot template if sysroot is unset (i.e. the build option is None)
1378+
# As a test, we'll set the sysroot to self.test_prefix, as it has to be a directory that is guaranteed to exist
1379+
update_build_option('sysroot', self.test_prefix)
1380+
1381+
ec = EasyConfig(test_ec)
1382+
self.assertEqual(ec['configopts'], "--some-opt=%s/" % self.test_prefix)
1383+
self.assertEqual(ec['buildopts'], "--some-opt=%s/" % self.test_prefix)
1384+
self.assertEqual(ec['installopts'], "--some-opt=%s/" % self.test_prefix)
1385+
13531386
def test_constant_doc(self):
13541387
"""test constant documentation"""
13551388
doc = avail_easyconfig_constants()
@@ -1602,18 +1635,15 @@ def test_filter_deps(self):
16021635
test_ecs_dir = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'easyconfigs', 'test_ecs')
16031636
ec_file = os.path.join(test_ecs_dir, 'f', 'foss', 'foss-2018a.eb')
16041637
ec = EasyConfig(ec_file)
1605-
deps = sorted([dep['name'] for dep in ec.dependencies()])
1606-
self.assertEqual(deps, ['FFTW', 'GCC', 'OpenBLAS', 'OpenMPI', 'ScaLAPACK'])
1638+
self.assertEqual(ec.dependency_names(), {'FFTW', 'GCC', 'OpenBLAS', 'OpenMPI', 'ScaLAPACK'})
16071639

16081640
# test filtering multiple deps
16091641
init_config(build_options={'filter_deps': ['FFTW', 'ScaLAPACK']})
1610-
deps = sorted([dep['name'] for dep in ec.dependencies()])
1611-
self.assertEqual(deps, ['GCC', 'OpenBLAS', 'OpenMPI'])
1642+
self.assertEqual(ec.dependency_names(), {'GCC', 'OpenBLAS', 'OpenMPI'})
16121643

16131644
# test filtering of non-existing dep
16141645
init_config(build_options={'filter_deps': ['zlib']})
1615-
deps = sorted([dep['name'] for dep in ec.dependencies()])
1616-
self.assertEqual(deps, ['FFTW', 'GCC', 'OpenBLAS', 'OpenMPI', 'ScaLAPACK'])
1646+
self.assertEqual(ec.dependency_names(), {'FFTW', 'GCC', 'OpenBLAS', 'OpenMPI', 'ScaLAPACK'})
16171647

16181648
# test parsing of value passed to --filter-deps
16191649
opts = init_config(args=[])
@@ -1647,6 +1677,7 @@ def test_filter_deps(self):
16471677
init_config(build_options=build_options)
16481678
ec = EasyConfig(ec_file, validate=False)
16491679
self.assertEqual(ec.dependencies(), [])
1680+
self.assertEqual(ec.dependency_names(), set())
16501681

16511682
def test_replaced_easyconfig_parameters(self):
16521683
"""Test handling of replaced easyconfig parameters."""
@@ -1835,6 +1866,9 @@ def test_external_dependencies(self):
18351866
}
18361867
self.assertEqual(deps[7]['external_module_metadata'], cray_netcdf_metadata)
18371868

1869+
# External module names are omitted
1870+
self.assertEqual(ec.dependency_names(), {'intel'})
1871+
18381872
# provide file with partial metadata for some external modules;
18391873
# metadata obtained from probing modules should be added to it...
18401874
metadata = os.path.join(self.test_prefix, 'external_modules_metadata.cfg')
@@ -1863,6 +1897,7 @@ def test_external_dependencies(self):
18631897
deps = ec.dependencies()
18641898

18651899
self.assertEqual(len(deps), 8)
1900+
self.assertEqual(ec.dependency_names(), {'intel'})
18661901

18671902
for idx in [0, 1, 2, 6]:
18681903
self.assertEqual(deps[idx]['external_module_metadata'], {})
@@ -3229,6 +3264,7 @@ def test_template_constant_dict(self):
32293264
'nameletter': 'g',
32303265
'nameletterlower': 'g',
32313266
'parallel': None,
3267+
'sysroot': '',
32323268
'toolchain_name': 'foss',
32333269
'toolchain_version': '2018a',
32343270
'version': '1.5',
@@ -3311,6 +3347,7 @@ def test_template_constant_dict(self):
33113347
'pyminver': '7',
33123348
'pyshortver': '3.7',
33133349
'pyver': '3.7.2',
3350+
'sysroot': '',
33143351
'version': '0.01',
33153352
'version_major': '0',
33163353
'version_major_minor': '0.01',
@@ -3375,6 +3412,7 @@ def test_template_constant_dict(self):
33753412
'namelower': 'foo',
33763413
'nameletter': 'f',
33773414
'nameletterlower': 'f',
3415+
'sysroot': '',
33783416
'version': '1.2.3',
33793417
'version_major': '1',
33803418
'version_major_minor': '1.2',

0 commit comments

Comments
 (0)