Skip to content
Merged
Changes from all commits
Commits
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
92 changes: 84 additions & 8 deletions easybuild/easyblocks/o/openblas.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

@author: Andrew Edmondson (University of Birmingham)
@author: Alex Domingo (Vrije Universiteit Brussel)
@author: Jasper Grimm (University of York)
@author: Kenneth Hoste (Ghent University)
"""
import os
Expand All @@ -12,6 +13,7 @@
from easybuild.framework.easyconfig import CUSTOM
from easybuild.tools.build_log import EasyBuildError, print_warning
from easybuild.tools.config import build_option
from easybuild.tools.filetools import apply_regex_substitutions
from easybuild.tools.run import run_shell_cmd
from easybuild.tools.systemtools import AARCH64, POWER, get_cpu_architecture, get_shared_lib_ext
from easybuild.tools.toolchain.compiler import OPTARCH_GENERIC
Expand All @@ -28,6 +30,9 @@ class EB_OpenBLAS(ConfigureMake):
def extra_options():
"""Custom easyconfig parameters for OpenBLAS easyblock."""
extra_vars = {
'enable_ilp64': [True, "Also build OpenBLAS with 64-bit integer support", CUSTOM],
'ilp64_lib_suffix': ['64', "Library name suffix to use when building with 64-bit integers", CUSTOM],
'ilp64_symbol_suffix': ['64_', "Symbol suffix to use when building with 64-bit integers", CUSTOM],
'max_failing_lapack_tests_num_errors': [0, "Maximum number of LAPACK tests failing "
"due to numerical errors", CUSTOM],
'max_failing_lapack_tests_other_errors': [0, "Maximum number of LAPACK tests failing "
Expand All @@ -38,9 +43,27 @@ def extra_options():

return ConfigureMake.extra_options(extra_vars)

def __init__(self, *args, **kwargs):
""" Ensure iterative build if also building with 64-bit integer support """
super(EB_OpenBLAS, self).__init__(*args, **kwargs)

if self.cfg['enable_ilp64']:
if not isinstance(self.cfg['buildopts'], list):
niter = 1 + sum([bool(self.cfg[x]) for x in ['ilp64_lib_suffix', 'ilp64_symbol_suffix']])
# ensure iterative build by duplicating buildopts
self.cfg['buildopts'] = [self.cfg['buildopts']] * niter
else:
print_warning("buildopts cannot be a list when 'enable_ilp64' is enabled; ignoring 'enable_ilp64'")
self.cfg['enable_ilp64'] = False

self.orig_opts = {
'buildopts': '',
'testopts': '',
'installopts': '',
}

def configure_step(self):
""" set up some options - but no configure command to run"""

default_opts = {
'BINARY': '64',
'CC': os.getenv('CC'),
Expand All @@ -50,6 +73,26 @@ def configure_step(self):
'USE_THREAD': '1',
}

ilp64_lib_opts = {
'INTERFACE64': '1',
'LIBPREFIX': f"libopenblas{self.cfg['ilp64_lib_suffix']}",
}
ilp64_symbol_opts = {
'INTERFACE64': '1',
'SYMBOLSUFFIX': self.cfg['ilp64_symbol_suffix'],
}

# ensure build/test/install options don't persist between iterations
if self.cfg['enable_ilp64']:
if self.iter_idx > 0:
# reset to original build/test/install options
for key in self.orig_opts.keys():
self.cfg[key] = self.orig_opts[key]
else:
# store original options
for key in self.orig_opts.keys():
self.orig_opts[key] = self.cfg[key]

if '%s=' % TARGET in self.cfg['buildopts']:
# Add any TARGET in buildopts to default_opts, so it is passed to testopts and installopts
for buildopt in self.cfg['buildopts'].split():
Expand Down Expand Up @@ -78,10 +121,23 @@ def configure_step(self):
self.log.info("Replaced -mcpu=generic with -mtune=generic in $CFLAGS")
env.setvar('CFLAGS', cflags)

for key in sorted(default_opts.keys()):
all_opts = default_opts.copy()
if self.iter_idx > 0 and self.cfg['enable_ilp64']:
# update build/test/install options for ILP64
if self.cfg['ilp64_lib_suffix'] and self.cfg['ilp64_symbol_suffix']:
if self.iter_idx == 1:
all_opts.update(ilp64_lib_opts)
else:
all_opts.update(ilp64_symbol_opts)
elif self.cfg['ilp64_lib_suffix']:
all_opts.update(ilp64_lib_opts)
elif self.cfg['ilp64_symbol_suffix']:
all_opts.update(ilp64_symbol_opts)

for key in sorted(all_opts.keys()):
for opts_key in ['buildopts', 'testopts', 'installopts']:
if '%s=' % key not in self.cfg[opts_key]:
self.cfg.update(opts_key, "%s='%s'" % (key, default_opts[key]))
if f'{key}=' not in self.cfg[opts_key]:
self.cfg.update(opts_key, f"{key}='{all_opts[key]}'")

self.cfg.update('installopts', 'PREFIX=%s' % self.installdir)

Expand All @@ -101,15 +157,27 @@ def build_step(self):
# Pass CFLAGS through command line to avoid redefinitions (issue xianyi/OpenBLAS#818)
cflags = 'CFLAGS'
if os.environ[cflags]:
self.cfg.update('buildopts', "%s='%s'" % (cflags, os.environ[cflags]))
self.cfg.update('buildopts', f"{cflags}='{os.environ[cflags]}'")
del os.environ[cflags]
self.log.info("Environment variable %s unset and passed through command line" % cflags)
self.log.info(f"Environment variable {cflags} unset and passed through command line")

makecmd = f'make {self.parallel_flag}'

cmd = ' '.join([self.cfg['prebuildopts'], makecmd, ' '.join(build_parts), self.cfg['buildopts']])
run_shell_cmd(cmd)

def install_step(self):
"""Fix libsuffix in openblas64.pc if it exists"""
super(EB_OpenBLAS, self).install_step()
if self.iter_idx > 0 and self.cfg['enable_ilp64'] and self.cfg['ilp64_lib_suffix']:
filepath = os.path.join(self.installdir, 'lib', 'pkgconfig', 'openblas64.pc')
if os.path.exists(filepath):
regex_subs = [
(r'^libsuffix=.*$', f"libsuffix={self.cfg['ilp64_lib_suffix']}"),
(r'^Name: openblas$', 'Name: openblas64'),
]
apply_regex_substitutions(filepath, regex_subs, backup=False)

def check_lapack_test_results(self, test_output):
"""Check output of OpenBLAS' LAPACK test suite ('make lapack-test')."""

Expand Down Expand Up @@ -155,7 +223,7 @@ def test_step(self):
run_tests += [self.cfg['runtest']]

for runtest in run_tests:
cmd = "%s make %s %s" % (self.cfg['pretestopts'], runtest, self.cfg['testopts'])
cmd = f"{self.cfg['pretestopts']} make {runtest} {self.cfg['testopts']}"
res = run_shell_cmd(cmd)

# Raise an error if any test failed
Expand All @@ -170,10 +238,18 @@ def test_step(self):

def sanity_check_step(self):
""" Custom sanity check for OpenBLAS """
shlib_ext = get_shared_lib_ext()
custom_paths = {
'files': ['include/cblas.h', 'include/f77blas.h', 'include/lapacke_config.h', 'include/lapacke.h',
'include/lapacke_mangling.h', 'include/lapacke_utils.h', 'include/openblas_config.h',
'lib/libopenblas.a', 'lib/libopenblas.%s' % get_shared_lib_ext()],
'lib/libopenblas.a', f'lib/libopenblas.{shlib_ext}'],
'dirs': [],
}
if self.cfg['enable_ilp64']:
for suffixtype in 'lib', 'symbol':
filename_suffix = self.cfg[f'ilp64_{suffixtype}_suffix']
if filename_suffix:
custom_paths['files'].extend(f"lib/libopenblas{filename_suffix}.{ext}"
for ext in ['a', shlib_ext])

super().sanity_check_step(custom_paths=custom_paths)