From 66e9b1279a306e872008f41624bd12246484c42d Mon Sep 17 00:00:00 2001 From: "Adam J. Stewart" Date: Tue, 6 Aug 2019 18:20:23 -0500 Subject: Overhaul numpy package (#12170) * Add numpy 1.17.0 * Overhaul numpy package * Flake8 fixes * Undefined reference fix * HeaderList and LibraryList need an arg * veclibfort has no headers * Add patch for older versions of py-numpy * Remove py-meep hack from py-numpy package * libflame: always add max arg hack flag * Fix build with GCC 4.8 * Compiler flags come from self.compiler * Only apply -std=c99 to cflags * Try to fix libflame package * Fix ATLAS build on macOS * --force-clang flag added in 3.10.3 --- var/spack/repos/builtin/packages/atlas/package.py | 31 +- .../repos/builtin/packages/libflame/package.py | 9 +- .../repos/builtin/packages/py-meep/package.py | 5 +- .../packages/py-numpy/blas-lapack-order.patch | 347 +++++++++++++++++++++ .../repos/builtin/packages/py-numpy/package.py | 280 +++++++++-------- .../repos/builtin/packages/veclibfort/package.py | 6 + 6 files changed, 544 insertions(+), 134 deletions(-) create mode 100644 var/spack/repos/builtin/packages/py-numpy/blas-lapack-order.patch (limited to 'var') diff --git a/var/spack/repos/builtin/packages/atlas/package.py b/var/spack/repos/builtin/packages/atlas/package.py index ee9d8ae3f9..5e69b8f9a8 100644 --- a/var/spack/repos/builtin/packages/atlas/package.py +++ b/var/spack/repos/builtin/packages/atlas/package.py @@ -18,17 +18,15 @@ class Atlas(Package): """ homepage = "http://math-atlas.sourceforge.net/" - version('3.11.39', '5f3252fa980f5f060f93edd4669321e2', - url='http://sourceforge.net/projects/math-atlas/files/Developer%20%28unstable%29/3.11.39/atlas3.11.39.tar.bz2') + # Developer (unstable) + version('3.11.41', sha256='477d567a8d683e891d786e9e8bb6ad6659daa9ba18e8dd0e2f70b7a54095f8de') + version('3.11.39', '5f3252fa980f5f060f93edd4669321e2') + version('3.11.34', '0b6c5389c095c4c8785fd0f724ec6825') - version('3.11.34', '0b6c5389c095c4c8785fd0f724ec6825', - url='http://sourceforge.net/projects/math-atlas/files/Developer%20%28unstable%29/3.11.34/atlas3.11.34.tar.bz2') + # Stable + version('3.10.3', 'd6ce4f16c2ad301837cfb3dade2f7cef', preferred=True) + version('3.10.2', 'a4e21f343dec8f22e7415e339f09f6da') - version('3.10.3', 'd6ce4f16c2ad301837cfb3dade2f7cef', - url='https://sourceforge.net/projects/math-atlas/files/Stable/3.10.3/atlas3.10.3.tar.bz2') - - version('3.10.2', 'a4e21f343dec8f22e7415e339f09f6da', - url='https://sourceforge.net/projects/math-atlas/files/Stable/3.10.2/atlas3.10.2.tar.bz2', preferred=True) # not all packages (e.g. Trilinos@12.6.3) stopped using deprecated in 3.6.0 # Lapack routines. Stick with 3.5.0 until this is fixed. resource(name='lapack', @@ -57,6 +55,16 @@ class Atlas(Package): parallel = False + def url_for_version(self, version): + url = 'https://sourceforge.net/projects/math-atlas/files/' + + if version >= Version('3.11'): + url += 'Developer%20%28unstable%29/{0}/atlas{0}.tar.bz2' + else: + url += 'Stable/{0}/atlas{0}.tar.bz2' + + return url.format(version) + def patch(self): # Disable thread check. LLNL's environment does not allow # disabling of CPU throttling in a way that ATLAS actually @@ -94,6 +102,11 @@ class Atlas(Package): '-C', 'if', spack_f77 ]) + # Workaround for macOS Clang: + # http://math-atlas.sourceforge.net/atlas_install/node66.html + if spec.satisfies('@3.10.3: %clang platform=darwin'): + options.append('--force-clang=' + spack_cc) + # Lapack resource to provide full lapack build. Note that # ATLAS only provides a few LAPACK routines natively. options.append('--with-netlib-lapack-tarfile=%s' % diff --git a/var/spack/repos/builtin/packages/libflame/package.py b/var/spack/repos/builtin/packages/libflame/package.py index d3b547e5c5..bad6b0a223 100644 --- a/var/spack/repos/builtin/packages/libflame/package.py +++ b/var/spack/repos/builtin/packages/libflame/package.py @@ -51,6 +51,12 @@ class Libflame(AutotoolsPackage): # https://groups.google.com/forum/#!topic/libflame-discuss/lQKEfjyudOY patch('Makefile_5.1.0.patch', when='@5.1.0') + def flag_handler(self, name, flags): + # -std=gnu99 at least required, old versions of GCC default to -std=c90 + if self.spec.satisfies('%gcc@:5.1') and name == 'cflags': + flags.append('-std=gnu99') + return (flags, None, None) + def configure_args(self): config_args = [] @@ -83,7 +89,6 @@ class Libflame(AutotoolsPackage): config_args.append("--disable-supermatrix") # https://github.com/flame/libflame/issues/21 - if self.spec.satisfies('@5.1.99:'): - config_args.append("--enable-max-arg-list-hack") + config_args.append("--enable-max-arg-list-hack") return config_args diff --git a/var/spack/repos/builtin/packages/py-meep/package.py b/var/spack/repos/builtin/packages/py-meep/package.py index 764f46e7b9..b7dea8b798 100644 --- a/var/spack/repos/builtin/packages/py-meep/package.py +++ b/var/spack/repos/builtin/packages/py-meep/package.py @@ -38,7 +38,10 @@ class PyMeep(PythonPackage): def common_args(self, spec, prefix): include_dirs = [ spec['meep'].prefix.include, - spec['py-numpy'].include + os.path.join( + spec['py-numpy'].prefix, + spec['python'].package.python_include_dir + ) ] library_dirs = [ diff --git a/var/spack/repos/builtin/packages/py-numpy/blas-lapack-order.patch b/var/spack/repos/builtin/packages/py-numpy/blas-lapack-order.patch new file mode 100644 index 0000000000..ac27d87dcd --- /dev/null +++ b/var/spack/repos/builtin/packages/py-numpy/blas-lapack-order.patch @@ -0,0 +1,347 @@ +Allows you to specify order of BLAS/LAPACK preference +https://github.com/numpy/numpy/pull/13132 +diff --git a/numpy/distutils/system_info.py b/numpy/distutils/system_info.py +index 806f4f7d3..0480d7b5a 100644 +--- a/numpy/distutils/system_info.py ++++ b/numpy/distutils/system_info.py +@@ -474,6 +474,13 @@ class LapackSrcNotFoundError(LapackNotFoundError): + the LAPACK_SRC environment variable.""" + + ++class BlasOptNotFoundError(NotFoundError): ++ """ ++ Optimized (vendor) Blas libraries are not found. ++ Falls back to netlib Blas library which has worse performance. ++ A better performance should be easily gained by switching ++ Blas library.""" ++ + class BlasNotFoundError(NotFoundError): + """ + Blas (http://www.netlib.org/blas/) libraries not found. +@@ -1541,139 +1548,219 @@ Make sure that -lgfortran is used for C++ extensions. + class lapack_opt_info(system_info): + + notfounderror = LapackNotFoundError ++ # Default order of LAPACK checks ++ lapack_order = ['mkl', 'openblas', 'atlas', 'accelerate', 'lapack'] + +- def calc_info(self): ++ def _calc_info_mkl(self): ++ info = get_info('lapack_mkl') ++ if info: ++ self.set_info(**info) ++ return True ++ return False + +- lapack_mkl_info = get_info('lapack_mkl') +- if lapack_mkl_info: +- self.set_info(**lapack_mkl_info) +- return ++ def _calc_info_openblas(self): ++ info = get_info('openblas_lapack') ++ if info: ++ self.set_info(**info) ++ return True ++ info = get_info('openblas_clapack') ++ if info: ++ self.set_info(**info) ++ return True ++ return False + +- openblas_info = get_info('openblas_lapack') +- if openblas_info: +- self.set_info(**openblas_info) +- return ++ def _calc_info_atlas(self): ++ info = get_info('atlas_3_10_threads') ++ if not info: ++ info = get_info('atlas_3_10') ++ if not info: ++ info = get_info('atlas_threads') ++ if not info: ++ info = get_info('atlas') ++ if info: ++ # Figure out if ATLAS has lapack... ++ # If not we need the lapack library, but not BLAS! ++ l = info.get('define_macros', []) ++ if ('ATLAS_WITH_LAPACK_ATLAS', None) in l \ ++ or ('ATLAS_WITHOUT_LAPACK', None) in l: ++ # Get LAPACK (with possible warnings) ++ # If not found we don't accept anything ++ # since we can't use ATLAS with LAPACK! ++ lapack_info = self._get_info_lapack() ++ if not lapack_info: ++ return False ++ dict_append(info, **lapack_info) ++ self.set_info(**info) ++ return True ++ return False + +- openblas_info = get_info('openblas_clapack') +- if openblas_info: +- self.set_info(**openblas_info) +- return ++ def _calc_info_accelerate(self): ++ info = get_info('accelerate') ++ if info: ++ self.set_info(**info) ++ return True ++ return False + +- atlas_info = get_info('atlas_3_10_threads') +- if not atlas_info: +- atlas_info = get_info('atlas_3_10') +- if not atlas_info: +- atlas_info = get_info('atlas_threads') +- if not atlas_info: +- atlas_info = get_info('atlas') +- +- accelerate_info = get_info('accelerate') +- if accelerate_info and not atlas_info: +- self.set_info(**accelerate_info) +- return ++ def _get_info_blas(self): ++ # Default to get the optimized BLAS implementation ++ info = get_info('blas_opt') ++ if not info: ++ warnings.warn(BlasNotFoundError.__doc__ or '', stacklevel=3) ++ info_src = get_info('blas_src') ++ if not info_src: ++ warnings.warn(BlasSrcNotFoundError.__doc__ or '', stacklevel=3) ++ return {} ++ dict_append(info, libraries=[('fblas_src', info_src)]) ++ return info + +- need_lapack = 0 +- need_blas = 0 +- info = {} +- if atlas_info: +- l = atlas_info.get('define_macros', []) +- if ('ATLAS_WITH_LAPACK_ATLAS', None) in l \ +- or ('ATLAS_WITHOUT_LAPACK', None) in l: +- need_lapack = 1 +- info = atlas_info ++ def _get_info_lapack(self): ++ info = get_info('lapack') ++ if not info: ++ warnings.warn(LapackNotFoundError.__doc__ or '', stacklevel=3) ++ info_src = get_info('lapack_src') ++ if not info_src: ++ warnings.warn(LapackSrcNotFoundError.__doc__ or '', stacklevel=3) ++ return {} ++ dict_append(info, libraries=[('flapack_src', info_src)]) ++ return info + +- else: +- warnings.warn(AtlasNotFoundError.__doc__, stacklevel=2) +- need_blas = 1 +- need_lapack = 1 ++ def _calc_info_lapack(self): ++ info = self._get_info_lapack() ++ if info: ++ info_blas = self._get_info_blas() ++ dict_append(info, **info_blas) + dict_append(info, define_macros=[('NO_ATLAS_INFO', 1)]) ++ self.set_info(**info) ++ return True ++ return False + +- if need_lapack: +- lapack_info = get_info('lapack') +- #lapack_info = {} ## uncomment for testing +- if lapack_info: +- dict_append(info, **lapack_info) +- else: +- warnings.warn(LapackNotFoundError.__doc__, stacklevel=2) +- lapack_src_info = get_info('lapack_src') +- if not lapack_src_info: +- warnings.warn(LapackSrcNotFoundError.__doc__, stacklevel=2) +- return +- dict_append(info, libraries=[('flapack_src', lapack_src_info)]) +- +- if need_blas: +- blas_info = get_info('blas') +- if blas_info: +- dict_append(info, **blas_info) +- else: +- warnings.warn(BlasNotFoundError.__doc__, stacklevel=2) +- blas_src_info = get_info('blas_src') +- if not blas_src_info: +- warnings.warn(BlasSrcNotFoundError.__doc__, stacklevel=2) +- return +- dict_append(info, libraries=[('fblas_src', blas_src_info)]) ++ def calc_info(self): ++ user_order = os.environ.get('NPY_LAPACK_ORDER', None) ++ if user_order is None: ++ lapack_order = self.lapack_order ++ else: ++ # the user has requested the order of the ++ # check they are all in the available list, a COMMA SEPARATED list ++ user_order = user_order.lower().split(',') ++ non_existing = [] ++ lapack_order = [] ++ for order in user_order: ++ if order in self.lapack_order: ++ lapack_order.append(order) ++ elif len(order) > 0: ++ non_existing.append(order) ++ if len(non_existing) > 0: ++ raise ValueError("lapack_opt_info user defined " ++ "LAPACK order has unacceptable " ++ "values: {}".format(non_existing)) ++ ++ for lapack in lapack_order: ++ if getattr(self, '_calc_info_{}'.format(lapack))(): ++ return + +- self.set_info(**info) +- return ++ if 'lapack' not in lapack_order: ++ # Since the user may request *not* to use any library, we still need ++ # to raise warnings to signal missing packages! ++ warnings.warn(LapackNotFoundError.__doc__ or '', stacklevel=2) ++ warnings.warn(LapackSrcNotFoundError.__doc__ or '', stacklevel=2) + + + class blas_opt_info(system_info): + + notfounderror = BlasNotFoundError ++ # Default order of BLAS checks ++ blas_order = ['mkl', 'blis', 'openblas', 'atlas', 'accelerate', 'blas'] + +- def calc_info(self): ++ def _calc_info_mkl(self): ++ info = get_info('blas_mkl') ++ if info: ++ self.set_info(**info) ++ return True ++ return False + +- blas_mkl_info = get_info('blas_mkl') +- if blas_mkl_info: +- self.set_info(**blas_mkl_info) +- return ++ def _calc_info_blis(self): ++ info = get_info('blis') ++ if info: ++ self.set_info(**info) ++ return True ++ return False + +- blis_info = get_info('blis') +- if blis_info: +- self.set_info(**blis_info) +- return ++ def _calc_info_openblas(self): ++ info = get_info('openblas') ++ if info: ++ self.set_info(**info) ++ return True ++ return False + +- openblas_info = get_info('openblas') +- if openblas_info: +- self.set_info(**openblas_info) +- return ++ def _calc_info_atlas(self): ++ info = get_info('atlas_3_10_blas_threads') ++ if not info: ++ info = get_info('atlas_3_10_blas') ++ if not info: ++ info = get_info('atlas_blas_threads') ++ if not info: ++ info = get_info('atlas_blas') ++ if info: ++ self.set_info(**info) ++ return True ++ return False + +- atlas_info = get_info('atlas_3_10_blas_threads') +- if not atlas_info: +- atlas_info = get_info('atlas_3_10_blas') +- if not atlas_info: +- atlas_info = get_info('atlas_blas_threads') +- if not atlas_info: +- atlas_info = get_info('atlas_blas') +- +- accelerate_info = get_info('accelerate') +- if accelerate_info and not atlas_info: +- self.set_info(**accelerate_info) +- return ++ def _calc_info_accelerate(self): ++ info = get_info('accelerate') ++ if info: ++ self.set_info(**info) ++ return True ++ return False + +- need_blas = 0 ++ def _calc_info_blas(self): ++ # Warn about a non-optimized BLAS library ++ warnings.warn(BlasOptNotFoundError.__doc__ or '', stacklevel=3) + info = {} +- if atlas_info: +- info = atlas_info ++ dict_append(info, define_macros=[('NO_ATLAS_INFO', 1)]) ++ ++ blas = get_info('blas') ++ if blas: ++ dict_append(info, **blas) + else: +- warnings.warn(AtlasNotFoundError.__doc__, stacklevel=2) +- need_blas = 1 +- dict_append(info, define_macros=[('NO_ATLAS_INFO', 1)]) ++ # Not even BLAS was found! ++ warnings.warn(BlasNotFoundError.__doc__ or '', stacklevel=3) + +- if need_blas: +- blas_info = get_info('blas') +- if blas_info: +- dict_append(info, **blas_info) +- else: +- warnings.warn(BlasNotFoundError.__doc__, stacklevel=2) +- blas_src_info = get_info('blas_src') +- if not blas_src_info: +- warnings.warn(BlasSrcNotFoundError.__doc__, stacklevel=2) +- return +- dict_append(info, libraries=[('fblas_src', blas_src_info)]) ++ blas_src = get_info('blas_src') ++ if not blas_src: ++ warnings.warn(BlasSrcNotFoundError.__doc__ or '', stacklevel=3) ++ return False ++ dict_append(info, libraries=[('fblas_src', blas_src)]) + + self.set_info(**info) +- return ++ return True ++ ++ def calc_info(self): ++ user_order = os.environ.get('NPY_BLAS_ORDER', None) ++ if user_order is None: ++ blas_order = self.blas_order ++ else: ++ # the user has requested the order of the ++ # check they are all in the available list ++ user_order = user_order.lower().split(',') ++ non_existing = [] ++ blas_order = [] ++ for order in user_order: ++ if order in self.blas_order: ++ blas_order.append(order) ++ elif len(order) > 0: ++ non_existing.append(order) ++ if len(non_existing) > 0: ++ raise ValueError("blas_opt_info user defined BLAS order has unacceptable values: {}".format(non_existing)) ++ ++ for blas in blas_order: ++ if getattr(self, '_calc_info_{}'.format(blas))(): ++ return ++ ++ if 'blas' not in blas_order: ++ # Since the user may request *not* to use any library, we still need ++ # to raise warnings to signal missing packages! ++ warnings.warn(BlasNotFoundError.__doc__ or '', stacklevel=2) ++ warnings.warn(BlasSrcNotFoundError.__doc__ or '', stacklevel=2) + + + class blas_info(system_info): diff --git a/var/spack/repos/builtin/packages/py-numpy/package.py b/var/spack/repos/builtin/packages/py-numpy/package.py index dd3f2507e3..9e5a4d160a 100644 --- a/var/spack/repos/builtin/packages/py-numpy/package.py +++ b/var/spack/repos/builtin/packages/py-numpy/package.py @@ -15,8 +15,9 @@ class PyNumpy(PythonPackage): number capabilities""" homepage = "http://www.numpy.org/" - url = "https://pypi.io/packages/source/n/numpy/numpy-1.16.4.zip" + url = "https://pypi.io/packages/source/n/numpy/numpy-1.17.0.zip" + maintainers = ['adamjstewart'] install_time_test_callbacks = ['install_test', 'import_module_test'] import_modules = [ @@ -26,6 +27,7 @@ class PyNumpy(PythonPackage): 'numpy.distutils.command', 'numpy.distutils.fcompiler' ] + version('1.17.0', sha256='951fefe2fb73f84c620bec4e001e80a80ddaa1b84dce244ded7f1e0cbe0ed34a') version('1.16.4', sha256='7242be12a58fec245ee9734e625964b97cf7e3f2f7d016603f9e56660ce479c7') version('1.16.3', sha256='78a6f89da87eeb48014ec652a65c4ffde370c036d780a995edaeb121d3625621') version('1.16.2', sha256='6c692e3879dde0b67a9dc78f9bfb6f61c666b4562fd8619632d7043fb5b691b0') @@ -62,6 +64,8 @@ class PyNumpy(PythonPackage): variant('lapack', default=True, description='Build with LAPACK support') depends_on('python@2.7:2.8,3.4:', type=('build', 'run')) + depends_on('python@2.7:2.8,3.5:', type=('build', 'run'), when='@1.16:') + depends_on('python@3.5:', type=('build', 'run'), when='@1.17:') depends_on('py-setuptools', type='build') depends_on('blas', when='+blas') depends_on('lapack', when='+lapack') @@ -69,129 +73,175 @@ class PyNumpy(PythonPackage): depends_on('py-nose@1.0.0:', when='@:1.14', type='test') depends_on('py-pytest', when='@1.15:', type='test') - def setup_dependent_package(self, module, dependent_spec): - python_version = self.spec['python'].version.up_to(2) + # Allows you to specify order of BLAS/LAPACK preference + # https://github.com/numpy/numpy/pull/13132 + patch('blas-lapack-order.patch', when='@1.15:1.16') - self.spec.include = join_path( - self.prefix.lib, - 'python{0}'.format(python_version), - 'site-packages', - 'numpy/core/include') + # GCC 4.8 is the minimum version that works + conflicts('%gcc@:4.7', msg='GCC 4.8+ required') - def patch(self): + def flag_handler(self, name, flags): + # -std=c99 at least required, old versions of GCC default to -std=c90 + if self.spec.satisfies('%gcc@:5.1') and name == 'cflags': + flags.append(self.compiler.c99_flag) + return (flags, None, None) + + @run_before('build') + def set_blas_lapack(self): + # https://numpy.org/devdocs/user/building.html + # https://github.com/numpy/numpy/blob/master/site.cfg.example + + # Skip if no BLAS/LAPACK requested spec = self.spec + if '+blas' not in spec and '+lapack' not in spec: + return def write_library_dirs(f, dirs): - f.write('library_dirs=%s\n' % dirs) - if not ((platform.system() == "Darwin") and + f.write('library_dirs = {0}\n'.format(dirs)) + if not ((platform.system() == 'Darwin') and (Version(platform.mac_ver()[0]).up_to(2) == Version( '10.12'))): - f.write('rpath=%s\n' % dirs) + f.write('rpath = {0}\n'.format(dirs)) - # for build notes see http://www.scipy.org/scipylib/building/linux.html - blas_info = [] - lapack_info = [] - lapackblas_info = [] + blas_libs = LibraryList([]) + blas_headers = HeaderList([]) + if '+blas' in spec: + blas_libs = spec['blas'].libs + blas_headers = spec['blas'].headers + lapack_libs = LibraryList([]) + lapack_headers = HeaderList([]) if '+lapack' in spec: - lapack_info += spec['lapack'].libs + lapack_libs = spec['lapack'].libs + lapack_headers = spec['lapack'].headers + + lapackblas_libs = lapack_libs + blas_libs + lapackblas_headers = lapack_headers + blas_headers + + blas_lib_names = ','.join(blas_libs.names) + blas_lib_dirs = ':'.join(blas_libs.directories) + blas_header_dirs = ':'.join(blas_headers.directories) + + lapack_lib_names = ','.join(lapack_libs.names) + lapack_lib_dirs = ':'.join(lapack_libs.directories) + lapack_header_dirs = ':'.join(lapack_headers.directories) + + lapackblas_lib_names = ','.join(lapackblas_libs.names) + lapackblas_lib_dirs = ':'.join(lapackblas_libs.directories) + lapackblas_header_dirs = ':'.join(lapackblas_headers.directories) + + # Tell numpy where to find BLAS/LAPACK libraries + with open('site.cfg', 'w') as f: + if '^intel-mkl' in spec or '^intel-parallel-studio+mkl' in spec: + f.write('[mkl]\n') + # FIXME: as of @1.11.2, numpy does not work with separately + # specified threading and interface layers. A workaround is a + # terribly bad idea to use mkl_rt. In this case Spack will no + # longer be able to guarantee that one and the same variant of + # Blas/Lapack (32/64bit, threaded/serial) is used within the + # DAG. This may lead to a lot of hard-to-debug segmentation + # faults on user's side. Users may also break working + # installation by (unconsciously) setting environment variable + # to switch between different interface and threading layers + # dynamically. From this perspective it is no different from + # throwing away RPATH's and using LD_LIBRARY_PATH throughout + # Spack. + f.write('libraries = {0}\n'.format('mkl_rt')) + write_library_dirs(f, lapackblas_lib_dirs) + f.write('include_dirs = {0}\n'.format(lapackblas_header_dirs)) + + if '^blis' in spec: + f.write('[blis]\n') + f.write('libraries = {0}\n'.format(blas_lib_names)) + write_library_dirs(f, blas_lib_dirs) + f.write('include_dirs = {0}\n'.format(blas_header_dirs)) + + if '^openblas' in spec: + f.write('[openblas]\n') + f.write('libraries = {0}\n'.format(lapackblas_lib_names)) + write_library_dirs(f, lapackblas_lib_dirs) + f.write('include_dirs = {0}\n'.format(lapackblas_header_dirs)) + + if '^libflame' in spec: + f.write('[flame]\n') + f.write('libraries = {0}\n'.format(lapack_lib_names)) + write_library_dirs(f, lapack_lib_dirs) + f.write('include_dirs = {0}\n'.format(lapack_header_dirs)) + + if '^atlas' in spec: + f.write('[atlas]\n') + f.write('libraries = {0}\n'.format(lapackblas_lib_names)) + write_library_dirs(f, lapackblas_lib_dirs) + f.write('include_dirs = {0}\n'.format(lapackblas_header_dirs)) + + if '^veclibfort' in spec: + f.write('[accelerate]\n') + f.write('libraries = {0}\n'.format(lapackblas_lib_names)) + write_library_dirs(f, lapackblas_lib_dirs) + + if '^netlib-lapack' in spec: + # netlib requires blas and lapack listed + # separately so that scipy can find them + if spec.satisfies('+blas'): + f.write('[blas]\n') + f.write('libraries = {0}\n'.format(blas_lib_names)) + write_library_dirs(f, blas_lib_dirs) + f.write('include_dirs = {0}\n'.format(blas_header_dirs)) + if spec.satisfies('+lapack'): + f.write('[lapack]\n') + f.write('libraries = {0}\n'.format(lapack_lib_names)) + write_library_dirs(f, lapack_lib_dirs) + f.write('include_dirs = {0}\n'.format(lapack_header_dirs)) - if '+blas' in spec: - blas_info += spec['blas'].libs - - lapackblas_info = lapack_info + blas_info - - def write_empty_libs(f, provider): - f.write('[{0}]\n'.format(provider)) - f.write('libraries=\n') - write_library_dirs(f, '') - - if '+blas' in spec or '+lapack' in spec: - # note that one should not use [blas_opt] and [lapack_opt], see - # https://github.com/numpy/numpy/commit/ffd4332262ee0295cb942c94ed124f043d801eb6 - with open('site.cfg', 'w') as f: - # Unfortunately, numpy prefers to provide each BLAS/LAPACK - # differently. - blas_names = ','.join(blas_info.names) - blas_dirs = ':'.join(blas_info.directories) - lapack_names = ','.join(lapack_info.names) - lapack_dirs = ':'.join(lapack_info.directories) - lapackblas_names = ','.join(lapackblas_info.names) - lapackblas_dirs = ':'.join(lapackblas_info.directories) - - handled_blas_and_lapack = False - - # Special treatment for some (!) BLAS/LAPACK. Note that - # in this case library_dirs can not be specified within [ALL]. - if '^openblas' in spec: - f.write('[openblas]\n') - f.write('libraries=%s\n' % lapackblas_names) - write_library_dirs(f, lapackblas_dirs) - handled_blas_and_lapack = True - else: - write_empty_libs(f, 'openblas') - - if '^mkl' in spec: - # numpy does not expect system libraries needed for MKL - # here. - # names = [x for x in names if x.startswith('mkl')] - # FIXME: as of @1.11.2, numpy does not work with separately - # specified threading and interface layers. A workaround is - # a terribly bad idea to use mkl_rt. In this case Spack - # will no longer be able to guarantee that one and the - # same variant of Blas/Lapack (32/64bit, threaded/serial) - # is used within the DAG. This may lead to a lot of - # hard-to-debug segmentation faults on user's side. Users - # may also break working installation by (unconsciously) - # setting environment variable to switch between different - # interface and threading layers dynamically. From this - # perspective it is no different from throwing away RPATH's - # and using LD_LIBRARY_PATH throughout Spack. - f.write('[mkl]\n') - f.write('mkl_libs=%s\n' % 'mkl_rt') - write_library_dirs(f, lapackblas_dirs) - handled_blas_and_lapack = True - else: - # Without explicitly setting the search directories to be - # an empty list, numpy may retrieve and use mkl libs from - # the system. - write_empty_libs(f, 'mkl') - - if '^atlas' in spec: - f.write('[atlas]\n') - f.write('atlas_libs=%s\n' % lapackblas_names) - write_library_dirs(f, lapackblas_dirs) - handled_blas_and_lapack = True - else: - write_empty_libs(f, 'atlas') - - if '^netlib-lapack' in spec: - # netlib requires blas and lapack listed - # separately so that scipy can find them - if spec.satisfies('+blas'): - f.write('[blas]\n') - f.write('blas_libs=%s\n' % blas_names) - write_library_dirs(f, blas_dirs) - if spec.satisfies('+lapack'): - f.write('[lapack]\n') - f.write('lapack_libs=%s\n' % lapack_names) - write_library_dirs(f, lapack_dirs) - handled_blas_and_lapack = True - - if not handled_blas_and_lapack: - # The section title for the defaults changed in @1.10, see - # https://github.com/numpy/numpy/blob/master/site.cfg.example - if spec.satisfies('@:1.9.2'): - f.write('[DEFAULT]\n') - else: - f.write('[ALL]\n') - f.write('libraries=%s\n' % lapackblas_names) - write_library_dirs(f, lapackblas_dirs) + def setup_environment(self, spack_env, run_env): + # Tell numpy which BLAS/LAPACK libraries we want to use. + # https://github.com/numpy/numpy/pull/13132 + # https://numpy.org/devdocs/user/building.html#accelerated-blas-lapack-libraries + spec = self.spec + + # https://numpy.org/devdocs/user/building.html#blas + if '~blas' in spec: + blas = '' + elif spec['blas'].name == 'intel-mkl' or \ + spec['blas'].name == 'intel-parallel-studio': + blas = 'mkl' + elif spec['blas'].name == 'blis': + blas = 'blis' + elif spec['blas'].name == 'openblas': + blas = 'openblas' + elif spec['blas'].name == 'atlas': + blas = 'atlas' + elif spec['blas'].name == 'veclibfort': + blas = 'accelerate' + else: + blas = 'blas' + + spack_env.set('NPY_BLAS_ORDER', blas) + + # https://numpy.org/devdocs/user/building.html#lapack + if '~lapack' in spec: + lapack = '' + elif spec['lapack'].name == 'intel-mkl' or \ + spec['lapack'].name == 'intel-parallel-studio': + lapack = 'mkl' + elif spec['lapack'].name == 'openblas': + lapack = 'openblas' + elif spec['lapack'].name == 'libflame': + lapack = 'flame' + elif spec['lapack'].name == 'atlas': + lapack = 'atlas' + elif spec['lapack'].name == 'veclibfort': + lapack = 'accelerate' + else: + lapack = 'lapack' + + spack_env.set('NPY_LAPACK_ORDER', lapack) def build_args(self, spec, prefix): args = [] # From NumPy 1.10.0 on it's possible to do a parallel build. + # https://numpy.org/devdocs/user/building.html#parallel-builds if self.version >= Version('1.10.0'): # But Parallel build in Python 3.5+ is broken. See: # https://github.com/spack/spack/issues/7927 @@ -201,20 +251,6 @@ class PyNumpy(PythonPackage): return args - def setup_environment(self, spack_env, run_env): - # If py-numpy is installed as an external package, python won't - # be available in the spec. See #9149 for details. - if 'python' in self.spec: - python_version = self.spec['python'].version.up_to(2) - - include_path = join_path( - self.prefix.lib, - 'python{0}'.format(python_version), - 'site-packages', - 'numpy/core/include') - - run_env.prepend_path('CPATH', include_path) - def test(self): # `setup.py test` is not supported. Use one of the following # instead: diff --git a/var/spack/repos/builtin/packages/veclibfort/package.py b/var/spack/repos/builtin/packages/veclibfort/package.py index c1295dcf24..ad05471dae 100644 --- a/var/spack/repos/builtin/packages/veclibfort/package.py +++ b/var/spack/repos/builtin/packages/veclibfort/package.py @@ -33,6 +33,12 @@ class Veclibfort(Package): 'libvecLibFort', root=self.prefix, shared=shared, recursive=True ) + @property + def headers(self): + # veclibfort does not come with any headers. Return an empty list + # to avoid `spec['blas'].headers` from crashing. + return HeaderList([]) + def install(self, spec, prefix): if sys.platform != 'darwin': raise InstallError('vecLibFort can be installed on macOS only') -- cgit v1.2.3-70-g09d2