-
Notifications
You must be signed in to change notification settings - Fork 285
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add generic SystemMPI easyblock to generate module for existing MPI library installation #1106
Changes from 61 commits
c2fcd86
b3bdaae
902b9cc
8f789ce
da237ca
d3d4c5f
02520ce
3309b16
6d380fe
f89a9e4
78f6115
5e7eaa5
7fc6265
b9ced9d
1877b18
c52c92d
44026e6
7b7f704
abacb35
cfa57f6
212e53a
bcab0fa
c51a0f7
4f76ca0
7850024
eae2b8e
f4a3ea6
5a88443
b0ee25e
f144400
7b525b0
60b86ce
3d5263c
4096508
8e32528
ccee79b
89e13be
637a815
5f17da8
b62ac7c
8d0dd01
e3985f9
53b3c34
e88e2a1
0078d2f
0250ce3
6eaf360
2f5a09d
4a3cc32
61ba4e5
58cb72a
fddb19b
a1facb1
570cf47
3a1496a
951a974
7594102
477002f
c90a799
f35d52a
2206203
71cec42
aa2e77e
a43637a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,18 +27,64 @@ | |
|
||
@author Bernd Mohr (Juelich Supercomputing Centre) | ||
@author Kenneth Hoste (Ghent University) | ||
@author Alan O'Cais (Juelich Supercomputing Centre) | ||
""" | ||
import os | ||
import re | ||
from vsc.utils import fancylogger | ||
from distutils.version import LooseVersion | ||
|
||
from easybuild.easyblocks.generic.bundle import Bundle | ||
from easybuild.tools.filetools import read_file, which | ||
from easybuild.tools.run import run_cmd | ||
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 | ||
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') | ||
|
||
def extract_compiler_version(compiler_name): | ||
"""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) | ||
# 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) | ||
if res is None: | ||
raise EasyBuildError("Could not extract GCC version from %s", out) | ||
compiler_version = res.group(1) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. make sure |
||
elif compiler_name in ['icc', 'ifort']: | ||
# 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] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm, shouldn't we do a sanity check on this to make sure what we end up with looks like a version? e.g. check for only digits and |
||
# 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) | ||
|
||
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", | ||
compiler_name, version_regex.pattern, out) | ||
|
||
class SystemCompiler(Bundle): | ||
return compiler_version | ||
|
||
# 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. | ||
|
||
|
@@ -48,46 +94,49 @@ 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) | ||
@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__ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing docstring: def extra_options():
"""Add custom easyconfig parameters for SystemCompiler easyblock."""
# Gather ... |
||
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()) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. change accordingly if you list |
||
# 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({ | ||
'generate_standalone_module': [ | ||
False, | ||
"Add known path/library extensions and environment variables for the compiler to the final module", | ||
CUSTOM | ||
], | ||
}) | ||
return extra_vars | ||
|
||
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) | ||
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) | ||
|
||
# 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 = extract_compiler_version(compiler_name) | ||
|
||
# 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) | ||
|
@@ -103,12 +152,27 @@ 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 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)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use the above as fallback rather than overriding it; also, use elif LooseVersion(self.compiler_version) >= LooseVersion('2016'):
# for versions 2016+ of Intel compilers they changed the installation path
self.compiler_prefix = os.path.dirname(os.path.dirname(self.compiler_prefix))
else:
# strip off 'bin/intel*/icc'
self.compiler_prefix = os.path.dirname(os.path.dirname(os.path.dirname(path_to_compiler))) |
||
|
||
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) | ||
|
||
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'", | ||
|
@@ -127,15 +191,41 @@ def __init__(self, *args, **kwargs): | |
self.orig_version = self.cfg['version'] | ||
self.orig_installdir = self.installdir | ||
|
||
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, *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: | ||
raise EasyBuildError("I don't know how to do the prepare_step for %s", self.cfg['name']) | ||
else: | ||
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.""" | ||
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, or empty dict if | ||
generate_standalone_module parameter is False | ||
""" | ||
return {} | ||
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: | ||
raise EasyBuildError("I don't know how to generate module var guesses for %s", self.cfg['name']) | ||
else: | ||
guesses = {} | ||
return guesses | ||
|
||
def make_module_step(self, fake=False): | ||
""" | ||
|
@@ -168,3 +258,39 @@ 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, *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, *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: | ||
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) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @boegel For a system
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This error only occurs when There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should I be doing nothing if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're not using the customised There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I ran into this same problem when testing the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, confirmed, works with the |
||
return extras | ||
|
||
def post_install_step(self, *args, **kwargs): | ||
"""Do nothing.""" | ||
pass | ||
|
||
def cleanup_step(self): | ||
"""Do nothing.""" | ||
pass | ||
|
||
def permissions_step(self): | ||
"""Do nothing.""" | ||
pass | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need to redefine these either if we derive from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are required because they are not inherited from bundle (which doesn't define them) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, ok |
||
|
||
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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there's a better way of fixing the issue you bumped into, see ocaisa#12