Reworking of lapack_shared_libs and similar properties (#1682)

* Turned <provider>_libs into an iterable

Modifications :
- added class LibraryList + unit tests
- added convenience functions `find_libraries` and `dedupe`
- modifed non Intel blas/lapack providers
- modified packages using blas_shared_libs and similar functions

* atlas : added pthread variant

* intel packages : added lapack_libs and blas_libs

* find_library_path : removed unused function

* PR review : fixed last issues

* LibraryList : added test on __add__ return type

* LibraryList : added __radd__ fixed unit tests

fix : failing unit tests due to missing `self`

* cp2k and dependecies : fixed blas-lapack related statements in package.py
This commit is contained in:
Massimiliano Culpo 2016-09-21 21:27:59 +02:00 committed by Todd Gamblin
parent 6b6f868f2a
commit d848559f70
33 changed files with 526 additions and 244 deletions

View File

@ -2090,12 +2090,11 @@ Blas and Lapack libraries
Different packages provide implementation of ``Blas`` and ``Lapack`` routines.
The names of the resulting static and/or shared libraries differ from package
to package. In order to make the ``install()`` method indifferent to the
to package. In order to make the ``install()`` method independent of the
choice of ``Blas`` implementation, each package which provides it
sets up ``self.spec.blas_shared_lib`` and ``self.spec.blas_static_lib`` to
point to the shared and static ``Blas`` libraries, respectively. The same
applies to packages which provide ``Lapack``. Package developers are advised to
use these variables, for example ``spec['blas'].blas_shared_lib`` instead of
sets up ``self.spec.blas_libs`` to point to the correct ``Blas`` libraries.
The same applies to packages which provide ``Lapack``. Package developers are advised to
use these variables, for example ``spec['blas'].blas_libs.joined()`` instead of
hard-coding ``join_path(spec['blas'].prefix.lib, 'libopenblas.so')``.
^^^^^^^^^^^^^^^^^^^^^

View File

@ -22,18 +22,22 @@
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
import os
import collections
import errno
import fileinput
import getpass
import glob
import numbers
import os
import re
import shutil
import stat
import errno
import getpass
from contextlib import contextmanager
import subprocess
import fileinput
import sys
from contextlib import contextmanager
import llnl.util.tty as tty
from llnl.util.lang import dedupe
__all__ = ['set_install_permissions', 'install', 'install_tree',
'traverse_tree',
@ -42,8 +46,8 @@
'filter_file',
'FileFilter', 'change_sed_delimiter', 'is_exe', 'force_symlink',
'set_executable', 'copy_mode', 'unset_executable_mode',
'remove_dead_links', 'remove_linked_tree', 'find_library_path',
'fix_darwin_install_name', 'to_link_flags', 'to_lib_name']
'remove_dead_links', 'remove_linked_tree',
'fix_darwin_install_name', 'find_libraries', 'LibraryList']
def filter_file(regex, repl, *filenames, **kwargs):
@ -440,35 +444,162 @@ def fix_darwin_install_name(path):
stdout=subprocess.PIPE).communicate()[0]
break
# Utilities for libraries
def to_lib_name(library):
"""Transforms a path to the library /path/to/lib<name>.xyz into <name>
class LibraryList(collections.Sequence):
"""Sequence of absolute paths to libraries
Provides a few convenience methods to manipulate library paths and get
commonly used compiler flags or names
"""
# Assume libXYZ.suffix
return os.path.basename(library)[3:].split(".")[0]
def __init__(self, libraries):
self.libraries = list(libraries)
def to_link_flags(library):
"""Transforms a path to a <library> into linking flags -L<dir> -l<name>.
@property
def directories(self):
"""Stable de-duplication of the directories where the libraries
reside
Return:
A string of linking flags.
>>> l = LibraryList(['/dir1/liba.a', '/dir2/libb.a', '/dir1/libc.a'])
>>> assert l.directories == ['/dir1', '/dir2']
"""
dir = os.path.dirname(library)
name = to_lib_name(library)
res = '-L%s -l%s' % (dir, name)
return res
return list(dedupe(
os.path.dirname(x) for x in self.libraries if os.path.dirname(x)
))
@property
def basenames(self):
"""Stable de-duplication of the base-names in the list
def find_library_path(libname, *paths):
"""Searches for a file called <libname> in each path.
Return:
directory where the library was found, if found. None otherwise.
>>> l = LibraryList(['/dir1/liba.a', '/dir2/libb.a', '/dir3/liba.a'])
>>> assert l.basenames == ['liba.a', 'libb.a']
"""
for path in paths:
library = join_path(path, libname)
if os.path.exists(library):
return path
return list(dedupe(os.path.basename(x) for x in self.libraries))
@property
def names(self):
"""Stable de-duplication of library names in the list
>>> l = LibraryList(['/dir1/liba.a', '/dir2/libb.a', '/dir3/liba.so'])
>>> assert l.names == ['a', 'b']
"""
return list(dedupe(x.split('.')[0][3:] for x in self.basenames))
@property
def search_flags(self):
"""Search flags for the libraries
>>> l = LibraryList(['/dir1/liba.a', '/dir2/libb.a', '/dir1/liba.so'])
>>> assert l.search_flags == '-L/dir1 -L/dir2'
"""
return ' '.join(['-L' + x for x in self.directories])
@property
def link_flags(self):
"""Link flags for the libraries
>>> l = LibraryList(['/dir1/liba.a', '/dir2/libb.a', '/dir1/liba.so'])
>>> assert l.search_flags == '-la -lb'
"""
return ' '.join(['-l' + name for name in self.names])
@property
def ld_flags(self):
"""Search flags + link flags
>>> l = LibraryList(['/dir1/liba.a', '/dir2/libb.a', '/dir1/liba.so'])
>>> assert l.search_flags == '-L/dir1 -L/dir2 -la -lb'
"""
return self.search_flags + ' ' + self.link_flags
def __getitem__(self, item):
cls = type(self)
if isinstance(item, numbers.Integral):
return self.libraries[item]
return cls(self.libraries[item])
def __add__(self, other):
return LibraryList(dedupe(self.libraries + list(other)))
def __radd__(self, other):
return self.__add__(other)
def __eq__(self, other):
return self.libraries == other.libraries
def __len__(self):
return len(self.libraries)
def joined(self, separator=' '):
return separator.join(self.libraries)
def __repr__(self):
return self.__class__.__name__ + '(' + repr(self.libraries) + ')'
def __str__(self):
return self.joined()
def find_libraries(args, root, shared=True, recurse=False):
"""Returns an iterable object containing a list of full paths to
libraries if found.
Args:
args: iterable object containing a list of library names to \
search for (e.g. 'libhdf5')
root: root folder where to start searching
shared: if True searches for shared libraries, otherwise for static
recurse: if False search only root folder, if True descends top-down \
from the root
Returns:
list of full paths to the libraries that have been found
"""
if not isinstance(args, collections.Sequence) or isinstance(args, str):
message = '{0} expects a sequence of strings as first argument'
message += ' [got {1} instead]'
raise TypeError(message.format(find_libraries.__name__, type(args)))
# Construct the right suffix for the library
if shared is True:
suffix = 'dylib' if sys.platform == 'darwin' else 'so'
else:
suffix = 'a'
# List of libraries we are searching with suffixes
libraries = ['{0}.{1}'.format(lib, suffix) for lib in args]
# Search method
if recurse is False:
search_method = _find_libraries_non_recursive
else:
search_method = _find_libraries_recursive
return search_method(libraries, root)
def _find_libraries_recursive(libraries, root):
library_dict = collections.defaultdict(list)
for path, _, files in os.walk(root):
for lib in libraries:
if lib in files:
library_dict[lib].append(
join_path(path, lib)
)
answer = []
for lib in libraries:
answer.extend(library_dict[lib])
return LibraryList(answer)
def _find_libraries_non_recursive(libraries, root):
def lib_or_none(lib):
library = join_path(root, lib)
if not os.path.exists(library):
return None
return library
return LibraryList(
[lib_or_none(lib) for lib in libraries if lib_or_none(lib) is not None]
)

View File

@ -374,6 +374,22 @@ def __iter__(self):
return wrapper()
def dedupe(sequence):
"""Yields a stable de-duplication of an hashable sequence
Args:
sequence: hashable sequence to be de-duplicated
Returns:
stable de-duplication of the sequence
"""
seen = set()
for x in sequence:
if x not in seen:
yield x
seen.add(x)
class RequiredAttributeError(ValueError):
def __init__(self, message):

View File

@ -527,6 +527,13 @@ def __init__(self, spec_like, *dep_like, **kwargs):
# XXX(deptype): default deptypes
self._add_dependency(spec, ('build', 'link'))
def __getattr__(self, item):
"""Delegate to self.package if the attribute is not in the spec"""
# This line is to avoid infinite recursion in case package is
# not present among self attributes
package = super(Spec, self).__getattribute__('package')
return getattr(package, item)
def get_dependency(self, name):
dep = self._dependencies.get(name)
if dep is not None:

View File

@ -53,6 +53,7 @@
'git_fetch',
'hg_fetch',
'install',
'library_list',
'link_tree',
'lock',
'make_executable',

View File

@ -0,0 +1,111 @@
##############################################################################
# Copyright (c) 2013-2016, Lawrence Livermore National Security, LLC.
# Produced at the Lawrence Livermore National Laboratory.
#
# This file is part of Spack.
# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
# LLNL-CODE-647188
#
# For details, see https://github.com/llnl/spack
# Please also see the LICENSE file for our notice and the LGPL.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License (as
# published by the Free Software Foundation) version 2.1, February 1999.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
# conditions of the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
##############################################################################
import unittest
from llnl.util.filesystem import LibraryList
class LibraryListTest(unittest.TestCase):
def setUp(self):
l = [
'/dir1/liblapack.a',
'/dir2/libfoo.dylib',
'/dir1/libblas.a',
'/dir3/libbar.so',
'libbaz.so'
]
self.liblist = LibraryList(l)
def test_repr(self):
x = eval(repr(self.liblist))
self.assertEqual(self.liblist, x)
def test_joined_and_str(self):
s1 = self.liblist.joined()
self.assertEqual(
s1,
'/dir1/liblapack.a /dir2/libfoo.dylib /dir1/libblas.a /dir3/libbar.so libbaz.so' # NOQA: ignore=E501
)
s2 = str(self.liblist)
self.assertEqual(s1, s2)
s3 = self.liblist.joined(';')
self.assertEqual(
s3,
'/dir1/liblapack.a;/dir2/libfoo.dylib;/dir1/libblas.a;/dir3/libbar.so;libbaz.so' # NOQA: ignore=E501
)
def test_flags(self):
search_flags = self.liblist.search_flags
self.assertTrue('-L/dir1' in search_flags)
self.assertTrue('-L/dir2' in search_flags)
self.assertTrue('-L/dir3' in search_flags)
self.assertTrue(isinstance(search_flags, str))
link_flags = self.liblist.link_flags
self.assertEqual(
link_flags,
'-llapack -lfoo -lblas -lbar -lbaz'
)
ld_flags = self.liblist.ld_flags
self.assertEqual(ld_flags, search_flags + ' ' + link_flags)
def test_paths_manipulation(self):
names = self.liblist.names
self.assertEqual(names, ['lapack', 'foo', 'blas', 'bar', 'baz'])
directories = self.liblist.directories
self.assertEqual(directories, ['/dir1', '/dir2', '/dir3'])
def test_get_item(self):
a = self.liblist[0]
self.assertEqual(a, '/dir1/liblapack.a')
b = self.liblist[:]
self.assertEqual(type(b), type(self.liblist))
self.assertEqual(self.liblist, b)
self.assertTrue(self.liblist is not b)
def test_add(self):
pylist = [
'/dir1/liblapack.a', # removed from the final list
'/dir2/libbaz.so',
'/dir4/libnew.a'
]
another = LibraryList(pylist)
l = self.liblist + another
self.assertEqual(len(l), 7)
# Invariant : l == l + l
self.assertEqual(l, l + l)
# Always produce an instance of LibraryList
self.assertEqual(
type(self.liblist),
type(self.liblist + pylist)
)
self.assertEqual(
type(pylist + self.liblist),
type(self.liblist)
)

View File

@ -46,18 +46,20 @@ class Armadillo(Package):
depends_on('hdf5', when='+hdf5')
def install(self, spec, prefix):
arpack = find_libraries(['libarpack'], root=spec[
'arpack-ng'].prefix.lib, shared=True)
superlu = find_libraries(['libsuperlu'], root=spec[
'superlu'].prefix, shared=False, recurse=True)
cmake_args = [
# ARPACK support
'-DARPACK_LIBRARY={0}/libarpack.{1}'.format(
spec['arpack-ng'].prefix.lib, dso_suffix),
'-DARPACK_LIBRARY={0}'.format(arpack.joined()),
# BLAS support
'-DBLAS_LIBRARY={0}'.format(spec['blas'].blas_shared_lib),
'-DBLAS_LIBRARY={0}'.format(spec['blas'].blas_libs.joined()),
# LAPACK support
'-DLAPACK_LIBRARY={0}'.format(spec['lapack'].lapack_shared_lib),
'-DLAPACK_LIBRARY={0}'.format(spec['lapack'].lapack_libs.joined()),
# SuperLU support
'-DSuperLU_INCLUDE_DIR={0}'.format(spec['superlu'].prefix.include),
'-DSuperLU_LIBRARY={0}/libsuperlu.a'.format(
spec['superlu'].prefix.lib64),
'-DSuperLU_LIBRARY={0}'.format(superlu.joined()),
# HDF5 support
'-DDETECT_HDF5={0}'.format('ON' if '+hdf5' in spec else 'OFF')
]

View File

@ -88,17 +88,16 @@ def install(self, spec, prefix):
options.append('-DCMAKE_INSTALL_NAME_DIR:PATH=%s/lib' % prefix)
# Make sure we use Spack's blas/lapack:
lapack_libs = spec['lapack'].lapack_libs.joined()
blas_libs = spec['blas'].blas_libs.joined()
options.extend([
'-DLAPACK_FOUND=true',
'-DLAPACK_INCLUDE_DIRS=%s' % spec['lapack'].prefix.include,
'-DLAPACK_LIBRARIES=%s' % (
spec['lapack'].lapack_shared_lib if '+shared' in spec else
spec['lapack'].lapack_static_lib),
'-DLAPACK_INCLUDE_DIRS={0}'.format(spec['lapack'].prefix.include),
'-DLAPACK_LIBRARIES={0}'.format(lapack_libs),
'-DBLAS_FOUND=true',
'-DBLAS_INCLUDE_DIRS=%s' % spec['blas'].prefix.include,
'-DBLAS_LIBRARIES=%s' % (
spec['blas'].blas_shared_lib if '+shared' in spec else
spec['blas'].blas_static_lib)
'-DBLAS_INCLUDE_DIRS={0}'.format(spec['blas'].prefix.include),
'-DBLAS_LIBRARIES={0}'.format(blas_libs)
])
if '+mpi' in spec:
@ -129,19 +128,12 @@ def install(self, spec, prefix):
'F77=%s' % spec['mpi'].mpif77
])
if '+shared' in spec:
options.extend([
'--with-blas=%s' % to_link_flags(
spec['blas'].blas_shared_lib),
'--with-lapack=%s' % to_link_flags(
spec['lapack'].lapack_shared_lib)
])
else:
options.extend([
'--with-blas=%s' % spec['blas'].blas_static_lib,
'--with-lapack=%s' % spec['lapack'].lapack_static_lib,
'--enable-shared=no'
'--with-blas={0}'.format(spec['blas'].blas_libs.ld_flags),
'--with-lapack={0}'.format(spec['lapack'].lapack_libs.ld_flags)
])
if '+shared' not in spec:
options.append('--enable-shared=no')
bootstrap()
configure(*options)

View File

@ -51,6 +51,7 @@ class Atlas(Package):
url='http://sourceforge.net/projects/math-atlas/files/Developer%20%28unstable%29/3.11.34/atlas3.11.34.tar.bz2')
variant('shared', default=True, description='Builds shared library')
variant('pthread', default=False, description='Use multithreaded libraries')
provides('blas')
provides('lapack')
@ -107,18 +108,32 @@ def install(self, spec, prefix):
make("install")
self.install_test()
def setup_dependent_package(self, module, dspec):
@property
def blas_libs(self):
# libsatlas.[so,dylib,dll ] contains all serial APIs (serial lapack,
# serial BLAS), and all ATLAS symbols needed to support them. Whereas
# libtatlas.[so,dylib,dll ] is parallel (multithreaded) version.
name = 'libsatlas.%s' % dso_suffix
libdir = find_library_path(name,
self.prefix.lib64,
self.prefix.lib)
is_threaded = '+pthread' in self.spec
if '+shared' in self.spec:
self.spec.blas_shared_lib = join_path(libdir, name)
self.spec.lapack_shared_lib = self.spec.blas_shared_lib
to_find = ['libtatlas'] if is_threaded else ['libsatlas']
shared = True
else:
interfaces = [
'libptcblas',
'libptf77blas'
] if is_threaded else [
'libcblas',
'libf77blas'
]
to_find = ['liblapack'] + interfaces + ['libatlas']
shared = False
return find_libraries(
to_find, root=self.prefix, shared=shared, recurse=True
)
@property
def lapack_libs(self):
return self.blas_libs
def install_test(self):
source_file = join_path(os.path.dirname(self.module.__file__),
@ -126,9 +141,8 @@ def install_test(self):
blessed_file = join_path(os.path.dirname(self.module.__file__),
'test_cblas_dgemm.output')
include_flags = ["-I%s" % join_path(self.spec.prefix, "include")]
link_flags = ["-L%s" % join_path(self.spec.prefix, "lib"),
"-lsatlas"]
include_flags = ["-I%s" % self.spec.prefix.include]
link_flags = self.lapack_libs.ld_flags
output = compile_c_and_execute(source_file, include_flags, link_flags)
compare_output_file(output, blessed_file)

View File

@ -95,7 +95,8 @@ def install(self, spec, prefix):
fcflags.extend([
'-I' + spec['fftw'].prefix.include
])
ldflags = ['-L' + spec['fftw'].prefix.lib]
fftw = find_libraries(['libfftw3'], root=spec['fftw'].prefix.lib)
ldflags = [fftw.search_flags]
libs = []
if '+plumed' in self.spec:
# Include Plumed.inc in the Makefile
@ -157,9 +158,8 @@ def install(self, spec, prefix):
),
'-I' + join_path(spec['pexsi'].prefix, 'fortran')
])
ldflags.extend([
'-L' + spec['scalapack'].prefix.lib
])
scalapack = spec['scalapack'].scalapack_libs
ldflags.append(scalapack.search_flags)
libs.extend([
join_path(spec['elpa'].prefix.lib,
'libelpa.{0}'.format(dso_suffix)),
@ -176,20 +176,15 @@ def install(self, spec, prefix):
'libmetis.{0}'.format(dso_suffix)
),
])
libs.extend(spec['scalapack'].scalapack_shared_libs)
libs.extend(scalapack)
libs.extend(self.spec['mpi'].mpicxx_shared_libs)
libs.extend(self.compiler.stdcxx_libs)
# LAPACK / BLAS
ldflags.extend([
'-L' + spec['lapack'].prefix.lib,
'-L' + spec['blas'].prefix.lib
])
libs.extend([
join_path(spec['fftw'].prefix.lib,
'libfftw3.{0}'.format(dso_suffix)),
spec['lapack'].lapack_shared_lib,
spec['blas'].blas_shared_lib
])
lapack = spec['lapack'].lapack_libs
blas = spec['blas'].blas_libs
ldflags.append((lapack + blas).search_flags)
libs.extend([str(x) for x in (fftw, lapack, blas)])
# Write compiler flags to file
mkf.write('CPPFLAGS = {0}\n'.format(' '.join(cppflags)))

View File

@ -123,6 +123,7 @@ def install(self, spec, prefix):
options.remove(word)
dsuf = 'dylib' if sys.platform == 'darwin' else 'so'
lapack_blas = spec['lapack'].lapack_libs + spec['blas'].blas_libs
options.extend([
'-DCMAKE_BUILD_TYPE=DebugRelease',
'-DDEAL_II_COMPONENT_EXAMPLES=ON',
@ -135,9 +136,7 @@ def install(self, spec, prefix):
'-DLAPACK_FOUND=true',
'-DLAPACK_INCLUDE_DIRS=%s;%s' % (
spec['lapack'].prefix.include, spec['blas'].prefix.include),
'-DLAPACK_LIBRARIES=%s;%s' % (
spec['lapack'].lapack_shared_lib,
spec['blas'].blas_shared_lib),
'-DLAPACK_LIBRARIES=%s' % lapack_blas.joined(';'),
'-DMUPARSER_DIR=%s' % spec['muparser'].prefix,
'-DUMFPACK_DIR=%s' % spec['suite-sparse'].prefix,
'-DTBB_DIR=%s' % spec['tbb'].prefix,

View File

@ -90,9 +90,9 @@ def configure(self, spec):
blas = 'blas.a'
lapack = 'lapack.a'
if '+blas' in spec:
blas = spec['blas'].blas_shared_lib
blas = spec['blas'].blas_libs.joined()
if '+lapack' in spec:
lapack = spec['lapack'].lapack_shared_lib
lapack = spec['lapack'].lapack_libs.joined()
# lapack must come before blas
config['LIB_LPK'] = ' '.join([lapack, blas])

View File

@ -87,9 +87,9 @@ def install(self, spec, prefix):
options.append('-DENABLE_OS_SPECIFIC_INSTALL=OFF')
# Make sure GMSH picks up correct BlasLapack by providing linker flags
options.append('-DBLAS_LAPACK_LIBRARIES=%s %s' %
(to_link_flags(spec['lapack'].lapack_shared_lib),
to_link_flags(spec['blas'].blas_shared_lib)))
blas_lapack = spec['lapack'].lapack_libs + spec['blas'].blas_libs
options.append(
'-DBLAS_LAPACK_LIBRARIES={0}'.format(blas_lapack.ld_flags))
# Gmsh does not have an option to compile against external metis.
# Its own Metis, however, fails to build

View File

@ -78,7 +78,7 @@ def configure(self, spec, arch):
'MPlib = -L{0}'.format(spec['mpi'].prefix.lib),
# Linear Algebra library (BLAS or VSIPL)
'LAinc = {0}'.format(spec['blas'].prefix.include),
'LAlib = {0}'.format(spec['blas'].blas_shared_lib),
'LAlib = {0}'.format(spec['blas'].blas_libs.joined()),
# F77 / C interface
'F2CDEFS = -DAdd_ -DF77_INTEGER=int -DStringSunStyle',
# HPL includes / libraries / specifics

View File

@ -60,13 +60,14 @@ def install(self, spec, prefix):
# to command the linker to include whole static libs' content into the
# shared lib
# Note: --with-(lapack|blas)_libs= needs space separated list of names
lapack = spec['lapack'].lapack_libs
blas = spec['blas'].blas_libs
configure_args = [
'--prefix=%s' % prefix,
'--with-lapack-libs=%s' % to_lib_name(
spec['lapack'].lapack_shared_lib),
'--with-lapack-libs=%s' % lapack.names,
'--with-lapack-lib-dirs=%s' % spec['lapack'].prefix.lib,
'--with-blas-libs=%s' % to_lib_name(
spec['blas'].blas_shared_lib),
'--with-blas-libs=%s' % blas.names,
'--with-blas-lib-dirs=%s' % spec['blas'].prefix.lib
]

View File

@ -45,6 +45,10 @@ class IntelParallelStudio(IntelInstaller):
variant('tools', default=True, description="Install the Intel Advisor, "
"VTune Amplifier, and Inspector tools")
variant('shared', default=True, description='Builds shared library')
variant('ilp64', default=False, description='64 bit integers')
variant('openmp', default=False, description='OpenMP multithreading layer')
provides('mpi', when='@cluster:+mpi')
provides('mkl', when='+mkl')
provides('daal', when='+daal')
@ -55,6 +59,28 @@ class IntelParallelStudio(IntelInstaller):
provides('lapack', when='+mkl')
# TODO: MKL also provides implementation of Scalapack.
@property
def blas_libs(self):
shared = True if '+shared' in self.spec else False
suffix = dso_suffix if '+shared' in self.spec else 'a'
mkl_integer = ['libmkl_intel_ilp64'] if '+ilp64' in self.spec else ['libmkl_intel_lp64'] # NOQA: ignore=E501
mkl_threading = ['libmkl_intel_thread'] if '+openmp' in self.spec else ['libmkl_sequential'] # NOQA: ignore=E501
mkl_libs = find_libraries(
mkl_integer + ['libmkl_core'] + mkl_threading,
root=join_path(self.prefix.lib, 'intel64'),
shared=shared
)
system_libs = [
'libpthread.{0}'.format(suffix),
'libm.{0}'.format(suffix),
'libdl.{0}'.format(suffix)
]
return mkl_libs + system_libs
@property
def lapack_libs(self):
return self.blas_libs
def check_variants(self, spec):
error_message = '\t{variant} can not be turned off if "+all" is set'
@ -171,24 +197,6 @@ def install(self, spec, prefix):
os.symlink(os.path.join(self.prefix.man, "common", "man1"),
os.path.join(self.prefix.man, "man1"))
def setup_dependent_package(self, module, dspec):
# For now use Single Dynamic Library:
# To set the threading layer at run time, use the
# mkl_set_threading_layer function or set MKL_THREADING_LAYER
# variable to one of the following values: INTEL, SEQUENTIAL, PGI.
# To set interface layer at run time, use the mkl_set_interface_layer
# function or set the MKL_INTERFACE_LAYER variable to LP64 or ILP64.
# Otherwise one would need to specify several libraries
# (e.g. mkl_intel_lp64;mkl_sequential;mkl_core), which reflect
# different interface and threading layers.
name = 'libmkl_rt.%s' % dso_suffix
libdir = find_library_path(name, self.prefix.lib64, self.prefix.lib)
self.spec.blas_shared_lib = join_path(libdir, name)
self.spec.lapack_shared_lib = self.spec.blas_shared_lib
def setup_environment(self, spack_env, run_env):
# TODO: Determine variables needed for the professional edition.

View File

@ -24,13 +24,40 @@ class Mkl(IntelInstaller):
version('11.3.3.210', 'f72546df27f5ebb0941b5d21fd804e34',
url="file://%s/l_mkl_11.3.3.210.tgz" % os.getcwd())
variant('shared', default=True, description='Builds shared library')
variant('ilp64', default=False, description='64 bit integers')
variant('openmp', default=False, description='OpenMP multithreading layer')
# virtual dependency
provides('blas')
provides('lapack')
# TODO: MKL also provides implementation of Scalapack.
def install(self, spec, prefix):
@property
def blas_libs(self):
shared = True if '+shared' in self.spec else False
suffix = dso_suffix if '+shared' in self.spec else 'a'
mkl_integer = ['libmkl_intel_ilp64'] if '+ilp64' in self.spec else ['libmkl_intel_lp64'] # NOQA: ignore=E501
mkl_threading = ['libmkl_sequential']
if '+openmp' in spec:
mkl_threading = ['libmkl_intel_thread'] if '%intel' in self.spec else ['libmkl_gnu_thread'] # NOQA: ignore=E501
mkl_libs = find_libraries(
mkl_integer + ['libmkl_core'] + mkl_threading,
root=join_path(self.prefix.lib, 'intel64'),
shared=shared
)
system_libs = [
'libpthread.{0}'.format(suffix),
'libm.{0}'.format(suffix),
'libdl.{0}'.format(suffix)
]
return mkl_libs + system_libs
@property
def lapack_libs(self):
return self.blas_libs
def install(self, spec, prefix):
self.intel_prefix = os.path.join(prefix, "pkg")
IntelInstaller.install(self, spec, prefix)
@ -45,25 +72,6 @@ def install(self, spec, prefix):
os.symlink(os.path.join(mkl_lib_dir, f),
os.path.join(self.prefix, "lib", f))
def setup_dependent_package(self, module, dspec):
# For now use Single Dynamic Library:
# To set the threading layer at run time, use the
# mkl_set_threading_layer function or set MKL_THREADING_LAYER
# variable to one of the following values: INTEL, SEQUENTIAL, PGI.
# To set interface layer at run time, use the mkl_set_interface_layer
# function or set the MKL_INTERFACE_LAYER variable to LP64 or ILP64.
# Otherwise one would need to specify several libraries
# (e.g. mkl_intel_lp64;mkl_sequential;mkl_core), which reflect
# different interface and threading layers.
name = 'libmkl_rt.%s' % dso_suffix
libdir = find_library_path(name, self.prefix.lib64, self.prefix.lib)
# Now set blas/lapack libs:
self.spec.blas_shared_lib = join_path(libdir, name)
self.spec.lapack_shared_lib = self.spec.blas_shared_lib
def setup_dependent_environment(self, spack_env, run_env, dependent_spec):
# set up MKLROOT for everyone using MKL package
spack_env.set('MKLROOT', self.prefix)

View File

@ -75,9 +75,8 @@ def write_makefile_inc(self):
raise RuntimeError(
'You cannot use the variants parmetis or ptscotch without mpi')
makefile_conf = ["LIBBLAS = %s" % to_link_flags(
self.spec['blas'].blas_shared_lib)
]
blas = self.spec['blas'].blas_libs
makefile_conf = ["LIBBLAS = %s" % blas.ld_flags]
orderings = ['-Dpord']
@ -136,11 +135,12 @@ def write_makefile_inc(self):
'OPTC = %s -O ' % fpic])
if '+mpi' in self.spec:
scalapack = self.spec['scalapack'].scalapack_libs
makefile_conf.extend(
["CC = %s" % join_path(self.spec['mpi'].prefix.bin, 'mpicc'),
"FC = %s" % join_path(self.spec['mpi'].prefix.bin, 'mpif90'),
"FL = %s" % join_path(self.spec['mpi'].prefix.bin, 'mpif90'),
"SCALAP = %s" % self.spec['scalapack'].fc_link,
"SCALAP = %s" % scalapack.ld_flags,
"MUMPS_TYPE = par"])
else:
makefile_conf.extend(

View File

@ -67,6 +67,20 @@ def patch(self):
'${CMAKE_CURRENT_SOURCE_DIR}/cmake/',
'CBLAS/CMakeLists.txt', string=True)
@property
def blas_libs(self):
shared = True if '+shared' in self.spec else False
return find_libraries(
['libblas'], root=self.prefix, shared=shared, recurse=True
)
@property
def lapack_libs(self):
shared = True if '+shared' in self.spec else False
return find_libraries(
['liblapack'], root=self.prefix, shared=shared, recurse=True
)
def install_one(self, spec, prefix, shared):
cmake_args = [
'-DBUILD_SHARED_LIBS:BOOL=%s' % ('ON' if shared else 'OFF'),
@ -100,18 +114,3 @@ def install(self, spec, prefix):
# Build shared libraries if requested.
if '+shared' in spec:
self.install_one(spec, prefix, True)
def setup_dependent_package(self, module, dspec):
# This is WIP for a prototype interface for virtual packages.
# We can update this as more builds start depending on BLAS/LAPACK.
libdir = find_library_path(
'libblas.a', self.prefix.lib64, self.prefix.lib)
self.spec.blas_static_lib = join_path(libdir, 'libblas.a')
self.spec.lapack_static_lib = join_path(libdir, 'liblapack.a')
if '+shared' in self.spec:
self.spec.blas_shared_lib = join_path(
libdir, 'libblas.%s' % dso_suffix)
self.spec.lapack_shared_lib = join_path(
libdir, 'liblapack.%s' % dso_suffix)

View File

@ -28,7 +28,8 @@
class NetlibScalapack(Package):
"""ScaLAPACK is a library of high-performance linear algebra routines for
parallel distributed memory machines"""
parallel distributed memory machines
"""
homepage = "http://www.netlib.org/scalapack/"
url = "http://www.netlib.org/scalapack/scalapack-2.0.2.tgz"
@ -39,10 +40,16 @@ class NetlibScalapack(Package):
# versions before 2.0.0 are not using cmake and requires blacs as
# a separated package
variant('shared', default=True,
description='Build the shared library version')
variant('fpic', default=False,
description="Build with -fpic compiler option")
variant(
'shared',
default=True,
description='Build the shared library version'
)
variant(
'fpic',
default=False,
description='Build with -fpic compiler option'
)
provides('scalapack')
@ -51,6 +58,13 @@ class NetlibScalapack(Package):
depends_on('blas')
depends_on('cmake', when='@2.0.0:', type='build')
@property
def scalapack_libs(self):
shared = True if '+shared' in self.spec else False
return find_libraries(
['libscalapack'], root=self.prefix, shared=shared, recurse=True
)
def install(self, spec, prefix):
options = [
"-DBUILD_SHARED_LIBS:BOOL=%s" % ('ON' if '+shared' in spec else
@ -60,14 +74,13 @@ def install(self, spec, prefix):
]
# Make sure we use Spack's Lapack:
blas = spec['blas'].blas_libs
lapack = spec['lapack'].lapack_libs
options.extend([
'-DLAPACK_FOUND=true',
'-DLAPACK_LIBRARIES=%s' % (
spec['lapack'].lapack_shared_lib if '+shared' in spec else
spec['lapack'].lapack_static_lib),
'-DBLAS_LIBRARIES=%s' % (
spec['blas'].blas_shared_lib if '+shared' in spec else
spec['blas'].blas_static_lib)
'-DLAPACK_INCLUDE_DIRS=%s' % spec['lapack'].prefix.include,
'-DLAPACK_LIBRARIES=%s' % (lapack.joined()),
'-DBLAS_LIBRARIES=%s' % (blas.joined())
])
if '+fpic' in spec:
@ -86,12 +99,3 @@ def install(self, spec, prefix):
# The shared libraries are not installed correctly on Darwin:
if (sys.platform == 'darwin') and ('+shared' in spec):
fix_darwin_install_name(prefix.lib)
def setup_dependent_package(self, module, dependent_spec):
spec = self.spec
lib_suffix = dso_suffix if '+shared' in spec else 'a'
spec.fc_link = '-L%s -lscalapack' % spec.prefix.lib
spec.cc_link = spec.fc_link
spec.libraries = [join_path(spec.prefix.lib,
'libscalapack.%s' % lib_suffix)]

View File

@ -66,6 +66,9 @@ class Nwchem(Package):
patch('Gcc6_macs_optfix.patch', when='@6.6', level=0)
def install(self, spec, prefix):
scalapack = spec['scalapack'].scalapack_libs
lapack = spec['lapack'].lapack_libs
blas = spec['blas'].blas_libs
# see http://www.nwchem-sw.org/index.php/Compiling_NWChem
args = []
args.extend([
@ -79,13 +82,11 @@ def install(self, spec, prefix):
'USE_PYTHONCONFIG=y',
'PYTHONVERSION=%s' % spec['python'].version.up_to(2),
'PYTHONHOME=%s' % spec['python'].prefix,
'BLASOPT=%s %s' % (
to_link_flags(spec['lapack'].lapack_shared_lib),
to_link_flags(spec['blas'].blas_shared_lib)),
'BLAS_LIB=%s' % to_link_flags(spec['blas'].blas_shared_lib),
'LAPACK_LIB=%s' % to_link_flags(spec['lapack'].lapack_shared_lib),
'BLASOPT=%s' % ((lapack + blas).ld_flags),
'BLAS_LIB=%s' % blas.ld_flags,
'LAPACK_LIB=%s' % lapack.ld_flags,
'USE_SCALAPACK=y',
'SCALAPACK=%s' % spec['scalapack'].fc_link,
'SCALAPACK=%s' % scalapack.ld_flags,
'NWCHEM_MODULES=all python',
'NWCHEM_LONG_PATHS=Y' # by default NWCHEM_TOP is 64 char max
])

View File

@ -46,13 +46,13 @@ class Octopus(Package):
# FEAST, Libfm, PFFT, ISF, PNFFT
def install(self, spec, prefix):
lapack = spec['lapack'].lapack_libs
blas = spec['blas'].blas_libs
args = []
args.extend([
'--prefix=%s' % prefix,
'--with-blas=%s' % to_link_flags(
spec['blas'].blas_shared_lib),
'--with-lapack=%s' % to_link_flags(
spec['lapack'].lapack_shared_lib),
'--with-blas=%s' % blas.ld_flags,
'--with-lapack=%s' % lapack.ld_flags,
'--with-gsl-prefix=%s' % spec['gsl'].prefix,
'--with-libxc-prefix=%s' % spec['libxc'].prefix,
'CC=%s' % spec['mpi'].mpicc,

View File

@ -51,6 +51,17 @@ class Openblas(Package):
patch('make.patch')
@property
def blas_libs(self):
shared = True if '+shared' in self.spec else False
return find_libraries(
['libopenblas'], root=self.prefix, shared=shared, recurse=True
)
@property
def lapack_libs(self):
return self.blas_libs
def install(self, spec, prefix):
# As of 06/2016 there is no mechanism to specify that packages which
# depends on Blas/Lapack need C or/and Fortran symbols. For now
@ -100,6 +111,9 @@ def install(self, spec, prefix):
# no quotes around prefix (spack doesn't use a shell)
make('install', "PREFIX=%s" % prefix, *make_defs)
# TODO : the links below are mainly there because client
# TODO : packages are wrongly written. Check if they can be removed
# Blas virtual package should provide blas.a and libblas.a
with working_dir(prefix.lib):
symlink('libopenblas.a', 'blas.a')
@ -120,21 +134,6 @@ def install(self, spec, prefix):
# test.
self.check_install(spec)
def setup_dependent_package(self, module, dspec):
# This is WIP for a prototype interface for virtual packages.
# We can update this as more builds start depending on BLAS/LAPACK.
libdir = find_library_path('libopenblas.a',
self.prefix.lib64,
self.prefix.lib)
self.spec.blas_static_lib = join_path(libdir, 'libopenblas.a')
self.spec.lapack_static_lib = self.spec.blas_static_lib
if '+shared' in self.spec:
self.spec.blas_shared_lib = join_path(libdir, 'libopenblas.%s' %
dso_suffix)
self.spec.lapack_shared_lib = self.spec.blas_shared_lib
def check_install(self, spec):
source_file = join_path(os.path.dirname(self.module.__file__),
'test_cblas_dgemm.c')

View File

@ -37,12 +37,8 @@ class Opium(Package):
depends_on('lapack')
def install(self, spec, prefix):
options = [
'LDFLAGS=%s %s' % (
to_link_flags(spec['lapack'].lapack_shared_lib),
to_link_flags(spec['blas'].blas_shared_lib)
)
]
libs = spec['lapack'].lapack_libs + spec['blas'].blas_libs
options = ['LDFLAGS=%s' % libs.ld_flags]
configure(*options)
with working_dir("src", create=False):

View File

@ -67,8 +67,8 @@ def install(self, spec, prefix):
'@PARMETIS_PREFIX': self.spec['parmetis'].prefix,
'@LAPACK_PREFIX': self.spec['lapack'].prefix,
'@BLAS_PREFIX': self.spec['blas'].prefix,
'@LAPACK_LIBS': self.spec['lapack'].lapack_shared_lib,
'@BLAS_LIBS': self.spec['lapack'].blas_shared_lib,
'@LAPACK_LIBS': self.spec['lapack'].lapack_libs.joined(),
'@BLAS_LIBS': self.spec['lapack'].blas_libs.joined(),
'@STDCXX_LIB': ' '.join(self.compiler.stdcxx_libs)
}

View File

@ -62,9 +62,10 @@ class Psi4(Package):
def install(self, spec, prefix):
cmake_args = [
'-DBLAS_TYPE={0}'.format(spec['blas'].name.upper()),
'-DBLAS_LIBRARIES={0}'.format(spec['blas'].blas_shared_lib),
'-DBLAS_LIBRARIES={0}'.format(spec['blas'].blas_libs.joined()),
'-DLAPACK_TYPE={0}'.format(spec['lapack'].name.upper()),
'-DLAPACK_LIBRARIES={0}'.format(spec['lapack'].lapack_shared_lib),
'-DLAPACK_LIBRARIES={0}'.format(
spec['lapack'].lapack_libs.joined()),
'-DBOOST_INCLUDEDIR={0}'.format(spec['boost'].prefix.include),
'-DBOOST_LIBRARYDIR={0}'.format(spec['boost'].prefix.lib),
'-DENABLE_CHEMPS2=OFF'

View File

@ -47,7 +47,7 @@ def install(self, spec, prefix):
env['ATLAS'] = join_path(
spec['atlas'].prefix.lib, 'libatlas.' + dso_suffix)
else:
env['BLAS'] = spec['blas'].blas_shared_lib
env['LAPACK'] = spec['lapack'].lapack_shared_lib
env['BLAS'] = spec['blas'].blas_libs.joined()
env['LAPACK'] = spec['lapack'].lapack_libs.joined()
python('setup.py', 'install', '--prefix=%s' % prefix)

View File

@ -80,8 +80,8 @@ def install(self, spec, prefix):
# Make sure Spack's Blas/Lapack is used. Otherwise System's
# Blas/Lapack might be picked up.
blas = to_link_flags(spec['blas'].blas_shared_lib)
lapack = to_link_flags(spec['lapack'].lapack_shared_lib)
blas = spec['blas'].blas_libs.ld_flags
lapack = spec['lapack'].lapack_libs.ld_flags
if '@4.5.1' in spec:
# adding -lstdc++ is clearly an ugly way to do this, but it follows
# with the TCOV path of SparseSuite 4.5.1's Suitesparse_config.mk

View File

@ -79,9 +79,9 @@ def install(self, spec, prefix):
if '+lapack' in spec:
cmake_args.extend([
'-DLAPACK_ENABLE=ON',
'-DLAPACK_LIBRARIES={0};{1}'.format(
spec['lapack'].lapack_shared_lib,
spec['blas'].blas_shared_lib
'-DLAPACK_LIBRARIES={0}'.format(
(spec['lapack'].lapack_libs +
spec['blas'].blas_libs).joined(';')
)
])
else:

View File

@ -46,15 +46,14 @@ class SuperluDist(Package):
depends_on('metis@5:')
def install(self, spec, prefix):
lapack_blas = spec['lapack'].lapack_libs + spec['blas'].blas_libs
makefile_inc = []
makefile_inc.extend([
'PLAT = _mac_x',
'DSuperLUroot = %s' % self.stage.source_path,
'DSUPERLULIB = $(DSuperLUroot)/lib/libsuperlu_dist.a',
'BLASDEF = -DUSE_VENDOR_BLAS',
'BLASLIB = %s %s' %
(to_link_flags(spec['lapack'].lapack_shared_lib),
to_link_flags(spec['blas'].blas_shared_lib)),
'BLASLIB = %s' % lapack_blas.ld_flags,
'METISLIB = -L%s -lmetis' % spec['metis'].prefix.lib,
'PARMETISLIB = -L%s -lparmetis' % spec['parmetis'].prefix.lib,
'FLIBS =',

View File

@ -42,7 +42,7 @@ def install(self, spec, prefix):
'-DCMAKE_POSITION_INDEPENDENT_CODE=ON',
# BLAS support
'-Denable_blaslib=OFF',
'-DBLAS_blas_LIBRARY={0}'.format(spec['blas'].blas_shared_lib)
'-DBLAS_blas_LIBRARY={0}'.format(spec['blas'].blas_libs.joined())
]
cmake_args.extend(std_cmake_args)

View File

@ -132,6 +132,8 @@ def install(self, spec, prefix):
mpi_bin = spec['mpi'].prefix.bin
# Note: -DXYZ_LIBRARY_NAMES= needs semicolon separated list of names
blas = spec['blas'].blas_libs
lapack = spec['lapack'].lapack_libs
options.extend([
'-DTrilinos_ENABLE_ALL_PACKAGES:BOOL=ON',
'-DTrilinos_ENABLE_ALL_OPTIONAL_PACKAGES:BOOL=ON',
@ -145,12 +147,10 @@ def install(self, spec, prefix):
'-DTPL_ENABLE_MPI:BOOL=ON',
'-DMPI_BASE_DIR:PATH=%s' % spec['mpi'].prefix,
'-DTPL_ENABLE_BLAS=ON',
'-DBLAS_LIBRARY_NAMES=%s' % to_lib_name(
spec['blas'].blas_shared_lib),
'-DBLAS_LIBRARY_NAMES=%s' % ';'.join(blas.names),
'-DBLAS_LIBRARY_DIRS=%s' % spec['blas'].prefix.lib,
'-DTPL_ENABLE_LAPACK=ON',
'-DLAPACK_LIBRARY_NAMES=%s' % to_lib_name(
spec['lapack'].lapack_shared_lib),
'-DLAPACK_LIBRARY_NAMES=%s' % ';'.join(lapack.names),
'-DLAPACK_LIBRARY_DIRS=%s' % spec['lapack'].prefix.lib,
'-DTrilinos_ENABLE_EXPLICIT_INSTANTIATION:BOOL=ON',
'-DTrilinos_ENABLE_CXX11:BOOL=ON',

View File

@ -47,13 +47,12 @@ class Wannier90(Package):
def install(self, spec, prefix):
lapack = self.spec['lapack'].lapack_libs
blas = self.spec['blas'].blas_libs
substitutions = {
'@F90': spack_fc,
'@MPIF90': self.spec['mpi'].mpifc,
'@LIBS': ' '.join([
self.spec['lapack'].lapack_shared_lib,
self.spec['lapack'].blas_shared_lib
])
'@LIBS': (lapack + blas).joined()
}
#######
# TODO : this part is replicated in PEXSI