From 182704176f49880be9f0e686dd4636e9e652fb82 Mon Sep 17 00:00:00 2001 From: Jasper Grimm Date: Tue, 17 Jan 2023 15:48:16 +0000 Subject: [PATCH 01/14] update scipy easyblock for scipy >= 1.9.0 to use meson/ninja --- easybuild/easyblocks/s/scipy.py | 79 +++++++++++++++++++++++++++++---- 1 file changed, 71 insertions(+), 8 deletions(-) diff --git a/easybuild/easyblocks/s/scipy.py b/easybuild/easyblocks/s/scipy.py index c4ebd92ed9..79acdb95fe 100644 --- a/easybuild/easyblocks/s/scipy.py +++ b/easybuild/easyblocks/s/scipy.py @@ -30,33 +30,96 @@ @author: Kenneth Hoste (Ghent University) @author: Pieter De Baets (Ghent University) @author: Jens Timmerman (Ghent University) +@author: Jasper Grimm (University of York) """ +import os from distutils.version import LooseVersion +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 det_pylibdir -import easybuild.tools.toolchain as toolchain +from easybuild.tools.build_log import EasyBuildError +from easybuild.tools.modules import get_software_root +from easybuild.tools.run import run_cmd -class EB_scipy(FortranPythonPackage): +class EB_scipy(FortranPythonPackage, MesonNinja): """Support for installing the scipy Python package as part of a Python installation.""" def __init__(self, *args, **kwargs): """Set scipy-specific test command.""" super(EB_scipy, self).__init__(*args, **kwargs) + self.use_meson = LooseVersion(self.version) >= LooseVersion('1.9') + self.pylibdir = None self.testinstall = True self.testcmd = "cd .. && %(python)s -c 'import numpy; import scipy; scipy.test(verbose=2)'" def configure_step(self): """Custom configure step for scipy: set extra installation options when needed.""" - super(EB_scipy, self).configure_step() + self.pylibdir = det_pylibdir() + + # 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} + self.cfg.update('configopts', configopts) + + pythonpath = os.getenv('PYTHONPATH') + env.setvar('PYTHONPATH', os.pathsep.join([os.path.join(self.installdir, self.pylibdir), pythonpath])) + + path = os.getenv('PATH') + env.setvar('PATH', os.pathsep.join([os.path.join(self.installdir, 'bin'), path])) + + MesonNinja.configure_step(self) + else: + super(EB_scipy, self).configure_step() + + 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: + super(EB_scipy, self).build_step() + + def test_step(self): + """Custom test step for scipy: skip if scipy >= 1.9.0""" + if self.use_meson: + # need to run `ninja install` before we can run the test + pass + else: + super(EB_scipy, self).build_step() - 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 install_step(self): + """Custom install step for scipy: use ninja for scipy >= 1.9.0""" + if self.use_meson: + self.cfg['installopts'] = "" + MesonNinja.install_step(self) + + # also run the test + python_root = get_software_root('Python') + python_bin = os.path.join(python_root, 'bin', 'python') + run_cmd(self.testcmd % {'python': python_bin}, log_all=True, simple=True) + else: + super(EB_scipy, self).install_step() def sanity_check_step(self, *args, **kwargs): """Custom sanity check for scipy.""" From 57d52d990dca8f653e662739f1c4adac9a365670 Mon Sep 17 00:00:00 2001 From: Jasper Grimm Date: Tue, 17 Jan 2023 15:54:38 +0000 Subject: [PATCH 02/14] linting --- easybuild/easyblocks/s/scipy.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/easybuild/easyblocks/s/scipy.py b/easybuild/easyblocks/s/scipy.py index 79acdb95fe..826ed358d1 100644 --- a/easybuild/easyblocks/s/scipy.py +++ b/easybuild/easyblocks/s/scipy.py @@ -60,7 +60,7 @@ def __init__(self, *args, **kwargs): def configure_step(self): """Custom configure step for scipy: set extra installation options when needed.""" self.pylibdir = det_pylibdir() - + # scipy >= 1.9.0 uses Meson/Ninja if self.use_meson: # configure BLAS/LAPACK library to use with Meson for scipy >= 1.9.0 @@ -72,14 +72,14 @@ def configure_step(self): elif lapack_lib == toolchain.OPENBLAS: blas_lapack = 'openblas' else: - raise EasyBuildError("Unknown BLAS/LAPACK library used: %s", lapack_lib) - + raise EasyBuildError("Unknown BLAS/LAPACK library used: %s", lapack_lib) + configopts = '-Dblas=%(blas_lapack)s -Dlapack=%(blas_lapack)s' % {'blas_lapack': blas_lapack} self.cfg.update('configopts', configopts) - + pythonpath = os.getenv('PYTHONPATH') env.setvar('PYTHONPATH', os.pathsep.join([os.path.join(self.installdir, self.pylibdir), pythonpath])) - + path = os.getenv('PATH') env.setvar('PATH', os.pathsep.join([os.path.join(self.installdir, 'bin'), path])) @@ -113,8 +113,8 @@ def install_step(self): if self.use_meson: self.cfg['installopts'] = "" MesonNinja.install_step(self) - - # also run the test + + # also run the test python_root = get_software_root('Python') python_bin = os.path.join(python_root, 'bin', 'python') run_cmd(self.testcmd % {'python': python_bin}, log_all=True, simple=True) From 1fec03336db8b93285c9ba2acf823e7b9c3d5cfa Mon Sep 17 00:00:00 2001 From: Jasper Grimm Date: Thu, 19 Jan 2023 15:21:36 +0000 Subject: [PATCH 03/14] improve scipy testing --- easybuild/easyblocks/s/scipy.py | 84 ++++++++++++++++++++++++++------- 1 file changed, 67 insertions(+), 17 deletions(-) diff --git a/easybuild/easyblocks/s/scipy.py b/easybuild/easyblocks/s/scipy.py index 826ed358d1..ecfa42b8ff 100644 --- a/easybuild/easyblocks/s/scipy.py +++ b/easybuild/easyblocks/s/scipy.py @@ -33,6 +33,7 @@ @author: Jasper Grimm (University of York) """ import os +import tempfile from distutils.version import LooseVersion import easybuild.tools.environment as env @@ -41,6 +42,7 @@ from easybuild.easyblocks.generic.mesonninja import MesonNinja from easybuild.easyblocks.generic.pythonpackage import det_pylibdir from easybuild.tools.build_log import EasyBuildError +from easybuild.tools.filetools import change_dir, mkdir, remove_dir from easybuild.tools.modules import get_software_root from easybuild.tools.run import run_cmd @@ -53,13 +55,16 @@ def __init__(self, *args, **kwargs): super(EB_scipy, self).__init__(*args, **kwargs) self.use_meson = LooseVersion(self.version) >= LooseVersion('1.9') - self.pylibdir = None self.testinstall = True - self.testcmd = "cd .. && %(python)s -c 'import numpy; import scipy; scipy.test(verbose=2)'" + self.testcmd = " && ".join([ + "cd ..", + "touch %(srcdir)s/.coveragerc", + "%(python)s %(srcdir)s/runtests.py -v --no-build --parallel %(parallel)s", + ]) def configure_step(self): """Custom configure step for scipy: set extra installation options when needed.""" - self.pylibdir = det_pylibdir() + pylibdir = det_pylibdir() # scipy >= 1.9.0 uses Meson/Ninja if self.use_meson: @@ -77,21 +82,23 @@ def configure_step(self): configopts = '-Dblas=%(blas_lapack)s -Dlapack=%(blas_lapack)s' % {'blas_lapack': blas_lapack} self.cfg.update('configopts', configopts) + # need to have already installed extensions in PATH, PYTHONPATH for configure/build/install steps pythonpath = os.getenv('PYTHONPATH') - env.setvar('PYTHONPATH', os.pathsep.join([os.path.join(self.installdir, self.pylibdir), pythonpath])) + 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: super(EB_scipy, self).configure_step() - 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 && ") + 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""" @@ -101,23 +108,66 @@ def build_step(self): super(EB_scipy, self).build_step() def test_step(self): - """Custom test step for scipy: skip if scipy >= 1.9.0""" + """Run available scipy unit tests. Adapted from numpy easyblock""" + if self.use_meson: - # need to run `ninja install` before we can run the test - pass + + # temporarily install scipy so we can run the test suite + tmpdir = tempfile.mkdtemp() + cwd = os.getcwd() + + cmd = " && ".join([ + # reconfigure meson to use temp dir + 'meson configure --prefix=%s' % tmpdir, + 'ninja -j %s install' % self.cfg['parallel'], + ]) + run_cmd(cmd, log_all=True, simple=True, verbose=False) + + abs_pylibdirs = [os.path.join(tmpdir, pylibdir) for pylibdir in self.all_pylibdirs] + for pylibdir in abs_pylibdirs: + mkdir(pylibdir, parents=True) + python_root = get_software_root('Python') + python_bin = os.path.join(python_root, 'bin', '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" % os.pathsep.join(abs_pylibdirs), + "", + ]) + self.cfg['runtest'] = self.testcmd % { + 'python': python_bin, + 'srcdir': self.cfg['start_dir'], + 'parallel': self.cfg['parallel'], + } + + MesonNinja.test_step(self) + + # reconfigure meson to use real install dir + os.chdir(cwd) + cmd = 'meson configure --prefix=%s' % self.installdir + run_cmd(cmd, log_all=True, simple=True, verbose=False) + + try: + remove_dir(tmpdir) + except OSError as err: + raise EasyBuildError("Failed to remove directory %s: %s", tmpdir, err) + else: - super(EB_scipy, self).build_step() + self.testcmd = self.testcmd % { + 'srcdir': self.cfg['start_dir'], + 'parallel': self.cfg['parallel'] + } + super(EB_scipy, self).test_step() def install_step(self): """Custom install step for scipy: use ninja for scipy >= 1.9.0""" if self.use_meson: + # ignore installopts inherited from FortranPythonPackage self.cfg['installopts'] = "" MesonNinja.install_step(self) - # also run the test - python_root = get_software_root('Python') - python_bin = os.path.join(python_root, 'bin', 'python') - run_cmd(self.testcmd % {'python': python_bin}, log_all=True, simple=True) else: super(EB_scipy, self).install_step() From 4e8e47ffb95107c711c6bde33405c4160f5dc510 Mon Sep 17 00:00:00 2001 From: Jasper Grimm Date: Thu, 19 Jan 2023 15:22:44 +0000 Subject: [PATCH 04/14] use change_dir --- easybuild/easyblocks/s/scipy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/s/scipy.py b/easybuild/easyblocks/s/scipy.py index ecfa42b8ff..06efba71d3 100644 --- a/easybuild/easyblocks/s/scipy.py +++ b/easybuild/easyblocks/s/scipy.py @@ -145,7 +145,7 @@ def test_step(self): MesonNinja.test_step(self) # reconfigure meson to use real install dir - os.chdir(cwd) + change_dir(cwd) cmd = 'meson configure --prefix=%s' % self.installdir run_cmd(cmd, log_all=True, simple=True, verbose=False) From b71ca1bff3ae520b3dacda217f3883e114f0451e Mon Sep 17 00:00:00 2001 From: Jasper Grimm Date: Thu, 19 Jan 2023 18:11:06 +0000 Subject: [PATCH 05/14] fix tests for non-meson builds --- easybuild/easyblocks/s/scipy.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/easybuild/easyblocks/s/scipy.py b/easybuild/easyblocks/s/scipy.py index 06efba71d3..de8a30f847 100644 --- a/easybuild/easyblocks/s/scipy.py +++ b/easybuild/easyblocks/s/scipy.py @@ -156,8 +156,9 @@ def test_step(self): else: self.testcmd = self.testcmd % { + 'python': '%(python)s', 'srcdir': self.cfg['start_dir'], - 'parallel': self.cfg['parallel'] + 'parallel': self.cfg['parallel'], } super(EB_scipy, self).test_step() From b18e343b17597ce673c58c0dac1e3374ead18577 Mon Sep 17 00:00:00 2001 From: Jasper Grimm Date: Fri, 20 Jan 2023 11:10:36 +0000 Subject: [PATCH 06/14] add extra variable to enable/disable running of slow scipy tests --- easybuild/easyblocks/s/scipy.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/easybuild/easyblocks/s/scipy.py b/easybuild/easyblocks/s/scipy.py index de8a30f847..895a19cab3 100644 --- a/easybuild/easyblocks/s/scipy.py +++ b/easybuild/easyblocks/s/scipy.py @@ -41,6 +41,7 @@ from easybuild.easyblocks.generic.fortranpythonpackage import FortranPythonPackage from easybuild.easyblocks.generic.mesonninja import MesonNinja from easybuild.easyblocks.generic.pythonpackage import det_pylibdir +from easybuild.framework.easyconfig import CUSTOM from easybuild.tools.build_log import EasyBuildError from easybuild.tools.filetools import change_dir, mkdir, remove_dir from easybuild.tools.modules import get_software_root @@ -50,6 +51,15 @@ class EB_scipy(FortranPythonPackage, 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], + }) + + return FortranPythonPackage.extra_options(extra_vars=extra_vars) + def __init__(self, *args, **kwargs): """Set scipy-specific test command.""" super(EB_scipy, self).__init__(*args, **kwargs) @@ -61,6 +71,8 @@ def __init__(self, *args, **kwargs): "touch %(srcdir)s/.coveragerc", "%(python)s %(srcdir)s/runtests.py -v --no-build --parallel %(parallel)s", ]) + if self.cfg['enable_slow_tests'] is True: + self.testcmd += " -m full " def configure_step(self): """Custom configure step for scipy: set extra installation options when needed.""" From 0060fa23574695a804b42e5af64c048d7c0f0139 Mon Sep 17 00:00:00 2001 From: Jasper Grimm Date: Fri, 20 Jan 2023 11:44:25 +0000 Subject: [PATCH 07/14] add option to ignore scipy test results --- easybuild/easyblocks/s/scipy.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/easybuild/easyblocks/s/scipy.py b/easybuild/easyblocks/s/scipy.py index 895a19cab3..d42c6d92e2 100644 --- a/easybuild/easyblocks/s/scipy.py +++ b/easybuild/easyblocks/s/scipy.py @@ -56,6 +56,7 @@ 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_test_result': [False, "Run scipy test suite, but ignore result (only log)", CUSTOM], }) return FortranPythonPackage.extra_options(extra_vars=extra_vars) @@ -66,13 +67,17 @@ def __init__(self, *args, **kwargs): self.use_meson = LooseVersion(self.version) >= LooseVersion('1.9') self.testinstall = True - 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'] is True: - self.testcmd += " -m full " + if self.cfg['ignore_test_result']: + # running tests this way exits with 0 regardless + 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'] is True: + self.testcmd += " -m full " def configure_step(self): """Custom configure step for scipy: set extra installation options when needed.""" From 40deb9f83814fe5e68e67847592ea0c82fe69475 Mon Sep 17 00:00:00 2001 From: Jasper Grimm Date: Fri, 20 Jan 2023 11:56:39 +0000 Subject: [PATCH 08/14] ignore test results by default to maintain current behaviour for older easyconfigs --- easybuild/easyblocks/s/scipy.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/easybuild/easyblocks/s/scipy.py b/easybuild/easyblocks/s/scipy.py index d42c6d92e2..13e87d0937 100644 --- a/easybuild/easyblocks/s/scipy.py +++ b/easybuild/easyblocks/s/scipy.py @@ -56,7 +56,8 @@ 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_test_result': [False, "Run scipy test suite, but ignore result (only log)", CUSTOM], + # ignore tests by default to maintain behaviour in older easyconfigs + 'ignore_test_result': [True, "Run scipy test suite, but ignore result (only log)", CUSTOM], }) return FortranPythonPackage.extra_options(extra_vars=extra_vars) From f2808b01201ec407f63790b6516cc1bfcd739661 Mon Sep 17 00:00:00 2001 From: Jasper Grimm Date: Mon, 23 Jan 2023 15:50:46 +0000 Subject: [PATCH 09/14] make changes suggested in review --- easybuild/easyblocks/s/scipy.py | 84 ++++++++++++++++----------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/easybuild/easyblocks/s/scipy.py b/easybuild/easyblocks/s/scipy.py index 13e87d0937..22a7e1cc40 100644 --- a/easybuild/easyblocks/s/scipy.py +++ b/easybuild/easyblocks/s/scipy.py @@ -40,15 +40,15 @@ 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 det_pylibdir +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, mkdir, remove_dir +from easybuild.tools.filetools import change_dir, copy_dir, mkdir from easybuild.tools.modules import get_software_root from easybuild.tools.run import run_cmd -class EB_scipy(FortranPythonPackage, MesonNinja): +class EB_scipy(FortranPythonPackage, PythonPackage, MesonNinja): """Support for installing the scipy Python package as part of a Python installation.""" @staticmethod @@ -57,19 +57,31 @@ def extra_options(): 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': [True, "Run scipy test suite, but ignore result (only log)", CUSTOM], + 'ignore_test_result': [None, "Run scipy test suite, but ignore result (only log)", CUSTOM], }) - return FortranPythonPackage.extra_options(extra_vars=extra_vars) + 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) - - self.use_meson = LooseVersion(self.version) >= LooseVersion('1.9') + # calling PythonPackage __init__ also lets MesonNinja work in an extension + PythonPackage.__init__(self, *args, **kwargs) self.testinstall = True + + 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 + # ignore installopts inherited from FortranPythonPackage + self.cfg['installopts'] = "" + else: + self.use_meson = False + if self.cfg['ignore_test_result']: - # running tests this way exits with 0 regardless + # maintains compatibility with easyconfigs predating scipy 1.9. Runs tests (serially) in + # 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([ @@ -77,12 +89,11 @@ def __init__(self, *args, **kwargs): "touch %(srcdir)s/.coveragerc", "%(python)s %(srcdir)s/runtests.py -v --no-build --parallel %(parallel)s", ]) - if self.cfg['enable_slow_tests'] is True: + 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.""" - pylibdir = det_pylibdir() # scipy >= 1.9.0 uses Meson/Ninja if self.use_meson: @@ -102,6 +113,7 @@ def configure_step(self): # 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') @@ -110,7 +122,7 @@ def configure_step(self): MesonNinja.configure_step(self) else: - super(EB_scipy, self).configure_step() + FortranPythonPackage.configure_step(self) if LooseVersion(self.version) >= LooseVersion('0.13'): # in recent scipy versions, additional compilation is done in the install step, @@ -123,72 +135,60 @@ def build_step(self): if self.use_meson: MesonNinja.build_step(self) else: - super(EB_scipy, self).build_step() + 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() - cmd = " && ".join([ - # reconfigure meson to use temp dir - 'meson configure --prefix=%s' % tmpdir, - 'ninja -j %s install' % self.cfg['parallel'], - ]) - run_cmd(cmd, log_all=True, simple=True, verbose=False) + tmp_builddir = os.path.join(tmpdir, 'build') + tmp_installdir = os.path.join(tmpdir, 'install') - abs_pylibdirs = [os.path.join(tmpdir, pylibdir) for pylibdir in self.all_pylibdirs] - for pylibdir in abs_pylibdirs: - mkdir(pylibdir, parents=True) - python_root = get_software_root('Python') - python_bin = os.path.join(python_root, 'bin', 'python') + # 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" % os.pathsep.join(abs_pylibdirs), + "export PYTHONPATH=%s:$PYTHONPATH" % tmp_pylibdir, "", ]) self.cfg['runtest'] = self.testcmd % { - 'python': python_bin, + 'python': self.python_cmd, 'srcdir': self.cfg['start_dir'], 'parallel': self.cfg['parallel'], } MesonNinja.test_step(self) - # reconfigure meson to use real install dir - change_dir(cwd) - cmd = 'meson configure --prefix=%s' % self.installdir - run_cmd(cmd, log_all=True, simple=True, verbose=False) - - try: - remove_dir(tmpdir) - except OSError as err: - raise EasyBuildError("Failed to remove directory %s: %s", tmpdir, err) - else: self.testcmd = self.testcmd % { 'python': '%(python)s', 'srcdir': self.cfg['start_dir'], 'parallel': self.cfg['parallel'], } - super(EB_scipy, self).test_step() + FortranPythonPackage.test_step(self) def install_step(self): """Custom install step for scipy: use ninja for scipy >= 1.9.0""" if self.use_meson: - # ignore installopts inherited from FortranPythonPackage - self.cfg['installopts'] = "" MesonNinja.install_step(self) else: - super(EB_scipy, self).install_step() + FortranPythonPackage.install_step(self) def sanity_check_step(self, *args, **kwargs): """Custom sanity check for scipy.""" @@ -200,4 +200,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) From 8d44a255b7f44b5019fbc62547d44867b8322f99 Mon Sep 17 00:00:00 2001 From: Jasper Grimm Date: Mon, 23 Jan 2023 15:53:07 +0000 Subject: [PATCH 10/14] appease the hound --- easybuild/easyblocks/s/scipy.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/easybuild/easyblocks/s/scipy.py b/easybuild/easyblocks/s/scipy.py index 22a7e1cc40..1df30766b9 100644 --- a/easybuild/easyblocks/s/scipy.py +++ b/easybuild/easyblocks/s/scipy.py @@ -43,9 +43,7 @@ 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, mkdir -from easybuild.tools.modules import get_software_root -from easybuild.tools.run import run_cmd +from easybuild.tools.filetools import change_dir, copy_dir class EB_scipy(FortranPythonPackage, PythonPackage, MesonNinja): @@ -77,7 +75,7 @@ def __init__(self, *args, **kwargs): self.cfg['installopts'] = "" else: self.use_meson = False - + if self.cfg['ignore_test_result']: # maintains compatibility with easyconfigs predating scipy 1.9. Runs tests (serially) in # a way that exits with code 0 regardless of test results, see: From 016118af4c54d9b604d91132e65a6020232443f7 Mon Sep 17 00:00:00 2001 From: Jasper Grimm Date: Thu, 16 Feb 2023 11:17:42 +0000 Subject: [PATCH 11/14] apply suggestions from review --- easybuild/easyblocks/s/scipy.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/easybuild/easyblocks/s/scipy.py b/easybuild/easyblocks/s/scipy.py index 1df30766b9..9473a2f145 100644 --- a/easybuild/easyblocks/s/scipy.py +++ b/easybuild/easyblocks/s/scipy.py @@ -54,8 +54,9 @@ 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], + # ignore test failures by default in scipy < 1.9 to maintain behaviour in older easyconfigs + 'ignore_test_result': [None, "Run scipy test suite, but ignore test failures (True/False/None). Default " + "(None) implies True for scipy < 1.9, and False for scipy ?= 1.9", CUSTOM], }) return PythonPackage.extra_options(extra_vars=extra_vars) @@ -68,17 +69,27 @@ def __init__(self, *args, **kwargs): 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 - # ignore installopts inherited from FortranPythonPackage - self.cfg['installopts'] = "" + self.cfg['ignore_test_result'] = False + + # strip inherited PythonPackage installopts + installopts = self.cfg['installopts'] + pythonpackage_installopts = ['--no-deps', '--ignore-installed', '--no-index', '--egg', + '--zip-ok', '--no-index'] + self.log.info("Stripping inherited PythonPackage installopts %s from installopts %s", + pythonpackage_installopts, installopts) + for i in pythonpackage_installopts: + installopts.replace(i, '') + self.cfg['installopts'] = installopts + else: self.use_meson = False if self.cfg['ignore_test_result']: - # maintains compatibility with easyconfigs predating scipy 1.9. Runs tests (serially) in - # a way that exits with code 0 regardless of test results, see: + # can be used to maintain compatibility with easyconfigs predating scipy 1.9. Runs tests + # (serially) in 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: @@ -106,8 +117,8 @@ def configure_step(self): else: raise EasyBuildError("Unknown BLAS/LAPACK library used: %s", lapack_lib) - configopts = '-Dblas=%(blas_lapack)s -Dlapack=%(blas_lapack)s' % {'blas_lapack': blas_lapack} - self.cfg.update('configopts', configopts) + for opt in ('blas', 'lapack'): + self.cfg.update('configopts', '-D%(opt)s=%(blas_lapack)s' % {'opt': opt, 'blas_lapack': blas_lapack}) # need to have already installed extensions in PATH, PYTHONPATH for configure/build/install steps pythonpath = os.getenv('PYTHONPATH') @@ -120,6 +131,7 @@ def configure_step(self): MesonNinja.configure_step(self) else: + # scipy < 1.9.0 uses install procedure using setup.py FortranPythonPackage.configure_step(self) if LooseVersion(self.version) >= LooseVersion('0.13'): @@ -184,7 +196,6 @@ def install_step(self): """Custom install step for scipy: use ninja for scipy >= 1.9.0""" if self.use_meson: MesonNinja.install_step(self) - else: FortranPythonPackage.install_step(self) From a69b47f42eef6eddb57c2a403580fc7f89e12b57 Mon Sep 17 00:00:00 2001 From: Jasper <65227842+jfgrimm@users.noreply.github.com> Date: Thu, 16 Feb 2023 13:38:32 +0000 Subject: [PATCH 12/14] Update easybuild/easyblocks/s/scipy.py Co-authored-by: Simon Branford <4967+branfosj@users.noreply.github.com> --- easybuild/easyblocks/s/scipy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easybuild/easyblocks/s/scipy.py b/easybuild/easyblocks/s/scipy.py index 9473a2f145..77504eed40 100644 --- a/easybuild/easyblocks/s/scipy.py +++ b/easybuild/easyblocks/s/scipy.py @@ -81,7 +81,7 @@ def __init__(self, *args, **kwargs): self.log.info("Stripping inherited PythonPackage installopts %s from installopts %s", pythonpackage_installopts, installopts) for i in pythonpackage_installopts: - installopts.replace(i, '') + installopts = installopts.replace(i, '') self.cfg['installopts'] = installopts else: From 41b424c47091f1a6686ace61700a73d87ced8f66 Mon Sep 17 00:00:00 2001 From: Jasper Grimm Date: Sat, 25 Feb 2023 09:00:24 +0000 Subject: [PATCH 13/14] fix default ignore_test_result logic --- easybuild/easyblocks/s/scipy.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/easybuild/easyblocks/s/scipy.py b/easybuild/easyblocks/s/scipy.py index 77504eed40..2ba91030dc 100644 --- a/easybuild/easyblocks/s/scipy.py +++ b/easybuild/easyblocks/s/scipy.py @@ -56,7 +56,7 @@ def extra_options(): 'enable_slow_tests': [False, "Run scipy test suite, including tests marked as slow", CUSTOM], # ignore test failures by default in scipy < 1.9 to maintain behaviour in older easyconfigs 'ignore_test_result': [None, "Run scipy test suite, but ignore test failures (True/False/None). Default " - "(None) implies True for scipy < 1.9, and False for scipy ?= 1.9", CUSTOM], + "(None) implies True for scipy < 1.9, and False for scipy >= 1.9", CUSTOM], }) return PythonPackage.extra_options(extra_vars=extra_vars) @@ -71,9 +71,6 @@ def __init__(self, *args, **kwargs): 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'] = False - # strip inherited PythonPackage installopts installopts = self.cfg['installopts'] pythonpackage_installopts = ['--no-deps', '--ignore-installed', '--no-index', '--egg', @@ -87,6 +84,13 @@ def __init__(self, *args, **kwargs): else: self.use_meson = False + if self.cfg['ignore_test_result'] is None: + # enforce scipy test suite results if not explicitly disabled for scipy >= 1.9 + if self.use_meson: + self.cfg['ignore_test_result'] = False + else: + self.cfg['ignore_test_result'] = True + if self.cfg['ignore_test_result']: # can be used to maintain compatibility with easyconfigs predating scipy 1.9. Runs tests # (serially) in a way that exits with code 0 regardless of test results, see: From f53a4c67e8f56155d7dc4ac5ca2a995e0ee8a028 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Sat, 25 Feb 2023 10:10:39 +0100 Subject: [PATCH 14/14] simplify logic to set default value for ignore_test_result in scipy easyblock --- easybuild/easyblocks/s/scipy.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/easybuild/easyblocks/s/scipy.py b/easybuild/easyblocks/s/scipy.py index 2ba91030dc..1c21a01fc2 100644 --- a/easybuild/easyblocks/s/scipy.py +++ b/easybuild/easyblocks/s/scipy.py @@ -54,7 +54,6 @@ 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 test failures by default in scipy < 1.9 to maintain behaviour in older easyconfigs 'ignore_test_result': [None, "Run scipy test suite, but ignore test failures (True/False/None). Default " "(None) implies True for scipy < 1.9, and False for scipy >= 1.9", CUSTOM], }) @@ -85,16 +84,15 @@ def __init__(self, *args, **kwargs): self.use_meson = False if self.cfg['ignore_test_result'] is None: - # enforce scipy test suite results if not explicitly disabled for scipy >= 1.9 - if self.use_meson: - self.cfg['ignore_test_result'] = False - else: - self.cfg['ignore_test_result'] = True + # automatically ignore scipy test suite results for scipy < 1.9, as we did in older easyconfigs + self.cfg['ignore_test_result'] = LooseVersion(self.version) < '1.9' + self.log.info("ignore_test_result not specified, so automatically set to %s for scipy %s", + self.cfg['ignore_test_result'], self.version) if self.cfg['ignore_test_result']: - # can be used to maintain compatibility with easyconfigs predating scipy 1.9. Runs tests - # (serially) in a way that exits with code 0 regardless of test results, see: - # https://github.com/easybuilders/easybuild-easyblocks/issues/2237 + # used to maintain compatibility with easyconfigs predating scipy 1.9; + # runs tests (serially) in 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([