Skip to content

Commit 69d3b1e

Browse files
authored
Merge pull request #1 from boegel/docker-packaging
sync with develop + restore use of 'build_option' to determine container type
2 parents fbd4f78 + 6466217 commit 69d3b1e

File tree

23 files changed

+541
-180
lines changed

23 files changed

+541
-180
lines changed

.flake8.ini

Lines changed: 0 additions & 9 deletions
This file was deleted.

.hound.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# configuration for houndci, see https://houndci.com/configuration#python
22
flake8:
33
enabled: true
4-
config_file: .flake8.ini
4+
config_file: setup.cfg

RELEASE_NOTES

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,28 @@ 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+
v3.6.1 (May 28th 2018)
7+
----------------------
8+
9+
bugfix release
10+
- various enhancements, including:
11+
- add support for enabling fallback in sanity check to consider lib64 equivalent for seemingly missing libraries (#2477)
12+
- add GITHUB_LOWER_SOURCE constant (#2491)
13+
- add 'exts_download_dep_fail' as known easyconfig parameter (#2493)
14+
- add support for passing custom messages on failing sanity check for extensions (#2494)
15+
- add definition for fosscuda toolchain (#2507)
16+
- various bug fixes, including:
17+
- make --inject-checksums always re-order source_urls/sources/patches/checksums (#2487)
18+
- fix git remote url in CONTRIBUTING.md (#2490)
19+
- make flake8 happy in easyblock.py (#2492)
20+
- handle missing permissions for adding labels in --new-pr (#2497)
21+
- restore tweaked $TMPDIR value after loading module (for sanity check) (#2498)
22+
- enhance get_module_path function to auto-detect generic vs software-specific easyblock class names (#2502)
23+
- don't blindly overwrite an existing easyconfig in tweak_one (#2504)
24+
- take account that PlaintextKeyring may be provided via keyrings.alt (#2505)
25+
- prepend location for temporary module file to $MODULEPATH with high priority + mark it as default in load_fake_module method (#2506)
26+
27+
628
v3.6.0 (April 26th 2018)
729
------------------------
830

easybuild/framework/easyblock.py

Lines changed: 96 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ def __init__(self, ec, hooks=None):
212212

213213
# keep track of initial environment we start in, so we can restore it if needed
214214
self.initial_environ = copy.deepcopy(os.environ)
215+
self.tweaked_env_vars = {}
215216

216217
# should we keep quiet?
217218
self.silent = build_option('silent')
@@ -1282,7 +1283,14 @@ def load_module(self, mod_paths=None, purge=True):
12821283
if self.mod_subdir and self.toolchain.name != DUMMY_TOOLCHAIN_NAME:
12831284
mods.insert(0, self.toolchain.det_short_module_name())
12841285

1286+
# pass initial environment, to use it for resetting the environment before loading the modules
12851287
self.modules_tool.load(mods, mod_paths=all_mod_paths, purge=purge, init_env=self.initial_environ)
1288+
1289+
# handle environment variables that need to be updated after loading modules
1290+
for var, val in sorted(self.tweaked_env_vars.items()):
1291+
self.log.info("Tweaking $%s: %s", var, val)
1292+
env.setvar(var, val)
1293+
12861294
else:
12871295
self.log.warning("Not loading module, since self.full_mod_name is not set.")
12881296

@@ -1297,7 +1305,7 @@ def load_fake_module(self, purge=False):
12971305
fake_mod_path = self.make_module_step(fake=True)
12981306

12991307
# load fake module
1300-
self.modules_tool.prepend_module_path(os.path.join(fake_mod_path, self.mod_subdir))
1308+
self.modules_tool.prepend_module_path(os.path.join(fake_mod_path, self.mod_subdir), priority=10000)
13011309
self.load_module(purge=purge)
13021310

13031311
return (fake_mod_path, env)
@@ -1752,6 +1760,14 @@ def prepare_step(self, start_dir=True):
17521760
self.toolchain.prepare(self.cfg['onlytcmod'], silent=self.silent, rpath_filter_dirs=self.rpath_filter_dirs,
17531761
rpath_include_dirs=self.rpath_include_dirs)
17541762

1763+
# keep track of environment variables that were tweaked and need to be restored after environment got reset
1764+
# $TMPDIR may be tweaked for OpenMPI 2.x, which doesn't like long $TMPDIR paths...
1765+
for var in ['TMPDIR']:
1766+
if os.environ.get(var) != self.initial_environ.get(var):
1767+
self.tweaked_env_vars[var] = os.environ.get(var)
1768+
self.log.info("Found tweaked value for $%s: %s (was: %s)",
1769+
var, self.tweaked_env_vars[var], self.initial_environ[var])
1770+
17551771
# handle allowed system dependencies
17561772
for (name, version) in self.cfg['allow_system_deps']:
17571773
# root is set to name, not an actual path
@@ -1859,7 +1875,7 @@ def extensions_step(self, fetch=False):
18591875

18601876
cls, inst = None, None
18611877
class_name = encode_class_name(ext['name'])
1862-
mod_path = get_module_path(class_name)
1878+
mod_path = get_module_path(class_name, generic=False)
18631879

18641880
# try instantiating extension-specific class
18651881
try:
@@ -2143,10 +2159,57 @@ def _sanity_check_step_dry_run(self, custom_paths=None, custom_commands=None, **
21432159
else:
21442160
self.log.debug("Skiping RPATH sanity check")
21452161

2162+
def _sanity_check_step_extensions(self):
2163+
"""Sanity check on extensions (if any)."""
2164+
failed_exts = []
2165+
for ext in self.ext_instances:
2166+
success, fail_msg = None, None
2167+
res = ext.sanity_check_step()
2168+
# if result is a tuple, we expect a (<bool (success)>, <custom_message>) format
2169+
if isinstance(res, tuple):
2170+
if len(res) != 2:
2171+
raise EasyBuildError("Wrong sanity check result type for '%s' extension: %s", ext.name, res)
2172+
success, fail_msg = res
2173+
else:
2174+
# if result of extension sanity check is not a 2-tuple, treat it as a boolean indicating success
2175+
success, fail_msg = res, "(see log for details)"
2176+
2177+
if not success:
2178+
fail_msg = "failing sanity check for '%s' extension: %s" % (ext.name, fail_msg)
2179+
failed_exts.append((ext.name, fail_msg))
2180+
self.log.warning(fail_msg)
2181+
else:
2182+
self.log.info("Sanity check for '%s' extension passed!", ext.name)
2183+
2184+
if failed_exts:
2185+
overall_fail_msg = "extensions sanity check failed for %d extensions: " % len(failed_exts)
2186+
self.log.warning(overall_fail_msg)
2187+
self.sanity_check_fail_msgs.append(overall_fail_msg + ', '.join(x[0] for x in failed_exts))
2188+
self.sanity_check_fail_msgs.extend(x[1] for x in failed_exts)
2189+
21462190
def _sanity_check_step(self, custom_paths=None, custom_commands=None, extension=False):
21472191
"""Real version of sanity_check_step method."""
21482192
paths, path_keys_and_check, commands = self._sanity_check_step_common(custom_paths, custom_commands)
21492193

2194+
# helper function to sanity check (alternatives for) one particular path
2195+
def check_path(xs, typ, check_fn):
2196+
"""Sanity check for one particular path."""
2197+
found = False
2198+
for name in xs:
2199+
path = os.path.join(self.installdir, name)
2200+
if check_fn(path):
2201+
self.log.debug("Sanity check: found %s %s in %s" % (typ, name, self.installdir))
2202+
found = True
2203+
break
2204+
else:
2205+
self.log.debug("Could not find %s %s in %s" % (typ, name, self.installdir))
2206+
2207+
return found
2208+
2209+
def xs2str(xs):
2210+
"""Human-readable version of alternative locations for a particular file/directory."""
2211+
return ' or '.join("'%s'" % x for x in xs)
2212+
21502213
# check sanity check paths
21512214
for key, (typ, check_fn) in path_keys_and_check.items():
21522215

@@ -2156,21 +2219,28 @@ def _sanity_check_step(self, custom_paths=None, custom_commands=None, extension=
21562219
elif not isinstance(xs, tuple):
21572220
raise EasyBuildError("Unsupported type '%s' encountered in %s, not a string or tuple",
21582221
key, type(xs))
2159-
found = False
2160-
for name in xs:
2161-
path = os.path.join(self.installdir, name)
2162-
if check_fn(path):
2163-
self.log.debug("Sanity check: found %s %s in %s" % (typ, name, self.installdir))
2164-
found = True
2165-
break
2166-
else:
2167-
self.log.debug("Could not find %s %s in %s" % (typ, name, self.installdir))
2222+
2223+
found = check_path(xs, typ, check_fn)
2224+
2225+
# for library files in lib/, also consider fallback to lib64/ equivalent (and vice versa)
2226+
if not found and build_option('lib64_fallback_sanity_check'):
2227+
xs_alt = None
2228+
if all(x.startswith('lib/') for x in xs):
2229+
xs_alt = [os.path.join('lib64', *os.path.split(x)[1:]) for x in xs]
2230+
elif all(x.startswith('lib64/') for x in xs):
2231+
xs_alt = [os.path.join('lib', *os.path.split(x)[1:]) for x in xs]
2232+
2233+
if xs_alt:
2234+
self.log.info("%s not found at %s in %s, consider fallback locations: %s",
2235+
typ, xs2str(xs), self.installdir, xs2str(xs_alt))
2236+
found = check_path(xs_alt, typ, check_fn)
2237+
21682238
if not found:
2169-
self.sanity_check_fail_msgs.append("no %s of %s in %s" % (typ, xs, self.installdir))
2170-
self.log.warning("Sanity check: %s" % self.sanity_check_fail_msgs[-1])
2239+
sanity_check_fail_msg = "no %s found at %s in %s" % (typ, xs2str(xs), self.installdir)
2240+
self.sanity_check_fail_msgs.append(sanity_check_fail_msg)
2241+
self.log.warning("Sanity check: %s", sanity_check_fail_msg)
21712242

2172-
cand_paths = ' or '.join(["'%s'" % x for x in xs])
2173-
trace_msg("%s %s found: %s" % (typ, cand_paths, ('FAILED', 'OK')[found]))
2243+
trace_msg("%s %s found: %s" % (typ, xs2str(xs), ('FAILED', 'OK')[found]))
21742244

21752245
fake_mod_data = None
21762246
# only load fake module for non-extensions, and not during dry run
@@ -2189,7 +2259,6 @@ def _sanity_check_step(self, custom_paths=None, custom_commands=None, extension=
21892259

21902260
# run sanity check commands
21912261
for command in commands:
2192-
21932262
out, ec = run_cmd(command, simple=False, log_ok=False, log_all=False, trace=False)
21942263
if ec != 0:
21952264
fail_msg = "sanity check command %s exited with code %s (output: %s)" % (command, ec, out)
@@ -2200,12 +2269,9 @@ def _sanity_check_step(self, custom_paths=None, custom_commands=None, extension=
22002269

22012270
trace_msg("running command '%s': %s" % (command, ('FAILED', 'OK')[ec == 0]))
22022271

2272+
# also run sanity check for extensions (unless we are an extension ourselves)
22032273
if not extension:
2204-
failed_exts = [ext.name for ext in self.ext_instances if not ext.sanity_check_step()]
2205-
2206-
if failed_exts:
2207-
self.sanity_check_fail_msgs.append("sanity checks for %s extensions failed!" % failed_exts)
2208-
self.log.warning("Sanity check: %s" % self.sanity_check_fail_msgs[-1])
2274+
self._sanity_check_step_extensions()
22092275

22102276
# cleanup
22112277
if fake_mod_data:
@@ -2221,21 +2287,21 @@ def _sanity_check_step(self, custom_paths=None, custom_commands=None, extension=
22212287

22222288
# pass or fail
22232289
if self.sanity_check_fail_msgs:
2224-
raise EasyBuildError("Sanity check failed: %s", ', '.join(self.sanity_check_fail_msgs))
2290+
raise EasyBuildError("Sanity check failed: %s", '\n'.join(self.sanity_check_fail_msgs))
22252291
else:
22262292
self.log.debug("Sanity check passed!")
22272293

2228-
def _set_module_as_default(self):
2294+
def _set_module_as_default(self, fake=False):
22292295
"""
2230-
Defining default module Version
2296+
Sets the default module version except if we are in dry run
22312297
2232-
sets the default module version except if we are in dry run.
2298+
:param fake: set default for 'fake' module in temporary location
22332299
"""
22342300
version = self.full_mod_name.split('/')[-1]
22352301
if self.dry_run:
22362302
dry_run_msg("Marked %s v%s as default version" % (self.name, version))
22372303
else:
2238-
mod_folderpath = os.path.dirname(self.module_generator.get_module_filepath())
2304+
mod_folderpath = os.path.dirname(self.module_generator.get_module_filepath(fake=fake))
22392305
self.module_generator.set_as_default(mod_folderpath, version)
22402306

22412307
def cleanup_step(self):
@@ -2343,8 +2409,10 @@ def make_module_step(self, fake=False):
23432409
else:
23442410
self.log.info("Skipping devel module...")
23452411

2346-
if build_option('set_default_module'):
2347-
self._set_module_as_default()
2412+
# always set default for temporary module file,
2413+
# to avoid that it gets overruled by an existing module file that is set as default
2414+
if fake or build_option('set_default_module'):
2415+
self._set_module_as_default(fake=fake)
23482416

23492417
return modpath
23502418

easybuild/framework/easyconfig/default.py

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -40,25 +40,25 @@
4040

4141
_log = fancylogger.getLogger('easyconfig.default', fname=False)
4242

43+
# constants for different categories of easyconfig parameters
44+
# use tuples so we can sort them based on the numbers
45+
HIDDEN = (-1, 'hidden')
46+
MANDATORY = (0, 'mandatory')
47+
CUSTOM = (1, 'easyblock-specific')
48+
TOOLCHAIN = (2, 'toolchain')
49+
BUILD = (3, 'build')
50+
FILEMANAGEMENT = (4, 'file-management')
51+
DEPENDENCIES = (5, 'dependencies')
52+
LICENSE = (6, 'license')
53+
EXTENSIONS = (7, 'extensions')
54+
MODULES = (8, 'modules')
55+
OTHER = (9, 'other')
56+
4357

4458
# we use a tuple here so we can sort them based on the numbers
45-
ALL_CATEGORIES = {
46-
'HIDDEN': (-1, 'hidden'),
47-
'MANDATORY': (0, 'mandatory'),
48-
'CUSTOM': (1, 'easyblock-specific'),
49-
'TOOLCHAIN': (2, 'toolchain'),
50-
'BUILD': (3, 'build'),
51-
'FILEMANAGEMENT': (4, 'file-management'),
52-
'DEPENDENCIES': (5, 'dependencies'),
53-
'LICENSE': (6, 'license'),
54-
'EXTENSIONS': (7, 'extensions'),
55-
'MODULES': (8, 'modules'),
56-
'OTHER': (9, 'other'),
57-
}
58-
# define constants so they can be used below
59-
# avoid that pylint complains about unknown variables in this file
60-
# pylint: disable=E0602
61-
globals().update(ALL_CATEGORIES)
59+
CATEGORY_NAMES = ['BUILD', 'CUSTOM', 'DEPENDENCIES', 'EXTENSIONS', 'FILEMANAGEMENT', 'HIDDEN',
60+
'LICENSE', 'MANDATORY', 'MODULES', 'OTHER', 'TOOLCHAIN']
61+
ALL_CATEGORIES = dict((name, eval(name)) for name in CATEGORY_NAMES)
6262

6363
# List of tuples. Each tuple has the following format (key, [default, help text, category])
6464
DEFAULT_CONFIG = {
@@ -151,6 +151,7 @@
151151
'license_server_port': [None, 'Port for license server', LICENSE],
152152

153153
# EXTENSIONS easyconfig parameters
154+
'exts_download_dep_fail': [False, "Fail if downloaded dependencies are detected for extensions", EXTENSIONS],
154155
'exts_classmap': [{}, "Map of extension name to class for handling build and installation.", EXTENSIONS],
155156
'exts_defaultclass': [None, "List of module for and name of the default extension class", EXTENSIONS],
156157
'exts_default_options': [{}, "List of default options for extensions", EXTENSIONS],
@@ -171,17 +172,17 @@
171172
'moduleclass': ['base', 'Module class to be used for this software', MODULES],
172173
'moduleforceunload': [False, 'Force unload of all modules when loading the extension', MODULES],
173174
'moduleloadnoconflict': [False, "Don't check for conflicts, unload other versions instead ", MODULES],
174-
'module_depends_on' : [False, 'Use depends_on (Lmod 7.6.1+) for dependencies in generated module '
175-
'(implies recursive unloading of modules).', MODULES],
175+
'module_depends_on': [False, 'Use depends_on (Lmod 7.6.1+) for dependencies in generated module '
176+
'(implies recursive unloading of modules).', MODULES],
176177
'recursive_module_unload': [False, 'Recursive unload of all dependencies when unloading module', MODULES],
177178

178179
# MODULES documentation easyconfig parameters
179180
# (docurls is part of MANDATORY)
180181
'docpaths': [None, "List of paths for documentation relative to installation directory", MODULES],
181182
'examples': [None, "Free-form text with examples on using the software", MODULES],
182183
'site_contacts': [None, "String/list of strings with site contacts for the software", MODULES],
183-
'upstream_contacts': [None, ("String/list of strings with upstream contact addresses "
184-
"(e.g., support e-mail, mailing list, bugtracker)"), MODULES],
184+
'upstream_contacts': [None, "String/list of strings with upstream contact addresses "
185+
"(e.g., support e-mail, mailing list, bugtracker)", MODULES],
185186
'usage': [None, "Usage instructions for the software", MODULES],
186187
'whatis': [None, "List of brief (one line) description entries for the software", MODULES],
187188

0 commit comments

Comments
 (0)