From a277d17dafdd1800ef178ad1e4f336845bbc9cef Mon Sep 17 00:00:00 2001 From: Damian Alvarez <swmanage@jwlogin04.juwels> Date: Tue, 25 May 2021 19:01:19 +0200 Subject: [PATCH] To support the new oneAPI-based toolchains, and other preparations for the 2020 stage update --- Custom_Hooks/eb_hooks.py | 41 ++++++++++++-- .../flexible_custom_hierarchical_mns.py | 53 +++++++++++++++---- 2 files changed, 80 insertions(+), 14 deletions(-) diff --git a/Custom_Hooks/eb_hooks.py b/Custom_Hooks/eb_hooks.py index da66022ca..b96dfd0b7 100644 --- a/Custom_Hooks/eb_hooks.py +++ b/Custom_Hooks/eb_hooks.py @@ -8,7 +8,7 @@ from easybuild.tools.build_log import EasyBuildError, print_warning from easybuild.tools.toolchain.toolchain import SYSTEM_TOOLCHAIN_NAME from easybuild.toolchains.compiler.systemcompiler import TC_CONSTANT_SYSTEM -SUPPORTED_COMPILERS = ["GCC", "iccifort", "PGI", "GCCcore"] +SUPPORTED_COMPILERS = ["GCC", "iccifort", "intel-compilers", "NVHPC", "PGI", "GCCcore"] SUPPORTED_MPIS = ["impi", "psmpi", "OpenMPI", "MVAPICH2"] # Maintain toplevel list for easy use of --try-toolchain SUPPORTED_TOPLEVEL_TOOLCHAIN_FAMILIES = [ @@ -17,14 +17,28 @@ SUPPORTED_TOPLEVEL_TOOLCHAIN_FAMILIES = [ "iomkl", "gpsmkl", "gomkl", + "npsmkl", + "nomkl", "pmvmklc", "gmvmklc", ] +SUPPORTED_MPI_TOOLCHAIN_FAMILIES = [ + "iimpi", + "ipsmpi", + "iompi", + "gpsmpi", + "gompi", + "npsmpi", + "nompi", + "gmvapich2c", + "pmvapich2c", +] # Could potentially make a dictionary of names and supported versions here but that is # probably overkill SUPPORTED_TOOLCHAIN_FAMILIES = ( SUPPORTED_COMPILERS - + ["gcccoremkl", "gpsmpi", "ipsmpi", "iimpi", "iompi", "gmvapich2c", "pmvapich2c"] + + ["gcccoremkl"] + + SUPPORTED_MPI_TOOLCHAIN_FAMILIES + SUPPORTED_TOPLEVEL_TOOLCHAIN_FAMILIES ) # Also maintain a list of CUDA enabled compilers @@ -37,6 +51,7 @@ REQUIRE_MODALTSOFTNAME = { "impi": "IntelMPI", "psmpi": "ParaStationMPI", "iccifort": "Intel", + "intel-compilers": "Intel", } @@ -61,7 +76,8 @@ def parse_hook(ec, *args, **kwargs): ec.log.info( "[parse hook] Injecting recursive module unloading for supported compiler" ) - # MPI are are a family (in the Lmod sense) + + # MPIs are a family (in the Lmod sense) if ec.name in SUPPORTED_MPIS: key = "modluafooter" value = 'family("mpi")' @@ -72,6 +88,21 @@ def parse_hook(ec, *args, **kwargs): ec[key] = value ec.log.info("[parse hook] Injecting Lmod mpi family") + # MPIs require to load mpi-settings + if ec.name in SUPPORTED_MPIS: + key = "modluafooter" + value = ''' +if not ( isloaded("mpi-settings") ) then + load("mpi-settings") +end + ''' + if key in ec_dict: + if not value in ec_dict[key]: + ec[key] = "\n".join([ec[key], value]) + else: + ec[key] = value + ec.log.info("[parse hook] Injecting mpi-settings loading") + # Check if we need to use 'modaltsoftname' if ec.name in REQUIRE_MODALTSOFTNAME: key = "modaltsoftname" @@ -113,7 +144,7 @@ def parse_hook(ec, *args, **kwargs): site_contacts = None if "stages" in install_path().lower(): users_groups = [grp.getgrgid(g).gr_name for g in os.getgroups()] - if any(group in user_groups for group in ["swmanage", "software"]): + if any(group in users_groups for group in ["swmanage", "software"]): site_contacts = "sc@fz-juelich.de" if site_contacts is None: # Inject the user @@ -229,7 +260,7 @@ def pre_ready_hook(self, *args, **kwargs): def end_hook(*args, **kwargs): - # If the user is part of the development group and the installation is systemwide, + # If the user is part of the development group and the installation is systemwide, # rebuild the system cache "Refresh Lmod's cache" diff --git a/Custom_MNS/flexible_custom_hierarchical_mns.py b/Custom_MNS/flexible_custom_hierarchical_mns.py index 640e87e73..eaf5a8e67 100644 --- a/Custom_MNS/flexible_custom_hierarchical_mns.py +++ b/Custom_MNS/flexible_custom_hierarchical_mns.py @@ -36,6 +36,7 @@ COMP_NAME_VERSION_TEMPLATES = { # Compiler relevant version numbers comp_relevant_versions = { 'intel': 1, + 'intel-compilers': 1, 'PGI': 1, 'NVHPC': 1, # The compilers load GCCcore/version. So GCC and GCCcore can't really be flexible, since GCCcore will always be loaded @@ -64,7 +65,7 @@ class FlexibleCustomHierarchicalMNS(HierarchicalMNS): <name>/<version>[-<toolchain>] """ # We rename our iccifort compiler to INTEL and this needs a hard fix because it is a toolchain - if name == 'iccifort': + if name == 'iccifort' or name == 'intel-compilers': modname_regex = re.compile('^%s/\S+$' % re.escape('Intel')) elif name == 'psmpi': modname_regex = re.compile('^%s/\S+$' % re.escape('ParaStationMPI')) @@ -97,20 +98,22 @@ class FlexibleCustomHierarchicalMNS(HierarchicalMNS): mpi_ver = self.det_full_version(mpi_info) mpi_name = mpi_info['name'] + # We'll start ignoring suffixes in MPI toolchains. But let's keep the code around for a bit, in case it needs + # to be readded. Not elegant, I know, but digging up in git is always extra effort. + # Find suffix, if any, to be appended. Try to be clever, since the suffix is embedded in the version # and sometimes the version might include a string that looks like a suffix (ie: psmpi-5.4.0-1) if mpi_name in mpi_relevant_versions: # Find possible suffixes possible_suffixes = mpi_ver.split('-')[1:] + suffix = '' # Match the '-1' that is a typical part of psmpi's version - if possible_suffixes: - if re.match('^\d$', possible_suffixes[0]): - suffix_index = 2 - else: - suffix_index = 1 - suffix = '-'.join(mpi_ver.split('-')[suffix_index:]) - else: - suffix = '' + #if possible_suffixes: + # if re.match('^\d$', possible_suffixes[0]): + # suffix_index = 2 + # else: + # suffix_index = 1 + # suffix = '-'.join(mpi_ver.split('-')[suffix_index:]) mpi_ver = '.'.join(mpi_ver.split('.')[:mpi_relevant_versions[mpi_name]]) if suffix: @@ -118,6 +121,38 @@ class FlexibleCustomHierarchicalMNS(HierarchicalMNS): return mpi_name, mpi_ver + def det_toolchain_compilers_name_version(self, tc_comps): + """ + Determine toolchain compiler tag, for given list of compilers. + """ + if tc_comps is None: + # no compiler in toolchain, system toolchain + res = None + elif len(tc_comps) == 1: + tc_comp = tc_comps[0] + if tc_comp is None: + res = None + else: + # Rename intel-compilers to intel, just to keep it consistent with installations pre oneAPI + if tc_comp['name'] == 'intel-compilers': + tc_comp['name'] = 'intel' + res = (tc_comp['name'], self.det_full_version(tc_comp)) + else: + comp_versions = dict([(comp['name'], self.det_full_version(comp)) for comp in tc_comps]) + comp_names = comp_versions.keys() + key = ','.join(sorted(comp_names)) + if key in COMP_NAME_VERSION_TEMPLATES: + tc_comp_name, tc_comp_ver_tmpl = COMP_NAME_VERSION_TEMPLATES[key] + tc_comp_ver = tc_comp_ver_tmpl % comp_versions + # make sure that icc/ifort versions match (unless not existing as separate modules) + if tc_comp_name == 'intel' and comp_versions.get('icc') != comp_versions.get('ifort'): + raise EasyBuildError("Bumped into different versions for Intel compilers: %s", comp_versions) + else: + raise EasyBuildError("Unknown set of toolchain compilers, module naming scheme needs work: %s", + comp_names) + res = (tc_comp_name, tc_comp_ver) + return res + def det_module_subdir(self, ec): """ Determine module subdirectory, relative to the top of the module path. -- GitLab