From c2fcd868c4d730a120b1107b5cd50470538431ce Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 10 Feb 2017 22:46:17 +0100 Subject: [PATCH 01/59] First go at a system OpenMPI easyblock --- easybuild/easyblocks/generic/systemmpi.py | 160 ++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 easybuild/easyblocks/generic/systemmpi.py diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py new file mode 100644 index 0000000000..321f403160 --- /dev/null +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -0,0 +1,160 @@ +## +# Copyright 2015-2016 Ghent University +# +# This file is part of EasyBuild, +# originally created by the HPC team of Ghent University (http://ugent.be/hpc/en), +# with support of Ghent University (http://ugent.be/hpc), +# the Flemish Supercomputer Centre (VSC) (https://www.vscentrum.be), +# Flemish Research Foundation (FWO) (http://www.fwo.be/en) +# and the Department of Economy, Science and Innovation (EWI) (http://www.ewi-vlaanderen.be/en). +# +# http://github.com/hpcugent/easybuild +# +# EasyBuild is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation v2. +# +# EasyBuild is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with EasyBuild. If not, see . +## +""" +EasyBuild support for using (already installed/existing) system MPI instead of a full install via EasyBuild. + +@author Alan O'Cais (Juelich Supercomputing Centre) +""" +import os +import re + +from easybuild.easyblocks.generic.bundle import Bundle +from easybuild.tools.filetools import read_file, which +from easybuild.tools.run import run_cmd +from easybuild.framework.easyconfig.easyconfig import ActiveMNS +from easybuild.tools.build_log import EasyBuildError + + +class SystemMPI(Bundle): + """ + Support for generating a module file for the system compiler with specified name. + + The compiler is expected to be available in $PATH, required libraries are assumed to be readily available. + + Specifying 'system' as a version leads to using the derived compiler version in the generated module; + if an actual version is specified, it is checked against the derived version of the system compiler that was found. + """ + + def extract_ompi_version(self, txt): + """Extract MPI version from provided string.""" + # look for 3-4 digit version number, surrounded by spaces + # examples: + # gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11) + # Intel(R) C Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 15.0.1.133 Build 20141023 + version_regex = re.compile(r'\s(v[0-9]+(?:\.[0-9]+){1,3})\s', re.M) + res = version_regex.search(txt) + if res: + self.mpi_version = res.group(1)[1:] + self.log.debug("Extracted MPI version '%s' from: %s", self.mpi_version, txt) + else: + raise EasyBuildError("Failed to extract OpenMPI version using regex pattern '%s' from: %s", + version_regex.pattern, txt) + + def __init__(self, *args, **kwargs): + """Extra initialization: determine system compiler version and prefix.""" + super(SystemMPI, self).__init__(*args, **kwargs) + + # Determine MPI path (real path, with resolved symlinks) + mpi_name = self.cfg['name'].lower() + mpi_c_compiler = 'mpicc' + path_to_mpi_c_compiler = which(mpi_c_compiler) + if path_to_mpi_c_compiler: + path_to_mpi_c_compiler = os.path.realpath(path_to_mpi_c_compiler) + self.log.info("Found path to MPI implementation '%s' mpicc compiler (with symlinks resolved): %s", + mpi_name, path_to_mpi_c_compiler) + else: + raise EasyBuildError("%s not found in $PATH", mpi_c_compiler) + + # Determine compiler version and installation prefix + if mpi_name == 'openmpi': + out, _ = run_cmd("ompi_info --version", simple=False) + self.extract_ompi_version(out) + + # extract the installation prefix + self.mpi_prefix, _ = run_cmd("ompi_info --path prefix|awk '{print $2}'", simple=False) + # drop the carriage return + self.mpi_prefix = self.mpi_prefix[:-1] + # verify that toolchain compiler and compiler in MPI wrappers match + #TODO + + # extract any OpenMPI environment variables in the current environment and ensure they are added to the + # final module + #TODO + + else: + raise EasyBuildError("Unknown system MPI implementation %s", mpi_name) + + self.log.debug("Derived version/install prefix for system MPI %s: %s, %s", + mpi_name, self.mpi_version, self.mpi_prefix) + + # If EasyConfig specified "real" version (not 'system' which means 'derive automatically'), check it + if self.cfg['version'] == 'system': + self.log.info("Found specified version '%s', going with derived compiler version '%s'", + self.cfg['version'], self.mpi_version) + elif self.cfg['version'] != self.mpi_version: + raise EasyBuildError("Specified version (%s) does not match version reported by MPI (%s)" % + (self.cfg['version'], self.mpi_version)) + + # fix installdir and module names (may differ because of changes to version) + mns = ActiveMNS() + self.cfg.full_mod_name = mns.det_full_module_name(self.cfg) + self.cfg.short_mod_name = mns.det_short_module_name(self.cfg) + self.cfg.mod_subdir = mns.det_module_subdir(self.cfg) + + # keep track of original values, for restoring later + self.orig_version = self.cfg['version'] + self.orig_installdir = self.installdir + + def make_installdir(self, dontcreate=None): + """Custom implementation of make installdir: do nothing, do not touch system compiler directories and files.""" + pass + + def make_module_req_guess(self): + """ + A dictionary of possible directories to look for. Return empty dict for a system MPI. + """ + return {} + + def make_module_step(self, fake=False): + """ + Custom module step for SystemMPI: make 'EBROOT' and 'EBVERSION' reflect actual system MPI version + and install path. + """ + # For module file generation: temporarily set version and installdir to system compiler values + self.cfg['version'] = self.mpi_version + self.installdir = self.mpi_prefix + + # Generate module + res = super(SystemMPI, self).make_module_step(fake=fake) + + # Reset version and installdir to EasyBuild values + self.installdir = self.orig_installdir + self.cfg['version'] = self.orig_version + return res + + def make_module_extend_modpath(self): + """ + Custom prepend-path statements for extending $MODULEPATH: use version specified in easyconfig file (e.g., + "system") rather than the actual version (e.g., "4.8.2"). + """ + # temporarily set switch back to version specified in easyconfig file (e.g., "system") + self.cfg['version'] = self.orig_version + + # Retrieve module path extensions + res = super(SystemMPI, self).make_module_extend_modpath() + + # Reset to actual compiler version (e.g., "4.8.2") + self.cfg['version'] = self.mpi_version + return res From b3bdaae1135a2f6257f14b78a37bebbf47212027 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Mon, 13 Feb 2017 14:44:15 +0100 Subject: [PATCH 02/59] Switch computer --- easybuild/easyblocks/generic/systemcompiler.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 1cbd14b1ab..3bb06e04ee 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -69,6 +69,8 @@ def __init__(self, *args, **kwargs): # Determine compiler path (real path, with resolved symlinks) compiler_name = self.cfg['name'].lower() + if compiler_name == 'gcccore': + compiler_name = 'gcc' path_to_compiler = which(compiler_name) if path_to_compiler: path_to_compiler = os.path.realpath(path_to_compiler) From 902b9cc79185e24f9465e5a2114ca0196c22e7d7 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Tue, 14 Feb 2017 16:41:27 +0100 Subject: [PATCH 03/59] Make extract_compiler_version available to SystemMPI Include checks on compiler in toolchain and underneath MPI Add any relevant envvars in the environment to the module --- .../easyblocks/generic/systemcompiler.py | 49 ++++++---- easybuild/easyblocks/generic/systemmpi.py | 97 +++++++++++++------ 2 files changed, 94 insertions(+), 52 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 3bb06e04ee..68d0144ac4 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -38,6 +38,29 @@ from easybuild.tools.build_log import EasyBuildError +def extract_compiler_version(compiler_name): + """Extract compiler version from provided string.""" + # look for 3-4 digit version number, surrounded by spaces + # examples: + # gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11) + # Intel(R) C Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 15.0.1.133 Build 20141023 + if compiler_name == 'gcc': + out, _ = run_cmd("gcc --version", simple=False) + elif compiler_name in ['icc', 'ifort']: + out, _ = run_cmd("%s -V" % compiler_name, simple=False) + else: + raise EasyBuildError("Unknown compiler %s" % compiler_name) + + version_regex = re.compile(r'\s([0-9]+(?:\.[0-9]+){1,3})\s', re.M) + res = version_regex.search(out) + if res: + compiler_version = res.group(1) + debug_msg = "Extracted compiler version '%s' for %s from: %s", compiler_version, compiler_name, out + return compiler_version, debug_msg + else: + raise EasyBuildError("Failed to extract compiler version for %s using regex pattern '%s' from: %s", + compiler_version, version_regex.pattern, txt) + class SystemCompiler(Bundle): """ Support for generating a module file for the system compiler with specified name. @@ -48,20 +71,6 @@ class SystemCompiler(Bundle): if an actual version is specified, it is checked against the derived version of the system compiler that was found. """ - def extract_compiler_version(self, txt): - """Extract compiler version from provided string.""" - # look for 3-4 digit version number, surrounded by spaces - # examples: - # gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11) - # Intel(R) C Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 15.0.1.133 Build 20141023 - version_regex = re.compile(r'\s([0-9]+(?:\.[0-9]+){1,3})\s', re.M) - res = version_regex.search(txt) - if res: - self.compiler_version = res.group(1) - self.log.debug("Extracted compiler version '%s' from: %s", self.compiler_version, txt) - else: - raise EasyBuildError("Failed to extract compiler version using regex pattern '%s' from: %s", - version_regex.pattern, txt) def __init__(self, *args, **kwargs): """Extra initialization: determine system compiler version and prefix.""" @@ -78,18 +87,16 @@ def __init__(self, *args, **kwargs): else: raise EasyBuildError("%s not found in $PATH", compiler_name) - # Determine compiler version and installation prefix - if compiler_name == 'gcc': - out, _ = run_cmd("gcc --version", simple=False) - self.extract_compiler_version(out) + # Determine compiler version + self.compiler_version, debug_msg = extract_compiler_version(compiler_name) + self.log.debug(debug_msg) + # Determine installation prefix + if compiler_name == 'gcc': # strip off 'bin/gcc' self.compiler_prefix = os.path.dirname(os.path.dirname(path_to_compiler)) elif compiler_name in ['icc', 'ifort']: - out, _ = run_cmd("%s -V" % compiler_name, simple=False) - self.extract_compiler_version(out) - intelvars_fn = path_to_compiler + 'vars.sh' if os.path.isfile(intelvars_fn): self.log.debug("Trying to determine compiler install prefix from %s", intelvars_fn) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index 321f403160..55fba924c7 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -31,6 +31,8 @@ import re from easybuild.easyblocks.generic.bundle import Bundle +from easybuild.easyblocks.generic.systemcompiler import extract_compiler_version +from easybuild.tools.modules import get_software_version from easybuild.tools.filetools import read_file, which from easybuild.tools.run import run_cmd from easybuild.framework.easyconfig.easyconfig import ActiveMNS @@ -39,66 +41,80 @@ class SystemMPI(Bundle): """ - Support for generating a module file for the system compiler with specified name. + Support for generating a module file for the system mpi with specified name. - The compiler is expected to be available in $PATH, required libraries are assumed to be readily available. + The mpi compiler is expected to be available in $PATH, required libraries are assumed to be readily available. - Specifying 'system' as a version leads to using the derived compiler version in the generated module; - if an actual version is specified, it is checked against the derived version of the system compiler that was found. + Specifying 'system' as a version leads to using the derived mpi version in the generated module; + if an actual version is specified, it is checked against the derived version of the system mpi that was found. """ - def extract_ompi_version(self, txt): + def extract_ompi_setting(self, search, txt): """Extract MPI version from provided string.""" # look for 3-4 digit version number, surrounded by spaces # examples: # gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11) # Intel(R) C Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 15.0.1.133 Build 20141023 - version_regex = re.compile(r'\s(v[0-9]+(?:\.[0-9]+){1,3})\s', re.M) + version_regex = re.compile(r'^\s+%s: (.*)$' % search, re.M) res = version_regex.search(txt) if res: - self.mpi_version = res.group(1)[1:] - self.log.debug("Extracted MPI version '%s' from: %s", self.mpi_version, txt) + setting = res.group(1) + self.log.debug("Extracted OpenMPI setting %s: '%s' from search text", search, setting) + return setting else: - raise EasyBuildError("Failed to extract OpenMPI version using regex pattern '%s' from: %s", + raise EasyBuildError("Failed to extract OpenMPI setting using regex pattern '%s' from: %s", version_regex.pattern, txt) def __init__(self, *args, **kwargs): - """Extra initialization: determine system compiler version and prefix.""" + """Extra initialization: determine system MPI version, prefix and any associated envvars.""" super(SystemMPI, self).__init__(*args, **kwargs) - # Determine MPI path (real path, with resolved symlinks) + # Determine MPI path (real path, with resolved symlinks) to ensure it exists mpi_name = self.cfg['name'].lower() - mpi_c_compiler = 'mpicc' - path_to_mpi_c_compiler = which(mpi_c_compiler) - if path_to_mpi_c_compiler: - path_to_mpi_c_compiler = os.path.realpath(path_to_mpi_c_compiler) - self.log.info("Found path to MPI implementation '%s' mpicc compiler (with symlinks resolved): %s", - mpi_name, path_to_mpi_c_compiler) + + # Check we have MPI wrappers + if mpi_name != 'impi': + mpi_c_wrapper = 'mpicc' + else: + mpi_c_wrapper = 'mpiicc' + path_to_mpi_c_wrapper = which(mpi_c_wrapper) + if path_to_mpi_c_wrapper: + path_to_mpi_c_wrapper = os.path.realpath(path_to_mpi_c_wrapper) + self.log.info("Found path to MPI implementation '%s' %s compiler (with symlinks resolved): %s", + mpi_name, mpi_c_wrapper, path_to_mpi_c_wrapper) else: - raise EasyBuildError("%s not found in $PATH", mpi_c_compiler) + raise EasyBuildError("%s not found in $PATH", mpi_c_wrapper) # Determine compiler version and installation prefix if mpi_name == 'openmpi': - out, _ = run_cmd("ompi_info --version", simple=False) - self.extract_ompi_version(out) + output_of_ompi_info, _ = run_cmd("ompi_info", simple=False) + # Extract the version of OpenMPI + self.mpi_version = self.extract_ompi_setting("Open MPI", output_of_ompi_info) - # extract the installation prefix - self.mpi_prefix, _ = run_cmd("ompi_info --path prefix|awk '{print $2}'", simple=False) - # drop the carriage return - self.mpi_prefix = self.mpi_prefix[:-1] - # verify that toolchain compiler and compiler in MPI wrappers match - #TODO + # Extract the installation prefix + self.mpi_prefix = self.extract_ompi_setting("Prefix", output_of_ompi_info) - # extract any OpenMPI environment variables in the current environment and ensure they are added to the + # Extract any OpenMPI environment variables in the current environment and ensure they are added to the # final module - #TODO + raw_env = os.environ + self.mpi_envvars = dict({(k, v) for k, v in raw_env.iteritems() if k.startswith("OMPI_")}) + + # Extract the C compiler used underneath OpenMPI, check for the definition of OMPI_MPICC + self.mpi_c_compiler = self.extract_ompi_setting("C compiler", output_of_ompi_info) + # TODO :Add support for impi else: raise EasyBuildError("Unknown system MPI implementation %s", mpi_name) self.log.debug("Derived version/install prefix for system MPI %s: %s, %s", mpi_name, self.mpi_version, self.mpi_prefix) + # For the version of the underlying C compiler need to explicitly extract (to be certain) + self.compiler_version, debug_msg = extract_compiler_version(self.mpi_c_compiler) + self.log.debug(debug_msg) + self.log.debug("Derived compiler/version for C compiler underneath system MPI %s: %s, %s", + mpi_name, self.mpi_c_compiler, self.compiler_version) + # If EasyConfig specified "real" version (not 'system' which means 'derive automatically'), check it if self.cfg['version'] == 'system': self.log.info("Found specified version '%s', going with derived compiler version '%s'", @@ -123,7 +139,7 @@ def make_installdir(self, dontcreate=None): def make_module_req_guess(self): """ - A dictionary of possible directories to look for. Return empty dict for a system MPI. + A dictionary of possible directories to look for. Return empty dict for a system compiler. """ return {} @@ -132,6 +148,14 @@ def make_module_step(self, fake=False): Custom module step for SystemMPI: make 'EBROOT' and 'EBVERSION' reflect actual system MPI version and install path. """ + # First let's verify that the toolchain and the compilers under MPI match + c_compiler_name = self.toolchain.COMPILER_CC + compiler_version = get_software_version(self.toolchain.COMPILER_MODULE_NAME[0]) + + if self.mpi_c_compiler != c_compiler_name and self.compiler_version != compiler_version: + raise EasyBuildError("C compiler for toolchain (%s/%s) and underneath MPI (%s/%s) do not match!", + c_compiler_name, compiler_version, self.mpi_c_compiler, self.compiler_version) + # For module file generation: temporarily set version and installdir to system compiler values self.cfg['version'] = self.mpi_version self.installdir = self.mpi_prefix @@ -149,12 +173,23 @@ def make_module_extend_modpath(self): Custom prepend-path statements for extending $MODULEPATH: use version specified in easyconfig file (e.g., "system") rather than the actual version (e.g., "4.8.2"). """ - # temporarily set switch back to version specified in easyconfig file (e.g., "system") + # temporarly set switch back to version specified in easyconfig file (e.g., "system") self.cfg['version'] = self.orig_version # Retrieve module path extensions res = super(SystemMPI, self).make_module_extend_modpath() # Reset to actual compiler version (e.g., "4.8.2") - self.cfg['version'] = self.mpi_version + self.cfg['version'] = self.compiler_version return res + + def make_module_extra(self): + """Add all the build environment variables.""" + txt = super(SystemMPI, self).make_module_extra() + + # include environment variables defined for MPI implementation + for key, val in sorted(self.mpi_envvars.items()): + txt += self.module_generator.set_environment(key, val) + + self.log.debug("make_module_extra added this: %s" % txt) + return txt From 8f789ce926390d96c6a5f05a017ab6103d3fd4bc Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Wed, 15 Feb 2017 09:50:32 +0100 Subject: [PATCH 04/59] Update outdated comments --- easybuild/easyblocks/generic/systemcompiler.py | 2 +- easybuild/easyblocks/generic/systemmpi.py | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 68d0144ac4..8b9d61eac8 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -39,7 +39,7 @@ def extract_compiler_version(compiler_name): - """Extract compiler version from provided string.""" + """Extract compiler version for provided compiler_name.""" # look for 3-4 digit version number, surrounded by spaces # examples: # gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index 55fba924c7..cd92b2ab5f 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -50,11 +50,8 @@ class SystemMPI(Bundle): """ def extract_ompi_setting(self, search, txt): - """Extract MPI version from provided string.""" - # look for 3-4 digit version number, surrounded by spaces - # examples: - # gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11) - # Intel(R) C Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 15.0.1.133 Build 20141023 + """Extract a particular OpenMPI setting from provided string.""" + version_regex = re.compile(r'^\s+%s: (.*)$' % search, re.M) res = version_regex.search(txt) if res: @@ -62,8 +59,8 @@ def extract_ompi_setting(self, search, txt): self.log.debug("Extracted OpenMPI setting %s: '%s' from search text", search, setting) return setting else: - raise EasyBuildError("Failed to extract OpenMPI setting using regex pattern '%s' from: %s", - version_regex.pattern, txt) + raise EasyBuildError("Failed to extract OpenMPI setting '%s' using regex pattern '%s' from: %s", + search, version_regex.pattern, txt) def __init__(self, *args, **kwargs): """Extra initialization: determine system MPI version, prefix and any associated envvars.""" From da237ca4ea9b9dd9766fa66c21c49e6046414afb Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Wed, 15 Feb 2017 09:51:38 +0100 Subject: [PATCH 05/59] Fix variable typo --- easybuild/easyblocks/generic/systemcompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 8b9d61eac8..d69950df38 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -59,7 +59,7 @@ def extract_compiler_version(compiler_name): return compiler_version, debug_msg else: raise EasyBuildError("Failed to extract compiler version for %s using regex pattern '%s' from: %s", - compiler_version, version_regex.pattern, txt) + compiler_name, version_regex.pattern, txt) class SystemCompiler(Bundle): """ From d3d4c5f6363a6199395140f8a3faa6c11f6a584f Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Wed, 15 Feb 2017 10:52:28 +0100 Subject: [PATCH 06/59] Correctly update version in make_module_extend_modpath --- easybuild/easyblocks/generic/systemmpi.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index cd92b2ab5f..ecd3eb12d6 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -107,14 +107,14 @@ def __init__(self, *args, **kwargs): mpi_name, self.mpi_version, self.mpi_prefix) # For the version of the underlying C compiler need to explicitly extract (to be certain) - self.compiler_version, debug_msg = extract_compiler_version(self.mpi_c_compiler) + self.c_compiler_version, debug_msg = extract_compiler_version(self.mpi_c_compiler) self.log.debug(debug_msg) self.log.debug("Derived compiler/version for C compiler underneath system MPI %s: %s, %s", - mpi_name, self.mpi_c_compiler, self.compiler_version) + mpi_name, self.mpi_c_compiler, self.c_compiler_version) # If EasyConfig specified "real" version (not 'system' which means 'derive automatically'), check it if self.cfg['version'] == 'system': - self.log.info("Found specified version '%s', going with derived compiler version '%s'", + self.log.info("Found specified version '%s', going with derived MPI version '%s'", self.cfg['version'], self.mpi_version) elif self.cfg['version'] != self.mpi_version: raise EasyBuildError("Specified version (%s) does not match version reported by MPI (%s)" % @@ -149,9 +149,9 @@ def make_module_step(self, fake=False): c_compiler_name = self.toolchain.COMPILER_CC compiler_version = get_software_version(self.toolchain.COMPILER_MODULE_NAME[0]) - if self.mpi_c_compiler != c_compiler_name and self.compiler_version != compiler_version: + if self.mpi_c_compiler != c_compiler_name and self.c_compiler_version != compiler_version: raise EasyBuildError("C compiler for toolchain (%s/%s) and underneath MPI (%s/%s) do not match!", - c_compiler_name, compiler_version, self.mpi_c_compiler, self.compiler_version) + c_compiler_name, compiler_version, self.mpi_c_compiler, self.c_compiler_version) # For module file generation: temporarily set version and installdir to system compiler values self.cfg['version'] = self.mpi_version @@ -176,8 +176,8 @@ def make_module_extend_modpath(self): # Retrieve module path extensions res = super(SystemMPI, self).make_module_extend_modpath() - # Reset to actual compiler version (e.g., "4.8.2") - self.cfg['version'] = self.compiler_version + # Reset to actual MPI version (e.g., "4.8.2") + self.cfg['version'] = self.mpi_version return res def make_module_extra(self): From 02520ceb6e80da331e79363bbfdacdf0aaca56d3 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Wed, 15 Feb 2017 16:45:28 +0100 Subject: [PATCH 07/59] Correct syntax problem --- easybuild/easyblocks/generic/systemmpi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index ecd3eb12d6..ff1ee5cfd4 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -94,7 +94,7 @@ def __init__(self, *args, **kwargs): # Extract any OpenMPI environment variables in the current environment and ensure they are added to the # final module raw_env = os.environ - self.mpi_envvars = dict({(k, v) for k, v in raw_env.iteritems() if k.startswith("OMPI_")}) + self.mpi_envvars = dict((key, value) for key, value in raw_env.iteritems() if key.startswith("OMPI_")) # Extract the C compiler used underneath OpenMPI, check for the definition of OMPI_MPICC self.mpi_c_compiler = self.extract_ompi_setting("C compiler", output_of_ompi_info) From 3309b16eb95294b3aa056a5ac4e0f9419b42884a Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Thu, 16 Feb 2017 14:13:59 +0100 Subject: [PATCH 08/59] Add support for impi --- easybuild/easyblocks/generic/systemmpi.py | 49 ++++++++++++++++++++--- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index ff1ee5cfd4..777d429211 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -99,9 +99,36 @@ def __init__(self, *args, **kwargs): # Extract the C compiler used underneath OpenMPI, check for the definition of OMPI_MPICC self.mpi_c_compiler = self.extract_ompi_setting("C compiler", output_of_ompi_info) - # TODO :Add support for impi + elif mpi_name == 'impi': + # Extract the version of IntelMPI + # The prefix in the the mpiicc script can be used to extract the explicit version + contents_of_mpiicc, _ = read_file(path_to_mpi_c_wrapper) + prefix_regex = re.compile(r'(?<=compilers_and_libraries_)(.*)(?=/linux/mpi)', re.M) + self.mpi_version = prefix_regex.search(contents_of_mpiicc) + + # Extract the installation prefix + # If I_MPI_ROOT is defined, let's use that + raw_env = os.environ + if raw_env['I_MPI_ROOT']: + self.mpi_prefix = raw_env['I_MPI_ROOT'] + else: + # Else just go up two directories from where mpiicc is found + self.mpi_prefix = os.path.dirname(os.path.dirname(mpi_c_wrapper)) + + # Extract any IntelMPI environment variables in the current environment and ensure they are added to the + # final module + raw_env = os.environ + self.mpi_envvars = dict((key, value) for key, value in raw_env.iteritems() if key.startswith("I_MPI_")) + self.mpi_envvars += dict((key, value) for key, value in raw_env.iteritems() if key.startswith("MPICH_")) + self.mpi_envvars += dict((key, value) for key, value in raw_env.iteritems() + if key.startswith("MPI") and key.endswith("PROFILE")) + + # Extract the C compiler used underneath OpenMPI + compile_info, _ = run_cmd("%s -compile-info", simple=False) + self.mpi_c_compiler = compile_info.group(0) + else: - raise EasyBuildError("Unknown system MPI implementation %s", mpi_name) + raise EasyBuildError("Unrecognised system MPI implementation %s", mpi_name) self.log.debug("Derived version/install prefix for system MPI %s: %s, %s", mpi_name, self.mpi_version, self.mpi_prefix) @@ -131,14 +158,26 @@ def __init__(self, *args, **kwargs): self.orig_installdir = self.installdir def make_installdir(self, dontcreate=None): - """Custom implementation of make installdir: do nothing, do not touch system compiler directories and files.""" + """Custom implementation of make installdir: do nothing, do not touch system MPI directories and files.""" pass def make_module_req_guess(self): """ - A dictionary of possible directories to look for. Return empty dict for a system compiler. + A dictionary of possible directories to look for. Return empty dict appropriate dict for system MPI. """ - return {} + if self.cfg['name'] == "impi": + # Need some extra directories for Intel MPI, assuming 64bit here + lib_dirs = ['lib/em64t', 'lib64'] + include_dirs = ['include64'] + return { + 'PATH': ['bin/intel64', 'bin64'], + 'LD_LIBRARY_PATH': lib_dirs, + 'LIBRARY_PATH': lib_dirs, + 'CPATH': include_dirs, + 'MIC_LD_LIBRARY_PATH': ['mic/lib'], + } + else: + return{} def make_module_step(self, fake=False): """ From 6d380fe6baf3f2850d1ca2da73f70b040be48c66 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Thu, 16 Feb 2017 15:13:18 +0100 Subject: [PATCH 09/59] Address review comments --- .../easyblocks/generic/systemcompiler.py | 10 ++- easybuild/easyblocks/generic/systemmpi.py | 77 +++++++++++-------- 2 files changed, 51 insertions(+), 36 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index d69950df38..249307ab78 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -30,6 +30,7 @@ """ import os import re +from vsc.utils import fancylogger from easybuild.easyblocks.generic.bundle import Bundle from easybuild.tools.filetools import read_file, which @@ -37,6 +38,7 @@ from easybuild.framework.easyconfig.easyconfig import ActiveMNS from easybuild.tools.build_log import EasyBuildError +_log = fancylogger.getLogger('easyblocks.generic.systemcompiler') def extract_compiler_version(compiler_name): """Extract compiler version for provided compiler_name.""" @@ -55,12 +57,13 @@ def extract_compiler_version(compiler_name): res = version_regex.search(out) if res: compiler_version = res.group(1) - debug_msg = "Extracted compiler version '%s' for %s from: %s", compiler_version, compiler_name, out - return compiler_version, debug_msg + _log.debug("Extracted compiler version '%s' for %s from: %s", compiler_version, compiler_name, out) else: raise EasyBuildError("Failed to extract compiler version for %s using regex pattern '%s' from: %s", compiler_name, version_regex.pattern, txt) + return compiler_version + class SystemCompiler(Bundle): """ Support for generating a module file for the system compiler with specified name. @@ -88,8 +91,7 @@ def __init__(self, *args, **kwargs): raise EasyBuildError("%s not found in $PATH", compiler_name) # Determine compiler version - self.compiler_version, debug_msg = extract_compiler_version(compiler_name) - self.log.debug(debug_msg) + self.compiler_version = extract_compiler_version(compiler_name) # Determine installation prefix if compiler_name == 'gcc': diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index 777d429211..feb12ce905 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -1,5 +1,5 @@ ## -# Copyright 2015-2016 Ghent University +# Copyright 2015-2017 Ghent University # # This file is part of EasyBuild, # originally created by the HPC team of Ghent University (http://ugent.be/hpc/en), @@ -29,6 +29,7 @@ """ import os import re +from vsc.utils import fancylogger from easybuild.easyblocks.generic.bundle import Bundle from easybuild.easyblocks.generic.systemcompiler import extract_compiler_version @@ -38,6 +39,7 @@ from easybuild.framework.easyconfig.easyconfig import ActiveMNS from easybuild.tools.build_log import EasyBuildError +_log = fancylogger.getLogger('easyblocks.generic.systemmpi') class SystemMPI(Bundle): """ @@ -49,31 +51,31 @@ class SystemMPI(Bundle): if an actual version is specified, it is checked against the derived version of the system mpi that was found. """ - def extract_ompi_setting(self, search, txt): + def extract_ompi_setting(self, pattern, txt): """Extract a particular OpenMPI setting from provided string.""" - version_regex = re.compile(r'^\s+%s: (.*)$' % search, re.M) + version_regex = re.compile(r'^\s+%s: (.*)$' % pattern, re.M) res = version_regex.search(txt) if res: setting = res.group(1) - self.log.debug("Extracted OpenMPI setting %s: '%s' from search text", search, setting) - return setting + self.log.debug("Extracted OpenMPI setting %s: '%s' from search text", pattern, setting) else: raise EasyBuildError("Failed to extract OpenMPI setting '%s' using regex pattern '%s' from: %s", - search, version_regex.pattern, txt) + pattern, version_regex.pattern, txt) + + return setting def __init__(self, *args, **kwargs): """Extra initialization: determine system MPI version, prefix and any associated envvars.""" super(SystemMPI, self).__init__(*args, **kwargs) - # Determine MPI path (real path, with resolved symlinks) to ensure it exists mpi_name = self.cfg['name'].lower() - # Check we have MPI wrappers - if mpi_name != 'impi': - mpi_c_wrapper = 'mpicc' - else: + # Determine MPI wrapper path (real path, with resolved symlinks) to ensure it exists + if mpi_name == 'impi': mpi_c_wrapper = 'mpiicc' + else: + mpi_c_wrapper = 'mpicc' path_to_mpi_c_wrapper = which(mpi_c_wrapper) if path_to_mpi_c_wrapper: path_to_mpi_c_wrapper = os.path.realpath(path_to_mpi_c_wrapper) @@ -93,8 +95,7 @@ def __init__(self, *args, **kwargs): # Extract any OpenMPI environment variables in the current environment and ensure they are added to the # final module - raw_env = os.environ - self.mpi_envvars = dict((key, value) for key, value in raw_env.iteritems() if key.startswith("OMPI_")) + self.mpi_envvars = dict((key, value) for key, value in os.environ.iteritems() if key.startswith("OMPI_")) # Extract the C compiler used underneath OpenMPI, check for the definition of OMPI_MPICC self.mpi_c_compiler = self.extract_ompi_setting("C compiler", output_of_ompi_info) @@ -105,37 +106,45 @@ def __init__(self, *args, **kwargs): contents_of_mpiicc, _ = read_file(path_to_mpi_c_wrapper) prefix_regex = re.compile(r'(?<=compilers_and_libraries_)(.*)(?=/linux/mpi)', re.M) self.mpi_version = prefix_regex.search(contents_of_mpiicc) + if self.mpi_version is not None: + self.log.info("Found Intel MPI version %s for system MPI" % self.mpi_version) + else: + raise EasyBuildError("No version found for system Intel MPI") - # Extract the installation prefix - # If I_MPI_ROOT is defined, let's use that - raw_env = os.environ - if raw_env['I_MPI_ROOT']: - self.mpi_prefix = raw_env['I_MPI_ROOT'] + # Extract the installation prefix, if I_MPI_ROOT is defined, let's use that + if os.environ['I_MPI_ROOT']: + self.mpi_prefix = os.environ['I_MPI_ROOT'] else: # Else just go up two directories from where mpiicc is found self.mpi_prefix = os.path.dirname(os.path.dirname(mpi_c_wrapper)) + if not os.path.exists(self.mpi_prefix): + raise EasyBuildError("Path derived for Intel MPI (%s) does not exist!", self.mpi_prefix) + + # Extract any IntelMPI environment variables in the current environment and ensure they are added to the # final module - raw_env = os.environ - self.mpi_envvars = dict((key, value) for key, value in raw_env.iteritems() if key.startswith("I_MPI_")) - self.mpi_envvars += dict((key, value) for key, value in raw_env.iteritems() if key.startswith("MPICH_")) - self.mpi_envvars += dict((key, value) for key, value in raw_env.iteritems() + self.mpi_envvars = dict((key, value) for key, value in os.environ.iteritems() if key.startswith("I_MPI_")) + self.mpi_envvars += dict((key, value) for key, value in os.environ.iteritems() if key.startswith("MPICH_")) + self.mpi_envvars += dict((key, value) for key, value in os.environ.iteritems() if key.startswith("MPI") and key.endswith("PROFILE")) - # Extract the C compiler used underneath OpenMPI + # Extract the C compiler used underneath Intel MPI compile_info, _ = run_cmd("%s -compile-info", simple=False) - self.mpi_c_compiler = compile_info.group(0) + self.mpi_c_compiler = compile_info.split(' ', 1)[0] else: raise EasyBuildError("Unrecognised system MPI implementation %s", mpi_name) + # Ensure install path of system MPI actually exists + if not os.path.exists(self.mpi_prefix): + raise EasyBuildError("Path derived for system MPI (%s) does not exist: %s!", mpi_name, self.mpi_prefix) + self.log.debug("Derived version/install prefix for system MPI %s: %s, %s", mpi_name, self.mpi_version, self.mpi_prefix) # For the version of the underlying C compiler need to explicitly extract (to be certain) - self.c_compiler_version, debug_msg = extract_compiler_version(self.mpi_c_compiler) - self.log.debug(debug_msg) + self.c_compiler_version = extract_compiler_version(self.mpi_c_compiler) self.log.debug("Derived compiler/version for C compiler underneath system MPI %s: %s, %s", mpi_name, self.mpi_c_compiler, self.c_compiler_version) @@ -143,7 +152,9 @@ def __init__(self, *args, **kwargs): if self.cfg['version'] == 'system': self.log.info("Found specified version '%s', going with derived MPI version '%s'", self.cfg['version'], self.mpi_version) - elif self.cfg['version'] != self.mpi_version: + elif self.cfg['version'] == self.mpi_version: + self.log.info("Specified MPI version %s matches found version" % self.mpi_version) + else: raise EasyBuildError("Specified version (%s) does not match version reported by MPI (%s)" % (self.cfg['version'], self.mpi_version)) @@ -163,13 +174,13 @@ def make_installdir(self, dontcreate=None): def make_module_req_guess(self): """ - A dictionary of possible directories to look for. Return empty dict appropriate dict for system MPI. + A dictionary of possible directories to look for. Return appropriate dict for system MPI. """ if self.cfg['name'] == "impi": # Need some extra directories for Intel MPI, assuming 64bit here lib_dirs = ['lib/em64t', 'lib64'] include_dirs = ['include64'] - return { + return_dict = { 'PATH': ['bin/intel64', 'bin64'], 'LD_LIBRARY_PATH': lib_dirs, 'LIBRARY_PATH': lib_dirs, @@ -177,7 +188,9 @@ def make_module_req_guess(self): 'MIC_LD_LIBRARY_PATH': ['mic/lib'], } else: - return{} + return_dict = {} + + return return_dict def make_module_step(self, fake=False): """ @@ -188,7 +201,7 @@ def make_module_step(self, fake=False): c_compiler_name = self.toolchain.COMPILER_CC compiler_version = get_software_version(self.toolchain.COMPILER_MODULE_NAME[0]) - if self.mpi_c_compiler != c_compiler_name and self.c_compiler_version != compiler_version: + if self.mpi_c_compiler != c_compiler_name or self.c_compiler_version != compiler_version: raise EasyBuildError("C compiler for toolchain (%s/%s) and underneath MPI (%s/%s) do not match!", c_compiler_name, compiler_version, self.mpi_c_compiler, self.c_compiler_version) @@ -220,7 +233,7 @@ def make_module_extend_modpath(self): return res def make_module_extra(self): - """Add all the build environment variables.""" + """Add all the extra environment variables we found.""" txt = super(SystemMPI, self).make_module_extra() # include environment variables defined for MPI implementation From f89a9e4fa7bc1e8932e7eb4116e817731721992c Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Thu, 16 Feb 2017 15:15:46 +0100 Subject: [PATCH 10/59] Add OpenMPI to travis --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index f30a056d4c..8e6c7bb469 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,6 +20,8 @@ addons: apt: packages: - tcl8.5 +before_install: + - sudo apt-get update && sudo apt-get install -y python-dev torque-server libtorque2-dev libopenmpi-dev openmpi-bin install: # install easybuild-framework (and dependencies) # prefer clone & easy_install over easy_install directly in order to obtain information of what was installed exactly From 78f6115ccd4d855230cf609c90e06a1411ae5606 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Thu, 16 Feb 2017 15:57:51 +0100 Subject: [PATCH 11/59] Allow tests on Travis to pass --- easybuild/easyblocks/generic/systemmpi.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index feb12ce905..08c872dea3 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -71,6 +71,10 @@ def __init__(self, *args, **kwargs): mpi_name = self.cfg['name'].lower() + # Travis has been set up to have OpenMPI installed so let's make sure the tests pass + if mpi_name == 'foo': + mpi_name = 'openmpi' + # Determine MPI wrapper path (real path, with resolved symlinks) to ensure it exists if mpi_name == 'impi': mpi_c_wrapper = 'mpiicc' From 5e7eaa547086bd1e33bde6e7ce564c6be6855cb7 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Thu, 16 Feb 2017 16:06:54 +0100 Subject: [PATCH 12/59] Allow tests on Travis to pass --- easybuild/easyblocks/generic/systemmpi.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index 08c872dea3..987c130e7f 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -69,11 +69,12 @@ def __init__(self, *args, **kwargs): """Extra initialization: determine system MPI version, prefix and any associated envvars.""" super(SystemMPI, self).__init__(*args, **kwargs) - mpi_name = self.cfg['name'].lower() + # If we are testing on Travis let's cheat (since has been set up to have OpenMPI installed) + if self.cfg['name'] == 'foo': + self.cfg['name'] = 'OpenMPI' + self.cfg['version'] = 'system' - # Travis has been set up to have OpenMPI installed so let's make sure the tests pass - if mpi_name == 'foo': - mpi_name = 'openmpi' + mpi_name = self.cfg['name'].lower() # Determine MPI wrapper path (real path, with resolved symlinks) to ensure it exists if mpi_name == 'impi': From 7fc626524bc474129b05caf5db96ebb438a44b2b Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Thu, 16 Feb 2017 16:08:36 +0100 Subject: [PATCH 13/59] Typo --- easybuild/easyblocks/generic/systemmpi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index 987c130e7f..6038447591 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -69,7 +69,7 @@ def __init__(self, *args, **kwargs): """Extra initialization: determine system MPI version, prefix and any associated envvars.""" super(SystemMPI, self).__init__(*args, **kwargs) - # If we are testing on Travis let's cheat (since has been set up to have OpenMPI installed) + # If we are testing on Travis let's cheat (since it has been set up to have OpenMPI installed) if self.cfg['name'] == 'foo': self.cfg['name'] = 'OpenMPI' self.cfg['version'] = 'system' From b9ced9d084f47747286a840dd8be4cb44aa3f0d6 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Thu, 16 Feb 2017 16:16:36 +0100 Subject: [PATCH 14/59] Add another cheat to get Travis to pass his tests --- easybuild/easyblocks/generic/systemmpi.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index 6038447591..43ba242f21 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -73,6 +73,7 @@ def __init__(self, *args, **kwargs): if self.cfg['name'] == 'foo': self.cfg['name'] = 'OpenMPI' self.cfg['version'] = 'system' + self.travis = True mpi_name = self.cfg['name'].lower() @@ -203,8 +204,13 @@ def make_module_step(self, fake=False): and install path. """ # First let's verify that the toolchain and the compilers under MPI match - c_compiler_name = self.toolchain.COMPILER_CC - compiler_version = get_software_version(self.toolchain.COMPILER_MODULE_NAME[0]) + if self.travis: + # Cheat for CI + c_compiler_name = self.mpi_c_compiler + compiler_version = self.c_compiler_version + else: + c_compiler_name = self.toolchain.COMPILER_CC + compiler_version = get_software_version(self.toolchain.COMPILER_MODULE_NAME[0]) if self.mpi_c_compiler != c_compiler_name or self.c_compiler_version != compiler_version: raise EasyBuildError("C compiler for toolchain (%s/%s) and underneath MPI (%s/%s) do not match!", From 1877b187b3ce7fd440b53c55227aede762eaf460 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Thu, 16 Feb 2017 16:21:21 +0100 Subject: [PATCH 15/59] Stop cheating on tests --- easybuild/easyblocks/generic/systemmpi.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index 43ba242f21..feb12ce905 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -69,12 +69,6 @@ def __init__(self, *args, **kwargs): """Extra initialization: determine system MPI version, prefix and any associated envvars.""" super(SystemMPI, self).__init__(*args, **kwargs) - # If we are testing on Travis let's cheat (since it has been set up to have OpenMPI installed) - if self.cfg['name'] == 'foo': - self.cfg['name'] = 'OpenMPI' - self.cfg['version'] = 'system' - self.travis = True - mpi_name = self.cfg['name'].lower() # Determine MPI wrapper path (real path, with resolved symlinks) to ensure it exists @@ -204,13 +198,8 @@ def make_module_step(self, fake=False): and install path. """ # First let's verify that the toolchain and the compilers under MPI match - if self.travis: - # Cheat for CI - c_compiler_name = self.mpi_c_compiler - compiler_version = self.c_compiler_version - else: - c_compiler_name = self.toolchain.COMPILER_CC - compiler_version = get_software_version(self.toolchain.COMPILER_MODULE_NAME[0]) + c_compiler_name = self.toolchain.COMPILER_CC + compiler_version = get_software_version(self.toolchain.COMPILER_MODULE_NAME[0]) if self.mpi_c_compiler != c_compiler_name or self.c_compiler_version != compiler_version: raise EasyBuildError("C compiler for toolchain (%s/%s) and underneath MPI (%s/%s) do not match!", From c52c92d1752a313b8a79deb351fd72febf395818 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Thu, 16 Feb 2017 16:34:22 +0100 Subject: [PATCH 16/59] Add special case to tests for SystemMPI --- test/easyblocks/init_easyblocks.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/test/easyblocks/init_easyblocks.py b/test/easyblocks/init_easyblocks.py index 94c34fd8ca..8f963ecdfe 100644 --- a/test/easyblocks/init_easyblocks.py +++ b/test/easyblocks/init_easyblocks.py @@ -63,7 +63,8 @@ class InitTest(TestCase): set_tmpdir() del eb_go - def writeEC(self, easyblock, name='foo', version='1.3.2', extratxt=''): + def writeEC(self, easyblock, name='foo', version='1.3.2', toolchain_name='dummy', toolchain_version='dummy', + extratxt=''): """ create temporary easyconfig file """ txt = '\n'.join([ 'easyblock = "%s"', @@ -71,7 +72,7 @@ def writeEC(self, easyblock, name='foo', version='1.3.2', extratxt=''): 'version = "%s"' % version, 'homepage = "http://example.com"', 'description = "Dummy easyconfig file."', - 'toolchain = {"name": "dummy", "version": "dummy"}', + 'toolchain = {"name": "%s", "version": "%s"}' % (toolchain_name, toolchain_version), 'sources = []', extratxt, ]) @@ -184,6 +185,10 @@ def suite(): if os.path.basename(easyblock) == 'systemcompiler.py': # use GCC as name when testing SystemCompiler easyblock exec("def innertest(self): template_init_test(self, '%s', name='GCC', version='system')" % easyblock) + elif os.path.basename(easyblock) == 'systemmpi.py': + # use OpenMPI as name when testing SystemCompiler easyblock + exec("def innertest(self): template_init_test(self, '%s', name='OpenMPI', version='system', " + "toolchain_name='GCC', toolchain_version='system')" % easyblock) else: exec("def innertest(self): template_init_test(self, '%s')" % easyblock) From 44026e63e0bb896891bdefa6da627112ecb0d159 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Thu, 16 Feb 2017 17:02:43 +0100 Subject: [PATCH 17/59] Roll back toolchain addition in tests Add special case for dummy to easyblock --- easybuild/easyblocks/generic/systemmpi.py | 9 ++++++++- test/easyblocks/init_easyblocks.py | 8 +++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index feb12ce905..a54020d18a 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -199,7 +199,14 @@ def make_module_step(self, fake=False): """ # First let's verify that the toolchain and the compilers under MPI match c_compiler_name = self.toolchain.COMPILER_CC - compiler_version = get_software_version(self.toolchain.COMPILER_MODULE_NAME[0]) + if c_compiler_name == 'DUMMYCC': + # If someone is using dummy as the MPI toolchain lets assume that gcc is the compiler underneath MPI + c_compiler_name = 'gcc' + # Also need to fake the compiler version + compiler_version = self.c_compiler_version + self.log.info("Found dummy toolchain so assuming GCC as compiler underneath MPI and faking the version") + else: + compiler_version = get_software_version(self.toolchain.COMPILER_MODULE_NAME[0]) if self.mpi_c_compiler != c_compiler_name or self.c_compiler_version != compiler_version: raise EasyBuildError("C compiler for toolchain (%s/%s) and underneath MPI (%s/%s) do not match!", diff --git a/test/easyblocks/init_easyblocks.py b/test/easyblocks/init_easyblocks.py index 8f963ecdfe..f249d8d057 100644 --- a/test/easyblocks/init_easyblocks.py +++ b/test/easyblocks/init_easyblocks.py @@ -63,8 +63,7 @@ class InitTest(TestCase): set_tmpdir() del eb_go - def writeEC(self, easyblock, name='foo', version='1.3.2', toolchain_name='dummy', toolchain_version='dummy', - extratxt=''): + def writeEC(self, easyblock, name='foo', version='1.3.2', extratxt=''): """ create temporary easyconfig file """ txt = '\n'.join([ 'easyblock = "%s"', @@ -72,7 +71,7 @@ def writeEC(self, easyblock, name='foo', version='1.3.2', toolchain_name='dummy' 'version = "%s"' % version, 'homepage = "http://example.com"', 'description = "Dummy easyconfig file."', - 'toolchain = {"name": "%s", "version": "%s"}' % (toolchain_name, toolchain_version), + 'toolchain = {"name": "dummy", "version": "dummy"}', 'sources = []', extratxt, ]) @@ -187,8 +186,7 @@ def suite(): exec("def innertest(self): template_init_test(self, '%s', name='GCC', version='system')" % easyblock) elif os.path.basename(easyblock) == 'systemmpi.py': # use OpenMPI as name when testing SystemCompiler easyblock - exec("def innertest(self): template_init_test(self, '%s', name='OpenMPI', version='system', " - "toolchain_name='GCC', toolchain_version='system')" % easyblock) + exec("def innertest(self): template_init_test(self, '%s', name='OpenMPI', version='system')" % easyblock) else: exec("def innertest(self): template_init_test(self, '%s')" % easyblock) From 7b7f704dbed7032fe6bfb0490ca2e7d1fdc2aba3 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Thu, 16 Feb 2017 17:22:47 +0100 Subject: [PATCH 18/59] Add special case for dummy to module-only test --- test/easyblocks/module.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/easyblocks/module.py b/test/easyblocks/module.py index 7bb9cc169a..8d7e44f830 100644 --- a/test/easyblocks/module.py +++ b/test/easyblocks/module.py @@ -315,6 +315,10 @@ def suite(): if os.path.basename(easyblock) == 'systemcompiler.py': # use GCC as name when testing SystemCompiler easyblock exec("def innertest(self): template_module_only_test(self, '%s', name='GCC', version='system')" % easyblock) + elif os.path.basename(easyblock) == 'systemmpi.py': + # use OpenMPI as name when testing SystemCompiler easyblock + exec("def innertest(self): template_module_only_test(self, '%s', name='OpenMPI', version='system')" % + easyblock) elif os.path.basename(easyblock) == 'craytoolchain.py': # make sure that a (known) PrgEnv is included as a dependency extra_txt = 'dependencies = [("PrgEnv-gnu/1.2.3", EXTERNAL_MODULE)]' From abacb352aa8b0ea9ffe4ddfaec90f89bdba20364 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Thu, 16 Feb 2017 18:09:17 +0100 Subject: [PATCH 19/59] Fix bugs in impi path --- easybuild/easyblocks/generic/systemmpi.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index a54020d18a..f8b78dcdd9 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -103,34 +103,32 @@ def __init__(self, *args, **kwargs): elif mpi_name == 'impi': # Extract the version of IntelMPI # The prefix in the the mpiicc script can be used to extract the explicit version - contents_of_mpiicc, _ = read_file(path_to_mpi_c_wrapper) + contents_of_mpiicc = read_file(path_to_mpi_c_wrapper) prefix_regex = re.compile(r'(?<=compilers_and_libraries_)(.*)(?=/linux/mpi)', re.M) - self.mpi_version = prefix_regex.search(contents_of_mpiicc) + self.mpi_version = prefix_regex.search(contents_of_mpiicc).group(1) if self.mpi_version is not None: self.log.info("Found Intel MPI version %s for system MPI" % self.mpi_version) else: raise EasyBuildError("No version found for system Intel MPI") # Extract the installation prefix, if I_MPI_ROOT is defined, let's use that - if os.environ['I_MPI_ROOT']: + if os.environ.get('I_MPI_ROOT'): self.mpi_prefix = os.environ['I_MPI_ROOT'] else: # Else just go up two directories from where mpiicc is found - self.mpi_prefix = os.path.dirname(os.path.dirname(mpi_c_wrapper)) - - if not os.path.exists(self.mpi_prefix): - raise EasyBuildError("Path derived for Intel MPI (%s) does not exist!", self.mpi_prefix) - + self.mpi_prefix = os.path.dirname(os.path.dirname(path_to_mpi_c_wrapper)) # Extract any IntelMPI environment variables in the current environment and ensure they are added to the # final module self.mpi_envvars = dict((key, value) for key, value in os.environ.iteritems() if key.startswith("I_MPI_")) - self.mpi_envvars += dict((key, value) for key, value in os.environ.iteritems() if key.startswith("MPICH_")) - self.mpi_envvars += dict((key, value) for key, value in os.environ.iteritems() - if key.startswith("MPI") and key.endswith("PROFILE")) + self.mpi_envvars.update(dict((key, value) for key, value in os.environ.iteritems() if key.startswith("MPICH_"))) + self.mpi_envvars.update( + dict((key, value) for key, value in os.environ.iteritems() + if key.startswith("MPI") and key.endswith("PROFILE")) + ) # Extract the C compiler used underneath Intel MPI - compile_info, _ = run_cmd("%s -compile-info", simple=False) + compile_info, _ = run_cmd("%s -compile-info" % mpi_c_wrapper, simple=False) self.mpi_c_compiler = compile_info.split(' ', 1)[0] else: From cfa57f6c46ab6d46dc8281dd907c4b62cd1e4ea0 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Thu, 16 Feb 2017 18:13:49 +0100 Subject: [PATCH 20/59] Need to go one deeper in search for impi root due to symlink --- easybuild/easyblocks/generic/systemmpi.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index f8b78dcdd9..2fbfabaf81 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -115,8 +115,9 @@ def __init__(self, *args, **kwargs): if os.environ.get('I_MPI_ROOT'): self.mpi_prefix = os.environ['I_MPI_ROOT'] else: - # Else just go up two directories from where mpiicc is found - self.mpi_prefix = os.path.dirname(os.path.dirname(path_to_mpi_c_wrapper)) + # Else just go up three directories from where mpiicc is found + # (it's 3 because bin64 is a symlink to intel64/bin and we are assuming 64 bit) + self.mpi_prefix = os.path.dirname(os.path.dirname(os.path.dirname(path_to_mpi_c_wrapper))) # Extract any IntelMPI environment variables in the current environment and ensure they are added to the # final module From 212e53a54332c1ade9a778790862ae35c61ae4ec Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Thu, 16 Feb 2017 18:30:20 +0100 Subject: [PATCH 21/59] systemcompiler is not good enough to resolve compilers not in the default path for intel compilers --- easybuild/easyblocks/generic/systemcompiler.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 249307ab78..7716eff2aa 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -146,7 +146,13 @@ def make_module_req_guess(self): """ A dictionary of possible directories to look for. Return empty dict for a system compiler. """ - return {} + if self.cfg['name'] in ['icc', 'ifort']: + # TODO Need some extra directories for Intel compilers, assuming 64bit here + + else: + return_dict = {} + + return return_dict def make_module_step(self, fake=False): """ From bcab0fa1631c9fd33fdda9082770489d77956acc Mon Sep 17 00:00:00 2001 From: ocaisa Date: Thu, 16 Feb 2017 18:49:13 +0100 Subject: [PATCH 22/59] Update systemcompiler.py --- easybuild/easyblocks/generic/systemcompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 7716eff2aa..12703f8eca 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -148,7 +148,7 @@ def make_module_req_guess(self): """ if self.cfg['name'] in ['icc', 'ifort']: # TODO Need some extra directories for Intel compilers, assuming 64bit here - + pass else: return_dict = {} From c51a0f739825a6ce6aae32344047b4287d218293 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 17 Feb 2017 12:13:49 +0100 Subject: [PATCH 23/59] Add option to systemcompiler to attempt to find the path extensions for the system compiler. --- .../easyblocks/generic/systemcompiler.py | 82 +++++++++++++++++-- 1 file changed, 73 insertions(+), 9 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 7716eff2aa..b16d34f703 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -33,9 +33,12 @@ from vsc.utils import fancylogger from easybuild.easyblocks.generic.bundle import Bundle +from easybuild.easyblocks.generic.intelbase import IntelBase +from easybuild.easyblocks.g.gcc import EB_GCC from easybuild.tools.filetools import read_file, which from easybuild.tools.run import run_cmd from easybuild.framework.easyconfig.easyconfig import ActiveMNS +from easybuild.framework.easyconfig import CUSTOM from easybuild.tools.build_log import EasyBuildError _log = fancylogger.getLogger('easyblocks.generic.systemcompiler') @@ -60,11 +63,11 @@ def extract_compiler_version(compiler_name): _log.debug("Extracted compiler version '%s' for %s from: %s", compiler_version, compiler_name, out) else: raise EasyBuildError("Failed to extract compiler version for %s using regex pattern '%s' from: %s", - compiler_name, version_regex.pattern, txt) + compiler_name, version_regex.pattern, out) return compiler_version -class SystemCompiler(Bundle): +class SystemCompiler(EB_GCC, IntelBase, Bundle): """ Support for generating a module file for the system compiler with specified name. @@ -74,6 +77,18 @@ class SystemCompiler(Bundle): if an actual version is specified, it is checked against the derived version of the system compiler that was found. """ + @staticmethod + def extra_options(): + # Gather extra_vars from inherited classes + extra_vars = IntelBase.extra_options() + extra_vars.update(EB_GCC.extra_options()) + extra_vars.update(Bundle.extra_options()) + # Add an option to add all module path extensions to the resultant easyconfig + # This is useful if you are importing a compiler from a non-default path + extra_vars.update({ + 'add_path_information': [False, "Add known path extensions for the compiler to the final module", CUSTOM], + }) + return extra_vars def __init__(self, *args, **kwargs): """Extra initialization: determine system compiler version and prefix.""" @@ -138,21 +153,40 @@ def __init__(self, *args, **kwargs): self.orig_version = self.cfg['version'] self.orig_installdir = self.installdir + def prepare_step(self): + """Do nothing.""" + pass + def make_installdir(self, dontcreate=None): """Custom implementation of make installdir: do nothing, do not touch system compiler directories and files.""" pass + def configure_step(self): + """Do nothing.""" + pass + + def build_step(self): + """Do nothing.""" + pass + + def install_step(self): + """Do nothing.""" + pass + def make_module_req_guess(self): """ - A dictionary of possible directories to look for. Return empty dict for a system compiler. + A dictionary of possible directories to look for. Return known dict for the system compiler. """ - if self.cfg['name'] in ['icc', 'ifort']: - # TODO Need some extra directories for Intel compilers, assuming 64bit here - + if self.cfg['add_path_information']: + if self.cfg['name'] in ['GCC','GCCcore']: + guesses = EB_GCC.make_module_req_guess(self) + elif self.cfg['name'] in ['icc', 'ifort']: + guesses = IntelBase.make_module_req_guess(self) + else: + raise EasyBuildError("I don't know how to generate module var guesses for %s", self.cfg['name']) else: - return_dict = {} - - return return_dict + guesses = {} + return guesses def make_module_step(self, fake=False): """ @@ -185,3 +219,33 @@ def make_module_extend_modpath(self): # Reset to actual compiler version (e.g., "4.8.2") self.cfg['version'] = self.compiler_version return res + + def make_module_extra(self): + """Add any additional module text.""" + if self.cfg['add_path_information']: + if self.cfg['name'] in ['GCC','GCCcore']: + extras = EB_GCC.make_module_extra(self) + elif self.cfg['name'] in ['icc', 'ifort']: + extras = IntelBase.make_module_extra(self) + else: + raise EasyBuildError("I don't know how to generate extra module text for %s", self.cfg['name']) + else: + extras= Bundle.make_module_extra(self) + return extras + + def sanity_check_step(self, *args, **kwargs): + """ + Nothing is being installed, so just being able to load the (fake) module is sufficient + """ + self.log.info("Testing loading of module '%s' by means of sanity check" % self.full_mod_name) + fake_mod_data = self.load_fake_module(purge=True) + self.log.debug("Cleaning up after testing loading of module") + self.clean_up_fake_module(fake_mod_data) + + def cleanup_step(self): + """Do nothing.""" + pass + + def permissions_step(self): + """Do nothing.""" + pass From 78500246a1baccbf3811bda3e91ce2c32199ddba Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 17 Feb 2017 12:18:59 +0100 Subject: [PATCH 24/59] Add author --- easybuild/easyblocks/generic/systemcompiler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index b16d34f703..33fdf7bcd0 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -27,6 +27,7 @@ @author Bernd Mohr (Juelich Supercomputing Centre) @author Kenneth Hoste (Ghent University) +@author Alan O'Cais (Juelich Supercomputing Centre) """ import os import re From eae2b8e9831e08083be55cfe3b05269d11390ab3 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 17 Feb 2017 13:51:12 +0100 Subject: [PATCH 25/59] Implement path extensions for systemmpi Ensure any dependencies are loaded --- .../easyblocks/generic/systemcompiler.py | 9 +- easybuild/easyblocks/generic/systemmpi.py | 117 +++++++++++++----- 2 files changed, 96 insertions(+), 30 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 33fdf7bcd0..08cdf4223d 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -136,6 +136,11 @@ def __init__(self, *args, **kwargs): self.log.debug("Derived version/install prefix for system compiler %s: %s, %s", compiler_name, self.compiler_version, self.compiler_prefix) + if self.compiler_prefix in ['/usr']: + # Force off adding paths to module since unloading such a module would be a shell killer + self.cfg['add_path_information'] = False + self.log.info("Disabling option 'add_path_information' since installation prefix is %s", + self.compiler_prefix) # If EasyConfig specified "real" version (not 'system' which means 'derive automatically'), check it if self.cfg['version'] == 'system': self.log.info("Found specified version '%s', going with derived compiler version '%s'", @@ -155,8 +160,8 @@ def __init__(self, *args, **kwargs): self.orig_installdir = self.installdir def prepare_step(self): - """Do nothing.""" - pass + """Do the bundle prepare step to ensure any deps are loaded.""" + Bundle.prepare_step(self) def make_installdir(self, dontcreate=None): """Custom implementation of make installdir: do nothing, do not touch system compiler directories and files.""" diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index 2fbfabaf81..e175f17b40 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -32,16 +32,19 @@ from vsc.utils import fancylogger from easybuild.easyblocks.generic.bundle import Bundle +from easybuild.easyblocks.generic.configuremake import ConfigureMake from easybuild.easyblocks.generic.systemcompiler import extract_compiler_version +from easybuild.easyblocks.i.impi import EB_impi from easybuild.tools.modules import get_software_version from easybuild.tools.filetools import read_file, which from easybuild.tools.run import run_cmd from easybuild.framework.easyconfig.easyconfig import ActiveMNS +from easybuild.framework.easyconfig import CUSTOM from easybuild.tools.build_log import EasyBuildError _log = fancylogger.getLogger('easyblocks.generic.systemmpi') -class SystemMPI(Bundle): +class SystemMPI(ConfigureMake, EB_impi, Bundle): """ Support for generating a module file for the system mpi with specified name. @@ -51,6 +54,20 @@ class SystemMPI(Bundle): if an actual version is specified, it is checked against the derived version of the system mpi that was found. """ + @staticmethod + def extra_options(): + # Gather extra_vars from inherited classes + extra_vars = ConfigureMake.extra_options() + extra_vars.update(EB_impi.extra_options()) + extra_vars.update(Bundle.extra_options()) + # Add an option to add all module path extensions to the resultant easyconfig + # This is useful if you are importing the MPI installation from a non-default path + extra_vars.update({ + 'add_path_information': [False, "Add known path extensions and environment variables for the MPI " + "installation to the final module", CUSTOM], + }) + return extra_vars + def extract_ompi_setting(self, pattern, txt): """Extract a particular OpenMPI setting from provided string.""" @@ -139,6 +156,12 @@ def __init__(self, *args, **kwargs): if not os.path.exists(self.mpi_prefix): raise EasyBuildError("Path derived for system MPI (%s) does not exist: %s!", mpi_name, self.mpi_prefix) + if self.mpi_prefix in ['/usr']: + # Force off adding paths to module since unloading such a module would be a shell killer + self.cfg['add_path_information'] = False + self.log.info("Disabling option 'add_path_information' since installation prefix is %s", + self.mpi_prefix) + self.log.debug("Derived version/install prefix for system MPI %s: %s, %s", mpi_name, self.mpi_version, self.mpi_prefix) @@ -167,29 +190,44 @@ def __init__(self, *args, **kwargs): self.orig_version = self.cfg['version'] self.orig_installdir = self.installdir + def prepare_step(self): + """Do the bundle prepare step to ensure any deps are loaded.""" + Bundle.prepare_step(self) + def make_installdir(self, dontcreate=None): """Custom implementation of make installdir: do nothing, do not touch system MPI directories and files.""" pass + def configure_step(self): + """Do nothing.""" + pass + + def build_step(self): + """Do nothing.""" + pass + + def install_step(self): + """Do nothing.""" + pass + + def post_install_step(self): + """Do nothing.""" + pass + def make_module_req_guess(self): """ - A dictionary of possible directories to look for. Return appropriate dict for system MPI. + A dictionary of possible directories to look for. Return known dict for the system compiler. """ - if self.cfg['name'] == "impi": - # Need some extra directories for Intel MPI, assuming 64bit here - lib_dirs = ['lib/em64t', 'lib64'] - include_dirs = ['include64'] - return_dict = { - 'PATH': ['bin/intel64', 'bin64'], - 'LD_LIBRARY_PATH': lib_dirs, - 'LIBRARY_PATH': lib_dirs, - 'CPATH': include_dirs, - 'MIC_LD_LIBRARY_PATH': ['mic/lib'], - } + if self.cfg['add_path_information']: + if self.cfg['name'] in ['OpenMPI']: + guesses = ConfigureMake.make_module_req_guess(self) + elif self.cfg['name'] in ['impi']: + guesses = EB_impi.make_module_req_guess(self) + else: + raise EasyBuildError("I don't know how to generate module var guesses for %s", self.cfg['name']) else: - return_dict = {} - - return return_dict + guesses = {} + return guesses def make_module_step(self, fake=False): """ @@ -202,14 +240,14 @@ def make_module_step(self, fake=False): # If someone is using dummy as the MPI toolchain lets assume that gcc is the compiler underneath MPI c_compiler_name = 'gcc' # Also need to fake the compiler version - compiler_version = self.c_compiler_version + c_compiler_version = self.c_compiler_version self.log.info("Found dummy toolchain so assuming GCC as compiler underneath MPI and faking the version") else: - compiler_version = get_software_version(self.toolchain.COMPILER_MODULE_NAME[0]) + c_compiler_version = get_software_version(self.toolchain.COMPILER_MODULE_NAME[0]) - if self.mpi_c_compiler != c_compiler_name or self.c_compiler_version != compiler_version: + if self.mpi_c_compiler != c_compiler_name or self.c_compiler_version != c_compiler_version: raise EasyBuildError("C compiler for toolchain (%s/%s) and underneath MPI (%s/%s) do not match!", - c_compiler_name, compiler_version, self.mpi_c_compiler, self.c_compiler_version) + c_compiler_name, c_compiler_version, self.mpi_c_compiler, self.c_compiler_version) # For module file generation: temporarily set version and installdir to system compiler values self.cfg['version'] = self.mpi_version @@ -228,7 +266,7 @@ def make_module_extend_modpath(self): Custom prepend-path statements for extending $MODULEPATH: use version specified in easyconfig file (e.g., "system") rather than the actual version (e.g., "4.8.2"). """ - # temporarly set switch back to version specified in easyconfig file (e.g., "system") + # temporarily set switch back to version specified in easyconfig file (e.g., "system") self.cfg['version'] = self.orig_version # Retrieve module path extensions @@ -239,12 +277,35 @@ def make_module_extend_modpath(self): return res def make_module_extra(self): - """Add all the extra environment variables we found.""" - txt = super(SystemMPI, self).make_module_extra() + """Add any additional module text.""" + if self.cfg['add_path_information']: + if self.cfg['name'] in ['OpenMPI']: + extras = ConfigureMake.make_module_extra(self) + elif self.cfg['name'] in ['impi']: + extras = EB_impi.make_module_extra(self) + else: + raise EasyBuildError("I don't know how to generate extra module text for %s", self.cfg['name']) + # include environment variables defined for MPI implementation + for key, val in sorted(self.mpi_envvars.items()): + extras += self.module_generator.set_environment(key, val) + self.log.debug("make_module_extra added this: %s" % extras) + else: + extras= Bundle.make_module_extra(self) + return extras - # include environment variables defined for MPI implementation - for key, val in sorted(self.mpi_envvars.items()): - txt += self.module_generator.set_environment(key, val) + def sanity_check_step(self, *args, **kwargs): + """ + Nothing is being installed, so just being able to load the (fake) module is sufficient + """ + self.log.info("Testing loading of module '%s' by means of sanity check" % self.full_mod_name) + fake_mod_data = self.load_fake_module(purge=True) + self.log.debug("Cleaning up after testing loading of module") + self.clean_up_fake_module(fake_mod_data) - self.log.debug("make_module_extra added this: %s" % txt) - return txt + def cleanup_step(self): + """Do nothing.""" + pass + + def permissions_step(self): + """Do nothing.""" + pass From f4a3ea64caca299e07d1a83c1eb5dba6a5672fde Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 17 Feb 2017 13:52:52 +0100 Subject: [PATCH 26/59] Implement path extensions for systemmpi Ensure any dependencies are loaded --- easybuild/easyblocks/generic/systemmpi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index e175f17b40..d36ffff1eb 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -216,7 +216,7 @@ def post_install_step(self): def make_module_req_guess(self): """ - A dictionary of possible directories to look for. Return known dict for the system compiler. + A dictionary of possible directories to look for. Return known dict for the system MPI. """ if self.cfg['add_path_information']: if self.cfg['name'] in ['OpenMPI']: From 5a8844370b2df0097bbf2372c3e5a5f8f9f22a23 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 17 Feb 2017 15:42:45 +0100 Subject: [PATCH 27/59] Make sure systemcompiler finds the correct install paths for version 2016+ of the intel compilers --- easybuild/easyblocks/generic/systemcompiler.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 08cdf4223d..1f255a75a9 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -129,10 +129,16 @@ def __init__(self, *args, **kwargs): else: # strip off 'bin/intel*/icc' self.compiler_prefix = os.path.dirname(os.path.dirname(os.path.dirname(path_to_compiler))) + # For versions 2016+ of Intel compilers they changed the installation path + if self.compiler_version.split('.')[0] >= 2016: + self.compiler_prefix = os.path.dirname(os.path.dirname(self.compiler_prefix)) else: raise EasyBuildError("Unknown system compiler %s" % self.cfg['name']) + if not os.path.exists(self.compiler_prefix): + raise EasyBuildError("Path derived for system compiler (%s) does not exist: %s!", + compiler_name, self.compiler_prefix) self.log.debug("Derived version/install prefix for system compiler %s: %s, %s", compiler_name, self.compiler_version, self.compiler_prefix) From b0ee25e799a5f916f86894c160e54373c3bd57d0 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 17 Feb 2017 17:57:21 +0100 Subject: [PATCH 28/59] Try to get system compiler to match the correct version of intel compilers --- easybuild/easyblocks/generic/systemcompiler.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 1f255a75a9..291f4d314a 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -50,17 +50,21 @@ def extract_compiler_version(compiler_name): # examples: # gcc (GCC) 4.4.7 20120313 (Red Hat 4.4.7-11) # Intel(R) C Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 15.0.1.133 Build 20141023 + version_regex = re.compile(r'\s([0-9]+(?:\.[0-9]+){1,3})\s', re.M) if compiler_name == 'gcc': out, _ = run_cmd("gcc --version", simple=False) + res = version_regex.search(out) + compiler_version = res.group(1) elif compiler_name in ['icc', 'ifort']: - out, _ = run_cmd("%s -V" % compiler_name, simple=False) + # A fully resolved icc/ifort (without symlinks) includes the version in the path + # e.g. .../composer_xe_2015.3.187/bin/intel64/icc + out = (os.path.realpath(which(compiler_name)).split("/")).split("_") + # Match the last incidence since we don't know what might be in the path + compiler_version = version_regex.findall(out)[-1] else: raise EasyBuildError("Unknown compiler %s" % compiler_name) - version_regex = re.compile(r'\s([0-9]+(?:\.[0-9]+){1,3})\s', re.M) - res = version_regex.search(out) - if res: - compiler_version = res.group(1) + if compiler_version: _log.debug("Extracted compiler version '%s' for %s from: %s", compiler_version, compiler_name, out) else: raise EasyBuildError("Failed to extract compiler version for %s using regex pattern '%s' from: %s", From f14440079468d14754b2b86cb84fd3cf2285d5f3 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 17 Feb 2017 19:17:01 +0100 Subject: [PATCH 29/59] Fix compiler version detection for intel Fix module path guesses for icc/ifort (don't inherit from intelbase) --- .../easyblocks/generic/systemcompiler.py | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 291f4d314a..5837bc6478 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -34,7 +34,8 @@ from vsc.utils import fancylogger from easybuild.easyblocks.generic.bundle import Bundle -from easybuild.easyblocks.generic.intelbase import IntelBase +from easybuild.easyblocks.i.icc import EB_icc +from easybuild.easyblocks.i.ifort import EB_ifort from easybuild.easyblocks.g.gcc import EB_GCC from easybuild.tools.filetools import read_file, which from easybuild.tools.run import run_cmd @@ -58,9 +59,10 @@ def extract_compiler_version(compiler_name): elif compiler_name in ['icc', 'ifort']: # A fully resolved icc/ifort (without symlinks) includes the version in the path # e.g. .../composer_xe_2015.3.187/bin/intel64/icc - out = (os.path.realpath(which(compiler_name)).split("/")).split("_") - # Match the last incidence since we don't know what might be in the path - compiler_version = version_regex.findall(out)[-1] + print os.path.realpath(which(compiler_name)) + # Match the last incidence of _ since we don't know what might be in the path, then split it up + out = (os.path.realpath(which(compiler_name)).split("_")[-1]).split("/") + compiler_version = out[0] else: raise EasyBuildError("Unknown compiler %s" % compiler_name) @@ -72,7 +74,7 @@ def extract_compiler_version(compiler_name): return compiler_version -class SystemCompiler(EB_GCC, IntelBase, Bundle): +class SystemCompiler(EB_GCC, EB_icc, EB_ifort, Bundle): """ Support for generating a module file for the system compiler with specified name. @@ -85,7 +87,8 @@ class SystemCompiler(EB_GCC, IntelBase, Bundle): @staticmethod def extra_options(): # Gather extra_vars from inherited classes - extra_vars = IntelBase.extra_options() + extra_vars = EB_icc.extra_options() + extra_vars.update(EB_ifort.extra_options()) extra_vars.update(EB_GCC.extra_options()) extra_vars.update(Bundle.extra_options()) # Add an option to add all module path extensions to the resultant easyconfig @@ -196,8 +199,10 @@ def make_module_req_guess(self): if self.cfg['add_path_information']: if self.cfg['name'] in ['GCC','GCCcore']: guesses = EB_GCC.make_module_req_guess(self) - elif self.cfg['name'] in ['icc', 'ifort']: - guesses = IntelBase.make_module_req_guess(self) + elif self.cfg['name'] in ['icc']: + guesses = EB_icc.make_module_req_guess(self) + elif self.cfg['name'] in ['ifort']: + guesses = EB_ifort.make_module_req_guess(self) else: raise EasyBuildError("I don't know how to generate module var guesses for %s", self.cfg['name']) else: @@ -241,8 +246,10 @@ def make_module_extra(self): if self.cfg['add_path_information']: if self.cfg['name'] in ['GCC','GCCcore']: extras = EB_GCC.make_module_extra(self) - elif self.cfg['name'] in ['icc', 'ifort']: - extras = IntelBase.make_module_extra(self) + elif self.cfg['name'] in ['icc']: + guesses = EB_icc.make_module_extra(self) + elif self.cfg['name'] in ['ifort']: + guesses = EB_ifort.make_module_extra(self) else: raise EasyBuildError("I don't know how to generate extra module text for %s", self.cfg['name']) else: From 7b525b08a1c50184622f59db61a2b32ddd17cabb Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 17 Feb 2017 19:21:12 +0100 Subject: [PATCH 30/59] Fix dodgy variable name --- easybuild/easyblocks/generic/systemcompiler.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 5837bc6478..970784b750 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -59,7 +59,6 @@ def extract_compiler_version(compiler_name): elif compiler_name in ['icc', 'ifort']: # A fully resolved icc/ifort (without symlinks) includes the version in the path # e.g. .../composer_xe_2015.3.187/bin/intel64/icc - print os.path.realpath(which(compiler_name)) # Match the last incidence of _ since we don't know what might be in the path, then split it up out = (os.path.realpath(which(compiler_name)).split("_")[-1]).split("/") compiler_version = out[0] @@ -247,9 +246,9 @@ def make_module_extra(self): if self.cfg['name'] in ['GCC','GCCcore']: extras = EB_GCC.make_module_extra(self) elif self.cfg['name'] in ['icc']: - guesses = EB_icc.make_module_extra(self) + extras = EB_icc.make_module_extra(self) elif self.cfg['name'] in ['ifort']: - guesses = EB_ifort.make_module_extra(self) + extras = EB_ifort.make_module_extra(self) else: raise EasyBuildError("I don't know how to generate extra module text for %s", self.cfg['name']) else: From 60b86cee743b92a549b0ccdae280d79c442f80a8 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 17 Feb 2017 19:33:30 +0100 Subject: [PATCH 31/59] Allow picking up of the licence for intel compilers --- easybuild/easyblocks/generic/systemcompiler.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 970784b750..5f5cc3edf4 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -172,8 +172,18 @@ def __init__(self, *args, **kwargs): self.orig_installdir = self.installdir def prepare_step(self): - """Do the bundle prepare step to ensure any deps are loaded.""" - Bundle.prepare_step(self) + """Do the bundle prepare step to ensure any deps are loaded at a minimum.""" + if self.cfg['add_path_information']: + if self.cfg['name'] in ['GCC', 'GCCcore']: + EB_GCC.prepare_step(self) + elif self.cfg['name'] in ['icc']: + EB_icc.prepare_step(self) + elif self.cfg['name'] in ['ifort']: + EB_ifort.prepare_step(self) + else: + raise EasyBuildError("I don't know how to do the prepare_step for %s", self.cfg['name']) + else: + Bundle.prepare_step(self) def make_installdir(self, dontcreate=None): """Custom implementation of make installdir: do nothing, do not touch system compiler directories and files.""" From 3d5263c93080ea9ccd435dac1d96c664d89a0a67 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 17 Feb 2017 19:55:57 +0100 Subject: [PATCH 32/59] Make sure 201* is treated as a number --- easybuild/easyblocks/generic/systemcompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 5f5cc3edf4..d26073dffd 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -136,7 +136,7 @@ def __init__(self, *args, **kwargs): # strip off 'bin/intel*/icc' self.compiler_prefix = os.path.dirname(os.path.dirname(os.path.dirname(path_to_compiler))) # For versions 2016+ of Intel compilers they changed the installation path - if self.compiler_version.split('.')[0] >= 2016: + if int(self.compiler_version.split('.')[0]) >= 2016: self.compiler_prefix = os.path.dirname(os.path.dirname(self.compiler_prefix)) else: From 409650867270969a41b10e316da6c54b69ee288a Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Thu, 23 Feb 2017 10:07:07 +0100 Subject: [PATCH 33/59] Support older version of impi --- easybuild/easyblocks/generic/systemmpi.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index d36ffff1eb..b8bca608f4 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -122,7 +122,13 @@ def __init__(self, *args, **kwargs): # The prefix in the the mpiicc script can be used to extract the explicit version contents_of_mpiicc = read_file(path_to_mpi_c_wrapper) prefix_regex = re.compile(r'(?<=compilers_and_libraries_)(.*)(?=/linux/mpi)', re.M) - self.mpi_version = prefix_regex.search(contents_of_mpiicc).group(1) + if not prefix_regex.search(contents_of_mpiicc): + # We've got an old version of iimpi: + prefix_regex = re.compile(r'^prefix=(.*)$', re.M) + # In this case the version is the last part of the prefix + self.mpi_version = prefix_regex.search(contents_of_mpiicc).group(1).split("/")[-1] + else: + self.mpi_version = prefix_regex.search(contents_of_mpiicc).group(1) if self.mpi_version is not None: self.log.info("Found Intel MPI version %s for system MPI" % self.mpi_version) else: From 8e3252845c46245400bbf2d87c61fea1443c5a48 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Wed, 1 Mar 2017 16:13:11 +0100 Subject: [PATCH 34/59] Address comments --- .travis.yml | 3 +- .../easyblocks/generic/systemcompiler.py | 76 ++++++------ easybuild/easyblocks/generic/systemmpi.py | 113 ++++++++---------- test/easyblocks/init_easyblocks.py | 2 +- test/easyblocks/module.py | 2 +- 5 files changed, 89 insertions(+), 107 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8e6c7bb469..08fbbc4738 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,8 @@ addons: packages: - tcl8.5 before_install: - - sudo apt-get update && sudo apt-get install -y python-dev torque-server libtorque2-dev libopenmpi-dev openmpi-bin + # For testing systemmpi easyblock we need an OpenMPI instance + - sudo apt-get update && sudo apt-get install -y libopenmpi-dev openmpi-bin install: # install easybuild-framework (and dependencies) # prefer clone & easy_install over easy_install directly in order to obtain information of what was installed exactly diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index d26073dffd..9836efed26 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -55,15 +55,24 @@ def extract_compiler_version(compiler_name): if compiler_name == 'gcc': out, _ = run_cmd("gcc --version", simple=False) res = version_regex.search(out) + if res is None: + raise EasyBuildError("Could not extract GCC version from %s", out) compiler_version = res.group(1) elif compiler_name in ['icc', 'ifort']: - # A fully resolved icc/ifort (without symlinks) includes the version in the path + # A fully resolved icc/ifort (without symlinks) includes the release version in the path # e.g. .../composer_xe_2015.3.187/bin/intel64/icc - # Match the last incidence of _ since we don't know what might be in the path, then split it up + # Match the last incidence of _ since we don't know what might be in the path, then split it up on / out = (os.path.realpath(which(compiler_name)).split("_")[-1]).split("/") compiler_version = out[0] + # Check what we have looks like a version number (the regex we use requires spaces around the version number) + if version_regex.search(" " + compiler_version + " ") is None: + typical_install_path = '.../composer_xe_2015.3.187/bin/intel64/icc' + raise EasyBuildError( + "Derived Intel compiler version %s doesn't look correct, is compiler installed in a path like %s?", + typical_install_path, compiler_version + ) else: - raise EasyBuildError("Unknown compiler %s" % compiler_name) + raise EasyBuildError("Unknown compiler %s", compiler_name) if compiler_version: _log.debug("Extracted compiler version '%s' for %s from: %s", compiler_version, compiler_name, out) @@ -73,7 +82,7 @@ def extract_compiler_version(compiler_name): return compiler_version -class SystemCompiler(EB_GCC, EB_icc, EB_ifort, Bundle): +class SystemCompiler(Bundle, EB_GCC, EB_icc, EB_ifort): """ Support for generating a module file for the system compiler with specified name. @@ -86,14 +95,18 @@ class SystemCompiler(EB_GCC, EB_icc, EB_ifort, Bundle): @staticmethod def extra_options(): # Gather extra_vars from inherited classes - extra_vars = EB_icc.extra_options() - extra_vars.update(EB_ifort.extra_options()) + extra_vars = Bundle.extra_options() extra_vars.update(EB_GCC.extra_options()) - extra_vars.update(Bundle.extra_options()) + extra_vars.update(EB_icc.extra_options()) + extra_vars.update(EB_ifort.extra_options()) # Add an option to add all module path extensions to the resultant easyconfig # This is useful if you are importing a compiler from a non-default path extra_vars.update({ - 'add_path_information': [False, "Add known path extensions for the compiler to the final module", CUSTOM], + 'generate_standalone_module': [ + False, + "Add known path/library extensions and environment variables for the compiler to the final module", + CUSTOM + ], }) return extra_vars @@ -135,8 +148,10 @@ def __init__(self, *args, **kwargs): else: # strip off 'bin/intel*/icc' self.compiler_prefix = os.path.dirname(os.path.dirname(os.path.dirname(path_to_compiler))) - # For versions 2016+ of Intel compilers they changed the installation path - if int(self.compiler_version.split('.')[0]) >= 2016: + + # For versions 2016+ of Intel compilers they changed the installation path so must shave off 2 more + # directories from result of the above + if LooseVersion(self.compiler_version) >= LooseVersion('2016'): self.compiler_prefix = os.path.dirname(os.path.dirname(self.compiler_prefix)) else: @@ -148,11 +163,13 @@ def __init__(self, *args, **kwargs): self.log.debug("Derived version/install prefix for system compiler %s: %s, %s", compiler_name, self.compiler_version, self.compiler_prefix) - if self.compiler_prefix in ['/usr']: - # Force off adding paths to module since unloading such a module would be a shell killer - self.cfg['add_path_information'] = False - self.log.info("Disabling option 'add_path_information' since installation prefix is %s", - self.compiler_prefix) + if self.compiler_prefix in ['/usr', '/usr/local']: + if self.cfg['generate_standalone_module']: + # Force off adding paths to module since unloading such a module would be a potential shell killer + self.cfg['generate_standalone_module'] = False + self.log.warning("Disabling option 'generate_standalone_module' since installation prefix is %s", + self.compiler_prefix) + # If EasyConfig specified "real" version (not 'system' which means 'derive automatically'), check it if self.cfg['version'] == 'system': self.log.info("Found specified version '%s', going with derived compiler version '%s'", @@ -173,7 +190,7 @@ def __init__(self, *args, **kwargs): def prepare_step(self): """Do the bundle prepare step to ensure any deps are loaded at a minimum.""" - if self.cfg['add_path_information']: + if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['GCC', 'GCCcore']: EB_GCC.prepare_step(self) elif self.cfg['name'] in ['icc']: @@ -189,23 +206,12 @@ def make_installdir(self, dontcreate=None): """Custom implementation of make installdir: do nothing, do not touch system compiler directories and files.""" pass - def configure_step(self): - """Do nothing.""" - pass - - def build_step(self): - """Do nothing.""" - pass - - def install_step(self): - """Do nothing.""" - pass - def make_module_req_guess(self): """ - A dictionary of possible directories to look for. Return known dict for the system compiler. + A dictionary of possible directories to look for. Return known dict for the system compiler, or empty dict if + generate_standalone_module parameter is False """ - if self.cfg['add_path_information']: + if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['GCC','GCCcore']: guesses = EB_GCC.make_module_req_guess(self) elif self.cfg['name'] in ['icc']: @@ -252,7 +258,7 @@ def make_module_extend_modpath(self): def make_module_extra(self): """Add any additional module text.""" - if self.cfg['add_path_information']: + if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['GCC','GCCcore']: extras = EB_GCC.make_module_extra(self) elif self.cfg['name'] in ['icc']: @@ -273,11 +279,3 @@ def sanity_check_step(self, *args, **kwargs): fake_mod_data = self.load_fake_module(purge=True) self.log.debug("Cleaning up after testing loading of module") self.clean_up_fake_module(fake_mod_data) - - def cleanup_step(self): - """Do nothing.""" - pass - - def permissions_step(self): - """Do nothing.""" - pass diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index b8bca608f4..ef28a7ef33 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -38,13 +38,14 @@ from easybuild.tools.modules import get_software_version from easybuild.tools.filetools import read_file, which from easybuild.tools.run import run_cmd +from easybuild.tools.toolchain import DUMMY_TOOLCHAIN_NAME from easybuild.framework.easyconfig.easyconfig import ActiveMNS from easybuild.framework.easyconfig import CUSTOM from easybuild.tools.build_log import EasyBuildError _log = fancylogger.getLogger('easyblocks.generic.systemmpi') -class SystemMPI(ConfigureMake, EB_impi, Bundle): +class SystemMPI(Bundle, ConfigureMake, EB_impi): """ Support for generating a module file for the system mpi with specified name. @@ -57,14 +58,14 @@ class SystemMPI(ConfigureMake, EB_impi, Bundle): @staticmethod def extra_options(): # Gather extra_vars from inherited classes - extra_vars = ConfigureMake.extra_options() + extra_vars = Bundle.extra_options() + extra_vars.update(ConfigureMake.extra_options()) extra_vars.update(EB_impi.extra_options()) - extra_vars.update(Bundle.extra_options()) # Add an option to add all module path extensions to the resultant easyconfig # This is useful if you are importing the MPI installation from a non-default path extra_vars.update({ - 'add_path_information': [False, "Add known path extensions and environment variables for the MPI " - "installation to the final module", CUSTOM], + 'generate_standalone_module': [False, "Add known path extensions and environment variables for the MPI " + "installation to the final module", CUSTOM], }) return extra_vars @@ -122,21 +123,27 @@ def __init__(self, *args, **kwargs): # The prefix in the the mpiicc script can be used to extract the explicit version contents_of_mpiicc = read_file(path_to_mpi_c_wrapper) prefix_regex = re.compile(r'(?<=compilers_and_libraries_)(.*)(?=/linux/mpi)', re.M) - if not prefix_regex.search(contents_of_mpiicc): - # We've got an old version of iimpi: + + self.mpi_version = None + res = prefix_regex.search(contents_of_mpiicc) + if res: + self.mpi_version = res.group(1) + else: + # old iimpi version prefix_regex = re.compile(r'^prefix=(.*)$', re.M) - # In this case the version is the last part of the prefix - self.mpi_version = prefix_regex.search(contents_of_mpiicc).group(1).split("/")[-1] + res = prefix_regex.search(contents_of_mpiicc) + if res: + self.mpi_version = res.group(1).split('/')[-1] + + if self.mpi_version is None: + raise EasyBuildError("No version found for system Intel MPI") else: - self.mpi_version = prefix_regex.search(contents_of_mpiicc).group(1) - if self.mpi_version is not None: self.log.info("Found Intel MPI version %s for system MPI" % self.mpi_version) - else: - raise EasyBuildError("No version found for system Intel MPI") # Extract the installation prefix, if I_MPI_ROOT is defined, let's use that - if os.environ.get('I_MPI_ROOT'): - self.mpi_prefix = os.environ['I_MPI_ROOT'] + i_mpi_root = os.environ.get('I_MPI_ROOT') + if i_mpi_root: + self.mpi_prefix = i_mpi_root else: # Else just go up three directories from where mpiicc is found # (it's 3 because bin64 is a symlink to intel64/bin and we are assuming 64 bit) @@ -144,16 +151,19 @@ def __init__(self, *args, **kwargs): # Extract any IntelMPI environment variables in the current environment and ensure they are added to the # final module - self.mpi_envvars = dict((key, value) for key, value in os.environ.iteritems() if key.startswith("I_MPI_")) - self.mpi_envvars.update(dict((key, value) for key, value in os.environ.iteritems() if key.startswith("MPICH_"))) - self.mpi_envvars.update( - dict((key, value) for key, value in os.environ.iteritems() - if key.startswith("MPI") and key.endswith("PROFILE")) - ) + self.mpi_env_vars = {} + for key, value in os.environ.iteritems(): + if any(key.startswith(x) for x in ['I_MPI_', 'MPICH_']) or (key.startswith('MPI') and + key.endswith('PROFILE')): + self.mpi_env_vars[key] = value # Extract the C compiler used underneath Intel MPI - compile_info, _ = run_cmd("%s -compile-info" % mpi_c_wrapper, simple=False) - self.mpi_c_compiler = compile_info.split(' ', 1)[0] + compile_info, exit_code = run_cmd("%s -compile-info" % mpi_c_wrapper, simple=False) + if exit_code == 0: + self.mpi_c_compiler = compile_info.split(' ', 1)[0] + else: + raise EasyBuildError("Could not determine C compiler underneath Intel MPI, '%s -compiler-info' " + "returned %s", mpi_c_wrapper, compile_info) else: raise EasyBuildError("Unrecognised system MPI implementation %s", mpi_name) @@ -162,11 +172,12 @@ def __init__(self, *args, **kwargs): if not os.path.exists(self.mpi_prefix): raise EasyBuildError("Path derived for system MPI (%s) does not exist: %s!", mpi_name, self.mpi_prefix) - if self.mpi_prefix in ['/usr']: - # Force off adding paths to module since unloading such a module would be a shell killer - self.cfg['add_path_information'] = False - self.log.info("Disabling option 'add_path_information' since installation prefix is %s", - self.mpi_prefix) + if self.mpi_prefix in ['/usr', '/usr/local']: + if self.cfg['generate_standalone_module']: + # Force off adding paths to module since unloading such a module would be a potential shell killer + self.cfg['generate_standalone_module'] = False + self.log.warning("Disabling option 'generate_standalone_module' since installation prefix is %s", + self.compiler_prefix) self.log.debug("Derived version/install prefix for system MPI %s: %s, %s", mpi_name, self.mpi_version, self.mpi_prefix) @@ -183,8 +194,8 @@ def __init__(self, *args, **kwargs): elif self.cfg['version'] == self.mpi_version: self.log.info("Specified MPI version %s matches found version" % self.mpi_version) else: - raise EasyBuildError("Specified version (%s) does not match version reported by MPI (%s)" % - (self.cfg['version'], self.mpi_version)) + raise EasyBuildError("Specified version (%s) does not match version reported by MPI (%s)", + self.cfg['version'], self.mpi_version) # fix installdir and module names (may differ because of changes to version) mns = ActiveMNS() @@ -196,35 +207,15 @@ def __init__(self, *args, **kwargs): self.orig_version = self.cfg['version'] self.orig_installdir = self.installdir - def prepare_step(self): - """Do the bundle prepare step to ensure any deps are loaded.""" - Bundle.prepare_step(self) - def make_installdir(self, dontcreate=None): """Custom implementation of make installdir: do nothing, do not touch system MPI directories and files.""" pass - def configure_step(self): - """Do nothing.""" - pass - - def build_step(self): - """Do nothing.""" - pass - - def install_step(self): - """Do nothing.""" - pass - - def post_install_step(self): - """Do nothing.""" - pass - def make_module_req_guess(self): """ A dictionary of possible directories to look for. Return known dict for the system MPI. """ - if self.cfg['add_path_information']: + if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['OpenMPI']: guesses = ConfigureMake.make_module_req_guess(self) elif self.cfg['name'] in ['impi']: @@ -241,21 +232,21 @@ def make_module_step(self, fake=False): and install path. """ # First let's verify that the toolchain and the compilers under MPI match - c_compiler_name = self.toolchain.COMPILER_CC - if c_compiler_name == 'DUMMYCC': + if self.toolchain.name == DUMMY_TOOLCHAIN_NAME: # If someone is using dummy as the MPI toolchain lets assume that gcc is the compiler underneath MPI c_compiler_name = 'gcc' # Also need to fake the compiler version c_compiler_version = self.c_compiler_version self.log.info("Found dummy toolchain so assuming GCC as compiler underneath MPI and faking the version") else: + c_compiler_name = self.toolchain.COMPILER_CC c_compiler_version = get_software_version(self.toolchain.COMPILER_MODULE_NAME[0]) if self.mpi_c_compiler != c_compiler_name or self.c_compiler_version != c_compiler_version: raise EasyBuildError("C compiler for toolchain (%s/%s) and underneath MPI (%s/%s) do not match!", c_compiler_name, c_compiler_version, self.mpi_c_compiler, self.c_compiler_version) - # For module file generation: temporarily set version and installdir to system compiler values + # For module file generation: temporarily set version and installdir to system MPI values self.cfg['version'] = self.mpi_version self.installdir = self.mpi_prefix @@ -270,7 +261,7 @@ def make_module_step(self, fake=False): def make_module_extend_modpath(self): """ Custom prepend-path statements for extending $MODULEPATH: use version specified in easyconfig file (e.g., - "system") rather than the actual version (e.g., "4.8.2"). + "system") rather than the actual version (e.g., "2.0.2"). """ # temporarily set switch back to version specified in easyconfig file (e.g., "system") self.cfg['version'] = self.orig_version @@ -278,13 +269,13 @@ def make_module_extend_modpath(self): # Retrieve module path extensions res = super(SystemMPI, self).make_module_extend_modpath() - # Reset to actual MPI version (e.g., "4.8.2") + # Reset to actual MPI version (e.g., "2.0.2") self.cfg['version'] = self.mpi_version return res def make_module_extra(self): """Add any additional module text.""" - if self.cfg['add_path_information']: + if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['OpenMPI']: extras = ConfigureMake.make_module_extra(self) elif self.cfg['name'] in ['impi']: @@ -307,11 +298,3 @@ def sanity_check_step(self, *args, **kwargs): fake_mod_data = self.load_fake_module(purge=True) self.log.debug("Cleaning up after testing loading of module") self.clean_up_fake_module(fake_mod_data) - - def cleanup_step(self): - """Do nothing.""" - pass - - def permissions_step(self): - """Do nothing.""" - pass diff --git a/test/easyblocks/init_easyblocks.py b/test/easyblocks/init_easyblocks.py index f249d8d057..e94cd88be4 100644 --- a/test/easyblocks/init_easyblocks.py +++ b/test/easyblocks/init_easyblocks.py @@ -185,7 +185,7 @@ def suite(): # use GCC as name when testing SystemCompiler easyblock exec("def innertest(self): template_init_test(self, '%s', name='GCC', version='system')" % easyblock) elif os.path.basename(easyblock) == 'systemmpi.py': - # use OpenMPI as name when testing SystemCompiler easyblock + # use OpenMPI as name when testing SystemMPI easyblock exec("def innertest(self): template_init_test(self, '%s', name='OpenMPI', version='system')" % easyblock) else: exec("def innertest(self): template_init_test(self, '%s')" % easyblock) diff --git a/test/easyblocks/module.py b/test/easyblocks/module.py index 8d7e44f830..c324790fb1 100644 --- a/test/easyblocks/module.py +++ b/test/easyblocks/module.py @@ -316,7 +316,7 @@ def suite(): # use GCC as name when testing SystemCompiler easyblock exec("def innertest(self): template_module_only_test(self, '%s', name='GCC', version='system')" % easyblock) elif os.path.basename(easyblock) == 'systemmpi.py': - # use OpenMPI as name when testing SystemCompiler easyblock + # use OpenMPI as name when testing SystemMPI easyblock exec("def innertest(self): template_module_only_test(self, '%s', name='OpenMPI', version='system')" % easyblock) elif os.path.basename(easyblock) == 'craytoolchain.py': From ccee79b836b4672e75016d2c76d36b07a0bfd8b6 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Wed, 1 Mar 2017 16:38:03 +0100 Subject: [PATCH 35/59] Use the GCC init as the bundle causes errors because of unexpected values in extra_options --- easybuild/easyblocks/generic/systemcompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 9836efed26..decd2be7d5 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -112,7 +112,7 @@ def extra_options(): def __init__(self, *args, **kwargs): """Extra initialization: determine system compiler version and prefix.""" - super(SystemCompiler, self).__init__(*args, **kwargs) + EB_GCC.__init__(self, *args, **kwargs) # Determine compiler path (real path, with resolved symlinks) compiler_name = self.cfg['name'].lower() From 89e13be038cd2c23557cce7bdbdaf9e74ce15806 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Wed, 1 Mar 2017 16:49:28 +0100 Subject: [PATCH 36/59] Change order in extra_options to avoid the error in __init__ --- easybuild/easyblocks/generic/systemcompiler.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index decd2be7d5..76fd7dcf10 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -94,11 +94,11 @@ class SystemCompiler(Bundle, EB_GCC, EB_icc, EB_ifort): @staticmethod def extra_options(): - # Gather extra_vars from inherited classes - extra_vars = Bundle.extra_options() - extra_vars.update(EB_GCC.extra_options()) + # Gather extra_vars from inherited classes, order matters here to make this work without problems in __init__ + extra_vars = EB_GCC.extra_options() extra_vars.update(EB_icc.extra_options()) extra_vars.update(EB_ifort.extra_options()) + extra_vars.update(Bundle.extra_options()) # Add an option to add all module path extensions to the resultant easyconfig # This is useful if you are importing a compiler from a non-default path extra_vars.update({ @@ -112,7 +112,7 @@ def extra_options(): def __init__(self, *args, **kwargs): """Extra initialization: determine system compiler version and prefix.""" - EB_GCC.__init__(self, *args, **kwargs) + super(SystemCompiler, self).__init__(*args, **kwargs) # Determine compiler path (real path, with resolved symlinks) compiler_name = self.cfg['name'].lower() From 637a815c1c71b5105a705b98fcf70767b634bc6f Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Wed, 1 Mar 2017 16:54:12 +0100 Subject: [PATCH 37/59] Change order in extra_options to avoid the error in __init__ --- easybuild/easyblocks/generic/systemmpi.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index ef28a7ef33..5d6047739f 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -57,10 +57,10 @@ class SystemMPI(Bundle, ConfigureMake, EB_impi): @staticmethod def extra_options(): - # Gather extra_vars from inherited classes - extra_vars = Bundle.extra_options() - extra_vars.update(ConfigureMake.extra_options()) + # Gather extra_vars from inherited classes, order matters to make sure bundle initialises correctly + extra_vars = ConfigureMake.extra_options() extra_vars.update(EB_impi.extra_options()) + extra_vars.update(Bundle.extra_options()) # Add an option to add all module path extensions to the resultant easyconfig # This is useful if you are importing the MPI installation from a non-default path extra_vars.update({ From 5f17da83f8ad82b582456ff5ee64c396e632697e Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Wed, 1 Mar 2017 17:21:54 +0100 Subject: [PATCH 38/59] Add arguments to Bundle.make_module_extra --- easybuild/easyblocks/generic/systemcompiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 76fd7dcf10..922dbbc7b6 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -268,7 +268,7 @@ def make_module_extra(self): else: raise EasyBuildError("I don't know how to generate extra module text for %s", self.cfg['name']) else: - extras= Bundle.make_module_extra(self) + extras= Bundle.make_module_extra(self, altroot=None, altversion=None) return extras def sanity_check_step(self, *args, **kwargs): From b62ac7c965707b634b743b83744433fd72fbe833 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Wed, 1 Mar 2017 17:34:14 +0100 Subject: [PATCH 39/59] Fix error with make_module_extra when not having __init__ from the bundle --- easybuild/easyblocks/generic/systemcompiler.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 922dbbc7b6..1629dac83b 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -268,7 +268,10 @@ def make_module_extra(self): else: raise EasyBuildError("I don't know how to generate extra module text for %s", self.cfg['name']) else: - extras= Bundle.make_module_extra(self, altroot=None, altversion=None) + # Need some additional variables for the Bundle make_module_extra to work + self.altroot = None + self.altversion = None + extras= Bundle.make_module_extra(self) return extras def sanity_check_step(self, *args, **kwargs): From 8d0dd01809451f7c74215c013832a9794ef9be8d Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Wed, 1 Mar 2017 17:40:45 +0100 Subject: [PATCH 40/59] Ignore the intelbase prepare_step, just use the bundle one --- easybuild/easyblocks/generic/systemmpi.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index 5d6047739f..687d9c515e 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -207,6 +207,10 @@ def __init__(self, *args, **kwargs): self.orig_version = self.cfg['version'] self.orig_installdir = self.installdir + def prepare_step(self): + """Do the bundle prepare step to ensure any deps are loaded. No need to worry about licences for Intel MPI""" + Bundle.prepare_step(self) + def make_installdir(self, dontcreate=None): """Custom implementation of make installdir: do nothing, do not touch system MPI directories and files.""" pass From e3985f9e3bdead1996c59efde1990a3dd6a290bf Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Wed, 1 Mar 2017 18:16:56 +0100 Subject: [PATCH 41/59] Try to overcome make_module_extra error --- easybuild/easyblocks/generic/systemcompiler.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 1629dac83b..056ac259c7 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -269,9 +269,7 @@ def make_module_extra(self): raise EasyBuildError("I don't know how to generate extra module text for %s", self.cfg['name']) else: # Need some additional variables for the Bundle make_module_extra to work - self.altroot = None - self.altversion = None - extras= Bundle.make_module_extra(self) + extras= super(SystemCompiler, self).make_module_extra(altroot=self.altroot, altversion=self.altversion) return extras def sanity_check_step(self, *args, **kwargs): From 53b3c34ba52f0f582d9e4dcab1e62e2bb8ecf5d5 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Wed, 1 Mar 2017 18:25:35 +0100 Subject: [PATCH 42/59] Try to overcome make_module_extra error by passing the arguments down --- easybuild/easyblocks/generic/systemcompiler.py | 11 +++++------ easybuild/easyblocks/generic/systemmpi.py | 8 ++++---- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 056ac259c7..308ab63a17 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -256,20 +256,19 @@ def make_module_extend_modpath(self): self.cfg['version'] = self.compiler_version return res - def make_module_extra(self): + def make_module_extra(self, *args, **kwargs): """Add any additional module text.""" if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['GCC','GCCcore']: - extras = EB_GCC.make_module_extra(self) + extras = EB_GCC.make_module_extra(self, *args, **kwargs) elif self.cfg['name'] in ['icc']: - extras = EB_icc.make_module_extra(self) + extras = EB_icc.make_module_extra(self, *args, **kwargs) elif self.cfg['name'] in ['ifort']: - extras = EB_ifort.make_module_extra(self) + extras = EB_ifort.make_module_extra(self, *args, **kwargs) else: raise EasyBuildError("I don't know how to generate extra module text for %s", self.cfg['name']) else: - # Need some additional variables for the Bundle make_module_extra to work - extras= super(SystemCompiler, self).make_module_extra(altroot=self.altroot, altversion=self.altversion) + extras = super(SystemCompiler, self).make_module_extra(*args, **kwargs) return extras def sanity_check_step(self, *args, **kwargs): diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index 687d9c515e..0ff29cf6a2 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -277,13 +277,13 @@ def make_module_extend_modpath(self): self.cfg['version'] = self.mpi_version return res - def make_module_extra(self): + def make_module_extra(self, *args, **kwargs): """Add any additional module text.""" if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['OpenMPI']: - extras = ConfigureMake.make_module_extra(self) + extras = ConfigureMake.make_module_extra(self, *args, **kwargs) elif self.cfg['name'] in ['impi']: - extras = EB_impi.make_module_extra(self) + extras = EB_impi.make_module_extra(self, *args, **kwargs) else: raise EasyBuildError("I don't know how to generate extra module text for %s", self.cfg['name']) # include environment variables defined for MPI implementation @@ -291,7 +291,7 @@ def make_module_extra(self): extras += self.module_generator.set_environment(key, val) self.log.debug("make_module_extra added this: %s" % extras) else: - extras= Bundle.make_module_extra(self) + extras = super(SystemCompiler, self).make_module_extra(*args, **kwargs) return extras def sanity_check_step(self, *args, **kwargs): From e88e2a1e49778ba5ffe0b93262cebef5bc555884 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Wed, 1 Mar 2017 18:38:43 +0100 Subject: [PATCH 43/59] Try to overcome make_module_extra error by passing the arguments down --- easybuild/easyblocks/generic/systemmpi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index 0ff29cf6a2..008def886a 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -291,7 +291,7 @@ def make_module_extra(self, *args, **kwargs): extras += self.module_generator.set_environment(key, val) self.log.debug("make_module_extra added this: %s" % extras) else: - extras = super(SystemCompiler, self).make_module_extra(*args, **kwargs) + extras = super(SystemMPI, self).make_module_extra(*args, **kwargs) return extras def sanity_check_step(self, *args, **kwargs): From 0078d2f91328d74da030d9d062083bf3c0ade276 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Wed, 1 Mar 2017 18:54:18 +0100 Subject: [PATCH 44/59] Try to overcome make_module_extra error by passing the arguments down --- easybuild/easyblocks/generic/systemcompiler.py | 2 +- easybuild/easyblocks/generic/systemmpi.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 308ab63a17..0dd20e35e1 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -268,7 +268,7 @@ def make_module_extra(self, *args, **kwargs): else: raise EasyBuildError("I don't know how to generate extra module text for %s", self.cfg['name']) else: - extras = super(SystemCompiler, self).make_module_extra(*args, **kwargs) + extras = Bundle.make_module_extra(self, *args, **kwargs) return extras def sanity_check_step(self, *args, **kwargs): diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index 008def886a..64f935e842 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -291,7 +291,7 @@ def make_module_extra(self, *args, **kwargs): extras += self.module_generator.set_environment(key, val) self.log.debug("make_module_extra added this: %s" % extras) else: - extras = super(SystemMPI, self).make_module_extra(*args, **kwargs) + extras = Bundle.make_module_extra(self, *args, **kwargs) return extras def sanity_check_step(self, *args, **kwargs): From 0250ce3bf24ae000f699a12bfe1ccc9032d68e3d Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Wed, 1 Mar 2017 19:21:10 +0100 Subject: [PATCH 45/59] Try to overcome make_module_extra error by passing the arguments down --- easybuild/easyblocks/generic/bundle.py | 2 +- .../easyblocks/generic/systemcompiler.py | 18 ++++++++++++----- easybuild/easyblocks/generic/systemmpi.py | 20 +++++++++++++++---- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/easybuild/easyblocks/generic/bundle.py b/easybuild/easyblocks/generic/bundle.py index 471c054e03..faff68857b 100644 --- a/easybuild/easyblocks/generic/bundle.py +++ b/easybuild/easyblocks/generic/bundle.py @@ -158,7 +158,7 @@ def install_step(self): def make_module_extra(self): """Set extra stuff in module file, e.g. $EBROOT*, $EBVERSION*, etc.""" - return super(Bundle, self).make_module_extra(altroot=self.altroot, altversion=self.altversion) + return EasyBlock.make_module_extra(self, altroot=self.altroot, altversion=self.altversion) def sanity_check_step(self, *args, **kwargs): """ diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 0dd20e35e1..9da5f49e2b 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -256,21 +256,29 @@ def make_module_extend_modpath(self): self.cfg['version'] = self.compiler_version return res - def make_module_extra(self, *args, **kwargs): + def make_module_extra(self): """Add any additional module text.""" if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['GCC','GCCcore']: - extras = EB_GCC.make_module_extra(self, *args, **kwargs) + extras = EB_GCC.make_module_extra(self) elif self.cfg['name'] in ['icc']: - extras = EB_icc.make_module_extra(self, *args, **kwargs) + extras = EB_icc.make_module_extra(self) elif self.cfg['name'] in ['ifort']: - extras = EB_ifort.make_module_extra(self, *args, **kwargs) + extras = EB_ifort.make_module_extra(self) else: raise EasyBuildError("I don't know how to generate extra module text for %s", self.cfg['name']) else: - extras = Bundle.make_module_extra(self, *args, **kwargs) + extras = super(SystemCompiler, self).make_module_extra() return extras + def cleanup_step(self): + """Do nothing.""" + pass + + def permissions_step(self): + """Do nothing.""" + pass + def sanity_check_step(self, *args, **kwargs): """ Nothing is being installed, so just being able to load the (fake) module is sufficient diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index 64f935e842..523850dce6 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -215,6 +215,10 @@ def make_installdir(self, dontcreate=None): """Custom implementation of make installdir: do nothing, do not touch system MPI directories and files.""" pass + def post_install_step(self): + """Do nothing.""" + pass + def make_module_req_guess(self): """ A dictionary of possible directories to look for. Return known dict for the system MPI. @@ -277,13 +281,13 @@ def make_module_extend_modpath(self): self.cfg['version'] = self.mpi_version return res - def make_module_extra(self, *args, **kwargs): + def make_module_extra(self): """Add any additional module text.""" if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['OpenMPI']: - extras = ConfigureMake.make_module_extra(self, *args, **kwargs) + extras = ConfigureMake.make_module_extra(self) elif self.cfg['name'] in ['impi']: - extras = EB_impi.make_module_extra(self, *args, **kwargs) + extras = EB_impi.make_module_extra(self) else: raise EasyBuildError("I don't know how to generate extra module text for %s", self.cfg['name']) # include environment variables defined for MPI implementation @@ -291,9 +295,17 @@ def make_module_extra(self, *args, **kwargs): extras += self.module_generator.set_environment(key, val) self.log.debug("make_module_extra added this: %s" % extras) else: - extras = Bundle.make_module_extra(self, *args, **kwargs) + extras = super(SystemMPI, self).make_module_extra() return extras + def cleanup_step(self): + """Do nothing.""" + pass + + def permissions_step(self): + """Do nothing.""" + pass + def sanity_check_step(self, *args, **kwargs): """ Nothing is being installed, so just being able to load the (fake) module is sufficient From 6eaf360fa91094e29bc1f843963f521d2f1547b4 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Wed, 1 Mar 2017 19:33:50 +0100 Subject: [PATCH 46/59] Add missing LooseVersion --- easybuild/easyblocks/generic/systemcompiler.py | 1 + 1 file changed, 1 insertion(+) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 9da5f49e2b..8be4182502 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -32,6 +32,7 @@ import os import re from vsc.utils import fancylogger +from distutils.version import LooseVersion from easybuild.easyblocks.generic.bundle import Bundle from easybuild.easyblocks.i.icc import EB_icc From 2f5a09d5fdb85ce746ed3eaaa989e17cfa1ad9d1 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Wed, 1 Mar 2017 19:42:06 +0100 Subject: [PATCH 47/59] Add missing LooseVersion --- easybuild/easyblocks/generic/systemmpi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index 523850dce6..c99f326d82 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -113,7 +113,7 @@ def __init__(self, *args, **kwargs): # Extract any OpenMPI environment variables in the current environment and ensure they are added to the # final module - self.mpi_envvars = dict((key, value) for key, value in os.environ.iteritems() if key.startswith("OMPI_")) + self.mpi_env_vars = dict((key, value) for key, value in os.environ.iteritems() if key.startswith("OMPI_")) # Extract the C compiler used underneath OpenMPI, check for the definition of OMPI_MPICC self.mpi_c_compiler = self.extract_ompi_setting("C compiler", output_of_ompi_info) @@ -291,7 +291,7 @@ def make_module_extra(self): else: raise EasyBuildError("I don't know how to generate extra module text for %s", self.cfg['name']) # include environment variables defined for MPI implementation - for key, val in sorted(self.mpi_envvars.items()): + for key, val in sorted(self.mpi_env_vars.items()): extras += self.module_generator.set_environment(key, val) self.log.debug("make_module_extra added this: %s" % extras) else: From 4a3cc32ef4ab75e9be4e5eb07729549b3dea5c4b Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 15 Sep 2017 13:34:09 +0200 Subject: [PATCH 48/59] Address comments for systemcompiler.py --- .../easyblocks/generic/systemcompiler.py | 23 ++++++++++--------- easybuild/easyblocks/generic/systemmpi.py | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 8be4182502..155fbc26b5 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -35,10 +35,10 @@ from distutils.version import LooseVersion from easybuild.easyblocks.generic.bundle import Bundle -from easybuild.easyblocks.i.icc import EB_icc -from easybuild.easyblocks.i.ifort import EB_ifort -from easybuild.easyblocks.g.gcc import EB_GCC -from easybuild.tools.filetools import read_file, which +from easybuild.easyblocks.icc import EB_icc +from easybuild.easyblocks.ifort import EB_ifort +from easybuild.easyblocks.gcc import EB_GCC +from easybuild.tools.filetools import read_file, which, resolve_path from easybuild.tools.run import run_cmd from easybuild.framework.easyconfig.easyconfig import ActiveMNS from easybuild.framework.easyconfig import CUSTOM @@ -63,7 +63,7 @@ def extract_compiler_version(compiler_name): # A fully resolved icc/ifort (without symlinks) includes the release version in the path # e.g. .../composer_xe_2015.3.187/bin/intel64/icc # Match the last incidence of _ since we don't know what might be in the path, then split it up on / - out = (os.path.realpath(which(compiler_name)).split("_")[-1]).split("/") + out = (resolve_path(which(compiler_name)).split("_")[-1]).split("/") compiler_version = out[0] # Check what we have looks like a version number (the regex we use requires spaces around the version number) if version_regex.search(" " + compiler_version + " ") is None: @@ -95,6 +95,7 @@ class SystemCompiler(Bundle, EB_GCC, EB_icc, EB_ifort): @staticmethod def extra_options(): + """Add custom easyconfig parameters for SystemCompiler easyblock.""" # Gather extra_vars from inherited classes, order matters here to make this work without problems in __init__ extra_vars = EB_GCC.extra_options() extra_vars.update(EB_icc.extra_options()) @@ -121,7 +122,7 @@ def __init__(self, *args, **kwargs): compiler_name = 'gcc' path_to_compiler = which(compiler_name) if path_to_compiler: - path_to_compiler = os.path.realpath(path_to_compiler) + path_to_compiler = resolve_path(path_to_compiler) self.log.info("Found path to compiler '%s' (with symlinks resolved): %s", compiler_name, path_to_compiler) else: raise EasyBuildError("%s not found in $PATH", compiler_name) @@ -257,19 +258,19 @@ def make_module_extend_modpath(self): self.cfg['version'] = self.compiler_version return res - def make_module_extra(self): + def make_module_extra(self, *args, **kwargs): """Add any additional module text.""" if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['GCC','GCCcore']: - extras = EB_GCC.make_module_extra(self) + extras = EB_GCC.make_module_extra(self, *args, **kwargs) elif self.cfg['name'] in ['icc']: - extras = EB_icc.make_module_extra(self) + extras = EB_icc.make_module_extra(self, *args, **kwargs) elif self.cfg['name'] in ['ifort']: - extras = EB_ifort.make_module_extra(self) + extras = EB_ifort.make_module_extra(self, *args, **kwargs) else: raise EasyBuildError("I don't know how to generate extra module text for %s", self.cfg['name']) else: - extras = super(SystemCompiler, self).make_module_extra() + extras = super(SystemCompiler, self).make_module_extra(*args, **kwargs) return extras def cleanup_step(self): diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index c99f326d82..eda98de4d7 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -34,7 +34,7 @@ from easybuild.easyblocks.generic.bundle import Bundle from easybuild.easyblocks.generic.configuremake import ConfigureMake from easybuild.easyblocks.generic.systemcompiler import extract_compiler_version -from easybuild.easyblocks.i.impi import EB_impi +from easybuild.easyblocks.impi import EB_impi from easybuild.tools.modules import get_software_version from easybuild.tools.filetools import read_file, which from easybuild.tools.run import run_cmd From 58cb72ac3399390699d55e991ff1660d8e8d6ce0 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 15 Sep 2017 13:45:18 +0200 Subject: [PATCH 49/59] Address comments for systemmpi.py --- .../easyblocks/generic/systemcompiler.py | 4 +-- easybuild/easyblocks/generic/systemmpi.py | 27 +++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index f5bf8616b1..3925406393 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -38,11 +38,11 @@ from easybuild.easyblocks.icc import EB_icc from easybuild.easyblocks.ifort import EB_ifort from easybuild.easyblocks.gcc import EB_GCC -from easybuild.tools.filetools import read_file, which, resolve_path -from easybuild.tools.run import run_cmd from easybuild.framework.easyconfig.easyconfig import ActiveMNS from easybuild.framework.easyconfig import CUSTOM from easybuild.tools.build_log import EasyBuildError +from easybuild.tools.filetools import read_file, resolve_path, which +from easybuild.tools.run import run_cmd _log = fancylogger.getLogger('easyblocks.generic.systemcompiler') diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index eda98de4d7..c0b464c044 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -29,21 +29,18 @@ """ import os import re -from vsc.utils import fancylogger from easybuild.easyblocks.generic.bundle import Bundle from easybuild.easyblocks.generic.configuremake import ConfigureMake from easybuild.easyblocks.generic.systemcompiler import extract_compiler_version from easybuild.easyblocks.impi import EB_impi -from easybuild.tools.modules import get_software_version -from easybuild.tools.filetools import read_file, which -from easybuild.tools.run import run_cmd -from easybuild.tools.toolchain import DUMMY_TOOLCHAIN_NAME from easybuild.framework.easyconfig.easyconfig import ActiveMNS from easybuild.framework.easyconfig import CUSTOM from easybuild.tools.build_log import EasyBuildError - -_log = fancylogger.getLogger('easyblocks.generic.systemmpi') +from easybuild.tools.filetools import read_file, resolve_path, which +from easybuild.tools.modules import get_software_version +from easybuild.tools.run import run_cmd +from easybuild.tools.toolchain import DUMMY_TOOLCHAIN_NAME class SystemMPI(Bundle, ConfigureMake, EB_impi): """ @@ -57,6 +54,7 @@ class SystemMPI(Bundle, ConfigureMake, EB_impi): @staticmethod def extra_options(): + """Add custom easyconfig parameters for SystemMPI easyblock.""" # Gather extra_vars from inherited classes, order matters to make sure bundle initialises correctly extra_vars = ConfigureMake.extra_options() extra_vars.update(EB_impi.extra_options()) @@ -96,7 +94,7 @@ def __init__(self, *args, **kwargs): mpi_c_wrapper = 'mpicc' path_to_mpi_c_wrapper = which(mpi_c_wrapper) if path_to_mpi_c_wrapper: - path_to_mpi_c_wrapper = os.path.realpath(path_to_mpi_c_wrapper) + path_to_mpi_c_wrapper = resolve_path(path_to_mpi_c_wrapper) self.log.info("Found path to MPI implementation '%s' %s compiler (with symlinks resolved): %s", mpi_name, mpi_c_wrapper, path_to_mpi_c_wrapper) else: @@ -153,8 +151,9 @@ def __init__(self, *args, **kwargs): # final module self.mpi_env_vars = {} for key, value in os.environ.iteritems(): - if any(key.startswith(x) for x in ['I_MPI_', 'MPICH_']) or (key.startswith('MPI') and - key.endswith('PROFILE')): + i_mpi_key = key.startswith('I_MPI_') or key.startswith('MPICH_') + mpi_profile_key = key.startswith('MPI') and key.endswith('PROFILE') + if i_mpi_key or mpi_profile_key: self.mpi_env_vars[key] = value # Extract the C compiler used underneath Intel MPI @@ -281,13 +280,13 @@ def make_module_extend_modpath(self): self.cfg['version'] = self.mpi_version return res - def make_module_extra(self): + def make_module_extra(self, *args, **kwargs): """Add any additional module text.""" if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['OpenMPI']: - extras = ConfigureMake.make_module_extra(self) + extras = ConfigureMake.make_module_extra(self, *args, **kwargs) elif self.cfg['name'] in ['impi']: - extras = EB_impi.make_module_extra(self) + extras = EB_impi.make_module_extra(self, *args, **kwargs) else: raise EasyBuildError("I don't know how to generate extra module text for %s", self.cfg['name']) # include environment variables defined for MPI implementation @@ -295,7 +294,7 @@ def make_module_extra(self): extras += self.module_generator.set_environment(key, val) self.log.debug("make_module_extra added this: %s" % extras) else: - extras = super(SystemMPI, self).make_module_extra() + extras = super(SystemMPI, self).make_module_extra(*args, **kwargs) return extras def cleanup_step(self): From fddb19b9912ce4dc4f0737409e610ae0d766a6d5 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 15 Sep 2017 14:25:28 +0200 Subject: [PATCH 50/59] Allow either icc or gcc to sit underneath impi --- easybuild/easyblocks/generic/systemmpi.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index c0b464c044..f03708e0c2 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -89,10 +89,20 @@ def __init__(self, *args, **kwargs): # Determine MPI wrapper path (real path, with resolved symlinks) to ensure it exists if mpi_name == 'impi': + # For impi the version information is only found in *some* of the wrappers it ships, in particular it is + # not in mpicc mpi_c_wrapper = 'mpiicc' + path_to_mpi_c_wrapper = which(mpi_c_wrapper) + if not path_to_mpi_c_wrapper: + mpi_c_wrapper = 'mpigcc' + path_to_mpi_c_wrapper = which(mpi_c_wrapper) + if not path_to_mpi_c_wrapper: + raise EasyBuildError("Could not find suitable MPI wrapper to extract version for impi", + mpi_c_wrapper) else: mpi_c_wrapper = 'mpicc' - path_to_mpi_c_wrapper = which(mpi_c_wrapper) + path_to_mpi_c_wrapper = which(mpi_c_wrapper) + if path_to_mpi_c_wrapper: path_to_mpi_c_wrapper = resolve_path(path_to_mpi_c_wrapper) self.log.info("Found path to MPI implementation '%s' %s compiler (with symlinks resolved): %s", @@ -118,18 +128,18 @@ def __init__(self, *args, **kwargs): elif mpi_name == 'impi': # Extract the version of IntelMPI - # The prefix in the the mpiicc script can be used to extract the explicit version - contents_of_mpiicc = read_file(path_to_mpi_c_wrapper) + # The prefix in the the mpiicc (or mpigcc) script can be used to extract the explicit version + contents_of_mpixcc = read_file(path_to_mpi_c_wrapper) prefix_regex = re.compile(r'(?<=compilers_and_libraries_)(.*)(?=/linux/mpi)', re.M) self.mpi_version = None - res = prefix_regex.search(contents_of_mpiicc) + res = prefix_regex.search(contents_of_mpixcc) if res: self.mpi_version = res.group(1) else: # old iimpi version prefix_regex = re.compile(r'^prefix=(.*)$', re.M) - res = prefix_regex.search(contents_of_mpiicc) + res = prefix_regex.search(contents_of_mpixcc) if res: self.mpi_version = res.group(1).split('/')[-1] From a1facb196ac636959373f1c07038bdffd89666c5 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 15 Sep 2017 15:07:10 +0200 Subject: [PATCH 51/59] Add `*args, **kwargs` to prepare_step(s) --- easybuild/easyblocks/generic/systemcompiler.py | 2 +- easybuild/easyblocks/generic/systemmpi.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 3925406393..d120261299 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -190,7 +190,7 @@ def __init__(self, *args, **kwargs): self.orig_version = self.cfg['version'] self.orig_installdir = self.installdir - def prepare_step(self): + def prepare_step(self, *args, **kwargs): """Do the bundle prepare step to ensure any deps are loaded at a minimum.""" if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['GCC', 'GCCcore']: diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index f03708e0c2..fe8505afd4 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -216,7 +216,7 @@ def __init__(self, *args, **kwargs): self.orig_version = self.cfg['version'] self.orig_installdir = self.installdir - def prepare_step(self): + def prepare_step(self, *args, **kwargs): """Do the bundle prepare step to ensure any deps are loaded. No need to worry about licences for Intel MPI""" Bundle.prepare_step(self) From 570cf4702afc8bb387710c0e71470f90c61f04c5 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 15 Sep 2017 15:09:16 +0200 Subject: [PATCH 52/59] Add `*args, **kwargs` to prepare_step(s)...in full this time --- easybuild/easyblocks/generic/systemcompiler.py | 8 ++++---- easybuild/easyblocks/generic/systemmpi.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index d120261299..9f3a41e40d 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -194,15 +194,15 @@ def prepare_step(self, *args, **kwargs): """Do the bundle prepare step to ensure any deps are loaded at a minimum.""" if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['GCC', 'GCCcore']: - EB_GCC.prepare_step(self) + EB_GCC.prepare_step(self, *args, **kwargs) elif self.cfg['name'] in ['icc']: - EB_icc.prepare_step(self) + EB_icc.prepare_step(self, *args, **kwargs) elif self.cfg['name'] in ['ifort']: - EB_ifort.prepare_step(self) + EB_ifort.prepare_step(self, *args, **kwargs) else: raise EasyBuildError("I don't know how to do the prepare_step for %s", self.cfg['name']) else: - Bundle.prepare_step(self) + Bundle.prepare_step(self, *args, **kwargs) def make_installdir(self, dontcreate=None): """Custom implementation of make installdir: do nothing, do not touch system compiler directories and files.""" diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index fe8505afd4..c38a1422ab 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -218,7 +218,7 @@ def __init__(self, *args, **kwargs): def prepare_step(self, *args, **kwargs): """Do the bundle prepare step to ensure any deps are loaded. No need to worry about licences for Intel MPI""" - Bundle.prepare_step(self) + Bundle.prepare_step(self, *args, **kwargs) def make_installdir(self, dontcreate=None): """Custom implementation of make installdir: do nothing, do not touch system MPI directories and files.""" From 3a1496a0c73fbd5f6bbe41bf789d22fc3d55e55b Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 15 Sep 2017 17:09:04 +0200 Subject: [PATCH 53/59] Add `*args, **kwargs` to prepare_step(s)...in full this time --- easybuild/easyblocks/generic/systemcompiler.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 9f3a41e40d..8a4dac3e91 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -35,7 +35,6 @@ from distutils.version import LooseVersion from easybuild.easyblocks.generic.bundle import Bundle -from easybuild.easyblocks.icc import EB_icc from easybuild.easyblocks.ifort import EB_ifort from easybuild.easyblocks.gcc import EB_GCC from easybuild.framework.easyconfig.easyconfig import ActiveMNS @@ -83,7 +82,8 @@ def extract_compiler_version(compiler_name): return compiler_version -class SystemCompiler(Bundle, EB_GCC, EB_icc, EB_ifort): +# No need to inherit from EB_icc since EB_ifort already inherits from that +class SystemCompiler(Bundle, EB_GCC, EB_ifort): """ Support for generating a module file for the system compiler with specified name. @@ -98,7 +98,6 @@ def extra_options(): """Add custom easyconfig parameters for SystemCompiler easyblock.""" # Gather extra_vars from inherited classes, order matters here to make this work without problems in __init__ extra_vars = EB_GCC.extra_options() - extra_vars.update(EB_icc.extra_options()) extra_vars.update(EB_ifort.extra_options()) extra_vars.update(Bundle.extra_options()) # Add an option to add all module path extensions to the resultant easyconfig @@ -195,8 +194,6 @@ def prepare_step(self, *args, **kwargs): if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['GCC', 'GCCcore']: EB_GCC.prepare_step(self, *args, **kwargs) - elif self.cfg['name'] in ['icc']: - EB_icc.prepare_step(self, *args, **kwargs) elif self.cfg['name'] in ['ifort']: EB_ifort.prepare_step(self, *args, **kwargs) else: @@ -216,8 +213,6 @@ def make_module_req_guess(self): if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['GCC','GCCcore']: guesses = EB_GCC.make_module_req_guess(self) - elif self.cfg['name'] in ['icc']: - guesses = EB_icc.make_module_req_guess(self) elif self.cfg['name'] in ['ifort']: guesses = EB_ifort.make_module_req_guess(self) else: @@ -263,8 +258,6 @@ def make_module_extra(self, *args, **kwargs): if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['GCC','GCCcore']: extras = EB_GCC.make_module_extra(self, *args, **kwargs) - elif self.cfg['name'] in ['icc']: - extras = EB_icc.make_module_extra(self, *args, **kwargs) elif self.cfg['name'] in ['ifort']: extras = EB_ifort.make_module_extra(self, *args, **kwargs) else: From 951a9741345e95d586965abc8aaed19bc4c56bbd Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 15 Sep 2017 17:32:31 +0200 Subject: [PATCH 54/59] Revert silly changes --- easybuild/easyblocks/generic/systemcompiler.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index 8a4dac3e91..d108c5ab0b 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -35,6 +35,7 @@ from distutils.version import LooseVersion from easybuild.easyblocks.generic.bundle import Bundle +from easybuild.easyblocks.icc import EB_icc from easybuild.easyblocks.ifort import EB_ifort from easybuild.easyblocks.gcc import EB_GCC from easybuild.framework.easyconfig.easyconfig import ActiveMNS @@ -98,6 +99,7 @@ def extra_options(): """Add custom easyconfig parameters for SystemCompiler easyblock.""" # Gather extra_vars from inherited classes, order matters here to make this work without problems in __init__ extra_vars = EB_GCC.extra_options() + extra_vars.update(EB_icc.extra_options()) extra_vars.update(EB_ifort.extra_options()) extra_vars.update(Bundle.extra_options()) # Add an option to add all module path extensions to the resultant easyconfig @@ -194,6 +196,8 @@ def prepare_step(self, *args, **kwargs): if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['GCC', 'GCCcore']: EB_GCC.prepare_step(self, *args, **kwargs) + elif self.cfg['name'] in ['icc']: + EB_icc.prepare_step(self, *args, **kwargs) elif self.cfg['name'] in ['ifort']: EB_ifort.prepare_step(self, *args, **kwargs) else: @@ -213,6 +217,8 @@ def make_module_req_guess(self): if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['GCC','GCCcore']: guesses = EB_GCC.make_module_req_guess(self) + elif self.cfg['name'] in ['icc']: + guesses = EB_icc.make_module_req_guess(self) elif self.cfg['name'] in ['ifort']: guesses = EB_ifort.make_module_req_guess(self) else: @@ -258,6 +264,8 @@ def make_module_extra(self, *args, **kwargs): if self.cfg['generate_standalone_module']: if self.cfg['name'] in ['GCC','GCCcore']: extras = EB_GCC.make_module_extra(self, *args, **kwargs) + elif self.cfg['name'] in ['icc']: + extras = EB_icc.make_module_extra(self, *args, **kwargs) elif self.cfg['name'] in ['ifort']: extras = EB_ifort.make_module_extra(self, *args, **kwargs) else: From 7594102d9473d23c39fca7acec70917b7919a6d2 Mon Sep 17 00:00:00 2001 From: Alan O'Cais Date: Fri, 15 Sep 2017 18:35:59 +0200 Subject: [PATCH 55/59] Fix attribute bug --- easybuild/easyblocks/generic/systemmpi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index c38a1422ab..f3bbd8038d 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -186,7 +186,7 @@ def __init__(self, *args, **kwargs): # Force off adding paths to module since unloading such a module would be a potential shell killer self.cfg['generate_standalone_module'] = False self.log.warning("Disabling option 'generate_standalone_module' since installation prefix is %s", - self.compiler_prefix) + self.mpi_prefix) self.log.debug("Derived version/install prefix for system MPI %s: %s, %s", mpi_name, self.mpi_version, self.mpi_prefix) From 477002f3866b451a2eef68d8b8cccf48f76bf339 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Sat, 30 Sep 2017 16:12:18 +0200 Subject: [PATCH 56/59] pass in post_install_step to avoid problems with symlinks created by GCC easyblock --- easybuild/easyblocks/generic/systemcompiler.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index d108c5ab0b..b33653c92d 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -274,6 +274,10 @@ def make_module_extra(self, *args, **kwargs): extras = super(SystemCompiler, self).make_module_extra(*args, **kwargs) return extras + def post_install_step(self, *args, **kwargs): + """Do nothing.""" + pass + def cleanup_step(self): """Do nothing.""" pass From f35d52ac345004e419a3ba9b03b07fc39db70111 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Sat, 30 Sep 2017 19:51:45 +0200 Subject: [PATCH 57/59] fix error reporting for missing MPI compiler wrapper for impi --- easybuild/easyblocks/generic/systemmpi.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/easybuild/easyblocks/generic/systemmpi.py b/easybuild/easyblocks/generic/systemmpi.py index f3bbd8038d..eb5a15370d 100644 --- a/easybuild/easyblocks/generic/systemmpi.py +++ b/easybuild/easyblocks/generic/systemmpi.py @@ -97,8 +97,7 @@ def __init__(self, *args, **kwargs): mpi_c_wrapper = 'mpigcc' path_to_mpi_c_wrapper = which(mpi_c_wrapper) if not path_to_mpi_c_wrapper: - raise EasyBuildError("Could not find suitable MPI wrapper to extract version for impi", - mpi_c_wrapper) + raise EasyBuildError("Could not find suitable MPI wrapper to extract version for impi") else: mpi_c_wrapper = 'mpicc' path_to_mpi_c_wrapper = which(mpi_c_wrapper) From 71cec4210010f7a6efe45daafc364521e80c8cdb Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Wed, 11 Oct 2017 08:36:10 +0200 Subject: [PATCH 58/59] pass down *args and **kwargs in make_module_extra rather than working around it --- easybuild/easyblocks/generic/bundle.py | 8 ++++++-- easybuild/easyblocks/generic/intelbase.py | 4 ++-- easybuild/easyblocks/i/icc.py | 4 ++-- easybuild/easyblocks/i/impi.py | 4 ++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/easybuild/easyblocks/generic/bundle.py b/easybuild/easyblocks/generic/bundle.py index 3e18dcbacc..bde721e01f 100644 --- a/easybuild/easyblocks/generic/bundle.py +++ b/easybuild/easyblocks/generic/bundle.py @@ -160,9 +160,13 @@ def install_step(self): new_val = path env.setvar(envvar, new_val) - def make_module_extra(self): + def make_module_extra(self, *args, **kwargs): """Set extra stuff in module file, e.g. $EBROOT*, $EBVERSION*, etc.""" - return EasyBlock.make_module_extra(self, altroot=self.altroot, altversion=self.altversion) + if 'altroot' not in kwargs: + kwargs['altroot'] = self.altroot + if 'altversion' not in kwargs: + kwargs['altversion'] = self.altversion + return super(Bundle, self).make_module_extra(*args, **kwargs) def sanity_check_step(self, *args, **kwargs): """ diff --git a/easybuild/easyblocks/generic/intelbase.py b/easybuild/easyblocks/generic/intelbase.py index 0bbc712526..2caac63e32 100644 --- a/easybuild/easyblocks/generic/intelbase.py +++ b/easybuild/easyblocks/generic/intelbase.py @@ -389,9 +389,9 @@ def sanity_check_rpath(self): self.log.info("RPATH sanity check is skipped when using %s easyblock (derived from IntelBase)", self.__class__.__name__) - def make_module_extra(self): + def make_module_extra(self, *args, **kwargs): """Custom variable definitions in module file.""" - txt = super(IntelBase, self).make_module_extra() + txt = super(IntelBase, self).make_module_extra(*args, **kwargs) if self.requires_runtime_license: txt += self.module_generator.prepend_paths(self.license_env_var, [self.license_file], diff --git a/easybuild/easyblocks/i/icc.py b/easybuild/easyblocks/i/icc.py index 9af30aef14..b853f59bd0 100644 --- a/easybuild/easyblocks/i/icc.py +++ b/easybuild/easyblocks/i/icc.py @@ -217,9 +217,9 @@ def make_module_req_guess(self): return guesses - def make_module_extra(self): + def make_module_extra(self, *args, **kwargs): """Additional custom variables for icc: $INTEL_PYTHONHOME.""" - txt = super(EB_icc, self).make_module_extra() + txt = super(EB_icc, self).make_module_extra(*args, **kwargs) if self.debuggerpath: intel_pythonhome = os.path.join(self.installdir, self.debuggerpath, 'python', 'intel64') diff --git a/easybuild/easyblocks/i/impi.py b/easybuild/easyblocks/i/impi.py index 6b0ae60fc8..38e7fca5bb 100644 --- a/easybuild/easyblocks/i/impi.py +++ b/easybuild/easyblocks/i/impi.py @@ -195,9 +195,9 @@ def make_module_req_guess(self): 'MIC_LD_LIBRARY_PATH' : ['mic/lib'], } - def make_module_extra(self): + def make_module_extra(self, *args, **kwargs): """Overwritten from Application to add extra txt""" - txt = super(EB_impi, self).make_module_extra() + txt = super(EB_impi, self).make_module_extra(*args, **kwargs) txt += self.module_generator.set_environment('I_MPI_ROOT', self.installdir) if self.cfg['set_mpi_wrappers_compiler'] or self.cfg['set_mpi_wrappers_all']: for var in ['CC', 'CXX', 'F77', 'F90', 'FC']: From aa2e77e801e8a9a49eb0aba7df62e008d7cfaf70 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Wed, 11 Oct 2017 09:01:06 +0200 Subject: [PATCH 59/59] improve error reporting for missing compiler, fix error reporting for weird derived Intel compiler version --- .../easyblocks/generic/systemcompiler.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/easybuild/easyblocks/generic/systemcompiler.py b/easybuild/easyblocks/generic/systemcompiler.py index b33653c92d..d06b5b5026 100644 --- a/easybuild/easyblocks/generic/systemcompiler.py +++ b/easybuild/easyblocks/generic/systemcompiler.py @@ -63,20 +63,21 @@ def extract_compiler_version(compiler_name): # A fully resolved icc/ifort (without symlinks) includes the release version in the path # e.g. .../composer_xe_2015.3.187/bin/intel64/icc # Match the last incidence of _ since we don't know what might be in the path, then split it up on / - out = (resolve_path(which(compiler_name)).split("_")[-1]).split("/") - compiler_version = out[0] + compiler_path = which(compiler_name) + if compiler_path: + compiler_version = resolve_path(compiler_path).split('_')[-1].split('/')[0] + else: + raise EasyBuildError("Compiler command '%s' not found", compiler_name) # Check what we have looks like a version number (the regex we use requires spaces around the version number) - if version_regex.search(" " + compiler_version + " ") is None: - typical_install_path = '.../composer_xe_2015.3.187/bin/intel64/icc' - raise EasyBuildError( - "Derived Intel compiler version %s doesn't look correct, is compiler installed in a path like %s?", - typical_install_path, compiler_version - ) + if version_regex.search(' ' + compiler_version + ' ') is None: + error_msg = "Derived Intel compiler version '%s' doesn't look correct, " % compiler_version + error_msg += "is compiler installed in a path like '.../composer_xe_2015.3.187/bin/intel64/icc'?" + raise EasyBuildError(error_msg) else: raise EasyBuildError("Unknown compiler %s", compiler_name) if compiler_version: - _log.debug("Extracted compiler version '%s' for %s from: %s", compiler_version, compiler_name, out) + _log.debug("Extracted compiler version '%s' for %s", compiler_version, compiler_name) else: raise EasyBuildError("Failed to extract compiler version for %s using regex pattern '%s' from: %s", compiler_name, version_regex.pattern, out)