Might as well send it to the list...
Attached is HPC2N's easyhooks for various stuff.
On 2/12/19 11:52 AM, Loris Bennett wrote:
> Hi Åke,
>
> Thanks - hooks look like they are exactly what I need. Do you have a
> snippet regarding PMXi for OpenMPI which you would be willing to share?
>
> Cheers,
>
> Loris
>
>
> Åke Sandgren <[email protected]> writes:
>
>> This is where easyhooks is your friend.
>>
>> Especially if it is something you want to do for all OpenMPI builds
>> (with variations depending on versions).
>>
>> We add extra dependencies to OpenMPI and change configure flags to
>> enable external PMIx/UCX for instance.
>>
>> And add extra env vars to the module file and more.
>>
>> Our hook file is getting fairly big and complex, but it avoids having to
>> create our own easyconfig for OpenMPI, IntelMPI, GROMACS at the moment.
>>
>> On 2/12/19 10:06 AM, Loris Bennett wrote:
>>> Hi,
>>>
>>> I've just realised that I need to modify an easyconfig (I want to enable
>>> the configuration for Slurm in OpenMPI-3.1.1-GCC-7.3.0-2.30.eb, which is
>>> commented out by default). I could just make my own copy of the
>>> individual easyconfig, but I could also clone the easyconfig repo and
>>> make my changes there, particularly if I want to submit pull requests
>>> for other easyconfigs at a latter point.
>>>
>>> However, the relationship between the easyconfigs that come with
>>> EasyBuild and the ones in my local repo is unclear. If the change I
>>> make just affects the single module and none of the dependencies, then
>>> the case is the same as when I just make my own copy. However, if I
>>> modify something other modules depend on, then this will need to be
>>> added to the robot path.
--
Ake Sandgren, HPC2N, Umea University, S-90187 Umea, Sweden
Internet: [email protected] Phone: +46 90 7866134 Fax: +46 90-580 14
Mobile: +46 70 7716134 WWW: http://www.hpc2n.umu.se
# Hooks for HPC2N site changes.
#
# Author: Ake Sandgren, HPC2N
import os
from distutils.version import LooseVersion
from easybuild.framework.easyconfig.format.format import DEPENDENCY_PARAMETERS
from easybuild.tools.filetools import apply_regex_substitutions
from easybuild.tools.build_log import EasyBuildError
from easybuild.tools.modules import get_software_root
from easybuild.tools.systemtools import get_shared_lib_ext
# Add/remove dependencies and/or patches
# Access to the raw values before templating and such.
def parse_hook(ec):
# Internal helper function
def add_extra_dependencies(ec, dep_type, extra_deps):
"""dep_type: must be in DEPENDENCY_PARAMETERS or 'osdependencies'"""
ec.log.info("[parse hook] Adding %s: %s" % (dep_type, extra_deps))
if dep_type in DEPENDENCY_PARAMETERS:
for dep in extra_deps:
ec[dep_type].append(dep)
elif dep_type == 'osdependencies':
if isinstance(extra_deps, tuple):
ec[dep_type].append(extra_deps)
else:
raise EasyBuildError("parse_hook: Type of extra_deps argument (%s), for 'osdependencies' must be tuple, found %s" % (extra_deps, type(extra_deps)))
else:
raise EasyBuildError("parse_hook: Incorrect dependency type in add_extra_dependencies: %s" % dep_type)
extra_deps = []
if ec.name == 'OpenMPI':
if LooseVersion(ec.version) >= LooseVersion('2') and LooseVersion(ec.version) < LooseVersion('2.1.2'):
ec.log.info("[parse hook] Adding pmi and lustre patches")
if LooseVersion(ec.version) < LooseVersion('2.1.1'):
ec['patches'].append('OpenMPI-2.0.0_fix_bad-include_of_pmi_h.patch')
if LooseVersion(ec.version) < LooseVersion('2.0.2'):
ec['patches'].append('OpenMPI-2.0.1_fix_lustre.patch')
elif LooseVersion(ec.version) < LooseVersion('2.1'):
ec['patches'].append('OpenMPI-2.0.2_fix_lustre.patch')
elif LooseVersion(ec.version) < LooseVersion('2.1.1'):
ec['patches'].append('OpenMPI-2.1.0_fix_lustre.patch')
else:
ec['patches'].append('OpenMPI-2.1.1_fix_lustre.patch')
if LooseVersion(ec.version) == LooseVersion('4.0.0'):
ec['patches'].append('OpenMPI-4.0.0_fix_configure_bug.patch')
if LooseVersion(ec.version) >= LooseVersion('2.1'):
pmix_version = '1.2.5'
ucx_version = '1.4.0'
if LooseVersion(ec.version) >= LooseVersion('3'):
pmix_version = '2.2.1'
if LooseVersion(ec.version) >= LooseVersion('4'):
pmix_version = '3.0.2' # OpenMPI 4.0.0 is not compatible with PMIx 3.1.x
extra_deps.append(('PMIx', pmix_version))
# Use of external PMIx requires external libevent
# But PMIx already has it as a dependency so we don't need
# to explicitly set it.
extra_deps.append(('UCX', ucx_version))
if ec.name == 'impi':
pmix_version = '3.1.1'
extra_deps.append(('PMIx', pmix_version))
if extra_deps:
add_extra_dependencies(ec, 'dependencies', extra_deps)
def pre_configure_hook(self, *args, **kwargs):
if self.name == 'GROMACS':
# HPC2N always uses -DGMX_USE_NVML=ON on GPU builds
if get_software_root('CUDA'):
self.log.info("[pre-configure hook] Adding -DGMX_USE_NVML=ON")
self.cfg.update('configopts', "-DGMX_USE_NVML=ON ")
if self.name == 'OpenMPI':
extra_opts = ""
# Old versions don't work with PMIx, use slurms PMI1
if LooseVersion(self.version) < LooseVersion('2.1'):
extra_opts += "--with-pmi=/lap/slurm "
if LooseVersion(self.version) >= LooseVersion('2'):
extra_opts += "--with-munge "
# Using PMIx dependency in easyconfig, see above
if LooseVersion(self.version) >= LooseVersion('2.1'):
if get_software_root('PMIx'):
extra_opts += "--with-pmix=$EBROOTPMIX "
# Use of external PMIx requires external libevent
# We're using the libevent that comes from the PMIx dependency
if get_software_root('libevent'):
extra_opts += "--with-libevent=$EBROOTLIBEVENT "
else:
raise EasyBuildError("Error in pre_configure_hook for OpenMPI: External use of PMIx requires external libevent, which was not found. Check parse_hook for dependency settings.")
else:
raise EasyBuildError("Error in pre_configure_hook for OpenMPI: PMIx not defined in dependencies. Check parse_hook for dependency settings.")
if get_software_root('UCX'):
extra_opts += "--with-ucx=$EBROOTUCX "
if LooseVersion(self.version) >= LooseVersion('2'):
extra_opts += "--with-cma "
extra_opts += "--with-lustre "
# We still need to fix the knem package to install its
# pkg-config .pc file correctly, and we need a more generic
# install dir.
# extra_opts += "--with-knem=/opt/knem-1.1.2.90mlnx1 "
self.log.info("[pre-configure hook] Adding %s" % extra_opts)
self.cfg.update('configopts', extra_opts)
if LooseVersion(self.version) >= LooseVersion('2.1'):
self.log.info("[pre-configure hook] Re-enabling ucx")
self.cfg['configopts'] = self.cfg['configopts'].replace('--without-ucx', ' ')
self.log.info("[pre-configure hook] Re-enabling dlopen")
self.cfg['configopts'] = self.cfg['configopts'].replace('--disable-dlopen', ' ')
if self.name == 'PMIx':
self.log.info("[pre-configure hook] Adding --with-munge")
self.cfg.update('configopts', "--with-munge ")
if LooseVersion(self.version) >= LooseVersion('2'):
self.log.info("[pre-configure hook] Adding --with-tests-examples")
self.cfg.update('configopts', "--with-tests-examples ")
self.log.info("[pre-configure hook] Adding --disable-per-user-config-files")
self.cfg.update('configopts', "--disable-per-user-config-files")
def pre_build_hook(self, *args, **kwargs):
if self.name == 'pyslurm':
self.log.info("[pre-build hook] Adding --slurm=/lap/slurm")
self.cfg.update('buildopts', "--slurm=/lap/slurm ")
def post_install_hook(self, *args, **kwargs):
if self.name == 'impi':
# Fix mpirun from IntelMPI to explicitly unset I_MPI_PMI_LIBRARY
# it can only be used with srun.
self.log.info("[post-install hook] Unset I_MPI_PMI_LIBRARY in mpirun")
apply_regex_substitutions(os.path.join(self.installdir, "intel64", "bin", "mpirun"), [
(r'^(#!/bin/sh.*)$', r'\1\nunset I_MPI_PMI_LIBRARY'),
])
def pre_module_hook(self, *args, **kwargs):
if self.name == 'impi':
# Add I_MPI_PMI_LIBRARY to module for IntelMPI so it works with
# srun.
self.log.info("[pre-module hook] Set I_MPI_PMI_LIBRARY in impi module")
# Must be done this way, updating self.cfg['modextravars']
# directly doesn't work due to templating.
en_templ = self.cfg.enable_templating
self.cfg.enable_templating = False
shlib_ext = get_shared_lib_ext()
pmix_root = get_software_root('PMIx')
if pmix_root:
mpi_type = 'pmix_v3'
self.cfg['modextravars'].update({'I_MPI_PMI_LIBRARY': os.path.join(pmix_root, "lib", "libpmi." + shlib_ext)})
self.cfg['modextravars'].update({'SLURM_MPI_TYPE': mpi_type})
# Unfortunately UCX doesn't yet work for unknown reasons. Make sure it is off.
self.cfg['modextravars'].update({'SLURM_PMIX_DIRECT_CONN_UCX': 'false'})
else:
self.cfg['modextravars'].update({'I_MPI_PMI_LIBRARY': "/lap/slurm/lib/libpmi.so"})
self.cfg.enable_templating = en_templ
if self.name == 'OpenBLAS':
self.log.info("[pre-module hook] Set OMP_NUM_THREADS=1 in OpenBLAS module")
self.cfg.update('modluafooter', 'if ((mode() == "load" and os.getenv("OMP_NUM_THREADS") == nil) or (mode() == "unload" and os.getenv("__OpenBLAS_set_OMP_NUM_THREADS") == "1")) then setenv("OMP_NUM_THREADS","1"); setenv("__OpenBLAS_set_OMP_NUM_THREADS", "1") end')
if self.name == 'OpenMPI':
if LooseVersion(self.version) < LooseVersion('2.1'):
mpi_type = 'openmpi'
elif LooseVersion(self.version) < LooseVersion('3'):
mpi_type = 'pmix_v1'
elif LooseVersion(self.version) < LooseVersion('4'):
mpi_type = 'pmix_v2'
else:
mpi_type = 'pmix_v3'
self.log.info("[pre-module hook] Set SLURM_MPI_TYPE=%s in OpenMPI module" % mpi_type)
# Must be done this way, updating self.cfg['modextravars']
# directly doesn't work due to templating.
en_templ = self.cfg.enable_templating
self.cfg.enable_templating = False
self.cfg['modextravars'].update({'SLURM_MPI_TYPE': mpi_type})
# Unfortunately UCX doesn't yet work for unknown reasons. Make sure it is off.
self.cfg['modextravars'].update({'SLURM_PMIX_DIRECT_CONN_UCX': 'false'})
self.cfg.enable_templating = en_templ