Skip to content

Commit

Permalink
Merge branch 'develop' into fix_error_display
Browse files Browse the repository at this point in the history
  • Loading branch information
smoors authored Feb 17, 2020
2 parents 1f01afe + 49533e6 commit 690dff1
Show file tree
Hide file tree
Showing 27 changed files with 748 additions and 295 deletions.
24 changes: 10 additions & 14 deletions .github/workflows/unit_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,19 +153,15 @@ jobs:
EB_BOOTSTRAP_VERSION=$(grep '^EB_BOOTSTRAP_VERSION' easybuild/scripts/bootstrap_eb.py | sed 's/[^0-9.]//g')
EB_BOOTSTRAP_SHA256SUM=$(sha256sum easybuild/scripts/bootstrap_eb.py | cut -f1 -d' ')
EB_BOOTSTRAP_FOUND="$EB_BOOTSTRAP_VERSION $EB_BOOTSTRAP_SHA256SUM"
EB_BOOTSTRAP_EXPECTED="20190922.01 7927513e7448d886decfb1bb5daf840e85dc7367f57cc75e51b68f21fe109d53"
EB_BOOTSTRAP_EXPECTED="20200203.01 616bf3ce812c0844bf9ea3e690f9d88b394ed48f834ddb8424a73cf45fc64ea5"
test "$EB_BOOTSTRAP_FOUND" = "$EB_BOOTSTRAP_EXPECTED" || (echo "Version check on bootstrap script failed $EB_BOOTSTRAP_FOUND" && exit 1)
# test bootstrap script (only compatible with Python 2 for now)
if [[ ${{matrix.python}} =~ '2.' ]]; then
export PREFIX=/tmp/$USER/$GITHUB_SHA/eb_bootstrap
python easybuild/scripts/bootstrap_eb.py $PREFIX
# unset $PYTHONPATH to avoid mixing two EasyBuild 'installations' when testing bootstrapped EasyBuild module
unset PYTHONPATH
# simple sanity check on bootstrapped EasyBuild module (skip when testing with Python 3, for now)
module use $PREFIX/modules/all
module load EasyBuild
eb --version
else
echo "Testing of bootstrap script skipped when testing with Python ${{matrix.python}}"
fi
# test bootstrap script
export PREFIX=/tmp/$USER/$GITHUB_SHA/eb_bootstrap
python easybuild/scripts/bootstrap_eb.py $PREFIX
# unset $PYTHONPATH to avoid mixing two EasyBuild 'installations' when testing bootstrapped EasyBuild module
unset PYTHONPATH
# simple sanity check on bootstrapped EasyBuild module
module use $PREFIX/modules/all
module load EasyBuild
eb --version
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.idea
.pydevproject
.project
LICENSE_HEADER
Expand Down
12 changes: 6 additions & 6 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,12 @@ script:
- EB_BOOTSTRAP_VERSION=$(grep '^EB_BOOTSTRAP_VERSION' $TRAVIS_BUILD_DIR/easybuild/scripts/bootstrap_eb.py | sed 's/[^0-9.]//g')
- EB_BOOTSTRAP_SHA256SUM=$(sha256sum $TRAVIS_BUILD_DIR/easybuild/scripts/bootstrap_eb.py | cut -f1 -d' ')
- EB_BOOTSTRAP_FOUND="$EB_BOOTSTRAP_VERSION $EB_BOOTSTRAP_SHA256SUM"
- EB_BOOTSTRAP_EXPECTED="20190922.01 7927513e7448d886decfb1bb5daf840e85dc7367f57cc75e51b68f21fe109d53"
- EB_BOOTSTRAP_EXPECTED="20200203.01 616bf3ce812c0844bf9ea3e690f9d88b394ed48f834ddb8424a73cf45fc64ea5"
- test "$EB_BOOTSTRAP_FOUND" = "$EB_BOOTSTRAP_EXPECTED" || (echo "Version check on bootstrap script failed $EB_BOOTSTRAP_FOUND" && exit 1)
# test bootstrap script (skip when testing with Python 3 for now, since latest EasyBuild release is not compatible with Python 3 yet)
- 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
# test bootstrap script
- python $TRAVIS_BUILD_DIR/easybuild/scripts/bootstrap_eb.py /tmp/$TRAVIS_JOB_ID/eb_bootstrap
# unset $PYTHONPATH to avoid mixing two EasyBuild 'installations' when testing bootstrapped EasyBuild module
- unset PYTHONPATH
# simply sanity check on bootstrapped EasyBuild module (skip when testing with Python 3, for now)
- if [ ! "x$TRAVIS_PYTHON_VERSION" =~ x3.[0-9] ]; then module use /tmp/$TRAVIS_JOB_ID/eb_bootstrap/modules/all; fi
- if [ ! "x$TRAVIS_PYTHON_VERSION" =~ x3.[0-9] ]; then module load EasyBuild; eb --version; fi
# simply sanity check on bootstrapped EasyBuild module
- module use /tmp/$TRAVIS_JOB_ID/eb_bootstrap/modules/all
- module load EasyBuild; eb --version
32 changes: 32 additions & 0 deletions RELEASE_NOTES
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,38 @@ For more detailed information, please see the git log.

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

v4.1.1 (January 16th 2020)
--------------------------

update/bugfix release

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


v4.1.0 (December 4th 2019)
--------------------------

Expand Down
144 changes: 75 additions & 69 deletions easybuild/framework/easyblock.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
from easybuild.framework.easyconfig.style import MAX_LINE_LENGTH
from easybuild.framework.easyconfig.tools import get_paths_for
from easybuild.framework.easyconfig.templates import TEMPLATE_NAMES_EASYBLOCK_RUN_STEP, template_constant_dict
from easybuild.framework.extension import resolve_exts_filter_template
from easybuild.tools import config, run
from easybuild.tools.build_details import get_build_stats
from easybuild.tools.build_log import EasyBuildError, dry_run_msg, dry_run_warning, dry_run_set_dirs
Expand Down Expand Up @@ -1281,47 +1282,69 @@ def make_module_req(self):

lines = ['\n']
if os.path.isdir(self.installdir):
change_dir(self.installdir)
old_dir = change_dir(self.installdir)
else:
old_dir = None

if self.dry_run:
self.dry_run_msg("List of paths that would be searched and added to module file:\n")
note = "note: glob patterns are not expanded and existence checks "
note += "for paths are skipped for the statements below due to dry run"
lines.append(self.module_generator.comment(note))

# for these environment variables, the corresponding subdirectory must include at least one file
keys_requiring_files = set(('PATH', 'LD_LIBRARY_PATH', 'LIBRARY_PATH', 'CPATH',
'CMAKE_PREFIX_PATH', 'CMAKE_LIBRARY_PATH'))

for key, reqs in sorted(requirements.items()):
if isinstance(reqs, string_type):
self.log.warning("Hoisting string value %s into a list before iterating over it", reqs)
reqs = [reqs]
if self.dry_run:
self.dry_run_msg("List of paths that would be searched and added to module file:\n")
note = "note: glob patterns are not expanded and existence checks "
note += "for paths are skipped for the statements below due to dry run"
lines.append(self.module_generator.comment(note))

# for these environment variables, the corresponding subdirectory must include at least one file
keys_requiring_files = ('CPATH', 'LD_LIBRARY_PATH', 'LIBRARY_PATH', 'PATH')

for key in sorted(requirements):
if self.dry_run:
self.dry_run_msg(" $%s: %s" % (key, ', '.join(requirements[key])))
reqs = requirements[key]
if isinstance(reqs, string_type):
self.log.warning("Hoisting string value %s into a list before iterating over it", reqs)
reqs = [reqs]

for path in reqs:
# only use glob if the string is non-empty
if path and not self.dry_run:
paths = sorted(glob.glob(path))
if paths and key in keys_requiring_files:
# only retain paths that contain at least one file
retained_paths = [
path for path in paths
if os.path.isdir(os.path.join(self.installdir, path))
and dir_contains_files(os.path.join(self.installdir, path))
]
self.log.info("Only retaining paths for %s that contain at least one file: %s -> %s",
key, paths, retained_paths)
paths = retained_paths
else:
# empty string is a valid value here (i.e. to prepend the installation prefix, cfr $CUDA_HOME)
paths = [path]
self.dry_run_msg(" $%s: %s" % (key, ', '.join(reqs)))
# Don't expand globs or do any filtering below for dry run
paths = sorted(reqs)
else:
# Expand globs but only if the string is non-empty
# empty string is a valid value here (i.e. to prepend the installation prefix, cfr $CUDA_HOME)
paths = sorted(sum((glob.glob(path) if path else [path] for path in reqs), [])) # sum flattens to list

# If lib64 is just a symlink to lib we fixup the paths to avoid duplicates
lib64_is_symlink = (all(os.path.isdir(path) for path in ['lib', 'lib64'])
and os.path.samefile('lib', 'lib64'))
if lib64_is_symlink:
fixed_paths = []
for path in paths:
if (path + os.path.sep).startswith('lib64' + os.path.sep):
# We only need CMAKE_LIBRARY_PATH if there is a separate lib64 path, so skip symlink
if key == 'CMAKE_LIBRARY_PATH':
continue
path = path.replace('lib64', 'lib', 1)
fixed_paths.append(path)
if fixed_paths != paths:
self.log.info("Fixed symlink lib64 in paths for %s: %s -> %s", key, paths, fixed_paths)
paths = fixed_paths
# Use a set to remove duplicates, e.g. by having lib64 and lib which get fixed to lib and lib above
paths = sorted(set(paths))
if key in keys_requiring_files:
# only retain paths that contain at least one file
retained_paths = [
path for path in paths
if os.path.isdir(os.path.join(self.installdir, path))
and dir_contains_files(os.path.join(self.installdir, path))
]
if retained_paths != paths:
self.log.info("Only retaining paths for %s that contain at least one file: %s -> %s",
key, paths, retained_paths)
paths = retained_paths

if paths:
lines.append(self.module_generator.prepend_paths(key, paths))
if self.dry_run:
self.dry_run_msg('')

lines.append(self.module_generator.prepend_paths(key, paths))
if self.dry_run:
self.dry_run_msg('')
change_dir(self.orig_workdir)
if old_dir is not None:
change_dir(old_dir)

return ''.join(lines)

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

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

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

res = []
for ext in self.exts:
name = ext['name']
if 'options' in ext and 'modulename' in ext['options']:
modname = ext['options']['modulename']
else:
modname = name
tmpldict = {
'ext_name': modname,
'ext_version': ext.get('version'),
'src': ext.get('source'),
}

try:
cmd = cmdtmpl % tmpldict
except KeyError as err:
msg = "KeyError occurred on completing extension filter template: %s; "
msg += "'name'/'version' keys are no longer supported, should use 'ext_name'/'ext_version' instead"
self.log.nosupport(msg % err, '2.0')

if cmdinputtmpl:
stdin = cmdinputtmpl % tmpldict
(cmdstdouterr, ec) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, inp=stdin, regexp=False)
else:
(cmdstdouterr, ec) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, regexp=False)
cmd, stdin = resolve_exts_filter_template(exts_filter, ext)
(cmdstdouterr, ec) = run_cmd(cmd, log_all=False, log_ok=False, simple=False, inp=stdin, regexp=False)
self.log.info("exts_filter result %s %s", cmdstdouterr, ec)
if ec:
self.log.info("Not skipping %s" % name)
self.log.info("Not skipping %s" % ext['name'])
self.log.debug("exit code: %s, stdout/err: %s" % (ec, cmdstdouterr))
res.append(ext)
else:
self.log.info("Skipping %s" % name)
self.log.info("Skipping %s" % ext['name'])
self.exts = res

#
Expand Down Expand Up @@ -1600,8 +1600,9 @@ def post_iter_step(self):

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

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

def post_install_step(self):
"""
Expand Down
2 changes: 1 addition & 1 deletion easybuild/framework/easyconfig/default.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@
'exts_defaultclass': [None, "List of module for and name of the default extension class", EXTENSIONS],
'exts_default_options': [{}, "List of default options for extensions", EXTENSIONS],
'exts_filter': [None, ("Extension filter details: template for cmd and input to cmd "
"(templates for name, version and src)."), EXTENSIONS],
"(templates for ext_name, ext_version and src)."), EXTENSIONS],
'exts_list': [[], 'List with extensions added to the base installation', EXTENSIONS],

# MODULES easyconfig parameters
Expand Down
Loading

0 comments on commit 690dff1

Please sign in to comment.