Skip to content

Commit 690dff1

Browse files
authored
Merge branch 'develop' into fix_error_display
2 parents 1f01afe + 49533e6 commit 690dff1

27 files changed

Lines changed: 748 additions & 295 deletions

.github/workflows/unit_tests.yml

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -153,19 +153,15 @@ jobs:
153153
EB_BOOTSTRAP_VERSION=$(grep '^EB_BOOTSTRAP_VERSION' easybuild/scripts/bootstrap_eb.py | sed 's/[^0-9.]//g')
154154
EB_BOOTSTRAP_SHA256SUM=$(sha256sum easybuild/scripts/bootstrap_eb.py | cut -f1 -d' ')
155155
EB_BOOTSTRAP_FOUND="$EB_BOOTSTRAP_VERSION $EB_BOOTSTRAP_SHA256SUM"
156-
EB_BOOTSTRAP_EXPECTED="20190922.01 7927513e7448d886decfb1bb5daf840e85dc7367f57cc75e51b68f21fe109d53"
156+
EB_BOOTSTRAP_EXPECTED="20200203.01 616bf3ce812c0844bf9ea3e690f9d88b394ed48f834ddb8424a73cf45fc64ea5"
157157
test "$EB_BOOTSTRAP_FOUND" = "$EB_BOOTSTRAP_EXPECTED" || (echo "Version check on bootstrap script failed $EB_BOOTSTRAP_FOUND" && exit 1)
158158
159-
# test bootstrap script (only compatible with Python 2 for now)
160-
if [[ ${{matrix.python}} =~ '2.' ]]; then
161-
export PREFIX=/tmp/$USER/$GITHUB_SHA/eb_bootstrap
162-
python easybuild/scripts/bootstrap_eb.py $PREFIX
163-
# unset $PYTHONPATH to avoid mixing two EasyBuild 'installations' when testing bootstrapped EasyBuild module
164-
unset PYTHONPATH
165-
# simple sanity check on bootstrapped EasyBuild module (skip when testing with Python 3, for now)
166-
module use $PREFIX/modules/all
167-
module load EasyBuild
168-
eb --version
169-
else
170-
echo "Testing of bootstrap script skipped when testing with Python ${{matrix.python}}"
171-
fi
159+
# test bootstrap script
160+
export PREFIX=/tmp/$USER/$GITHUB_SHA/eb_bootstrap
161+
python easybuild/scripts/bootstrap_eb.py $PREFIX
162+
# unset $PYTHONPATH to avoid mixing two EasyBuild 'installations' when testing bootstrapped EasyBuild module
163+
unset PYTHONPATH
164+
# simple sanity check on bootstrapped EasyBuild module
165+
module use $PREFIX/modules/all
166+
module load EasyBuild
167+
eb --version

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
.idea
12
.pydevproject
23
.project
34
LICENSE_HEADER

.travis.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,12 @@ script:
100100
- EB_BOOTSTRAP_VERSION=$(grep '^EB_BOOTSTRAP_VERSION' $TRAVIS_BUILD_DIR/easybuild/scripts/bootstrap_eb.py | sed 's/[^0-9.]//g')
101101
- EB_BOOTSTRAP_SHA256SUM=$(sha256sum $TRAVIS_BUILD_DIR/easybuild/scripts/bootstrap_eb.py | cut -f1 -d' ')
102102
- EB_BOOTSTRAP_FOUND="$EB_BOOTSTRAP_VERSION $EB_BOOTSTRAP_SHA256SUM"
103-
- EB_BOOTSTRAP_EXPECTED="20190922.01 7927513e7448d886decfb1bb5daf840e85dc7367f57cc75e51b68f21fe109d53"
103+
- EB_BOOTSTRAP_EXPECTED="20200203.01 616bf3ce812c0844bf9ea3e690f9d88b394ed48f834ddb8424a73cf45fc64ea5"
104104
- test "$EB_BOOTSTRAP_FOUND" = "$EB_BOOTSTRAP_EXPECTED" || (echo "Version check on bootstrap script failed $EB_BOOTSTRAP_FOUND" && exit 1)
105-
# test bootstrap script (skip when testing with Python 3 for now, since latest EasyBuild release is not compatible with Python 3 yet)
106-
- 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
105+
# test bootstrap script
106+
- python $TRAVIS_BUILD_DIR/easybuild/scripts/bootstrap_eb.py /tmp/$TRAVIS_JOB_ID/eb_bootstrap
107107
# unset $PYTHONPATH to avoid mixing two EasyBuild 'installations' when testing bootstrapped EasyBuild module
108108
- unset PYTHONPATH
109-
# simply sanity check on bootstrapped EasyBuild module (skip when testing with Python 3, for now)
110-
- if [ ! "x$TRAVIS_PYTHON_VERSION" =~ x3.[0-9] ]; then module use /tmp/$TRAVIS_JOB_ID/eb_bootstrap/modules/all; fi
111-
- if [ ! "x$TRAVIS_PYTHON_VERSION" =~ x3.[0-9] ]; then module load EasyBuild; eb --version; fi
109+
# simply sanity check on bootstrapped EasyBuild module
110+
- module use /tmp/$TRAVIS_JOB_ID/eb_bootstrap/modules/all
111+
- module load EasyBuild; eb --version

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/framework/easyblock.py

Lines changed: 75 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
from easybuild.framework.easyconfig.style import MAX_LINE_LENGTH
6262
from easybuild.framework.easyconfig.tools import get_paths_for
6363
from easybuild.framework.easyconfig.templates import TEMPLATE_NAMES_EASYBLOCK_RUN_STEP, template_constant_dict
64+
from easybuild.framework.extension import resolve_exts_filter_template
6465
from easybuild.tools import config, run
6566
from easybuild.tools.build_details import get_build_stats
6667
from easybuild.tools.build_log import EasyBuildError, dry_run_msg, dry_run_warning, dry_run_set_dirs
@@ -1281,47 +1282,69 @@ def make_module_req(self):
12811282

12821283
lines = ['\n']
12831284
if os.path.isdir(self.installdir):
1284-
change_dir(self.installdir)
1285+
old_dir = change_dir(self.installdir)
1286+
else:
1287+
old_dir = None
12851288

1289+
if self.dry_run:
1290+
self.dry_run_msg("List of paths that would be searched and added to module file:\n")
1291+
note = "note: glob patterns are not expanded and existence checks "
1292+
note += "for paths are skipped for the statements below due to dry run"
1293+
lines.append(self.module_generator.comment(note))
1294+
1295+
# for these environment variables, the corresponding subdirectory must include at least one file
1296+
keys_requiring_files = set(('PATH', 'LD_LIBRARY_PATH', 'LIBRARY_PATH', 'CPATH',
1297+
'CMAKE_PREFIX_PATH', 'CMAKE_LIBRARY_PATH'))
1298+
1299+
for key, reqs in sorted(requirements.items()):
1300+
if isinstance(reqs, string_type):
1301+
self.log.warning("Hoisting string value %s into a list before iterating over it", reqs)
1302+
reqs = [reqs]
12861303
if self.dry_run:
1287-
self.dry_run_msg("List of paths that would be searched and added to module file:\n")
1288-
note = "note: glob patterns are not expanded and existence checks "
1289-
note += "for paths are skipped for the statements below due to dry run"
1290-
lines.append(self.module_generator.comment(note))
1291-
1292-
# for these environment variables, the corresponding subdirectory must include at least one file
1293-
keys_requiring_files = ('CPATH', 'LD_LIBRARY_PATH', 'LIBRARY_PATH', 'PATH')
1294-
1295-
for key in sorted(requirements):
1296-
if self.dry_run:
1297-
self.dry_run_msg(" $%s: %s" % (key, ', '.join(requirements[key])))
1298-
reqs = requirements[key]
1299-
if isinstance(reqs, string_type):
1300-
self.log.warning("Hoisting string value %s into a list before iterating over it", reqs)
1301-
reqs = [reqs]
1302-
1303-
for path in reqs:
1304-
# only use glob if the string is non-empty
1305-
if path and not self.dry_run:
1306-
paths = sorted(glob.glob(path))
1307-
if paths and key in keys_requiring_files:
1308-
# only retain paths that contain at least one file
1309-
retained_paths = [
1310-
path for path in paths
1311-
if os.path.isdir(os.path.join(self.installdir, path))
1312-
and dir_contains_files(os.path.join(self.installdir, path))
1313-
]
1314-
self.log.info("Only retaining paths for %s that contain at least one file: %s -> %s",
1315-
key, paths, retained_paths)
1316-
paths = retained_paths
1317-
else:
1318-
# empty string is a valid value here (i.e. to prepend the installation prefix, cfr $CUDA_HOME)
1319-
paths = [path]
1304+
self.dry_run_msg(" $%s: %s" % (key, ', '.join(reqs)))
1305+
# Don't expand globs or do any filtering below for dry run
1306+
paths = sorted(reqs)
1307+
else:
1308+
# Expand globs but only if the string is non-empty
1309+
# empty string is a valid value here (i.e. to prepend the installation prefix, cfr $CUDA_HOME)
1310+
paths = sorted(sum((glob.glob(path) if path else [path] for path in reqs), [])) # sum flattens to list
1311+
1312+
# If lib64 is just a symlink to lib we fixup the paths to avoid duplicates
1313+
lib64_is_symlink = (all(os.path.isdir(path) for path in ['lib', 'lib64'])
1314+
and os.path.samefile('lib', 'lib64'))
1315+
if lib64_is_symlink:
1316+
fixed_paths = []
1317+
for path in paths:
1318+
if (path + os.path.sep).startswith('lib64' + os.path.sep):
1319+
# We only need CMAKE_LIBRARY_PATH if there is a separate lib64 path, so skip symlink
1320+
if key == 'CMAKE_LIBRARY_PATH':
1321+
continue
1322+
path = path.replace('lib64', 'lib', 1)
1323+
fixed_paths.append(path)
1324+
if fixed_paths != paths:
1325+
self.log.info("Fixed symlink lib64 in paths for %s: %s -> %s", key, paths, fixed_paths)
1326+
paths = fixed_paths
1327+
# Use a set to remove duplicates, e.g. by having lib64 and lib which get fixed to lib and lib above
1328+
paths = sorted(set(paths))
1329+
if key in keys_requiring_files:
1330+
# only retain paths that contain at least one file
1331+
retained_paths = [
1332+
path for path in paths
1333+
if os.path.isdir(os.path.join(self.installdir, path))
1334+
and dir_contains_files(os.path.join(self.installdir, path))
1335+
]
1336+
if retained_paths != paths:
1337+
self.log.info("Only retaining paths for %s that contain at least one file: %s -> %s",
1338+
key, paths, retained_paths)
1339+
paths = retained_paths
1340+
1341+
if paths:
1342+
lines.append(self.module_generator.prepend_paths(key, paths))
1343+
if self.dry_run:
1344+
self.dry_run_msg('')
13201345

1321-
lines.append(self.module_generator.prepend_paths(key, paths))
1322-
if self.dry_run:
1323-
self.dry_run_msg('')
1324-
change_dir(self.orig_workdir)
1346+
if old_dir is not None:
1347+
change_dir(old_dir)
13251348

13261349
return ''.join(lines)
13271350

@@ -1341,6 +1364,8 @@ def make_module_req_guess(self):
13411364
'CLASSPATH': ['*.jar'],
13421365
'XDG_DATA_DIRS': ['share'],
13431366
'GI_TYPELIB_PATH': [os.path.join(x, 'girepository-*') for x in lib_paths],
1367+
'CMAKE_PREFIX_PATH': [''],
1368+
'CMAKE_LIBRARY_PATH': ['lib64'], # lib and lib32 are searched through the above
13441369
}
13451370

13461371
def load_module(self, mod_paths=None, purge=True, extra_modules=None):
@@ -1446,43 +1471,18 @@ def skip_extensions(self):
14461471

14471472
if not exts_filter or len(exts_filter) == 0:
14481473
raise EasyBuildError("Skipping of extensions, but no exts_filter set in easyconfig")
1449-
elif isinstance(exts_filter, string_type) or len(exts_filter) != 2:
1450-
raise EasyBuildError('exts_filter should be a list or tuple of ("command","input")')
1451-
cmdtmpl = exts_filter[0]
1452-
cmdinputtmpl = exts_filter[1]
14531474

14541475
res = []
14551476
for ext in self.exts:
1456-
name = ext['name']
1457-
if 'options' in ext and 'modulename' in ext['options']:
1458-
modname = ext['options']['modulename']
1459-
else:
1460-
modname = name
1461-
tmpldict = {
1462-
'ext_name': modname,
1463-
'ext_version': ext.get('version'),
1464-
'src': ext.get('source'),
1465-
}
1466-
1467-
try:
1468-
cmd = cmdtmpl % tmpldict
1469-
except KeyError as err:
1470-
msg = "KeyError occurred on completing extension filter template: %s; "
1471-
msg += "'name'/'version' keys are no longer supported, should use 'ext_name'/'ext_version' instead"
1472-
self.log.nosupport(msg % err, '2.0')
1473-
1474-
if cmdinputtmpl:
1475-
stdin = cmdinputtmpl % tmpldict
1476-
(cmdstdouterr, ec) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, inp=stdin, regexp=False)
1477-
else:
1478-
(cmdstdouterr, ec) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False)
1477+
cmd, stdin = resolve_exts_filter_template(exts_filter, ext)
1478+
(cmdstdouterr, ec) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, inp=stdin, regexp=False)
14791479
self.log.info("exts_filter result %s %s", cmdstdouterr, ec)
14801480
if ec:
1481-
self.log.info("Not skipping %s" % name)
1481+
self.log.info("Not skipping %s" % ext['name'])
14821482
self.log.debug("exit code: %s, stdout/err: %s" % (ec, cmdstdouterr))
14831483
res.append(ext)
14841484
else:
1485-
self.log.info("Skipping %s" % name)
1485+
self.log.info("Skipping %s" % ext['name'])
14861486
self.exts = res
14871487

14881488
#
@@ -1600,8 +1600,9 @@ def post_iter_step(self):
16001600

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

16061607
# we need to take into account that builddependencies is always a list
16071608
# we're only iterating over it if it's a list of lists
@@ -2232,6 +2233,11 @@ def fix_shebang(self):
22322233
if should_patch:
22332234
contents = shebang_regex.sub(shebang, contents)
22342235
write_file(path, contents)
2236+
elif not contents.startswith('#!'):
2237+
self.log.info("The file '%s' doesn't have any shebang present, inserting it as first line.",
2238+
path)
2239+
contents = shebang + "\n" + contents
2240+
write_file(path, contents)
22352241

22362242
def post_install_step(self):
22372243
"""

easybuild/framework/easyconfig/default.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@
167167
'exts_defaultclass': [None, "List of module for and name of the default extension class", EXTENSIONS],
168168
'exts_default_options': [{}, "List of default options for extensions", EXTENSIONS],
169169
'exts_filter': [None, ("Extension filter details: template for cmd and input to cmd "
170-
"(templates for name, version and src)."), EXTENSIONS],
170+
"(templates for ext_name, ext_version and src)."), EXTENSIONS],
171171
'exts_list': [[], 'List with extensions added to the base installation', EXTENSIONS],
172172

173173
# MODULES easyconfig parameters

0 commit comments

Comments
 (0)