Skip to content
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

update scipy easyblock for scipy >= 1.9.0 to use meson/ninja #2862

Merged
merged 15 commits into from
Feb 25, 2023
Merged
Changes from 10 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
146 changes: 138 additions & 8 deletions easybuild/easyblocks/s/scipy.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,34 +30,164 @@
@author: Kenneth Hoste (Ghent University)
@author: Pieter De Baets (Ghent University)
@author: Jens Timmerman (Ghent University)
@author: Jasper Grimm (University of York)
"""
import os
import tempfile
from distutils.version import LooseVersion

from easybuild.easyblocks.generic.fortranpythonpackage import FortranPythonPackage
from easybuild.easyblocks.generic.pythonpackage import det_pylibdir
import easybuild.tools.environment as env
import easybuild.tools.toolchain as toolchain
from easybuild.easyblocks.generic.fortranpythonpackage import FortranPythonPackage
from easybuild.easyblocks.generic.mesonninja import MesonNinja
from easybuild.easyblocks.generic.pythonpackage import PythonPackage, det_pylibdir
from easybuild.framework.easyconfig import CUSTOM
from easybuild.tools.build_log import EasyBuildError
from easybuild.tools.filetools import change_dir, copy_dir


class EB_scipy(FortranPythonPackage):
class EB_scipy(FortranPythonPackage, PythonPackage, MesonNinja):
"""Support for installing the scipy Python package as part of a Python installation."""

@staticmethod
def extra_options():
"""Easyconfig parameters specific to scipy."""
extra_vars = ({
'enable_slow_tests': [False, "Run scipy test suite, including tests marked as slow", CUSTOM],
# ignore tests by default to maintain behaviour in older easyconfigs
'ignore_test_result': [None, "Run scipy test suite, but ignore result (only log)", CUSTOM],
jfgrimm marked this conversation as resolved.
Show resolved Hide resolved
})

return PythonPackage.extra_options(extra_vars=extra_vars)

def __init__(self, *args, **kwargs):
"""Set scipy-specific test command."""
super(EB_scipy, self).__init__(*args, **kwargs)

# calling PythonPackage __init__ also lets MesonNinja work in an extension
PythonPackage.__init__(self, *args, **kwargs)
self.testinstall = True
self.testcmd = "cd .. && %(python)s -c 'import numpy; import scipy; scipy.test(verbose=2)'"

if LooseVersion(self.version) >= LooseVersion('1.9'):
self.use_meson = True
# enforce scipy test suite results if not explicitly disabled for scipy >= 1.9
if self.cfg['ignore_test_result'] is None:
self.cfg['ignore_test_result'] = True
jfgrimm marked this conversation as resolved.
Show resolved Hide resolved
# ignore installopts inherited from FortranPythonPackage
jfgrimm marked this conversation as resolved.
Show resolved Hide resolved
self.cfg['installopts'] = ""
Copy link
Member

Choose a reason for hiding this comment

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

We should at least also log the original installopts value if we're going to do a hard reset here...

Copy link
Member Author

Choose a reason for hiding this comment

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

updated to strip the ones inherited from PythonPackage, perhaps that's better?

else:
self.use_meson = False

if self.cfg['ignore_test_result']:
# maintains compatibility with easyconfigs predating scipy 1.9. Runs tests (serially) in
jfgrimm marked this conversation as resolved.
Show resolved Hide resolved
# a way that exits with code 0 regardless of test results, see:
# https://github.com/easybuilders/easybuild-easyblocks/issues/2237
self.testcmd = "cd .. && %(python)s -c 'import numpy; import scipy; scipy.test(verbose=2)'"
else:
self.testcmd = " && ".join([
"cd ..",
"touch %(srcdir)s/.coveragerc",
"%(python)s %(srcdir)s/runtests.py -v --no-build --parallel %(parallel)s",
])
if self.cfg['enable_slow_tests']:
self.testcmd += " -m full "

def configure_step(self):
"""Custom configure step for scipy: set extra installation options when needed."""
super(EB_scipy, self).configure_step()

# scipy >= 1.9.0 uses Meson/Ninja
if self.use_meson:
# configure BLAS/LAPACK library to use with Meson for scipy >= 1.9.0
lapack_lib = self.toolchain.lapack_family()
if lapack_lib == toolchain.FLEXIBLAS:
blas_lapack = 'flexiblas'
elif lapack_lib == toolchain.INTELMKL:
blas_lapack = 'mkl'
elif lapack_lib == toolchain.OPENBLAS:
blas_lapack = 'openblas'
else:
raise EasyBuildError("Unknown BLAS/LAPACK library used: %s", lapack_lib)

configopts = '-Dblas=%(blas_lapack)s -Dlapack=%(blas_lapack)s' % {'blas_lapack': blas_lapack}
jfgrimm marked this conversation as resolved.
Show resolved Hide resolved
self.cfg.update('configopts', configopts)

# need to have already installed extensions in PATH, PYTHONPATH for configure/build/install steps
pythonpath = os.getenv('PYTHONPATH')
pylibdir = det_pylibdir()
env.setvar('PYTHONPATH', os.pathsep.join([os.path.join(self.installdir, pylibdir), pythonpath]))

path = os.getenv('PATH')
env.setvar('PATH', os.pathsep.join([os.path.join(self.installdir, 'bin'), path]))

MesonNinja.configure_step(self)

else:
jfgrimm marked this conversation as resolved.
Show resolved Hide resolved
FortranPythonPackage.configure_step(self)

if LooseVersion(self.version) >= LooseVersion('0.13'):
# in recent scipy versions, additional compilation is done in the install step,
# which requires unsetting $LDFLAGS
if self.toolchain.comp_family() in [toolchain.GCC, toolchain.CLANGGCC]: # @UndefinedVariable
self.cfg.update('preinstallopts', "unset LDFLAGS && ")

def build_step(self):
"""Custom build step for scipy: use ninja for scipy >= 1.9.0"""
if self.use_meson:
MesonNinja.build_step(self)
else:
FortranPythonPackage.build_step(self)

def test_step(self):
"""Run available scipy unit tests. Adapted from numpy easyblock"""

if self.use_meson:
# temporarily install scipy so we can run the test suite
tmpdir = tempfile.mkdtemp()
cwd = os.getcwd()

tmp_builddir = os.path.join(tmpdir, 'build')
tmp_installdir = os.path.join(tmpdir, 'install')

# create a copy of the builddir
copy_dir(cwd, tmp_builddir)
change_dir(tmp_builddir)

# reconfigure (to update prefix), and install to tmpdir
MesonNinja.configure_step(self, cmd_prefix=tmp_installdir)
MesonNinja.install_step(self)

tmp_pylibdir = [os.path.join(tmp_installdir, det_pylibdir())]
self.prepare_python()

self.cfg['pretestopts'] = " && ".join([
# LDFLAGS should not be set when testing numpy/scipy, because it overwrites whatever numpy/scipy sets
# see http://projects.scipy.org/numpy/ticket/182
"unset LDFLAGS",
"export PYTHONPATH=%s:$PYTHONPATH" % tmp_pylibdir,
"",
])
self.cfg['runtest'] = self.testcmd % {
'python': self.python_cmd,
'srcdir': self.cfg['start_dir'],
'parallel': self.cfg['parallel'],
}

MesonNinja.test_step(self)

else:
self.testcmd = self.testcmd % {
'python': '%(python)s',
'srcdir': self.cfg['start_dir'],
'parallel': self.cfg['parallel'],
}
FortranPythonPackage.test_step(self)

def install_step(self):
"""Custom install step for scipy: use ninja for scipy >= 1.9.0"""
if self.use_meson:
MesonNinja.install_step(self)

jfgrimm marked this conversation as resolved.
Show resolved Hide resolved
else:
FortranPythonPackage.install_step(self)

def sanity_check_step(self, *args, **kwargs):
"""Custom sanity check for scipy."""

Expand All @@ -68,4 +198,4 @@ def sanity_check_step(self, *args, **kwargs):
'dirs': [det_pylibdir()],
}

return super(EB_scipy, self).sanity_check_step(custom_paths=custom_paths)
return PythonPackage.sanity_check_step(self, custom_paths=custom_paths)