Skip to content

Commit f486168

Browse files
committed
Merge branch 'develop' into 5.0.x
2 parents 122f208 + d977648 commit f486168

39 files changed

+889
-55
lines changed

easybuild/framework/easyblock.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1730,9 +1730,15 @@ def _make_extension_list(self):
17301730
17311731
Each entry should be a (name, version) tuple or just (name, ) if no version exists
17321732
"""
1733-
# We need only name and version, so don't resolve templates
17341733
# Each extension in exts_list is either a string or a list/tuple with name, version as first entries
1735-
return [(ext, ) if isinstance(ext, str) else ext[:2] for ext in self.cfg.get_ref('exts_list')]
1734+
# As name can be a templated value we must resolve templates
1735+
exts_list = []
1736+
for ext in self.cfg.get_ref('exts_list'):
1737+
if isinstance(ext, str):
1738+
exts_list.append((resolve_template(ext, self.cfg.template_values), ))
1739+
else:
1740+
exts_list.append((resolve_template(ext[0], self.cfg.template_values), ext[1]))
1741+
return exts_list
17361742

17371743
def make_extension_string(self, name_version_sep='-', ext_sep=', ', sort=True):
17381744
"""
@@ -4644,8 +4650,14 @@ def inject_checksums(ecs, checksum_type):
46444650
"""
46454651
def make_list_lines(values, indent_level):
46464652
"""Make lines for list of values."""
4653+
def to_str(s):
4654+
if isinstance(s, string_type):
4655+
return "'%s'" % s
4656+
else:
4657+
return str(s)
4658+
46474659
line_indent = INDENT_4SPACES * indent_level
4648-
return [line_indent + "'%s'," % x for x in values]
4660+
return [line_indent + to_str(x) + ',' for x in values]
46494661

46504662
def make_checksum_lines(checksums, indent_level):
46514663
"""Make lines for list of checksums."""

easybuild/framework/easyconfig/tools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,7 @@ def find_related_easyconfigs(path, ec):
465465
if len(parsed_version) >= 2:
466466
version_patterns.append(r'%s\.%s\.\w+' % tuple(parsed_version[:2])) # major/minor version match
467467
if parsed_version != parsed_version[0]:
468-
version_patterns.append(r'%s\.[\d-]+\.\w+' % parsed_version[0]) # major version match
468+
version_patterns.append(r'%s\.[\d-]+(\.\w+)*' % parsed_version[0]) # major version match
469469
version_patterns.append(r'[\w.]+') # any version
470470

471471
regexes = []

easybuild/framework/easyconfig/tweak.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,7 @@ def tweak(easyconfigs, build_specs, modtool, targetdirs=None):
9090
tweaked_ecs_path, tweaked_ecs_deps_path = None, None
9191
if targetdirs is not None:
9292
tweaked_ecs_path, tweaked_ecs_deps_path = targetdirs
93-
# make sure easyconfigs all feature the same toolchain (otherwise we *will* run into trouble)
94-
toolchains = nub(['%(name)s/%(version)s' % ec['ec']['toolchain'] for ec in easyconfigs])
95-
if len(toolchains) > 1:
96-
raise EasyBuildError("Multiple toolchains featured in easyconfigs, --try-X not supported in that case: %s",
97-
toolchains)
98-
# Toolchain is unique, let's store it
99-
source_toolchain = easyconfigs[-1]['ec']['toolchain']
10093
modifying_toolchains_or_deps = False
101-
target_toolchain = {}
10294
src_to_dst_tc_mapping = {}
10395
revert_to_regex = False
10496

@@ -116,6 +108,16 @@ def tweak(easyconfigs, build_specs, modtool, targetdirs=None):
116108
revert_to_regex = True
117109

118110
if not revert_to_regex:
111+
# make sure easyconfigs all feature the same toolchain (otherwise we *will* run into trouble)
112+
toolchains = nub(['%(name)s/%(version)s' % ec['ec']['toolchain'] for ec in easyconfigs])
113+
if len(toolchains) > 1:
114+
raise EasyBuildError("Multiple toolchains featured in easyconfigs, "
115+
"--try-X not supported in that case: %s",
116+
toolchains)
117+
# Toolchain is unique, let's store it
118+
source_toolchain = easyconfigs[-1]['ec']['toolchain']
119+
target_toolchain = {}
120+
119121
# we're doing something that involves the toolchain hierarchy;
120122
# obtain full dependency graph for specified easyconfigs;
121123
# easyconfigs will be ordered 'top-to-bottom' (toolchains and dependencies appearing first)

easybuild/toolchains/compiler/intel_compilers.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,9 @@ def set_variables(self):
109109
self.options.options_map['loose'] = ['fp-model fast']
110110
# fp-model fast=2 gives "warning: overriding '-ffp-model=fast=2' option with '-ffp-model=fast'"
111111
self.options.options_map['veryloose'] = ['fp-model fast']
112-
# recommended in porting guide
113-
self.options.options_map['openmp'] = ['fiopenmp']
112+
# recommended in porting guide: qopenmp, unlike fiopenmp, works for both classic and oneapi compilers
113+
# https://www.intel.com/content/www/us/en/developer/articles/guide/porting-guide-for-ifort-to-ifx.html
114+
self.options.options_map['openmp'] = ['qopenmp']
114115

115116
# -xSSE2 is not supported by Intel oneAPI compilers,
116117
# so use -march=x86-64 -mtune=generic when using optarch=GENERIC

easybuild/toolchains/linalg/flexiblas.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ class FlexiBLAS(LinAlg):
7070
"""
7171
BLAS_MODULE_NAME = ['FlexiBLAS']
7272
BLAS_LIB = ['flexiblas']
73+
BLAS_LIB_MT = ['flexiblas']
7374
BLAS_INCLUDE_DIR = [os.path.join('include', 'flexiblas')]
7475
BLAS_FAMILY = TC_CONSTANT_FLEXIBLAS
7576

easybuild/tools/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ def mk_full_default_path(name, prefix=DEFAULT_PREFIX):
240240
'job_polling_interval',
241241
'job_target_resource',
242242
'locks_dir',
243+
'module_cache_suffix',
243244
'modules_footer',
244245
'modules_header',
245246
'mpi_cmd_template',

easybuild/tools/loose_version.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ def _cmp(self, other):
8181
def __eq__(self, other):
8282
return self._cmp(other) == 0
8383

84+
def __ne__(self, other):
85+
return self._cmp(other) != 0
86+
8487
def __lt__(self, other):
8588
return self._cmp(other) < 0
8689

easybuild/tools/module_generator.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
from easybuild.tools.config import build_option, get_module_syntax, install_path
4949
from easybuild.tools.filetools import convert_name, mkdir, read_file, remove_file, resolve_path, symlink, write_file
5050
from easybuild.tools.modules import ROOT_ENV_VAR_NAME_PREFIX, EnvironmentModulesC, Lmod, modules_tool
51-
from easybuild.tools.utilities import get_subclasses, quote_str
51+
from easybuild.tools.utilities import get_subclasses, nub, quote_str
5252

5353

5454
_log = fancylogger.getLogger('module_generator', fname=False)
@@ -666,7 +666,7 @@ def _generate_help_text(self):
666666
if multi_deps:
667667
compatible_modules_txt = '\n'.join([
668668
"This module is compatible with the following modules, one of each line is required:",
669-
] + ['* %s' % d for d in multi_deps])
669+
] + ['* %s' % d for d in nub(multi_deps)])
670670
lines.extend(self._generate_section("Compatible modules", compatible_modules_txt))
671671

672672
# Extensions (if any)

easybuild/tools/modules.py

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,13 +1323,82 @@ class EnvironmentModules(EnvironmentModulesTcl):
13231323
MAX_VERSION = None
13241324
VERSION_REGEXP = r'^Modules\s+Release\s+(?P<version>\d\S*)\s'
13251325

1326+
def __init__(self, *args, **kwargs):
1327+
"""Constructor, set Environment Modules-specific class variable values."""
1328+
# ensure in-depth modulepath search (MODULES_AVAIL_INDEPTH has been introduced in v4.3)
1329+
setvar('MODULES_AVAIL_INDEPTH', '1', verbose=False)
1330+
# match against module name start (MODULES_SEARCH_MATCH has been introduced in v4.3)
1331+
setvar('MODULES_SEARCH_MATCH', 'starts_with', verbose=False)
1332+
# ensure no debug message (MODULES_VERBOSITY has been introduced in v4.3)
1333+
setvar('MODULES_VERBOSITY', 'normal', verbose=False)
1334+
# make module search case sensitive (search is case insensitive by default since v5.0)
1335+
setvar('MODULES_ICASE', 'never', verbose=False)
1336+
# disable extended default (introduced in v4.4 and enabled by default in v5.0)
1337+
setvar('MODULES_EXTENDED_DEFAULT', '0', verbose=False)
1338+
# hard disable output redirection, output messages are expected on stderr
1339+
setvar('MODULES_REDIRECT_OUTPUT', '0', verbose=False)
1340+
# make sure modulefile cache is ignored (cache mechanism supported since v5.3)
1341+
setvar('MODULES_IGNORE_CACHE', '1', verbose=False)
1342+
# ensure only module names are returned on avail (MODULES_AVAIL_TERSE_OUTPUT added in v4.7)
1343+
setvar('MODULES_AVAIL_TERSE_OUTPUT', '', verbose=False)
1344+
# ensure only module names are returned on list (MODULES_LIST_TERSE_OUTPUT added in v4.7)
1345+
setvar('MODULES_LIST_TERSE_OUTPUT', '', verbose=False)
1346+
1347+
super(EnvironmentModules, self).__init__(*args, **kwargs)
1348+
1349+
def check_module_function(self, allow_mismatch=False, regex=None):
1350+
"""Check whether selected module tool matches 'module' function definition."""
1351+
# Modules 5.1.0+: module command is called from _module_raw shell function
1352+
# Modules 4.2.0..5.0.1: module command is called from _module_raw shell function if it has
1353+
# been initialized in an interactive shell session (i.e., a session attached to a tty)
1354+
if self.testing:
1355+
if '_module_raw' in os.environ:
1356+
out, ec = os.environ['_module_raw'], 0
1357+
else:
1358+
out, ec = None, 1
1359+
else:
1360+
cmd = "type _module_raw"
1361+
out, ec = run_cmd(cmd, simple=False, log_ok=False, log_all=False, force_in_dry_run=True, trace=False)
1362+
1363+
if regex is None:
1364+
regex = r".*%s" % os.path.basename(self.cmd)
1365+
mod_cmd_re = re.compile(regex, re.M)
1366+
1367+
if ec == 0 and mod_cmd_re.search(out):
1368+
self.log.debug("Found pattern '%s' in defined '_module_raw' function." % mod_cmd_re.pattern)
1369+
else:
1370+
self.log.debug("Pattern '%s' not found in '_module_raw' function, falling back to 'module' function",
1371+
mod_cmd_re.pattern)
1372+
super(EnvironmentModules, self).check_module_function(allow_mismatch, regex)
1373+
13261374
def check_module_output(self, cmd, stdout, stderr):
13271375
"""Check output of 'module' command, see if if is potentially invalid."""
13281376
if "_mlstatus = False" in stdout:
13291377
raise EasyBuildError("Failed module command detected: %s (stdout: %s, stderr: %s)", cmd, stdout, stderr)
13301378
else:
13311379
self.log.debug("No errors detected when running module command '%s'", cmd)
13321380

1381+
def get_setenv_value_from_modulefile(self, mod_name, var_name):
1382+
"""
1383+
Get value for specific 'setenv' statement from module file for the specified module.
1384+
1385+
:param mod_name: module name
1386+
:param var_name: name of the variable being set for which value should be returned
1387+
"""
1388+
# Tcl-based module tools produce "module show" output with setenv statements like:
1389+
# "setenv GCC_PATH /opt/gcc/8.3.0"
1390+
# "setenv VAR {some text}
1391+
# - line starts with 'setenv'
1392+
# - whitespace (spaces & tabs) around variable name
1393+
# - curly braces around value if it contain spaces
1394+
value = super(EnvironmentModules, self).get_setenv_value_from_modulefile(mod_name=mod_name,
1395+
var_name=var_name)
1396+
1397+
if value:
1398+
value = value.strip('{}')
1399+
1400+
return value
1401+
13331402

13341403
class Lmod(ModulesTool):
13351404
"""Interface to Lmod."""
@@ -1340,7 +1409,6 @@ class Lmod(ModulesTool):
13401409
DEPR_VERSION = '7.0.0'
13411410
REQ_VERSION_DEPENDS_ON = '7.6.1'
13421411
VERSION_REGEXP = r"^Modules\s+based\s+on\s+Lua:\s+Version\s+(?P<version>\d\S*)\s"
1343-
USER_CACHE_DIR = os.path.join(os.path.expanduser('~'), '.lmod.d', '.cache')
13441412

13451413
SHOW_HIDDEN_OPTION = '--show-hidden'
13461414

@@ -1356,7 +1424,14 @@ def __init__(self, *args, **kwargs):
13561424
setvar('LMOD_EXTENDED_DEFAULT', 'no', verbose=False)
13571425

13581426
super(Lmod, self).__init__(*args, **kwargs)
1359-
self.supports_depends_on = StrictVersion(self.version) >= StrictVersion(self.REQ_VERSION_DEPENDS_ON)
1427+
version = StrictVersion(self.version)
1428+
1429+
self.supports_depends_on = version >= self.REQ_VERSION_DEPENDS_ON
1430+
# See https://lmod.readthedocs.io/en/latest/125_personal_spider_cache.html
1431+
if version >= '8.7.12':
1432+
self.USER_CACHE_DIR = os.path.join(os.path.expanduser('~'), '.cache', 'lmod')
1433+
else:
1434+
self.USER_CACHE_DIR = os.path.join(os.path.expanduser('~'), '.lmod.d', '.cache')
13601435

13611436
def check_module_function(self, *args, **kwargs):
13621437
"""Check whether selected module tool matches 'module' function definition."""
@@ -1433,7 +1508,8 @@ def update(self):
14331508
# don't actually update local cache when testing, just return the cache contents
14341509
return stdout
14351510
else:
1436-
cache_fp = os.path.join(self.USER_CACHE_DIR, 'moduleT.lua')
1511+
suffix = build_option('module_cache_suffix') or ''
1512+
cache_fp = os.path.join(self.USER_CACHE_DIR, 'moduleT%s.lua' % suffix)
14371513
self.log.debug("Updating Lmod spider cache %s with output from '%s'", cache_fp, cmd)
14381514
cache_dir = os.path.dirname(cache_fp)
14391515
if not os.path.exists(cache_dir):

easybuild/tools/options.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,9 @@ def override_options(self):
455455
"environment variable and its value separated by a colon (':')",
456456
None, 'store', DEFAULT_MINIMAL_BUILD_ENV),
457457
'minimal-toolchains': ("Use minimal toolchain when resolving dependencies", None, 'store_true', False),
458+
'module-cache-suffix': ("Suffix to add to the cache file name (before the extension) "
459+
"when updating the modules tool cache",
460+
None, 'store', None),
458461
'module-only': ("Only generate module file(s); skip all steps except for %s" % ', '.join(MODULE_ONLY_STEPS),
459462
None, 'store_true', False),
460463
'modules-tool-version-check': ("Check version of modules tool being used", None, 'store_true', True),
@@ -1725,11 +1728,13 @@ def process_software_build_specs(options):
17251728
})
17261729

17271730
# provide both toolchain and toolchain_name/toolchain_version keys
1728-
if 'toolchain_name' in build_specs:
1731+
try:
17291732
build_specs['toolchain'] = {
17301733
'name': build_specs['toolchain_name'],
1731-
'version': build_specs.get('toolchain_version', None),
1734+
'version': build_specs['toolchain_version'],
17321735
}
1736+
except KeyError:
1737+
pass # Don't set toolchain key if we don't have both keys
17331738

17341739
# process --amend and --try-amend
17351740
if options.amend or options.try_amend:

0 commit comments

Comments
 (0)