Skip to content

Commit 75f0f59

Browse files
authored
Merge pull request #3164 from easybuilders/4.1.x
release EasyBuild v4.1.1
2 parents fe1439e + 3006ffc commit 75f0f59

File tree

274 files changed

+1397
-477
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

274 files changed

+1397
-477
lines changed

.github/workflows/unit_tests.yml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77
strategy:
88
matrix:
99
python: [2.7, 3.5, 3.6, 3.7, 3.8]
10-
modules_tool: [Lmod-7.8.22, Lmod-8.2.3, modules-tcl-1.147, modules-3.2.10, modules-4.1.4]
10+
modules_tool: [Lmod-7.8.22, Lmod-8.2.9, modules-tcl-1.147, modules-3.2.10, modules-4.1.4]
1111
module_syntax: [Lua, Tcl]
1212
# exclude some configuration for non-Lmod modules tool:
1313
# - don't test with Lua module syntax (only supported in Lmod)
@@ -138,6 +138,9 @@ jobs:
138138
eb --show-system-info
139139
# check GitHub configuration
140140
eb --check-github --github-user=easybuild_test
141+
# create file owned by root but writable by anyone (used by test_copy_file)
142+
sudo touch /tmp/file_to_overwrite_for_easybuild_test_copy_file.txt
143+
sudo chmod o+w /tmp/file_to_overwrite_for_easybuild_test_copy_file.txt
141144
# run test suite
142145
python -O -m test.framework.suite 2>&1 | tee test_framework_suite.log
143146
# try and make sure output of running tests is clean (no printed messages/warnings)
@@ -150,7 +153,7 @@ jobs:
150153
EB_BOOTSTRAP_VERSION=$(grep '^EB_BOOTSTRAP_VERSION' easybuild/scripts/bootstrap_eb.py | sed 's/[^0-9.]//g')
151154
EB_BOOTSTRAP_SHA256SUM=$(sha256sum easybuild/scripts/bootstrap_eb.py | cut -f1 -d' ')
152155
EB_BOOTSTRAP_FOUND="$EB_BOOTSTRAP_VERSION $EB_BOOTSTRAP_SHA256SUM"
153-
EB_BOOTSTRAP_EXPECTED="20190922.01 778762479d77df641c247aba33c13203c8c5b8275e47f59464bab93917aaaf22"
156+
EB_BOOTSTRAP_EXPECTED="20190922.01 7927513e7448d886decfb1bb5daf840e85dc7367f57cc75e51b68f21fe109d53"
154157
test "$EB_BOOTSTRAP_FOUND" = "$EB_BOOTSTRAP_EXPECTED" || (echo "Version check on bootstrap script failed $EB_BOOTSTRAP_FOUND" && exit 1)
155158
156159
# test bootstrap script (only compatible with Python 2 for now)

.travis.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ script:
8585
- EB_VERBOSE=1 eb --version
8686
# check GitHub configuration
8787
- eb --check-github --github-user=easybuild_test
88+
# create file owned by root but writable by anyone (used by test_copy_file)
89+
- sudo touch /tmp/file_to_overwrite_for_easybuild_test_copy_file.txt
90+
- sudo chmod o+w /tmp/file_to_overwrite_for_easybuild_test_copy_file.txt
8891
# run test suite
8992
- python -O -m test.framework.suite 2>&1 | tee test_framework_suite.log
9093
# try and make sure output of running tests is clean (no printed messages/warnings)
@@ -97,7 +100,7 @@ script:
97100
- EB_BOOTSTRAP_VERSION=$(grep '^EB_BOOTSTRAP_VERSION' $TRAVIS_BUILD_DIR/easybuild/scripts/bootstrap_eb.py | sed 's/[^0-9.]//g')
98101
- EB_BOOTSTRAP_SHA256SUM=$(sha256sum $TRAVIS_BUILD_DIR/easybuild/scripts/bootstrap_eb.py | cut -f1 -d' ')
99102
- EB_BOOTSTRAP_FOUND="$EB_BOOTSTRAP_VERSION $EB_BOOTSTRAP_SHA256SUM"
100-
- EB_BOOTSTRAP_EXPECTED="20190922.01 778762479d77df641c247aba33c13203c8c5b8275e47f59464bab93917aaaf22"
103+
- EB_BOOTSTRAP_EXPECTED="20190922.01 7927513e7448d886decfb1bb5daf840e85dc7367f57cc75e51b68f21fe109d53"
101104
- test "$EB_BOOTSTRAP_FOUND" = "$EB_BOOTSTRAP_EXPECTED" || (echo "Version check on bootstrap script failed $EB_BOOTSTRAP_FOUND" && exit 1)
102105
# test bootstrap script (skip when testing with Python 3 for now, since latest EasyBuild release is not compatible with Python 3 yet)
103106
- if [ ! "x$TRAVIS_PYTHON_VERSION" =~ x3.[0-9] ]; then python $TRAVIS_BUILD_DIR/easybuild/scripts/bootstrap_eb.py /tmp/$TRAVIS_JOB_ID/eb_bootstrap; fi

RELEASE_NOTES

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,38 @@ For more detailed information, please see the git log.
33

44
These release notes can also be consulted at https://easybuild.readthedocs.io/en/latest/Release_notes.html.
55

6+
v4.1.1 (January 16th 2020)
7+
--------------------------
8+
9+
update/bugfix release
10+
11+
- various enhancements, including:
12+
- add check_log_for_errors function (in easybuild.tools.run) to detect and handle multiple errors (#3118)
13+
- implement support for 'eb --show-ec' to show contents of specified easyconfig file (#3132)
14+
- also update $XDG_DATA_DIR (share/) and $GI_TYPELIB_PATH environment variables (lib*/girepository-*) in generated module files (#3133)
15+
- add support for --copy-ec to copy easyconfig file to specified location (#3142)
16+
- mention --disable-* option in --help output for boolean options enabled by default (#3151)
17+
- add --cuda-compute-capabilities configuration option (#3161)
18+
- various bug fixes, including:
19+
- ignore imports from vsc namespace made from pkgutil.py (#3120)
20+
- only actually change permissions using os.chmod in adjust_permissions if the current permissions are not correct already (#3125)
21+
- use shutil.copyfile to just copy file contents if target path exists and is owned by someone else (#3127)
22+
- fix or avoid warnings that commonly arise in build log (#3129)
23+
- disable buffering in asyncprocess.Popen using bufsize=0, to fix run_cmd_qa missing output (#3130)
24+
- update pip & install wheel package in generated Singularity container recipes (#3136)
25+
- avoid crash in modify_env & unset unset_env_vars when using (older versions) of Python 3.5 & 3.6 by using list(...) (#3140)
26+
- take into account that lib64 could be a symlink to lib (or vice versa) in get_software_libdir function (#3141)
27+
- only parse docstring if it exists in gen_easyblock_doc_section_rst function (#3144)
28+
- only add useful entries for $CPATH, $(LD_)LIBRARY_PATH and $PATH (non-empty directories) (#3145, #3152)
29+
- fix --list-software=detailed when using Python 3 by leveraging sort_looseversions function from py2vs3 module (#3146)
30+
- ensure subdirectories in software install directory have correct search (exec) permission (#3147)
31+
- take into account that a checksum value may be a tuple of valid checksum in EasyBlock.check_checksums (#3153)
32+
- other changes:
33+
- bump to Lmod 8.2.9 in GitHub CI config (#3115)
34+
- update copyright statements for 2020 (#3149)
35+
- make Hound CI code style checker ignore "Black would make changes" produced by flake8-black (#3162)
36+
37+
638
v4.1.0 (December 4th 2019)
739
--------------------------
840

easybuild/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
##
2-
# Copyright 2011-2019 Ghent University
2+
# Copyright 2011-2020 Ghent University
33
#
44
# This file is part of EasyBuild,
55
# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en),

easybuild/base/exceptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright 2015-2018 Ghent University
2+
# Copyright 2015-2020 Ghent University
33
#
44
# This file is part of EasyBuild,
55
# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en),

easybuild/base/fancylogger.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright 2011-2018 Ghent University
2+
# Copyright 2011-2020 Ghent University
33
#
44
# This file is part of EasyBuild,
55
# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en),

easybuild/base/generaloption.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright 2011-2018 Ghent University
2+
# Copyright 2011-2020 Ghent University
33
#
44
# This file is part of EasyBuild,
55
# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en),
@@ -1088,11 +1088,15 @@ def add_group_parser(self, opt_dict, description, prefix=None, otherdefaults=Non
10881088

10891089
if default is not None:
10901090
if len(str(default)) == 0:
1091-
extra_help.append("def ''") # empty string
1091+
extra_help.append("default: ''") # empty string
10921092
elif typ in ExtOption.TYPE_STRLIST:
1093-
extra_help.append("def %s" % sep.join(default))
1093+
extra_help.append("default: %s" % sep.join(default))
10941094
else:
1095-
extra_help.append("def %s" % default)
1095+
extra_help.append("default: %s" % default)
1096+
1097+
# for boolean options enabled by default, mention that they can be disabled using --disable-*
1098+
if default is True:
1099+
extra_help.append("disable with --disable-%s" % key)
10961100

10971101
if len(extra_help) > 0:
10981102
hlp += " (%s)" % ("; ".join(extra_help))

easybuild/base/testing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright 2014-2018 Ghent University
2+
# Copyright 2014-2020 Ghent University
33
#
44
# This file is part of EasyBuild,
55
# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en),

easybuild/framework/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
##
2-
# Copyright 2009-2019 Ghent University
2+
# Copyright 2009-2020 Ghent University
33
#
44
# This file is part of EasyBuild,
55
# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en),

easybuild/framework/easyblock.py

Lines changed: 57 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# #
2-
# Copyright 2009-2019 Ghent University
2+
# Copyright 2009-2020 Ghent University
33
#
44
# This file is part of EasyBuild,
55
# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en),
@@ -44,7 +44,6 @@
4444
import inspect
4545
import os
4646
import re
47-
import shutil
4847
import stat
4948
import tempfile
5049
import time
@@ -62,6 +61,7 @@
6261
from easybuild.framework.easyconfig.style import MAX_LINE_LENGTH
6362
from easybuild.framework.easyconfig.tools import get_paths_for
6463
from easybuild.framework.easyconfig.templates import TEMPLATE_NAMES_EASYBLOCK_RUN_STEP, template_constant_dict
64+
from easybuild.framework.extension import resolve_exts_filter_template
6565
from easybuild.tools import config, filetools
6666
from easybuild.tools.build_details import get_build_stats
6767
from easybuild.tools.build_log import EasyBuildError, dry_run_msg, dry_run_warning, dry_run_set_dirs
@@ -71,12 +71,12 @@
7171
from easybuild.tools.config import install_path, log_path, package_path, source_paths
7272
from easybuild.tools.environment import restore_env, sanitize_env
7373
from easybuild.tools.filetools import CHECKSUM_TYPE_MD5, CHECKSUM_TYPE_SHA256
74-
from easybuild.tools.filetools import adjust_permissions, apply_patch, apply_regex_substitutions, back_up_file
74+
from easybuild.tools.filetools import adjust_permissions, apply_patch, back_up_file
7575
from easybuild.tools.filetools import change_dir, convert_name, compute_checksum, copy_file, derive_alt_pypi_url
7676
from easybuild.tools.filetools import diff_files, download_file, encode_class_name, extract_file
7777
from easybuild.tools.filetools import find_backup_name_candidate, get_source_tarball_from_git, is_alt_pypi_url
7878
from easybuild.tools.filetools import is_sha256_checksum, mkdir, move_file, move_logs, read_file, remove_dir
79-
from easybuild.tools.filetools import remove_file, rmtree2, verify_checksum, weld_paths, write_file
79+
from easybuild.tools.filetools import remove_file, rmtree2, verify_checksum, weld_paths, write_file, dir_contains_files
8080
from easybuild.tools.hooks import BUILD_STEP, CLEANUP_STEP, CONFIGURE_STEP, EXTENSIONS_STEP, FETCH_STEP, INSTALL_STEP
8181
from easybuild.tools.hooks import MODULE_STEP, PACKAGE_STEP, PATCH_STEP, PERMISSIONS_STEP, POSTITER_STEP, POSTPROC_STEP
8282
from easybuild.tools.hooks import PREPARE_STEP, READY_STEP, SANITYCHECK_STEP, SOURCE_STEP, TEST_STEP, TESTCASES_STEP
@@ -1290,6 +1290,9 @@ def make_module_req(self):
12901290
note += "for paths are skipped for the statements below due to dry run"
12911291
lines.append(self.module_generator.comment(note))
12921292

1293+
# for these environment variables, the corresponding subdirectory must include at least one file
1294+
keys_requiring_files = ('CPATH', 'LD_LIBRARY_PATH', 'LIBRARY_PATH', 'PATH')
1295+
12931296
for key in sorted(requirements):
12941297
if self.dry_run:
12951298
self.dry_run_msg(" $%s: %s" % (key, ', '.join(requirements[key])))
@@ -1302,6 +1305,16 @@ def make_module_req(self):
13021305
# only use glob if the string is non-empty
13031306
if path and not self.dry_run:
13041307
paths = sorted(glob.glob(path))
1308+
if paths and key in keys_requiring_files:
1309+
# only retain paths that contain at least one file
1310+
retained_paths = [
1311+
path for path in paths
1312+
if os.path.isdir(os.path.join(self.installdir, path))
1313+
and dir_contains_files(os.path.join(self.installdir, path))
1314+
]
1315+
self.log.info("Only retaining paths for %s that contain at least one file: %s -> %s",
1316+
key, paths, retained_paths)
1317+
paths = retained_paths
13051318
else:
13061319
# empty string is a valid value here (i.e. to prepend the installation prefix, cfr $CUDA_HOME)
13071320
paths = [path]
@@ -1317,15 +1330,18 @@ def make_module_req_guess(self):
13171330
"""
13181331
A dictionary of possible directories to look for.
13191332
"""
1333+
lib_paths = ['lib', 'lib32', 'lib64']
13201334
return {
13211335
'PATH': ['bin', 'sbin'],
1322-
'LD_LIBRARY_PATH': ['lib', 'lib32', 'lib64'],
1323-
'LIBRARY_PATH': ['lib', 'lib32', 'lib64'],
1336+
'LD_LIBRARY_PATH': lib_paths,
1337+
'LIBRARY_PATH': lib_paths,
13241338
'CPATH': ['include'],
13251339
'MANPATH': ['man', os.path.join('share', 'man')],
1326-
'PKG_CONFIG_PATH': [os.path.join(x, 'pkgconfig') for x in ['lib', 'lib32', 'lib64', 'share']],
1340+
'PKG_CONFIG_PATH': [os.path.join(x, 'pkgconfig') for x in lib_paths + ['share']],
13271341
'ACLOCAL_PATH': [os.path.join('share', 'aclocal')],
13281342
'CLASSPATH': ['*.jar'],
1343+
'XDG_DATA_DIRS': ['share'],
1344+
'GI_TYPELIB_PATH': [os.path.join(x, 'girepository-*') for x in lib_paths],
13291345
}
13301346

13311347
def load_module(self, mod_paths=None, purge=True, extra_modules=None):
@@ -1431,43 +1447,18 @@ def skip_extensions(self):
14311447

14321448
if not exts_filter or len(exts_filter) == 0:
14331449
raise EasyBuildError("Skipping of extensions, but no exts_filter set in easyconfig")
1434-
elif isinstance(exts_filter, string_type) or len(exts_filter) != 2:
1435-
raise EasyBuildError('exts_filter should be a list or tuple of ("command","input")')
1436-
cmdtmpl = exts_filter[0]
1437-
cmdinputtmpl = exts_filter[1]
14381450

14391451
res = []
14401452
for ext in self.exts:
1441-
name = ext['name']
1442-
if 'options' in ext and 'modulename' in ext['options']:
1443-
modname = ext['options']['modulename']
1444-
else:
1445-
modname = name
1446-
tmpldict = {
1447-
'ext_name': modname,
1448-
'ext_version': ext.get('version'),
1449-
'src': ext.get('source'),
1450-
}
1451-
1452-
try:
1453-
cmd = cmdtmpl % tmpldict
1454-
except KeyError as err:
1455-
msg = "KeyError occurred on completing extension filter template: %s; "
1456-
msg += "'name'/'version' keys are no longer supported, should use 'ext_name'/'ext_version' instead"
1457-
self.log.nosupport(msg % err, '2.0')
1458-
1459-
if cmdinputtmpl:
1460-
stdin = cmdinputtmpl % tmpldict
1461-
(cmdstdouterr, ec) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, inp=stdin, regexp=False)
1462-
else:
1463-
(cmdstdouterr, ec) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False)
1453+
cmd, stdin = resolve_exts_filter_template(exts_filter, ext)
1454+
(cmdstdouterr, ec) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, inp=stdin, regexp=False)
14641455
self.log.info("exts_filter result %s %s", cmdstdouterr, ec)
14651456
if ec:
1466-
self.log.info("Not skipping %s" % name)
1457+
self.log.info("Not skipping %s" % ext['name'])
14671458
self.log.debug("exit code: %s, stdout/err: %s" % (ec, cmdstdouterr))
14681459
res.append(ext)
14691460
else:
1470-
self.log.info("Skipping %s" % name)
1461+
self.log.info("Skipping %s" % ext['name'])
14711462
self.exts = res
14721463

14731464
#
@@ -1585,8 +1576,9 @@ def post_iter_step(self):
15851576

15861577
def det_iter_cnt(self):
15871578
"""Determine iteration count based on configure/build/install options that may be lists."""
1588-
iter_opt_counts = [len(self.cfg[opt]) for opt in ITERATE_OPTIONS
1589-
if opt not in ['builddependencies'] and isinstance(self.cfg[opt], (list, tuple))]
1579+
# Using get_ref to avoid resolving templates as their required attributes may not be available yet
1580+
iter_opt_counts = [len(self.cfg.get_ref(opt)) for opt in ITERATE_OPTIONS
1581+
if opt not in ['builddependencies'] and isinstance(self.cfg.get_ref(opt), (list, tuple))]
15901582

15911583
# we need to take into account that builddependencies is always a list
15921584
# we're only iterating over it if it's a list of lists
@@ -1814,7 +1806,7 @@ def checksum_step(self):
18141806

18151807
def check_checksums_for(self, ent, sub='', source_cnt=None):
18161808
"""
1817-
Utility method: check whether checksums for all sources/patches are available, for given entity
1809+
Utility method: check whether SHA256 checksums for all sources/patches are available, for given entity
18181810
"""
18191811
ec_fn = os.path.basename(self.cfg.path)
18201812
checksum_issues = []
@@ -1841,8 +1833,19 @@ def check_checksums_for(self, ent, sub='', source_cnt=None):
18411833
if isinstance(checksum, dict):
18421834
checksum = checksum.get(fn)
18431835

1844-
if not is_sha256_checksum(checksum):
1845-
msg = "Non-SHA256 checksum found for %s: %s" % (fn, checksum)
1836+
# take into account that we may encounter a tuple of valid SHA256 checksums
1837+
# (see https://github.com/easybuilders/easybuild-framework/pull/2958)
1838+
if isinstance(checksum, tuple):
1839+
# 1st tuple item may indicate checksum type, must be SHA256 or else it's blatently ignored here
1840+
if len(checksum) == 2 and checksum[0] == CHECKSUM_TYPE_SHA256:
1841+
valid_checksums = (checksum[1],)
1842+
else:
1843+
valid_checksums = checksum
1844+
else:
1845+
valid_checksums = (checksum,)
1846+
1847+
if not all(is_sha256_checksum(c) for c in valid_checksums):
1848+
msg = "Non-SHA256 checksum(s) found for %s: %s" % (fn, valid_checksums)
18461849
checksum_issues.append(msg)
18471850

18481851
return checksum_issues
@@ -2760,19 +2763,31 @@ def permissions_step(self):
27602763

27612764
# add read permissions for everybody on all files, taking into account group (if any)
27622765
perms = stat.S_IRUSR | stat.S_IRGRP
2766+
# directory permissions: readable (r) & searchable (x)
2767+
dir_perms = stat.S_IXUSR | stat.S_IXGRP
27632768
self.log.debug("Ensuring read permissions for user/group on install dir (recursively)")
2769+
27642770
if self.group is None:
27652771
perms |= stat.S_IROTH
2772+
dir_perms |= stat.S_IXOTH
27662773
self.log.debug("Also ensuring read permissions for others on install dir (no group specified)")
27672774

27682775
umask = build_option('umask')
27692776
if umask is not None:
27702777
# umask is specified as a string, so interpret it first as integer in octal, then take complement (~)
27712778
perms &= ~int(umask, 8)
2779+
dir_perms &= ~int(umask, 8)
27722780
self.log.debug("Taking umask '%s' into account when ensuring read permissions to install dir", umask)
27732781

2782+
self.log.debug("Adding file read permissions in %s using '%s'", self.installdir, oct(perms))
27742783
adjust_permissions(self.installdir, perms, add=True, recursive=True, relative=True, ignore_errors=True)
2775-
self.log.info("Successfully added read permissions '%s' recursively on install dir", oct(perms))
2784+
2785+
# also ensure directories have exec permissions (so they can be opened)
2786+
self.log.debug("Adding directory search permissions in %s using '%s'", self.installdir, oct(dir_perms))
2787+
adjust_permissions(self.installdir, dir_perms, add=True, recursive=True, relative=True, onlydirs=True,
2788+
ignore_errors=True)
2789+
2790+
self.log.info("Successfully added read permissions recursively on install dir %s", self.installdir)
27762791

27772792
def test_cases_step(self):
27782793
"""

0 commit comments

Comments
 (0)