Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
c2fcd86
First go at a system OpenMPI easyblock
Feb 10, 2017
b3bdaae
Switch computer
Feb 13, 2017
902b9cc
Make extract_compiler_version available to SystemMPI
Feb 14, 2017
8f789ce
Update outdated comments
Feb 15, 2017
da237ca
Fix variable typo
Feb 15, 2017
d3d4c5f
Correctly update version in make_module_extend_modpath
Feb 15, 2017
02520ce
Correct syntax problem
Feb 15, 2017
3309b16
Add support for impi
Feb 16, 2017
6d380fe
Address review comments
Feb 16, 2017
f89a9e4
Add OpenMPI to travis
Feb 16, 2017
78f6115
Allow tests on Travis to pass
Feb 16, 2017
5e7eaa5
Allow tests on Travis to pass
Feb 16, 2017
7fc6265
Typo
Feb 16, 2017
b9ced9d
Add another cheat to get Travis to pass his tests
Feb 16, 2017
1877b18
Stop cheating on tests
Feb 16, 2017
c52c92d
Add special case to tests for SystemMPI
Feb 16, 2017
44026e6
Roll back toolchain addition in tests
Feb 16, 2017
7b7f704
Add special case for dummy to module-only test
Feb 16, 2017
abacb35
Fix bugs in impi path
Feb 16, 2017
cfa57f6
Need to go one deeper in search for impi root due to symlink
Feb 16, 2017
212e53a
systemcompiler is not good enough to resolve compilers not in the def…
Feb 16, 2017
bcab0fa
Update systemcompiler.py
Feb 16, 2017
c51a0f7
Add option to systemcompiler to attempt to find the path extensions f…
Feb 17, 2017
4f76ca0
Merge remote-tracking branch 'origin/systemmpi' into systemmpi
Feb 17, 2017
7850024
Add author
Feb 17, 2017
eae2b8e
Implement path extensions for systemmpi
Feb 17, 2017
f4a3ea6
Implement path extensions for systemmpi
Feb 17, 2017
5a88443
Make sure systemcompiler finds the correct install paths for version …
Feb 17, 2017
b0ee25e
Try to get system compiler to match the correct version of intel comp…
Feb 17, 2017
f144400
Fix compiler version detection for intel
Feb 17, 2017
7b525b0
Fix dodgy variable name
Feb 17, 2017
60b86ce
Allow picking up of the licence for intel compilers
Feb 17, 2017
3d5263c
Make sure 201* is treated as a number
Feb 17, 2017
4096508
Support older version of impi
Feb 23, 2017
8e32528
Address comments
Mar 1, 2017
ccee79b
Use the GCC init as the bundle causes errors because of unexpected va…
Mar 1, 2017
89e13be
Change order in extra_options to avoid the error in __init__
Mar 1, 2017
637a815
Change order in extra_options to avoid the error in __init__
Mar 1, 2017
5f17da8
Add arguments to Bundle.make_module_extra
Mar 1, 2017
b62ac7c
Fix error with make_module_extra when not having __init__ from the bu…
Mar 1, 2017
8d0dd01
Ignore the intelbase prepare_step, just use the bundle one
Mar 1, 2017
e3985f9
Try to overcome make_module_extra error
Mar 1, 2017
53b3c34
Try to overcome make_module_extra error by passing the arguments down
Mar 1, 2017
e88e2a1
Try to overcome make_module_extra error by passing the arguments down
Mar 1, 2017
0078d2f
Try to overcome make_module_extra error by passing the arguments down
Mar 1, 2017
0250ce3
Try to overcome make_module_extra error by passing the arguments down
Mar 1, 2017
6eaf360
Add missing LooseVersion
Mar 1, 2017
2f5a09d
Add missing LooseVersion
Mar 1, 2017
4a3cc32
Address comments for systemcompiler.py
Sep 15, 2017
61ba4e5
Merge branch 'develop' into systemmpi
Sep 15, 2017
58cb72a
Address comments for systemmpi.py
Sep 15, 2017
fddb19b
Allow either icc or gcc to sit underneath impi
Sep 15, 2017
a1facb1
Add `*args, **kwargs` to prepare_step(s)
Sep 15, 2017
570cf47
Add `*args, **kwargs` to prepare_step(s)...in full this time
Sep 15, 2017
3a1496a
Add `*args, **kwargs` to prepare_step(s)...in full this time
Sep 15, 2017
951a974
Revert silly changes
Sep 15, 2017
7594102
Fix attribute bug
Sep 15, 2017
477002f
pass in post_install_step to avoid problems with symlinks created by …
boegel Sep 30, 2017
c90a799
Merge pull request #10 from boegel/systemmpi
Sep 30, 2017
f35d52a
fix error reporting for missing MPI compiler wrapper for impi
boegel Sep 30, 2017
2206203
Merge pull request #11 from boegel/systemmpi
Sep 30, 2017
71cec42
pass down *args and **kwargs in make_module_extra rather than working…
boegel Oct 11, 2017
aa2e77e
improve error reporting for missing compiler, fix error reporting for…
boegel Oct 11, 2017
a43637a
Merge pull request #12 from boegel/systemmpi
Oct 11, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 30 additions & 21 deletions easybuild/easyblocks/generic/systemcompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Member Author

@ocaisa ocaisa Feb 17, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@geimer The version extracted here doesn't actually match the version used by EasyBuild:

extracted:  17.0.0.098
EB version: 2017.0.098

I could just extract that from the product path (for <2016 one search string, for later stuff another)

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't pass down the debug log message, just log here; also, we try to avoid 'inline' return statements

_log = fancylogger.getLogger('easyblocks.generic.systemcompiler')
...
def extract_compiler_version(compiler_name):

    ...
    if res:
        compiler_version = res.group(1)
        _log.debug("Extracted compiler version '%s' for %s from: %s", compiler_version, compiler_name, out)
    else:
        raise EasyBuildError("...")

    return compiler_version

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.
Expand All @@ -48,46 +71,32 @@ 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."""
super(SystemCompiler, self).__init__(*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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here too, use resolve_path

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)

# 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)
Expand Down
195 changes: 195 additions & 0 deletions easybuild/easyblocks/generic/systemmpi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
##
# Copyright 2015-2016 Ghent University
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2015-2017

#
# 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 <http://www.gnu.org/licenses/>.
##
"""
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.easyblocks.generic.systemcompiler import extract_compiler_version
from easybuild.tools.modules import get_software_version
from easybuild.tools.filetools import read_file, which
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

read_file is not used

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is now :)

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 mpi with specified name.
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 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_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
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update this comment!

version_regex = re.compile(r'^\s+%s: (.*)$' % search, re.M)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename search to pattern?

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid inline return please

else:
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 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment is in wrong place?

mpi_name = self.cfg['name'].lower()

# Check we have MPI wrappers
if mpi_name != 'impi':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use positive logic:

if mpi_name == 'impi':
    mpi_c_wrapper = 'mpiicc'
else:
    mpi_c_wrapper = 'mpicc'

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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we now have resolve_path for this (from filetools)

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:
raise EasyBuildError("%s not found in $PATH", mpi_c_wrapper)

# Determine compiler version and installation prefix
if mpi_name == 'openmpi':
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 = 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
# final module
raw_env = os.environ
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to introduce raw_env, just use os.environ on line below?

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'",
self.cfg['version'], self.mpi_version)
elif self.cfg['version'] != self.mpi_version:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use positive logic, use log.info to inform that specified version matches found version

raise EasyBuildError("Specified version (%s) does not match version reported by MPI (%s)" %
(self.cfg['version'], self.mpi_version))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use , rather than %, both in EasyBuildError and self.log.*


# 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."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/compiler/MPI/g

pass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a whole bunch of these are not needed if you derive from Bundle first?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nevermind, ok


def make_module_req_guess(self):
"""
A dictionary of possible directories to look for. Return empty dict for a system compiler.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/compiler/MPI/g

"""
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.
"""
# First let's verify that the toolchain and the compilers under MPI match
c_compiler_name = self.toolchain.COMPILER_CC
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this into the else

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/compiler/MPI/g

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").
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

s/4.8.2/2.0.2/g

"""
# 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.compiler_version
return res

def make_module_extra(self):
"""Add all the build environment variables."""
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix docstring (nothing is being built?)

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