2992 lines
103 KiB
Python
2992 lines
103 KiB
Python
|
#!/usr/bin/env python3
|
||
|
"""
|
||
|
This file defines a set of system_info classes for getting
|
||
|
information about various resources (libraries, library directories,
|
||
|
include directories, etc.) in the system. Usage:
|
||
|
info_dict = get_info(<name>)
|
||
|
where <name> is a string 'atlas','x11','fftw','lapack','blas',
|
||
|
'lapack_src', 'blas_src', etc. For a complete list of allowed names,
|
||
|
see the definition of get_info() function below.
|
||
|
|
||
|
Returned info_dict is a dictionary which is compatible with
|
||
|
distutils.setup keyword arguments. If info_dict == {}, then the
|
||
|
asked resource is not available (system_info could not find it).
|
||
|
|
||
|
Several *_info classes specify an environment variable to specify
|
||
|
the locations of software. When setting the corresponding environment
|
||
|
variable to 'None' then the software will be ignored, even when it
|
||
|
is available in system.
|
||
|
|
||
|
Global parameters:
|
||
|
system_info.search_static_first - search static libraries (.a)
|
||
|
in precedence to shared ones (.so, .sl) if enabled.
|
||
|
system_info.verbosity - output the results to stdout if enabled.
|
||
|
|
||
|
The file 'site.cfg' is looked for in
|
||
|
|
||
|
1) Directory of main setup.py file being run.
|
||
|
2) Home directory of user running the setup.py file as ~/.numpy-site.cfg
|
||
|
3) System wide directory (location of this file...)
|
||
|
|
||
|
The first one found is used to get system configuration options The
|
||
|
format is that used by ConfigParser (i.e., Windows .INI style). The
|
||
|
section ALL is not intended for general use.
|
||
|
|
||
|
Appropriate defaults are used if nothing is specified.
|
||
|
|
||
|
The order of finding the locations of resources is the following:
|
||
|
1. environment variable
|
||
|
2. section in site.cfg
|
||
|
3. DEFAULT section in site.cfg
|
||
|
4. System default search paths (see ``default_*`` variables below).
|
||
|
Only the first complete match is returned.
|
||
|
|
||
|
Currently, the following classes are available, along with their section names:
|
||
|
|
||
|
Numeric_info:Numeric
|
||
|
_numpy_info:Numeric
|
||
|
_pkg_config_info:None
|
||
|
accelerate_info:accelerate
|
||
|
agg2_info:agg2
|
||
|
amd_info:amd
|
||
|
atlas_3_10_blas_info:atlas
|
||
|
atlas_3_10_blas_threads_info:atlas
|
||
|
atlas_3_10_info:atlas
|
||
|
atlas_3_10_threads_info:atlas
|
||
|
atlas_blas_info:atlas
|
||
|
atlas_blas_threads_info:atlas
|
||
|
atlas_info:atlas
|
||
|
atlas_threads_info:atlas
|
||
|
blas64__opt_info:ALL # usage recommended (general ILP64 BLAS, 64_ symbol suffix)
|
||
|
blas_ilp64_opt_info:ALL # usage recommended (general ILP64 BLAS)
|
||
|
blas_ilp64_plain_opt_info:ALL # usage recommended (general ILP64 BLAS, no symbol suffix)
|
||
|
blas_info:blas
|
||
|
blas_mkl_info:mkl
|
||
|
blas_opt_info:ALL # usage recommended
|
||
|
blas_src_info:blas_src
|
||
|
blis_info:blis
|
||
|
boost_python_info:boost_python
|
||
|
dfftw_info:fftw
|
||
|
dfftw_threads_info:fftw
|
||
|
djbfft_info:djbfft
|
||
|
f2py_info:ALL
|
||
|
fft_opt_info:ALL
|
||
|
fftw2_info:fftw
|
||
|
fftw3_info:fftw3
|
||
|
fftw_info:fftw
|
||
|
fftw_threads_info:fftw
|
||
|
flame_info:flame
|
||
|
freetype2_info:freetype2
|
||
|
gdk_2_info:gdk_2
|
||
|
gdk_info:gdk
|
||
|
gdk_pixbuf_2_info:gdk_pixbuf_2
|
||
|
gdk_pixbuf_xlib_2_info:gdk_pixbuf_xlib_2
|
||
|
gdk_x11_2_info:gdk_x11_2
|
||
|
gtkp_2_info:gtkp_2
|
||
|
gtkp_x11_2_info:gtkp_x11_2
|
||
|
lapack64__opt_info:ALL # usage recommended (general ILP64 LAPACK, 64_ symbol suffix)
|
||
|
lapack_atlas_3_10_info:atlas
|
||
|
lapack_atlas_3_10_threads_info:atlas
|
||
|
lapack_atlas_info:atlas
|
||
|
lapack_atlas_threads_info:atlas
|
||
|
lapack_ilp64_opt_info:ALL # usage recommended (general ILP64 LAPACK)
|
||
|
lapack_ilp64_plain_opt_info:ALL # usage recommended (general ILP64 LAPACK, no symbol suffix)
|
||
|
lapack_info:lapack
|
||
|
lapack_mkl_info:mkl
|
||
|
lapack_opt_info:ALL # usage recommended
|
||
|
lapack_src_info:lapack_src
|
||
|
mkl_info:mkl
|
||
|
numarray_info:numarray
|
||
|
numerix_info:numerix
|
||
|
numpy_info:numpy
|
||
|
openblas64__info:openblas64_
|
||
|
openblas64__lapack_info:openblas64_
|
||
|
openblas_clapack_info:openblas
|
||
|
openblas_ilp64_info:openblas_ilp64
|
||
|
openblas_ilp64_lapack_info:openblas_ilp64
|
||
|
openblas_info:openblas
|
||
|
openblas_lapack_info:openblas
|
||
|
sfftw_info:fftw
|
||
|
sfftw_threads_info:fftw
|
||
|
system_info:ALL
|
||
|
umfpack_info:umfpack
|
||
|
wx_info:wx
|
||
|
x11_info:x11
|
||
|
xft_info:xft
|
||
|
|
||
|
Example:
|
||
|
----------
|
||
|
[DEFAULT]
|
||
|
# default section
|
||
|
library_dirs = /usr/lib:/usr/local/lib:/opt/lib
|
||
|
include_dirs = /usr/include:/usr/local/include:/opt/include
|
||
|
src_dirs = /usr/local/src:/opt/src
|
||
|
# search static libraries (.a) in preference to shared ones (.so)
|
||
|
search_static_first = 0
|
||
|
|
||
|
[fftw]
|
||
|
libraries = rfftw, fftw
|
||
|
|
||
|
[atlas]
|
||
|
library_dirs = /usr/lib/3dnow:/usr/lib/3dnow/atlas
|
||
|
# for overriding the names of the atlas libraries
|
||
|
libraries = lapack, f77blas, cblas, atlas
|
||
|
|
||
|
[x11]
|
||
|
library_dirs = /usr/X11R6/lib
|
||
|
include_dirs = /usr/X11R6/include
|
||
|
----------
|
||
|
|
||
|
Note that the ``libraries`` key is the default setting for libraries.
|
||
|
|
||
|
Authors:
|
||
|
Pearu Peterson <pearu@cens.ioc.ee>, February 2002
|
||
|
David M. Cooke <cookedm@physics.mcmaster.ca>, April 2002
|
||
|
|
||
|
Copyright 2002 Pearu Peterson all rights reserved,
|
||
|
Pearu Peterson <pearu@cens.ioc.ee>
|
||
|
Permission to use, modify, and distribute this software is given under the
|
||
|
terms of the NumPy (BSD style) license. See LICENSE.txt that came with
|
||
|
this distribution for specifics.
|
||
|
|
||
|
NO WARRANTY IS EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
|
||
|
|
||
|
"""
|
||
|
import sys
|
||
|
import os
|
||
|
import re
|
||
|
import copy
|
||
|
import warnings
|
||
|
import subprocess
|
||
|
import textwrap
|
||
|
|
||
|
from glob import glob
|
||
|
from functools import reduce
|
||
|
from configparser import NoOptionError
|
||
|
from configparser import RawConfigParser as ConfigParser
|
||
|
# It seems that some people are importing ConfigParser from here so is
|
||
|
# good to keep its class name. Use of RawConfigParser is needed in
|
||
|
# order to be able to load path names with percent in them, like
|
||
|
# `feature%2Fcool` which is common on git flow branch names.
|
||
|
|
||
|
from distutils.errors import DistutilsError
|
||
|
from distutils.dist import Distribution
|
||
|
import distutils.sysconfig
|
||
|
from numpy.distutils import log
|
||
|
from distutils.util import get_platform
|
||
|
|
||
|
from numpy.distutils.exec_command import (
|
||
|
find_executable, filepath_from_subprocess_output,
|
||
|
)
|
||
|
from numpy.distutils.misc_util import (is_sequence, is_string,
|
||
|
get_shared_lib_extension)
|
||
|
from numpy.distutils.command.config import config as cmd_config
|
||
|
from numpy.distutils import customized_ccompiler as _customized_ccompiler
|
||
|
from numpy.distutils import _shell_utils
|
||
|
import distutils.ccompiler
|
||
|
import tempfile
|
||
|
import shutil
|
||
|
|
||
|
|
||
|
# Determine number of bits
|
||
|
import platform
|
||
|
_bits = {'32bit': 32, '64bit': 64}
|
||
|
platform_bits = _bits[platform.architecture()[0]]
|
||
|
|
||
|
|
||
|
global_compiler = None
|
||
|
|
||
|
def customized_ccompiler():
|
||
|
global global_compiler
|
||
|
if not global_compiler:
|
||
|
global_compiler = _customized_ccompiler()
|
||
|
return global_compiler
|
||
|
|
||
|
|
||
|
def _c_string_literal(s):
|
||
|
"""
|
||
|
Convert a python string into a literal suitable for inclusion into C code
|
||
|
"""
|
||
|
# only these three characters are forbidden in C strings
|
||
|
s = s.replace('\\', r'\\')
|
||
|
s = s.replace('"', r'\"')
|
||
|
s = s.replace('\n', r'\n')
|
||
|
return '"{}"'.format(s)
|
||
|
|
||
|
|
||
|
def libpaths(paths, bits):
|
||
|
"""Return a list of library paths valid on 32 or 64 bit systems.
|
||
|
|
||
|
Inputs:
|
||
|
paths : sequence
|
||
|
A sequence of strings (typically paths)
|
||
|
bits : int
|
||
|
An integer, the only valid values are 32 or 64. A ValueError exception
|
||
|
is raised otherwise.
|
||
|
|
||
|
Examples:
|
||
|
|
||
|
Consider a list of directories
|
||
|
>>> paths = ['/usr/X11R6/lib','/usr/X11/lib','/usr/lib']
|
||
|
|
||
|
For a 32-bit platform, this is already valid:
|
||
|
>>> np.distutils.system_info.libpaths(paths,32)
|
||
|
['/usr/X11R6/lib', '/usr/X11/lib', '/usr/lib']
|
||
|
|
||
|
On 64 bits, we prepend the '64' postfix
|
||
|
>>> np.distutils.system_info.libpaths(paths,64)
|
||
|
['/usr/X11R6/lib64', '/usr/X11R6/lib', '/usr/X11/lib64', '/usr/X11/lib',
|
||
|
'/usr/lib64', '/usr/lib']
|
||
|
"""
|
||
|
if bits not in (32, 64):
|
||
|
raise ValueError("Invalid bit size in libpaths: 32 or 64 only")
|
||
|
|
||
|
# Handle 32bit case
|
||
|
if bits == 32:
|
||
|
return paths
|
||
|
|
||
|
# Handle 64bit case
|
||
|
out = []
|
||
|
for p in paths:
|
||
|
out.extend([p + '64', p])
|
||
|
|
||
|
return out
|
||
|
|
||
|
|
||
|
if sys.platform == 'win32':
|
||
|
default_lib_dirs = ['C:\\',
|
||
|
os.path.join(distutils.sysconfig.EXEC_PREFIX,
|
||
|
'libs')]
|
||
|
default_runtime_dirs = []
|
||
|
default_include_dirs = []
|
||
|
default_src_dirs = ['.']
|
||
|
default_x11_lib_dirs = []
|
||
|
default_x11_include_dirs = []
|
||
|
_include_dirs = [
|
||
|
'include',
|
||
|
'include/suitesparse',
|
||
|
]
|
||
|
_lib_dirs = [
|
||
|
'lib',
|
||
|
]
|
||
|
|
||
|
_include_dirs = [d.replace('/', os.sep) for d in _include_dirs]
|
||
|
_lib_dirs = [d.replace('/', os.sep) for d in _lib_dirs]
|
||
|
def add_system_root(library_root):
|
||
|
"""Add a package manager root to the include directories"""
|
||
|
global default_lib_dirs
|
||
|
global default_include_dirs
|
||
|
|
||
|
library_root = os.path.normpath(library_root)
|
||
|
|
||
|
default_lib_dirs.extend(
|
||
|
os.path.join(library_root, d) for d in _lib_dirs)
|
||
|
default_include_dirs.extend(
|
||
|
os.path.join(library_root, d) for d in _include_dirs)
|
||
|
|
||
|
# VCpkg is the de-facto package manager on windows for C/C++
|
||
|
# libraries. If it is on the PATH, then we append its paths here.
|
||
|
vcpkg = shutil.which('vcpkg')
|
||
|
if vcpkg:
|
||
|
vcpkg_dir = os.path.dirname(vcpkg)
|
||
|
if platform.architecture() == '32bit':
|
||
|
specifier = 'x86'
|
||
|
else:
|
||
|
specifier = 'x64'
|
||
|
|
||
|
vcpkg_installed = os.path.join(vcpkg_dir, 'installed')
|
||
|
for vcpkg_root in [
|
||
|
os.path.join(vcpkg_installed, specifier + '-windows'),
|
||
|
os.path.join(vcpkg_installed, specifier + '-windows-static'),
|
||
|
]:
|
||
|
add_system_root(vcpkg_root)
|
||
|
|
||
|
# Conda is another popular package manager that provides libraries
|
||
|
conda = shutil.which('conda')
|
||
|
if conda:
|
||
|
conda_dir = os.path.dirname(conda)
|
||
|
add_system_root(os.path.join(conda_dir, '..', 'Library'))
|
||
|
add_system_root(os.path.join(conda_dir, 'Library'))
|
||
|
|
||
|
else:
|
||
|
default_lib_dirs = libpaths(['/usr/local/lib', '/opt/lib', '/usr/lib',
|
||
|
'/opt/local/lib', '/sw/lib'], platform_bits)
|
||
|
default_runtime_dirs = []
|
||
|
default_include_dirs = ['/usr/local/include',
|
||
|
'/opt/include', '/usr/include',
|
||
|
# path of umfpack under macports
|
||
|
'/opt/local/include/ufsparse',
|
||
|
'/opt/local/include', '/sw/include',
|
||
|
'/usr/include/suitesparse']
|
||
|
default_src_dirs = ['.', '/usr/local/src', '/opt/src', '/sw/src']
|
||
|
|
||
|
default_x11_lib_dirs = libpaths(['/usr/X11R6/lib', '/usr/X11/lib',
|
||
|
'/usr/lib'], platform_bits)
|
||
|
default_x11_include_dirs = ['/usr/X11R6/include', '/usr/X11/include',
|
||
|
'/usr/include']
|
||
|
|
||
|
if os.path.exists('/usr/lib/X11'):
|
||
|
globbed_x11_dir = glob('/usr/lib/*/libX11.so')
|
||
|
if globbed_x11_dir:
|
||
|
x11_so_dir = os.path.split(globbed_x11_dir[0])[0]
|
||
|
default_x11_lib_dirs.extend([x11_so_dir, '/usr/lib/X11'])
|
||
|
default_x11_include_dirs.extend(['/usr/lib/X11/include',
|
||
|
'/usr/include/X11'])
|
||
|
|
||
|
with open(os.devnull, 'w') as tmp:
|
||
|
try:
|
||
|
p = subprocess.Popen(["gcc", "-print-multiarch"], stdout=subprocess.PIPE,
|
||
|
stderr=tmp)
|
||
|
except (OSError, DistutilsError):
|
||
|
# OSError if gcc is not installed, or SandboxViolation (DistutilsError
|
||
|
# subclass) if an old setuptools bug is triggered (see gh-3160).
|
||
|
pass
|
||
|
else:
|
||
|
triplet = str(p.communicate()[0].decode().strip())
|
||
|
if p.returncode == 0:
|
||
|
# gcc supports the "-print-multiarch" option
|
||
|
default_x11_lib_dirs += [os.path.join("/usr/lib/", triplet)]
|
||
|
default_lib_dirs += [os.path.join("/usr/lib/", triplet)]
|
||
|
|
||
|
|
||
|
if os.path.join(sys.prefix, 'lib') not in default_lib_dirs:
|
||
|
default_lib_dirs.insert(0, os.path.join(sys.prefix, 'lib'))
|
||
|
default_include_dirs.append(os.path.join(sys.prefix, 'include'))
|
||
|
default_src_dirs.append(os.path.join(sys.prefix, 'src'))
|
||
|
|
||
|
default_lib_dirs = [_m for _m in default_lib_dirs if os.path.isdir(_m)]
|
||
|
default_runtime_dirs = [_m for _m in default_runtime_dirs if os.path.isdir(_m)]
|
||
|
default_include_dirs = [_m for _m in default_include_dirs if os.path.isdir(_m)]
|
||
|
default_src_dirs = [_m for _m in default_src_dirs if os.path.isdir(_m)]
|
||
|
|
||
|
so_ext = get_shared_lib_extension()
|
||
|
|
||
|
|
||
|
def get_standard_file(fname):
|
||
|
"""Returns a list of files named 'fname' from
|
||
|
1) System-wide directory (directory-location of this module)
|
||
|
2) Users HOME directory (os.environ['HOME'])
|
||
|
3) Local directory
|
||
|
"""
|
||
|
# System-wide file
|
||
|
filenames = []
|
||
|
try:
|
||
|
f = __file__
|
||
|
except NameError:
|
||
|
f = sys.argv[0]
|
||
|
else:
|
||
|
sysfile = os.path.join(os.path.split(os.path.abspath(f))[0],
|
||
|
fname)
|
||
|
if os.path.isfile(sysfile):
|
||
|
filenames.append(sysfile)
|
||
|
|
||
|
# Home directory
|
||
|
# And look for the user config file
|
||
|
try:
|
||
|
f = os.path.expanduser('~')
|
||
|
except KeyError:
|
||
|
pass
|
||
|
else:
|
||
|
user_file = os.path.join(f, fname)
|
||
|
if os.path.isfile(user_file):
|
||
|
filenames.append(user_file)
|
||
|
|
||
|
# Local file
|
||
|
if os.path.isfile(fname):
|
||
|
filenames.append(os.path.abspath(fname))
|
||
|
|
||
|
return filenames
|
||
|
|
||
|
|
||
|
def get_info(name, notfound_action=0):
|
||
|
"""
|
||
|
notfound_action:
|
||
|
0 - do nothing
|
||
|
1 - display warning message
|
||
|
2 - raise error
|
||
|
"""
|
||
|
cl = {'atlas': atlas_info, # use lapack_opt or blas_opt instead
|
||
|
'atlas_threads': atlas_threads_info, # ditto
|
||
|
'atlas_blas': atlas_blas_info,
|
||
|
'atlas_blas_threads': atlas_blas_threads_info,
|
||
|
'lapack_atlas': lapack_atlas_info, # use lapack_opt instead
|
||
|
'lapack_atlas_threads': lapack_atlas_threads_info, # ditto
|
||
|
'atlas_3_10': atlas_3_10_info, # use lapack_opt or blas_opt instead
|
||
|
'atlas_3_10_threads': atlas_3_10_threads_info, # ditto
|
||
|
'atlas_3_10_blas': atlas_3_10_blas_info,
|
||
|
'atlas_3_10_blas_threads': atlas_3_10_blas_threads_info,
|
||
|
'lapack_atlas_3_10': lapack_atlas_3_10_info, # use lapack_opt instead
|
||
|
'lapack_atlas_3_10_threads': lapack_atlas_3_10_threads_info, # ditto
|
||
|
'flame': flame_info, # use lapack_opt instead
|
||
|
'mkl': mkl_info,
|
||
|
# openblas which may or may not have embedded lapack
|
||
|
'openblas': openblas_info, # use blas_opt instead
|
||
|
# openblas with embedded lapack
|
||
|
'openblas_lapack': openblas_lapack_info, # use blas_opt instead
|
||
|
'openblas_clapack': openblas_clapack_info, # use blas_opt instead
|
||
|
'blis': blis_info, # use blas_opt instead
|
||
|
'lapack_mkl': lapack_mkl_info, # use lapack_opt instead
|
||
|
'blas_mkl': blas_mkl_info, # use blas_opt instead
|
||
|
'accelerate': accelerate_info, # use blas_opt instead
|
||
|
'openblas64_': openblas64__info,
|
||
|
'openblas64__lapack': openblas64__lapack_info,
|
||
|
'openblas_ilp64': openblas_ilp64_info,
|
||
|
'openblas_ilp64_lapack': openblas_ilp64_lapack_info,
|
||
|
'x11': x11_info,
|
||
|
'fft_opt': fft_opt_info,
|
||
|
'fftw': fftw_info,
|
||
|
'fftw2': fftw2_info,
|
||
|
'fftw3': fftw3_info,
|
||
|
'dfftw': dfftw_info,
|
||
|
'sfftw': sfftw_info,
|
||
|
'fftw_threads': fftw_threads_info,
|
||
|
'dfftw_threads': dfftw_threads_info,
|
||
|
'sfftw_threads': sfftw_threads_info,
|
||
|
'djbfft': djbfft_info,
|
||
|
'blas': blas_info, # use blas_opt instead
|
||
|
'lapack': lapack_info, # use lapack_opt instead
|
||
|
'lapack_src': lapack_src_info,
|
||
|
'blas_src': blas_src_info,
|
||
|
'numpy': numpy_info,
|
||
|
'f2py': f2py_info,
|
||
|
'Numeric': Numeric_info,
|
||
|
'numeric': Numeric_info,
|
||
|
'numarray': numarray_info,
|
||
|
'numerix': numerix_info,
|
||
|
'lapack_opt': lapack_opt_info,
|
||
|
'lapack_ilp64_opt': lapack_ilp64_opt_info,
|
||
|
'lapack_ilp64_plain_opt': lapack_ilp64_plain_opt_info,
|
||
|
'lapack64__opt': lapack64__opt_info,
|
||
|
'blas_opt': blas_opt_info,
|
||
|
'blas_ilp64_opt': blas_ilp64_opt_info,
|
||
|
'blas_ilp64_plain_opt': blas_ilp64_plain_opt_info,
|
||
|
'blas64__opt': blas64__opt_info,
|
||
|
'boost_python': boost_python_info,
|
||
|
'agg2': agg2_info,
|
||
|
'wx': wx_info,
|
||
|
'gdk_pixbuf_xlib_2': gdk_pixbuf_xlib_2_info,
|
||
|
'gdk-pixbuf-xlib-2.0': gdk_pixbuf_xlib_2_info,
|
||
|
'gdk_pixbuf_2': gdk_pixbuf_2_info,
|
||
|
'gdk-pixbuf-2.0': gdk_pixbuf_2_info,
|
||
|
'gdk': gdk_info,
|
||
|
'gdk_2': gdk_2_info,
|
||
|
'gdk-2.0': gdk_2_info,
|
||
|
'gdk_x11_2': gdk_x11_2_info,
|
||
|
'gdk-x11-2.0': gdk_x11_2_info,
|
||
|
'gtkp_x11_2': gtkp_x11_2_info,
|
||
|
'gtk+-x11-2.0': gtkp_x11_2_info,
|
||
|
'gtkp_2': gtkp_2_info,
|
||
|
'gtk+-2.0': gtkp_2_info,
|
||
|
'xft': xft_info,
|
||
|
'freetype2': freetype2_info,
|
||
|
'umfpack': umfpack_info,
|
||
|
'amd': amd_info,
|
||
|
}.get(name.lower(), system_info)
|
||
|
return cl().get_info(notfound_action)
|
||
|
|
||
|
|
||
|
class NotFoundError(DistutilsError):
|
||
|
"""Some third-party program or library is not found."""
|
||
|
|
||
|
|
||
|
class AliasedOptionError(DistutilsError):
|
||
|
"""
|
||
|
Aliases entries in config files should not be existing.
|
||
|
In section '{section}' we found multiple appearances of options {options}."""
|
||
|
|
||
|
|
||
|
class AtlasNotFoundError(NotFoundError):
|
||
|
"""
|
||
|
Atlas (http://github.com/math-atlas/math-atlas) libraries not found.
|
||
|
Directories to search for the libraries can be specified in the
|
||
|
numpy/distutils/site.cfg file (section [atlas]) or by setting
|
||
|
the ATLAS environment variable."""
|
||
|
|
||
|
|
||
|
class FlameNotFoundError(NotFoundError):
|
||
|
"""
|
||
|
FLAME (http://www.cs.utexas.edu/~flame/web/) libraries not found.
|
||
|
Directories to search for the libraries can be specified in the
|
||
|
numpy/distutils/site.cfg file (section [flame])."""
|
||
|
|
||
|
|
||
|
class LapackNotFoundError(NotFoundError):
|
||
|
"""
|
||
|
Lapack (http://www.netlib.org/lapack/) libraries not found.
|
||
|
Directories to search for the libraries can be specified in the
|
||
|
numpy/distutils/site.cfg file (section [lapack]) or by setting
|
||
|
the LAPACK environment variable."""
|
||
|
|
||
|
|
||
|
class LapackSrcNotFoundError(LapackNotFoundError):
|
||
|
"""
|
||
|
Lapack (http://www.netlib.org/lapack/) sources not found.
|
||
|
Directories to search for the sources can be specified in the
|
||
|
numpy/distutils/site.cfg file (section [lapack_src]) or by setting
|
||
|
the LAPACK_SRC environment variable."""
|
||
|
|
||
|
|
||
|
class LapackILP64NotFoundError(NotFoundError):
|
||
|
"""
|
||
|
64-bit Lapack libraries not found.
|
||
|
Known libraries in numpy/distutils/site.cfg file are:
|
||
|
openblas64_, openblas_ilp64
|
||
|
"""
|
||
|
|
||
|
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.
|
||
|
Directories to search for the libraries can be specified in the
|
||
|
numpy/distutils/site.cfg file (section [blas]) or by setting
|
||
|
the BLAS environment variable."""
|
||
|
|
||
|
class BlasILP64NotFoundError(NotFoundError):
|
||
|
"""
|
||
|
64-bit Blas libraries not found.
|
||
|
Known libraries in numpy/distutils/site.cfg file are:
|
||
|
openblas64_, openblas_ilp64
|
||
|
"""
|
||
|
|
||
|
class BlasSrcNotFoundError(BlasNotFoundError):
|
||
|
"""
|
||
|
Blas (http://www.netlib.org/blas/) sources not found.
|
||
|
Directories to search for the sources can be specified in the
|
||
|
numpy/distutils/site.cfg file (section [blas_src]) or by setting
|
||
|
the BLAS_SRC environment variable."""
|
||
|
|
||
|
|
||
|
class FFTWNotFoundError(NotFoundError):
|
||
|
"""
|
||
|
FFTW (http://www.fftw.org/) libraries not found.
|
||
|
Directories to search for the libraries can be specified in the
|
||
|
numpy/distutils/site.cfg file (section [fftw]) or by setting
|
||
|
the FFTW environment variable."""
|
||
|
|
||
|
|
||
|
class DJBFFTNotFoundError(NotFoundError):
|
||
|
"""
|
||
|
DJBFFT (https://cr.yp.to/djbfft.html) libraries not found.
|
||
|
Directories to search for the libraries can be specified in the
|
||
|
numpy/distutils/site.cfg file (section [djbfft]) or by setting
|
||
|
the DJBFFT environment variable."""
|
||
|
|
||
|
|
||
|
class NumericNotFoundError(NotFoundError):
|
||
|
"""
|
||
|
Numeric (https://www.numpy.org/) module not found.
|
||
|
Get it from above location, install it, and retry setup.py."""
|
||
|
|
||
|
|
||
|
class X11NotFoundError(NotFoundError):
|
||
|
"""X11 libraries not found."""
|
||
|
|
||
|
|
||
|
class UmfpackNotFoundError(NotFoundError):
|
||
|
"""
|
||
|
UMFPACK sparse solver (https://www.cise.ufl.edu/research/sparse/umfpack/)
|
||
|
not found. Directories to search for the libraries can be specified in the
|
||
|
numpy/distutils/site.cfg file (section [umfpack]) or by setting
|
||
|
the UMFPACK environment variable."""
|
||
|
|
||
|
|
||
|
class system_info:
|
||
|
|
||
|
""" get_info() is the only public method. Don't use others.
|
||
|
"""
|
||
|
dir_env_var = None
|
||
|
# XXX: search_static_first is disabled by default, may disappear in
|
||
|
# future unless it is proved to be useful.
|
||
|
search_static_first = 0
|
||
|
# The base-class section name is a random word "ALL" and is not really
|
||
|
# intended for general use. It cannot be None nor can it be DEFAULT as
|
||
|
# these break the ConfigParser. See gh-15338
|
||
|
section = 'ALL'
|
||
|
saved_results = {}
|
||
|
|
||
|
notfounderror = NotFoundError
|
||
|
|
||
|
def __init__(self,
|
||
|
default_lib_dirs=default_lib_dirs,
|
||
|
default_include_dirs=default_include_dirs,
|
||
|
):
|
||
|
self.__class__.info = {}
|
||
|
self.local_prefixes = []
|
||
|
defaults = {'library_dirs': os.pathsep.join(default_lib_dirs),
|
||
|
'include_dirs': os.pathsep.join(default_include_dirs),
|
||
|
'runtime_library_dirs': os.pathsep.join(default_runtime_dirs),
|
||
|
'rpath': '',
|
||
|
'src_dirs': os.pathsep.join(default_src_dirs),
|
||
|
'search_static_first': str(self.search_static_first),
|
||
|
'extra_compile_args': '', 'extra_link_args': ''}
|
||
|
self.cp = ConfigParser(defaults)
|
||
|
self.files = []
|
||
|
self.files.extend(get_standard_file('.numpy-site.cfg'))
|
||
|
self.files.extend(get_standard_file('site.cfg'))
|
||
|
self.parse_config_files()
|
||
|
|
||
|
if self.section is not None:
|
||
|
self.search_static_first = self.cp.getboolean(
|
||
|
self.section, 'search_static_first')
|
||
|
assert isinstance(self.search_static_first, int)
|
||
|
|
||
|
def parse_config_files(self):
|
||
|
self.cp.read(self.files)
|
||
|
if not self.cp.has_section(self.section):
|
||
|
if self.section is not None:
|
||
|
self.cp.add_section(self.section)
|
||
|
|
||
|
def calc_libraries_info(self):
|
||
|
libs = self.get_libraries()
|
||
|
dirs = self.get_lib_dirs()
|
||
|
# The extensions use runtime_library_dirs
|
||
|
r_dirs = self.get_runtime_lib_dirs()
|
||
|
# Intrinsic distutils use rpath, we simply append both entries
|
||
|
# as though they were one entry
|
||
|
r_dirs.extend(self.get_runtime_lib_dirs(key='rpath'))
|
||
|
info = {}
|
||
|
for lib in libs:
|
||
|
i = self.check_libs(dirs, [lib])
|
||
|
if i is not None:
|
||
|
dict_append(info, **i)
|
||
|
else:
|
||
|
log.info('Library %s was not found. Ignoring' % (lib))
|
||
|
|
||
|
if r_dirs:
|
||
|
i = self.check_libs(r_dirs, [lib])
|
||
|
if i is not None:
|
||
|
# Swap library keywords found to runtime_library_dirs
|
||
|
# the libraries are insisting on the user having defined
|
||
|
# them using the library_dirs, and not necessarily by
|
||
|
# runtime_library_dirs
|
||
|
del i['libraries']
|
||
|
i['runtime_library_dirs'] = i.pop('library_dirs')
|
||
|
dict_append(info, **i)
|
||
|
else:
|
||
|
log.info('Runtime library %s was not found. Ignoring' % (lib))
|
||
|
|
||
|
return info
|
||
|
|
||
|
def set_info(self, **info):
|
||
|
if info:
|
||
|
lib_info = self.calc_libraries_info()
|
||
|
dict_append(info, **lib_info)
|
||
|
# Update extra information
|
||
|
extra_info = self.calc_extra_info()
|
||
|
dict_append(info, **extra_info)
|
||
|
self.saved_results[self.__class__.__name__] = info
|
||
|
|
||
|
def get_option_single(self, *options):
|
||
|
""" Ensure that only one of `options` are found in the section
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
*options : list of str
|
||
|
a list of options to be found in the section (``self.section``)
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
str :
|
||
|
the option that is uniquely found in the section
|
||
|
|
||
|
Raises
|
||
|
------
|
||
|
AliasedOptionError :
|
||
|
in case more than one of the options are found
|
||
|
"""
|
||
|
found = map(lambda opt: self.cp.has_option(self.section, opt), options)
|
||
|
found = list(found)
|
||
|
if sum(found) == 1:
|
||
|
return options[found.index(True)]
|
||
|
elif sum(found) == 0:
|
||
|
# nothing is found anyways
|
||
|
return options[0]
|
||
|
|
||
|
# Else we have more than 1 key found
|
||
|
if AliasedOptionError.__doc__ is None:
|
||
|
raise AliasedOptionError()
|
||
|
raise AliasedOptionError(AliasedOptionError.__doc__.format(
|
||
|
section=self.section, options='[{}]'.format(', '.join(options))))
|
||
|
|
||
|
|
||
|
def has_info(self):
|
||
|
return self.__class__.__name__ in self.saved_results
|
||
|
|
||
|
def calc_extra_info(self):
|
||
|
""" Updates the information in the current information with
|
||
|
respect to these flags:
|
||
|
extra_compile_args
|
||
|
extra_link_args
|
||
|
"""
|
||
|
info = {}
|
||
|
for key in ['extra_compile_args', 'extra_link_args']:
|
||
|
# Get values
|
||
|
opt = self.cp.get(self.section, key)
|
||
|
opt = _shell_utils.NativeParser.split(opt)
|
||
|
if opt:
|
||
|
tmp = {key: opt}
|
||
|
dict_append(info, **tmp)
|
||
|
return info
|
||
|
|
||
|
def get_info(self, notfound_action=0):
|
||
|
""" Return a dictionary with items that are compatible
|
||
|
with numpy.distutils.setup keyword arguments.
|
||
|
"""
|
||
|
flag = 0
|
||
|
if not self.has_info():
|
||
|
flag = 1
|
||
|
log.info(self.__class__.__name__ + ':')
|
||
|
if hasattr(self, 'calc_info'):
|
||
|
self.calc_info()
|
||
|
if notfound_action:
|
||
|
if not self.has_info():
|
||
|
if notfound_action == 1:
|
||
|
warnings.warn(self.notfounderror.__doc__, stacklevel=2)
|
||
|
elif notfound_action == 2:
|
||
|
raise self.notfounderror(self.notfounderror.__doc__)
|
||
|
else:
|
||
|
raise ValueError(repr(notfound_action))
|
||
|
|
||
|
if not self.has_info():
|
||
|
log.info(' NOT AVAILABLE')
|
||
|
self.set_info()
|
||
|
else:
|
||
|
log.info(' FOUND:')
|
||
|
|
||
|
res = self.saved_results.get(self.__class__.__name__)
|
||
|
if log.get_threshold() <= log.INFO and flag:
|
||
|
for k, v in res.items():
|
||
|
v = str(v)
|
||
|
if k in ['sources', 'libraries'] and len(v) > 270:
|
||
|
v = v[:120] + '...\n...\n...' + v[-120:]
|
||
|
log.info(' %s = %s', k, v)
|
||
|
log.info('')
|
||
|
|
||
|
return copy.deepcopy(res)
|
||
|
|
||
|
def get_paths(self, section, key):
|
||
|
dirs = self.cp.get(section, key).split(os.pathsep)
|
||
|
env_var = self.dir_env_var
|
||
|
if env_var:
|
||
|
if is_sequence(env_var):
|
||
|
e0 = env_var[-1]
|
||
|
for e in env_var:
|
||
|
if e in os.environ:
|
||
|
e0 = e
|
||
|
break
|
||
|
if not env_var[0] == e0:
|
||
|
log.info('Setting %s=%s' % (env_var[0], e0))
|
||
|
env_var = e0
|
||
|
if env_var and env_var in os.environ:
|
||
|
d = os.environ[env_var]
|
||
|
if d == 'None':
|
||
|
log.info('Disabled %s: %s',
|
||
|
self.__class__.__name__, '(%s is None)'
|
||
|
% (env_var,))
|
||
|
return []
|
||
|
if os.path.isfile(d):
|
||
|
dirs = [os.path.dirname(d)] + dirs
|
||
|
l = getattr(self, '_lib_names', [])
|
||
|
if len(l) == 1:
|
||
|
b = os.path.basename(d)
|
||
|
b = os.path.splitext(b)[0]
|
||
|
if b[:3] == 'lib':
|
||
|
log.info('Replacing _lib_names[0]==%r with %r' \
|
||
|
% (self._lib_names[0], b[3:]))
|
||
|
self._lib_names[0] = b[3:]
|
||
|
else:
|
||
|
ds = d.split(os.pathsep)
|
||
|
ds2 = []
|
||
|
for d in ds:
|
||
|
if os.path.isdir(d):
|
||
|
ds2.append(d)
|
||
|
for dd in ['include', 'lib']:
|
||
|
d1 = os.path.join(d, dd)
|
||
|
if os.path.isdir(d1):
|
||
|
ds2.append(d1)
|
||
|
dirs = ds2 + dirs
|
||
|
default_dirs = self.cp.get(self.section, key).split(os.pathsep)
|
||
|
dirs.extend(default_dirs)
|
||
|
ret = []
|
||
|
for d in dirs:
|
||
|
if len(d) > 0 and not os.path.isdir(d):
|
||
|
warnings.warn('Specified path %s is invalid.' % d, stacklevel=2)
|
||
|
continue
|
||
|
|
||
|
if d not in ret:
|
||
|
ret.append(d)
|
||
|
|
||
|
log.debug('( %s = %s )', key, ':'.join(ret))
|
||
|
return ret
|
||
|
|
||
|
def get_lib_dirs(self, key='library_dirs'):
|
||
|
return self.get_paths(self.section, key)
|
||
|
|
||
|
def get_runtime_lib_dirs(self, key='runtime_library_dirs'):
|
||
|
path = self.get_paths(self.section, key)
|
||
|
if path == ['']:
|
||
|
path = []
|
||
|
return path
|
||
|
|
||
|
def get_include_dirs(self, key='include_dirs'):
|
||
|
return self.get_paths(self.section, key)
|
||
|
|
||
|
def get_src_dirs(self, key='src_dirs'):
|
||
|
return self.get_paths(self.section, key)
|
||
|
|
||
|
def get_libs(self, key, default):
|
||
|
try:
|
||
|
libs = self.cp.get(self.section, key)
|
||
|
except NoOptionError:
|
||
|
if not default:
|
||
|
return []
|
||
|
if is_string(default):
|
||
|
return [default]
|
||
|
return default
|
||
|
return [b for b in [a.strip() for a in libs.split(',')] if b]
|
||
|
|
||
|
def get_libraries(self, key='libraries'):
|
||
|
if hasattr(self, '_lib_names'):
|
||
|
return self.get_libs(key, default=self._lib_names)
|
||
|
else:
|
||
|
return self.get_libs(key, '')
|
||
|
|
||
|
def library_extensions(self):
|
||
|
c = customized_ccompiler()
|
||
|
static_exts = []
|
||
|
if c.compiler_type != 'msvc':
|
||
|
# MSVC doesn't understand binutils
|
||
|
static_exts.append('.a')
|
||
|
if sys.platform == 'win32':
|
||
|
static_exts.append('.lib') # .lib is used by MSVC and others
|
||
|
if self.search_static_first:
|
||
|
exts = static_exts + [so_ext]
|
||
|
else:
|
||
|
exts = [so_ext] + static_exts
|
||
|
if sys.platform == 'cygwin':
|
||
|
exts.append('.dll.a')
|
||
|
if sys.platform == 'darwin':
|
||
|
exts.append('.dylib')
|
||
|
return exts
|
||
|
|
||
|
def check_libs(self, lib_dirs, libs, opt_libs=[]):
|
||
|
"""If static or shared libraries are available then return
|
||
|
their info dictionary.
|
||
|
|
||
|
Checks for all libraries as shared libraries first, then
|
||
|
static (or vice versa if self.search_static_first is True).
|
||
|
"""
|
||
|
exts = self.library_extensions()
|
||
|
info = None
|
||
|
for ext in exts:
|
||
|
info = self._check_libs(lib_dirs, libs, opt_libs, [ext])
|
||
|
if info is not None:
|
||
|
break
|
||
|
if not info:
|
||
|
log.info(' libraries %s not found in %s', ','.join(libs),
|
||
|
lib_dirs)
|
||
|
return info
|
||
|
|
||
|
def check_libs2(self, lib_dirs, libs, opt_libs=[]):
|
||
|
"""If static or shared libraries are available then return
|
||
|
their info dictionary.
|
||
|
|
||
|
Checks each library for shared or static.
|
||
|
"""
|
||
|
exts = self.library_extensions()
|
||
|
info = self._check_libs(lib_dirs, libs, opt_libs, exts)
|
||
|
if not info:
|
||
|
log.info(' libraries %s not found in %s', ','.join(libs),
|
||
|
lib_dirs)
|
||
|
|
||
|
return info
|
||
|
|
||
|
def _find_lib(self, lib_dir, lib, exts):
|
||
|
assert is_string(lib_dir)
|
||
|
# under windows first try without 'lib' prefix
|
||
|
if sys.platform == 'win32':
|
||
|
lib_prefixes = ['', 'lib']
|
||
|
else:
|
||
|
lib_prefixes = ['lib']
|
||
|
# for each library name, see if we can find a file for it.
|
||
|
for ext in exts:
|
||
|
for prefix in lib_prefixes:
|
||
|
p = self.combine_paths(lib_dir, prefix + lib + ext)
|
||
|
if p:
|
||
|
break
|
||
|
if p:
|
||
|
assert len(p) == 1
|
||
|
# ??? splitext on p[0] would do this for cygwin
|
||
|
# doesn't seem correct
|
||
|
if ext == '.dll.a':
|
||
|
lib += '.dll'
|
||
|
if ext == '.lib':
|
||
|
lib = prefix + lib
|
||
|
return lib
|
||
|
|
||
|
return False
|
||
|
|
||
|
def _find_libs(self, lib_dirs, libs, exts):
|
||
|
# make sure we preserve the order of libs, as it can be important
|
||
|
found_dirs, found_libs = [], []
|
||
|
for lib in libs:
|
||
|
for lib_dir in lib_dirs:
|
||
|
found_lib = self._find_lib(lib_dir, lib, exts)
|
||
|
if found_lib:
|
||
|
found_libs.append(found_lib)
|
||
|
if lib_dir not in found_dirs:
|
||
|
found_dirs.append(lib_dir)
|
||
|
break
|
||
|
return found_dirs, found_libs
|
||
|
|
||
|
def _check_libs(self, lib_dirs, libs, opt_libs, exts):
|
||
|
"""Find mandatory and optional libs in expected paths.
|
||
|
|
||
|
Missing optional libraries are silently forgotten.
|
||
|
"""
|
||
|
if not is_sequence(lib_dirs):
|
||
|
lib_dirs = [lib_dirs]
|
||
|
# First, try to find the mandatory libraries
|
||
|
found_dirs, found_libs = self._find_libs(lib_dirs, libs, exts)
|
||
|
if len(found_libs) > 0 and len(found_libs) == len(libs):
|
||
|
# Now, check for optional libraries
|
||
|
opt_found_dirs, opt_found_libs = self._find_libs(lib_dirs, opt_libs, exts)
|
||
|
found_libs.extend(opt_found_libs)
|
||
|
for lib_dir in opt_found_dirs:
|
||
|
if lib_dir not in found_dirs:
|
||
|
found_dirs.append(lib_dir)
|
||
|
info = {'libraries': found_libs, 'library_dirs': found_dirs}
|
||
|
return info
|
||
|
else:
|
||
|
return None
|
||
|
|
||
|
def combine_paths(self, *args):
|
||
|
"""Return a list of existing paths composed by all combinations
|
||
|
of items from the arguments.
|
||
|
"""
|
||
|
return combine_paths(*args)
|
||
|
|
||
|
|
||
|
class fft_opt_info(system_info):
|
||
|
|
||
|
def calc_info(self):
|
||
|
info = {}
|
||
|
fftw_info = get_info('fftw3') or get_info('fftw2') or get_info('dfftw')
|
||
|
djbfft_info = get_info('djbfft')
|
||
|
if fftw_info:
|
||
|
dict_append(info, **fftw_info)
|
||
|
if djbfft_info:
|
||
|
dict_append(info, **djbfft_info)
|
||
|
self.set_info(**info)
|
||
|
return
|
||
|
|
||
|
|
||
|
class fftw_info(system_info):
|
||
|
#variables to override
|
||
|
section = 'fftw'
|
||
|
dir_env_var = 'FFTW'
|
||
|
notfounderror = FFTWNotFoundError
|
||
|
ver_info = [{'name':'fftw3',
|
||
|
'libs':['fftw3'],
|
||
|
'includes':['fftw3.h'],
|
||
|
'macros':[('SCIPY_FFTW3_H', None)]},
|
||
|
{'name':'fftw2',
|
||
|
'libs':['rfftw', 'fftw'],
|
||
|
'includes':['fftw.h', 'rfftw.h'],
|
||
|
'macros':[('SCIPY_FFTW_H', None)]}]
|
||
|
|
||
|
def calc_ver_info(self, ver_param):
|
||
|
"""Returns True on successful version detection, else False"""
|
||
|
lib_dirs = self.get_lib_dirs()
|
||
|
incl_dirs = self.get_include_dirs()
|
||
|
|
||
|
opt = self.get_option_single(self.section + '_libs', 'libraries')
|
||
|
libs = self.get_libs(opt, ver_param['libs'])
|
||
|
info = self.check_libs(lib_dirs, libs)
|
||
|
if info is not None:
|
||
|
flag = 0
|
||
|
for d in incl_dirs:
|
||
|
if len(self.combine_paths(d, ver_param['includes'])) \
|
||
|
== len(ver_param['includes']):
|
||
|
dict_append(info, include_dirs=[d])
|
||
|
flag = 1
|
||
|
break
|
||
|
if flag:
|
||
|
dict_append(info, define_macros=ver_param['macros'])
|
||
|
else:
|
||
|
info = None
|
||
|
if info is not None:
|
||
|
self.set_info(**info)
|
||
|
return True
|
||
|
else:
|
||
|
log.info(' %s not found' % (ver_param['name']))
|
||
|
return False
|
||
|
|
||
|
def calc_info(self):
|
||
|
for i in self.ver_info:
|
||
|
if self.calc_ver_info(i):
|
||
|
break
|
||
|
|
||
|
|
||
|
class fftw2_info(fftw_info):
|
||
|
#variables to override
|
||
|
section = 'fftw'
|
||
|
dir_env_var = 'FFTW'
|
||
|
notfounderror = FFTWNotFoundError
|
||
|
ver_info = [{'name':'fftw2',
|
||
|
'libs':['rfftw', 'fftw'],
|
||
|
'includes':['fftw.h', 'rfftw.h'],
|
||
|
'macros':[('SCIPY_FFTW_H', None)]}
|
||
|
]
|
||
|
|
||
|
|
||
|
class fftw3_info(fftw_info):
|
||
|
#variables to override
|
||
|
section = 'fftw3'
|
||
|
dir_env_var = 'FFTW3'
|
||
|
notfounderror = FFTWNotFoundError
|
||
|
ver_info = [{'name':'fftw3',
|
||
|
'libs':['fftw3'],
|
||
|
'includes':['fftw3.h'],
|
||
|
'macros':[('SCIPY_FFTW3_H', None)]},
|
||
|
]
|
||
|
|
||
|
|
||
|
class dfftw_info(fftw_info):
|
||
|
section = 'fftw'
|
||
|
dir_env_var = 'FFTW'
|
||
|
ver_info = [{'name':'dfftw',
|
||
|
'libs':['drfftw', 'dfftw'],
|
||
|
'includes':['dfftw.h', 'drfftw.h'],
|
||
|
'macros':[('SCIPY_DFFTW_H', None)]}]
|
||
|
|
||
|
|
||
|
class sfftw_info(fftw_info):
|
||
|
section = 'fftw'
|
||
|
dir_env_var = 'FFTW'
|
||
|
ver_info = [{'name':'sfftw',
|
||
|
'libs':['srfftw', 'sfftw'],
|
||
|
'includes':['sfftw.h', 'srfftw.h'],
|
||
|
'macros':[('SCIPY_SFFTW_H', None)]}]
|
||
|
|
||
|
|
||
|
class fftw_threads_info(fftw_info):
|
||
|
section = 'fftw'
|
||
|
dir_env_var = 'FFTW'
|
||
|
ver_info = [{'name':'fftw threads',
|
||
|
'libs':['rfftw_threads', 'fftw_threads'],
|
||
|
'includes':['fftw_threads.h', 'rfftw_threads.h'],
|
||
|
'macros':[('SCIPY_FFTW_THREADS_H', None)]}]
|
||
|
|
||
|
|
||
|
class dfftw_threads_info(fftw_info):
|
||
|
section = 'fftw'
|
||
|
dir_env_var = 'FFTW'
|
||
|
ver_info = [{'name':'dfftw threads',
|
||
|
'libs':['drfftw_threads', 'dfftw_threads'],
|
||
|
'includes':['dfftw_threads.h', 'drfftw_threads.h'],
|
||
|
'macros':[('SCIPY_DFFTW_THREADS_H', None)]}]
|
||
|
|
||
|
|
||
|
class sfftw_threads_info(fftw_info):
|
||
|
section = 'fftw'
|
||
|
dir_env_var = 'FFTW'
|
||
|
ver_info = [{'name':'sfftw threads',
|
||
|
'libs':['srfftw_threads', 'sfftw_threads'],
|
||
|
'includes':['sfftw_threads.h', 'srfftw_threads.h'],
|
||
|
'macros':[('SCIPY_SFFTW_THREADS_H', None)]}]
|
||
|
|
||
|
|
||
|
class djbfft_info(system_info):
|
||
|
section = 'djbfft'
|
||
|
dir_env_var = 'DJBFFT'
|
||
|
notfounderror = DJBFFTNotFoundError
|
||
|
|
||
|
def get_paths(self, section, key):
|
||
|
pre_dirs = system_info.get_paths(self, section, key)
|
||
|
dirs = []
|
||
|
for d in pre_dirs:
|
||
|
dirs.extend(self.combine_paths(d, ['djbfft']) + [d])
|
||
|
return [d for d in dirs if os.path.isdir(d)]
|
||
|
|
||
|
def calc_info(self):
|
||
|
lib_dirs = self.get_lib_dirs()
|
||
|
incl_dirs = self.get_include_dirs()
|
||
|
info = None
|
||
|
for d in lib_dirs:
|
||
|
p = self.combine_paths(d, ['djbfft.a'])
|
||
|
if p:
|
||
|
info = {'extra_objects': p}
|
||
|
break
|
||
|
p = self.combine_paths(d, ['libdjbfft.a', 'libdjbfft' + so_ext])
|
||
|
if p:
|
||
|
info = {'libraries': ['djbfft'], 'library_dirs': [d]}
|
||
|
break
|
||
|
if info is None:
|
||
|
return
|
||
|
for d in incl_dirs:
|
||
|
if len(self.combine_paths(d, ['fftc8.h', 'fftfreq.h'])) == 2:
|
||
|
dict_append(info, include_dirs=[d],
|
||
|
define_macros=[('SCIPY_DJBFFT_H', None)])
|
||
|
self.set_info(**info)
|
||
|
return
|
||
|
return
|
||
|
|
||
|
|
||
|
class mkl_info(system_info):
|
||
|
section = 'mkl'
|
||
|
dir_env_var = 'MKLROOT'
|
||
|
_lib_mkl = ['mkl_rt']
|
||
|
|
||
|
def get_mkl_rootdir(self):
|
||
|
mklroot = os.environ.get('MKLROOT', None)
|
||
|
if mklroot is not None:
|
||
|
return mklroot
|
||
|
paths = os.environ.get('LD_LIBRARY_PATH', '').split(os.pathsep)
|
||
|
ld_so_conf = '/etc/ld.so.conf'
|
||
|
if os.path.isfile(ld_so_conf):
|
||
|
with open(ld_so_conf, 'r') as f:
|
||
|
for d in f:
|
||
|
d = d.strip()
|
||
|
if d:
|
||
|
paths.append(d)
|
||
|
intel_mkl_dirs = []
|
||
|
for path in paths:
|
||
|
path_atoms = path.split(os.sep)
|
||
|
for m in path_atoms:
|
||
|
if m.startswith('mkl'):
|
||
|
d = os.sep.join(path_atoms[:path_atoms.index(m) + 2])
|
||
|
intel_mkl_dirs.append(d)
|
||
|
break
|
||
|
for d in paths:
|
||
|
dirs = glob(os.path.join(d, 'mkl', '*'))
|
||
|
dirs += glob(os.path.join(d, 'mkl*'))
|
||
|
for sub_dir in dirs:
|
||
|
if os.path.isdir(os.path.join(sub_dir, 'lib')):
|
||
|
return sub_dir
|
||
|
return None
|
||
|
|
||
|
def __init__(self):
|
||
|
mklroot = self.get_mkl_rootdir()
|
||
|
if mklroot is None:
|
||
|
system_info.__init__(self)
|
||
|
else:
|
||
|
from .cpuinfo import cpu
|
||
|
if cpu.is_Itanium():
|
||
|
plt = '64'
|
||
|
elif cpu.is_Intel() and cpu.is_64bit():
|
||
|
plt = 'intel64'
|
||
|
else:
|
||
|
plt = '32'
|
||
|
system_info.__init__(
|
||
|
self,
|
||
|
default_lib_dirs=[os.path.join(mklroot, 'lib', plt)],
|
||
|
default_include_dirs=[os.path.join(mklroot, 'include')])
|
||
|
|
||
|
def calc_info(self):
|
||
|
lib_dirs = self.get_lib_dirs()
|
||
|
incl_dirs = self.get_include_dirs()
|
||
|
opt = self.get_option_single('mkl_libs', 'libraries')
|
||
|
mkl_libs = self.get_libs(opt, self._lib_mkl)
|
||
|
info = self.check_libs2(lib_dirs, mkl_libs)
|
||
|
if info is None:
|
||
|
return
|
||
|
dict_append(info,
|
||
|
define_macros=[('SCIPY_MKL_H', None),
|
||
|
('HAVE_CBLAS', None)],
|
||
|
include_dirs=incl_dirs)
|
||
|
if sys.platform == 'win32':
|
||
|
pass # win32 has no pthread library
|
||
|
else:
|
||
|
dict_append(info, libraries=['pthread'])
|
||
|
self.set_info(**info)
|
||
|
|
||
|
|
||
|
class lapack_mkl_info(mkl_info):
|
||
|
pass
|
||
|
|
||
|
|
||
|
class blas_mkl_info(mkl_info):
|
||
|
pass
|
||
|
|
||
|
|
||
|
class atlas_info(system_info):
|
||
|
section = 'atlas'
|
||
|
dir_env_var = 'ATLAS'
|
||
|
_lib_names = ['f77blas', 'cblas']
|
||
|
if sys.platform[:7] == 'freebsd':
|
||
|
_lib_atlas = ['atlas_r']
|
||
|
_lib_lapack = ['alapack_r']
|
||
|
else:
|
||
|
_lib_atlas = ['atlas']
|
||
|
_lib_lapack = ['lapack']
|
||
|
|
||
|
notfounderror = AtlasNotFoundError
|
||
|
|
||
|
def get_paths(self, section, key):
|
||
|
pre_dirs = system_info.get_paths(self, section, key)
|
||
|
dirs = []
|
||
|
for d in pre_dirs:
|
||
|
dirs.extend(self.combine_paths(d, ['atlas*', 'ATLAS*',
|
||
|
'sse', '3dnow', 'sse2']) + [d])
|
||
|
return [d for d in dirs if os.path.isdir(d)]
|
||
|
|
||
|
def calc_info(self):
|
||
|
lib_dirs = self.get_lib_dirs()
|
||
|
info = {}
|
||
|
opt = self.get_option_single('atlas_libs', 'libraries')
|
||
|
atlas_libs = self.get_libs(opt, self._lib_names + self._lib_atlas)
|
||
|
lapack_libs = self.get_libs('lapack_libs', self._lib_lapack)
|
||
|
atlas = None
|
||
|
lapack = None
|
||
|
atlas_1 = None
|
||
|
for d in lib_dirs:
|
||
|
# FIXME: lapack_atlas is unused
|
||
|
lapack_atlas = self.check_libs2(d, ['lapack_atlas'], [])
|
||
|
atlas = self.check_libs2(d, atlas_libs, [])
|
||
|
if atlas is not None:
|
||
|
lib_dirs2 = [d] + self.combine_paths(d, ['atlas*', 'ATLAS*'])
|
||
|
lapack = self.check_libs2(lib_dirs2, lapack_libs, [])
|
||
|
if lapack is not None:
|
||
|
break
|
||
|
if atlas:
|
||
|
atlas_1 = atlas
|
||
|
log.info(self.__class__)
|
||
|
if atlas is None:
|
||
|
atlas = atlas_1
|
||
|
if atlas is None:
|
||
|
return
|
||
|
include_dirs = self.get_include_dirs()
|
||
|
h = (self.combine_paths(lib_dirs + include_dirs, 'cblas.h') or [None])
|
||
|
h = h[0]
|
||
|
if h:
|
||
|
h = os.path.dirname(h)
|
||
|
dict_append(info, include_dirs=[h])
|
||
|
info['language'] = 'c'
|
||
|
if lapack is not None:
|
||
|
dict_append(info, **lapack)
|
||
|
dict_append(info, **atlas)
|
||
|
elif 'lapack_atlas' in atlas['libraries']:
|
||
|
dict_append(info, **atlas)
|
||
|
dict_append(info,
|
||
|
define_macros=[('ATLAS_WITH_LAPACK_ATLAS', None)])
|
||
|
self.set_info(**info)
|
||
|
return
|
||
|
else:
|
||
|
dict_append(info, **atlas)
|
||
|
dict_append(info, define_macros=[('ATLAS_WITHOUT_LAPACK', None)])
|
||
|
message = textwrap.dedent("""
|
||
|
*********************************************************************
|
||
|
Could not find lapack library within the ATLAS installation.
|
||
|
*********************************************************************
|
||
|
""")
|
||
|
warnings.warn(message, stacklevel=2)
|
||
|
self.set_info(**info)
|
||
|
return
|
||
|
|
||
|
# Check if lapack library is complete, only warn if it is not.
|
||
|
lapack_dir = lapack['library_dirs'][0]
|
||
|
lapack_name = lapack['libraries'][0]
|
||
|
lapack_lib = None
|
||
|
lib_prefixes = ['lib']
|
||
|
if sys.platform == 'win32':
|
||
|
lib_prefixes.append('')
|
||
|
for e in self.library_extensions():
|
||
|
for prefix in lib_prefixes:
|
||
|
fn = os.path.join(lapack_dir, prefix + lapack_name + e)
|
||
|
if os.path.exists(fn):
|
||
|
lapack_lib = fn
|
||
|
break
|
||
|
if lapack_lib:
|
||
|
break
|
||
|
if lapack_lib is not None:
|
||
|
sz = os.stat(lapack_lib)[6]
|
||
|
if sz <= 4000 * 1024:
|
||
|
message = textwrap.dedent("""
|
||
|
*********************************************************************
|
||
|
Lapack library (from ATLAS) is probably incomplete:
|
||
|
size of %s is %sk (expected >4000k)
|
||
|
|
||
|
Follow the instructions in the KNOWN PROBLEMS section of the file
|
||
|
numpy/INSTALL.txt.
|
||
|
*********************************************************************
|
||
|
""") % (lapack_lib, sz / 1024)
|
||
|
warnings.warn(message, stacklevel=2)
|
||
|
else:
|
||
|
info['language'] = 'f77'
|
||
|
|
||
|
atlas_version, atlas_extra_info = get_atlas_version(**atlas)
|
||
|
dict_append(info, **atlas_extra_info)
|
||
|
|
||
|
self.set_info(**info)
|
||
|
|
||
|
|
||
|
class atlas_blas_info(atlas_info):
|
||
|
_lib_names = ['f77blas', 'cblas']
|
||
|
|
||
|
def calc_info(self):
|
||
|
lib_dirs = self.get_lib_dirs()
|
||
|
info = {}
|
||
|
opt = self.get_option_single('atlas_libs', 'libraries')
|
||
|
atlas_libs = self.get_libs(opt, self._lib_names + self._lib_atlas)
|
||
|
atlas = self.check_libs2(lib_dirs, atlas_libs, [])
|
||
|
if atlas is None:
|
||
|
return
|
||
|
include_dirs = self.get_include_dirs()
|
||
|
h = (self.combine_paths(lib_dirs + include_dirs, 'cblas.h') or [None])
|
||
|
h = h[0]
|
||
|
if h:
|
||
|
h = os.path.dirname(h)
|
||
|
dict_append(info, include_dirs=[h])
|
||
|
info['language'] = 'c'
|
||
|
info['define_macros'] = [('HAVE_CBLAS', None)]
|
||
|
|
||
|
atlas_version, atlas_extra_info = get_atlas_version(**atlas)
|
||
|
dict_append(atlas, **atlas_extra_info)
|
||
|
|
||
|
dict_append(info, **atlas)
|
||
|
|
||
|
self.set_info(**info)
|
||
|
return
|
||
|
|
||
|
|
||
|
class atlas_threads_info(atlas_info):
|
||
|
dir_env_var = ['PTATLAS', 'ATLAS']
|
||
|
_lib_names = ['ptf77blas', 'ptcblas']
|
||
|
|
||
|
|
||
|
class atlas_blas_threads_info(atlas_blas_info):
|
||
|
dir_env_var = ['PTATLAS', 'ATLAS']
|
||
|
_lib_names = ['ptf77blas', 'ptcblas']
|
||
|
|
||
|
|
||
|
class lapack_atlas_info(atlas_info):
|
||
|
_lib_names = ['lapack_atlas'] + atlas_info._lib_names
|
||
|
|
||
|
|
||
|
class lapack_atlas_threads_info(atlas_threads_info):
|
||
|
_lib_names = ['lapack_atlas'] + atlas_threads_info._lib_names
|
||
|
|
||
|
|
||
|
class atlas_3_10_info(atlas_info):
|
||
|
_lib_names = ['satlas']
|
||
|
_lib_atlas = _lib_names
|
||
|
_lib_lapack = _lib_names
|
||
|
|
||
|
|
||
|
class atlas_3_10_blas_info(atlas_3_10_info):
|
||
|
_lib_names = ['satlas']
|
||
|
|
||
|
def calc_info(self):
|
||
|
lib_dirs = self.get_lib_dirs()
|
||
|
info = {}
|
||
|
opt = self.get_option_single('atlas_lib', 'libraries')
|
||
|
atlas_libs = self.get_libs(opt, self._lib_names)
|
||
|
atlas = self.check_libs2(lib_dirs, atlas_libs, [])
|
||
|
if atlas is None:
|
||
|
return
|
||
|
include_dirs = self.get_include_dirs()
|
||
|
h = (self.combine_paths(lib_dirs + include_dirs, 'cblas.h') or [None])
|
||
|
h = h[0]
|
||
|
if h:
|
||
|
h = os.path.dirname(h)
|
||
|
dict_append(info, include_dirs=[h])
|
||
|
info['language'] = 'c'
|
||
|
info['define_macros'] = [('HAVE_CBLAS', None)]
|
||
|
|
||
|
atlas_version, atlas_extra_info = get_atlas_version(**atlas)
|
||
|
dict_append(atlas, **atlas_extra_info)
|
||
|
|
||
|
dict_append(info, **atlas)
|
||
|
|
||
|
self.set_info(**info)
|
||
|
return
|
||
|
|
||
|
|
||
|
class atlas_3_10_threads_info(atlas_3_10_info):
|
||
|
dir_env_var = ['PTATLAS', 'ATLAS']
|
||
|
_lib_names = ['tatlas']
|
||
|
_lib_atlas = _lib_names
|
||
|
_lib_lapack = _lib_names
|
||
|
|
||
|
|
||
|
class atlas_3_10_blas_threads_info(atlas_3_10_blas_info):
|
||
|
dir_env_var = ['PTATLAS', 'ATLAS']
|
||
|
_lib_names = ['tatlas']
|
||
|
|
||
|
|
||
|
class lapack_atlas_3_10_info(atlas_3_10_info):
|
||
|
pass
|
||
|
|
||
|
|
||
|
class lapack_atlas_3_10_threads_info(atlas_3_10_threads_info):
|
||
|
pass
|
||
|
|
||
|
|
||
|
class lapack_info(system_info):
|
||
|
section = 'lapack'
|
||
|
dir_env_var = 'LAPACK'
|
||
|
_lib_names = ['lapack']
|
||
|
notfounderror = LapackNotFoundError
|
||
|
|
||
|
def calc_info(self):
|
||
|
lib_dirs = self.get_lib_dirs()
|
||
|
|
||
|
opt = self.get_option_single('lapack_libs', 'libraries')
|
||
|
lapack_libs = self.get_libs(opt, self._lib_names)
|
||
|
info = self.check_libs(lib_dirs, lapack_libs, [])
|
||
|
if info is None:
|
||
|
return
|
||
|
info['language'] = 'f77'
|
||
|
self.set_info(**info)
|
||
|
|
||
|
|
||
|
class lapack_src_info(system_info):
|
||
|
section = 'lapack_src'
|
||
|
dir_env_var = 'LAPACK_SRC'
|
||
|
notfounderror = LapackSrcNotFoundError
|
||
|
|
||
|
def get_paths(self, section, key):
|
||
|
pre_dirs = system_info.get_paths(self, section, key)
|
||
|
dirs = []
|
||
|
for d in pre_dirs:
|
||
|
dirs.extend([d] + self.combine_paths(d, ['LAPACK*/SRC', 'SRC']))
|
||
|
return [d for d in dirs if os.path.isdir(d)]
|
||
|
|
||
|
def calc_info(self):
|
||
|
src_dirs = self.get_src_dirs()
|
||
|
src_dir = ''
|
||
|
for d in src_dirs:
|
||
|
if os.path.isfile(os.path.join(d, 'dgesv.f')):
|
||
|
src_dir = d
|
||
|
break
|
||
|
if not src_dir:
|
||
|
#XXX: Get sources from netlib. May be ask first.
|
||
|
return
|
||
|
# The following is extracted from LAPACK-3.0/SRC/Makefile.
|
||
|
# Added missing names from lapack-lite-3.1.1/SRC/Makefile
|
||
|
# while keeping removed names for Lapack-3.0 compatibility.
|
||
|
allaux = '''
|
||
|
ilaenv ieeeck lsame lsamen xerbla
|
||
|
iparmq
|
||
|
''' # *.f
|
||
|
laux = '''
|
||
|
bdsdc bdsqr disna labad lacpy ladiv lae2 laebz laed0 laed1
|
||
|
laed2 laed3 laed4 laed5 laed6 laed7 laed8 laed9 laeda laev2
|
||
|
lagtf lagts lamch lamrg lanst lapy2 lapy3 larnv larrb larre
|
||
|
larrf lartg laruv las2 lascl lasd0 lasd1 lasd2 lasd3 lasd4
|
||
|
lasd5 lasd6 lasd7 lasd8 lasd9 lasda lasdq lasdt laset lasq1
|
||
|
lasq2 lasq3 lasq4 lasq5 lasq6 lasr lasrt lassq lasv2 pttrf
|
||
|
stebz stedc steqr sterf
|
||
|
|
||
|
larra larrc larrd larr larrk larrj larrr laneg laisnan isnan
|
||
|
lazq3 lazq4
|
||
|
''' # [s|d]*.f
|
||
|
lasrc = '''
|
||
|
gbbrd gbcon gbequ gbrfs gbsv gbsvx gbtf2 gbtrf gbtrs gebak
|
||
|
gebal gebd2 gebrd gecon geequ gees geesx geev geevx gegs gegv
|
||
|
gehd2 gehrd gelq2 gelqf gels gelsd gelss gelsx gelsy geql2
|
||
|
geqlf geqp3 geqpf geqr2 geqrf gerfs gerq2 gerqf gesc2 gesdd
|
||
|
gesv gesvd gesvx getc2 getf2 getrf getri getrs ggbak ggbal
|
||
|
gges ggesx ggev ggevx ggglm gghrd gglse ggqrf ggrqf ggsvd
|
||
|
ggsvp gtcon gtrfs gtsv gtsvx gttrf gttrs gtts2 hgeqz hsein
|
||
|
hseqr labrd lacon laein lags2 lagtm lahqr lahrd laic1 lals0
|
||
|
lalsa lalsd langb lange langt lanhs lansb lansp lansy lantb
|
||
|
lantp lantr lapll lapmt laqgb laqge laqp2 laqps laqsb laqsp
|
||
|
laqsy lar1v lar2v larf larfb larfg larft larfx largv larrv
|
||
|
lartv larz larzb larzt laswp lasyf latbs latdf latps latrd
|
||
|
latrs latrz latzm lauu2 lauum pbcon pbequ pbrfs pbstf pbsv
|
||
|
pbsvx pbtf2 pbtrf pbtrs pocon poequ porfs posv posvx potf2
|
||
|
potrf potri potrs ppcon ppequ pprfs ppsv ppsvx pptrf pptri
|
||
|
pptrs ptcon pteqr ptrfs ptsv ptsvx pttrs ptts2 spcon sprfs
|
||
|
spsv spsvx sptrf sptri sptrs stegr stein sycon syrfs sysv
|
||
|
sysvx sytf2 sytrf sytri sytrs tbcon tbrfs tbtrs tgevc tgex2
|
||
|
tgexc tgsen tgsja tgsna tgsy2 tgsyl tpcon tprfs tptri tptrs
|
||
|
trcon trevc trexc trrfs trsen trsna trsyl trti2 trtri trtrs
|
||
|
tzrqf tzrzf
|
||
|
|
||
|
lacn2 lahr2 stemr laqr0 laqr1 laqr2 laqr3 laqr4 laqr5
|
||
|
''' # [s|c|d|z]*.f
|
||
|
sd_lasrc = '''
|
||
|
laexc lag2 lagv2 laln2 lanv2 laqtr lasy2 opgtr opmtr org2l
|
||
|
org2r orgbr orghr orgl2 orglq orgql orgqr orgr2 orgrq orgtr
|
||
|
orm2l orm2r ormbr ormhr orml2 ormlq ormql ormqr ormr2 ormr3
|
||
|
ormrq ormrz ormtr rscl sbev sbevd sbevx sbgst sbgv sbgvd sbgvx
|
||
|
sbtrd spev spevd spevx spgst spgv spgvd spgvx sptrd stev stevd
|
||
|
stevr stevx syev syevd syevr syevx sygs2 sygst sygv sygvd
|
||
|
sygvx sytd2 sytrd
|
||
|
''' # [s|d]*.f
|
||
|
cz_lasrc = '''
|
||
|
bdsqr hbev hbevd hbevx hbgst hbgv hbgvd hbgvx hbtrd hecon heev
|
||
|
heevd heevr heevx hegs2 hegst hegv hegvd hegvx herfs hesv
|
||
|
hesvx hetd2 hetf2 hetrd hetrf hetri hetrs hpcon hpev hpevd
|
||
|
hpevx hpgst hpgv hpgvd hpgvx hprfs hpsv hpsvx hptrd hptrf
|
||
|
hptri hptrs lacgv lacp2 lacpy lacrm lacrt ladiv laed0 laed7
|
||
|
laed8 laesy laev2 lahef lanhb lanhe lanhp lanht laqhb laqhe
|
||
|
laqhp larcm larnv lartg lascl laset lasr lassq pttrf rot spmv
|
||
|
spr stedc steqr symv syr ung2l ung2r ungbr unghr ungl2 unglq
|
||
|
ungql ungqr ungr2 ungrq ungtr unm2l unm2r unmbr unmhr unml2
|
||
|
unmlq unmql unmqr unmr2 unmr3 unmrq unmrz unmtr upgtr upmtr
|
||
|
''' # [c|z]*.f
|
||
|
#######
|
||
|
sclaux = laux + ' econd ' # s*.f
|
||
|
dzlaux = laux + ' secnd ' # d*.f
|
||
|
slasrc = lasrc + sd_lasrc # s*.f
|
||
|
dlasrc = lasrc + sd_lasrc # d*.f
|
||
|
clasrc = lasrc + cz_lasrc + ' srot srscl ' # c*.f
|
||
|
zlasrc = lasrc + cz_lasrc + ' drot drscl ' # z*.f
|
||
|
oclasrc = ' icmax1 scsum1 ' # *.f
|
||
|
ozlasrc = ' izmax1 dzsum1 ' # *.f
|
||
|
sources = ['s%s.f' % f for f in (sclaux + slasrc).split()] \
|
||
|
+ ['d%s.f' % f for f in (dzlaux + dlasrc).split()] \
|
||
|
+ ['c%s.f' % f for f in (clasrc).split()] \
|
||
|
+ ['z%s.f' % f for f in (zlasrc).split()] \
|
||
|
+ ['%s.f' % f for f in (allaux + oclasrc + ozlasrc).split()]
|
||
|
sources = [os.path.join(src_dir, f) for f in sources]
|
||
|
# Lapack 3.1:
|
||
|
src_dir2 = os.path.join(src_dir, '..', 'INSTALL')
|
||
|
sources += [os.path.join(src_dir2, p + 'lamch.f') for p in 'sdcz']
|
||
|
# Lapack 3.2.1:
|
||
|
sources += [os.path.join(src_dir, p + 'larfp.f') for p in 'sdcz']
|
||
|
sources += [os.path.join(src_dir, 'ila' + p + 'lr.f') for p in 'sdcz']
|
||
|
sources += [os.path.join(src_dir, 'ila' + p + 'lc.f') for p in 'sdcz']
|
||
|
# Should we check here actual existence of source files?
|
||
|
# Yes, the file listing is different between 3.0 and 3.1
|
||
|
# versions.
|
||
|
sources = [f for f in sources if os.path.isfile(f)]
|
||
|
info = {'sources': sources, 'language': 'f77'}
|
||
|
self.set_info(**info)
|
||
|
|
||
|
atlas_version_c_text = r'''
|
||
|
/* This file is generated from numpy/distutils/system_info.py */
|
||
|
void ATL_buildinfo(void);
|
||
|
int main(void) {
|
||
|
ATL_buildinfo();
|
||
|
return 0;
|
||
|
}
|
||
|
'''
|
||
|
|
||
|
_cached_atlas_version = {}
|
||
|
|
||
|
|
||
|
def get_atlas_version(**config):
|
||
|
libraries = config.get('libraries', [])
|
||
|
library_dirs = config.get('library_dirs', [])
|
||
|
key = (tuple(libraries), tuple(library_dirs))
|
||
|
if key in _cached_atlas_version:
|
||
|
return _cached_atlas_version[key]
|
||
|
c = cmd_config(Distribution())
|
||
|
atlas_version = None
|
||
|
info = {}
|
||
|
try:
|
||
|
s, o = c.get_output(atlas_version_c_text,
|
||
|
libraries=libraries, library_dirs=library_dirs,
|
||
|
)
|
||
|
if s and re.search(r'undefined reference to `_gfortran', o, re.M):
|
||
|
s, o = c.get_output(atlas_version_c_text,
|
||
|
libraries=libraries + ['gfortran'],
|
||
|
library_dirs=library_dirs,
|
||
|
)
|
||
|
if not s:
|
||
|
warnings.warn(textwrap.dedent("""
|
||
|
*****************************************************
|
||
|
Linkage with ATLAS requires gfortran. Use
|
||
|
|
||
|
python setup.py config_fc --fcompiler=gnu95 ...
|
||
|
|
||
|
when building extension libraries that use ATLAS.
|
||
|
Make sure that -lgfortran is used for C++ extensions.
|
||
|
*****************************************************
|
||
|
"""), stacklevel=2)
|
||
|
dict_append(info, language='f90',
|
||
|
define_macros=[('ATLAS_REQUIRES_GFORTRAN', None)])
|
||
|
except Exception: # failed to get version from file -- maybe on Windows
|
||
|
# look at directory name
|
||
|
for o in library_dirs:
|
||
|
m = re.search(r'ATLAS_(?P<version>\d+[.]\d+[.]\d+)_', o)
|
||
|
if m:
|
||
|
atlas_version = m.group('version')
|
||
|
if atlas_version is not None:
|
||
|
break
|
||
|
|
||
|
# final choice --- look at ATLAS_VERSION environment
|
||
|
# variable
|
||
|
if atlas_version is None:
|
||
|
atlas_version = os.environ.get('ATLAS_VERSION', None)
|
||
|
if atlas_version:
|
||
|
dict_append(info, define_macros=[(
|
||
|
'ATLAS_INFO', _c_string_literal(atlas_version))
|
||
|
])
|
||
|
else:
|
||
|
dict_append(info, define_macros=[('NO_ATLAS_INFO', -1)])
|
||
|
return atlas_version or '?.?.?', info
|
||
|
|
||
|
if not s:
|
||
|
m = re.search(r'ATLAS version (?P<version>\d+[.]\d+[.]\d+)', o)
|
||
|
if m:
|
||
|
atlas_version = m.group('version')
|
||
|
if atlas_version is None:
|
||
|
if re.search(r'undefined symbol: ATL_buildinfo', o, re.M):
|
||
|
atlas_version = '3.2.1_pre3.3.6'
|
||
|
else:
|
||
|
log.info('Status: %d', s)
|
||
|
log.info('Output: %s', o)
|
||
|
|
||
|
elif atlas_version == '3.2.1_pre3.3.6':
|
||
|
dict_append(info, define_macros=[('NO_ATLAS_INFO', -2)])
|
||
|
else:
|
||
|
dict_append(info, define_macros=[(
|
||
|
'ATLAS_INFO', _c_string_literal(atlas_version))
|
||
|
])
|
||
|
result = _cached_atlas_version[key] = atlas_version, info
|
||
|
return result
|
||
|
|
||
|
|
||
|
class lapack_opt_info(system_info):
|
||
|
notfounderror = LapackNotFoundError
|
||
|
# List of all known BLAS libraries, in the default order
|
||
|
lapack_order = ['mkl', 'openblas', 'flame', 'atlas', 'accelerate', 'lapack']
|
||
|
order_env_var_name = 'NPY_LAPACK_ORDER'
|
||
|
|
||
|
def _calc_info_mkl(self):
|
||
|
info = get_info('lapack_mkl')
|
||
|
if info:
|
||
|
self.set_info(**info)
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
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
|
||
|
|
||
|
def _calc_info_flame(self):
|
||
|
info = get_info('flame')
|
||
|
if info:
|
||
|
self.set_info(**info)
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
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
|
||
|
|
||
|
def _calc_info_accelerate(self):
|
||
|
info = get_info('accelerate')
|
||
|
if info:
|
||
|
self.set_info(**info)
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
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
|
||
|
|
||
|
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
|
||
|
|
||
|
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
|
||
|
|
||
|
def _calc_info(self, name):
|
||
|
return getattr(self, '_calc_info_{}'.format(name))()
|
||
|
|
||
|
def calc_info(self):
|
||
|
user_order = os.environ.get(self.order_env_var_name, 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 self._calc_info(lapack):
|
||
|
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 _ilp64_opt_info_mixin:
|
||
|
symbol_suffix = None
|
||
|
symbol_prefix = None
|
||
|
|
||
|
def _check_info(self, info):
|
||
|
macros = dict(info.get('define_macros', []))
|
||
|
prefix = macros.get('BLAS_SYMBOL_PREFIX', '')
|
||
|
suffix = macros.get('BLAS_SYMBOL_SUFFIX', '')
|
||
|
|
||
|
if self.symbol_prefix not in (None, prefix):
|
||
|
return False
|
||
|
|
||
|
if self.symbol_suffix not in (None, suffix):
|
||
|
return False
|
||
|
|
||
|
return bool(info)
|
||
|
|
||
|
|
||
|
class lapack_ilp64_opt_info(lapack_opt_info, _ilp64_opt_info_mixin):
|
||
|
notfounderror = LapackILP64NotFoundError
|
||
|
lapack_order = ['openblas64_', 'openblas_ilp64']
|
||
|
order_env_var_name = 'NPY_LAPACK_ILP64_ORDER'
|
||
|
|
||
|
def _calc_info(self, name):
|
||
|
info = get_info(name + '_lapack')
|
||
|
if self._check_info(info):
|
||
|
self.set_info(**info)
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
|
||
|
class lapack_ilp64_plain_opt_info(lapack_ilp64_opt_info):
|
||
|
# Same as lapack_ilp64_opt_info, but fix symbol names
|
||
|
symbol_prefix = ''
|
||
|
symbol_suffix = ''
|
||
|
|
||
|
|
||
|
class lapack64__opt_info(lapack_ilp64_opt_info):
|
||
|
symbol_prefix = ''
|
||
|
symbol_suffix = '64_'
|
||
|
|
||
|
|
||
|
class blas_opt_info(system_info):
|
||
|
notfounderror = BlasNotFoundError
|
||
|
# List of all known BLAS libraries, in the default order
|
||
|
blas_order = ['mkl', 'blis', 'openblas', 'atlas', 'accelerate', 'blas']
|
||
|
order_env_var_name = 'NPY_BLAS_ORDER'
|
||
|
|
||
|
def _calc_info_mkl(self):
|
||
|
info = get_info('blas_mkl')
|
||
|
if info:
|
||
|
self.set_info(**info)
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
def _calc_info_blis(self):
|
||
|
info = get_info('blis')
|
||
|
if info:
|
||
|
self.set_info(**info)
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
def _calc_info_openblas(self):
|
||
|
info = get_info('openblas')
|
||
|
if info:
|
||
|
self.set_info(**info)
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
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
|
||
|
|
||
|
def _calc_info_accelerate(self):
|
||
|
info = get_info('accelerate')
|
||
|
if info:
|
||
|
self.set_info(**info)
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
def _calc_info_blas(self):
|
||
|
# Warn about a non-optimized BLAS library
|
||
|
warnings.warn(BlasOptNotFoundError.__doc__ or '', stacklevel=3)
|
||
|
info = {}
|
||
|
dict_append(info, define_macros=[('NO_ATLAS_INFO', 1)])
|
||
|
|
||
|
blas = get_info('blas')
|
||
|
if blas:
|
||
|
dict_append(info, **blas)
|
||
|
else:
|
||
|
# Not even BLAS was found!
|
||
|
warnings.warn(BlasNotFoundError.__doc__ or '', stacklevel=3)
|
||
|
|
||
|
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 True
|
||
|
|
||
|
def _calc_info(self, name):
|
||
|
return getattr(self, '_calc_info_{}'.format(name))()
|
||
|
|
||
|
def calc_info(self):
|
||
|
user_order = os.environ.get(self.order_env_var_name, 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 self._calc_info(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_ilp64_opt_info(blas_opt_info, _ilp64_opt_info_mixin):
|
||
|
notfounderror = BlasILP64NotFoundError
|
||
|
blas_order = ['openblas64_', 'openblas_ilp64']
|
||
|
order_env_var_name = 'NPY_BLAS_ILP64_ORDER'
|
||
|
|
||
|
def _calc_info(self, name):
|
||
|
info = get_info(name)
|
||
|
if self._check_info(info):
|
||
|
self.set_info(**info)
|
||
|
return True
|
||
|
return False
|
||
|
|
||
|
|
||
|
class blas_ilp64_plain_opt_info(blas_ilp64_opt_info):
|
||
|
symbol_prefix = ''
|
||
|
symbol_suffix = ''
|
||
|
|
||
|
|
||
|
class blas64__opt_info(blas_ilp64_opt_info):
|
||
|
symbol_prefix = ''
|
||
|
symbol_suffix = '64_'
|
||
|
|
||
|
|
||
|
class blas_info(system_info):
|
||
|
section = 'blas'
|
||
|
dir_env_var = 'BLAS'
|
||
|
_lib_names = ['blas']
|
||
|
notfounderror = BlasNotFoundError
|
||
|
|
||
|
def calc_info(self):
|
||
|
lib_dirs = self.get_lib_dirs()
|
||
|
opt = self.get_option_single('blas_libs', 'libraries')
|
||
|
blas_libs = self.get_libs(opt, self._lib_names)
|
||
|
info = self.check_libs(lib_dirs, blas_libs, [])
|
||
|
if info is None:
|
||
|
return
|
||
|
else:
|
||
|
info['include_dirs'] = self.get_include_dirs()
|
||
|
if platform.system() == 'Windows':
|
||
|
# The check for windows is needed because get_cblas_libs uses the
|
||
|
# same compiler that was used to compile Python and msvc is
|
||
|
# often not installed when mingw is being used. This rough
|
||
|
# treatment is not desirable, but windows is tricky.
|
||
|
info['language'] = 'f77' # XXX: is it generally true?
|
||
|
else:
|
||
|
lib = self.get_cblas_libs(info)
|
||
|
if lib is not None:
|
||
|
info['language'] = 'c'
|
||
|
info['libraries'] = lib
|
||
|
info['define_macros'] = [('HAVE_CBLAS', None)]
|
||
|
self.set_info(**info)
|
||
|
|
||
|
def get_cblas_libs(self, info):
|
||
|
""" Check whether we can link with CBLAS interface
|
||
|
|
||
|
This method will search through several combinations of libraries
|
||
|
to check whether CBLAS is present:
|
||
|
|
||
|
1. Libraries in ``info['libraries']``, as is
|
||
|
2. As 1. but also explicitly adding ``'cblas'`` as a library
|
||
|
3. As 1. but also explicitly adding ``'blas'`` as a library
|
||
|
4. Check only library ``'cblas'``
|
||
|
5. Check only library ``'blas'``
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
info : dict
|
||
|
system information dictionary for compilation and linking
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
libraries : list of str or None
|
||
|
a list of libraries that enables the use of CBLAS interface.
|
||
|
Returns None if not found or a compilation error occurs.
|
||
|
|
||
|
Since 1.17 returns a list.
|
||
|
"""
|
||
|
# primitive cblas check by looking for the header and trying to link
|
||
|
# cblas or blas
|
||
|
c = customized_ccompiler()
|
||
|
tmpdir = tempfile.mkdtemp()
|
||
|
s = textwrap.dedent("""\
|
||
|
#include <cblas.h>
|
||
|
int main(int argc, const char *argv[])
|
||
|
{
|
||
|
double a[4] = {1,2,3,4};
|
||
|
double b[4] = {5,6,7,8};
|
||
|
return cblas_ddot(4, a, 1, b, 1) > 10;
|
||
|
}""")
|
||
|
src = os.path.join(tmpdir, 'source.c')
|
||
|
try:
|
||
|
with open(src, 'wt') as f:
|
||
|
f.write(s)
|
||
|
|
||
|
try:
|
||
|
# check we can compile (find headers)
|
||
|
obj = c.compile([src], output_dir=tmpdir,
|
||
|
include_dirs=self.get_include_dirs())
|
||
|
except (distutils.ccompiler.CompileError, distutils.ccompiler.LinkError):
|
||
|
return None
|
||
|
|
||
|
# check we can link (find library)
|
||
|
# some systems have separate cblas and blas libs.
|
||
|
for libs in [info['libraries'], ['cblas'] + info['libraries'],
|
||
|
['blas'] + info['libraries'], ['cblas'], ['blas']]:
|
||
|
try:
|
||
|
c.link_executable(obj, os.path.join(tmpdir, "a.out"),
|
||
|
libraries=libs,
|
||
|
library_dirs=info['library_dirs'],
|
||
|
extra_postargs=info.get('extra_link_args', []))
|
||
|
return libs
|
||
|
except distutils.ccompiler.LinkError:
|
||
|
pass
|
||
|
finally:
|
||
|
shutil.rmtree(tmpdir)
|
||
|
return None
|
||
|
|
||
|
|
||
|
class openblas_info(blas_info):
|
||
|
section = 'openblas'
|
||
|
dir_env_var = 'OPENBLAS'
|
||
|
_lib_names = ['openblas']
|
||
|
_require_symbols = []
|
||
|
notfounderror = BlasNotFoundError
|
||
|
|
||
|
@property
|
||
|
def symbol_prefix(self):
|
||
|
try:
|
||
|
return self.cp.get(self.section, 'symbol_prefix')
|
||
|
except NoOptionError:
|
||
|
return ''
|
||
|
|
||
|
@property
|
||
|
def symbol_suffix(self):
|
||
|
try:
|
||
|
return self.cp.get(self.section, 'symbol_suffix')
|
||
|
except NoOptionError:
|
||
|
return ''
|
||
|
|
||
|
def _calc_info(self):
|
||
|
c = customized_ccompiler()
|
||
|
|
||
|
lib_dirs = self.get_lib_dirs()
|
||
|
|
||
|
# Prefer to use libraries over openblas_libs
|
||
|
opt = self.get_option_single('openblas_libs', 'libraries')
|
||
|
openblas_libs = self.get_libs(opt, self._lib_names)
|
||
|
|
||
|
info = self.check_libs(lib_dirs, openblas_libs, [])
|
||
|
|
||
|
if c.compiler_type == "msvc" and info is None:
|
||
|
from numpy.distutils.fcompiler import new_fcompiler
|
||
|
f = new_fcompiler(c_compiler=c)
|
||
|
if f and f.compiler_type == 'gnu95':
|
||
|
# Try gfortran-compatible library files
|
||
|
info = self.check_msvc_gfortran_libs(lib_dirs, openblas_libs)
|
||
|
# Skip lapack check, we'd need build_ext to do it
|
||
|
skip_symbol_check = True
|
||
|
elif info:
|
||
|
skip_symbol_check = False
|
||
|
info['language'] = 'c'
|
||
|
|
||
|
if info is None:
|
||
|
return None
|
||
|
|
||
|
# Add extra info for OpenBLAS
|
||
|
extra_info = self.calc_extra_info()
|
||
|
dict_append(info, **extra_info)
|
||
|
|
||
|
if not (skip_symbol_check or self.check_symbols(info)):
|
||
|
return None
|
||
|
|
||
|
info['define_macros'] = [('HAVE_CBLAS', None)]
|
||
|
if self.symbol_prefix:
|
||
|
info['define_macros'] += [('BLAS_SYMBOL_PREFIX', self.symbol_prefix)]
|
||
|
if self.symbol_suffix:
|
||
|
info['define_macros'] += [('BLAS_SYMBOL_SUFFIX', self.symbol_suffix)]
|
||
|
|
||
|
return info
|
||
|
|
||
|
def calc_info(self):
|
||
|
info = self._calc_info()
|
||
|
if info is not None:
|
||
|
self.set_info(**info)
|
||
|
|
||
|
def check_msvc_gfortran_libs(self, library_dirs, libraries):
|
||
|
# First, find the full path to each library directory
|
||
|
library_paths = []
|
||
|
for library in libraries:
|
||
|
for library_dir in library_dirs:
|
||
|
# MinGW static ext will be .a
|
||
|
fullpath = os.path.join(library_dir, library + '.a')
|
||
|
if os.path.isfile(fullpath):
|
||
|
library_paths.append(fullpath)
|
||
|
break
|
||
|
else:
|
||
|
return None
|
||
|
|
||
|
# Generate numpy.distutils virtual static library file
|
||
|
basename = self.__class__.__name__
|
||
|
tmpdir = os.path.join(os.getcwd(), 'build', basename)
|
||
|
if not os.path.isdir(tmpdir):
|
||
|
os.makedirs(tmpdir)
|
||
|
|
||
|
info = {'library_dirs': [tmpdir],
|
||
|
'libraries': [basename],
|
||
|
'language': 'f77'}
|
||
|
|
||
|
fake_lib_file = os.path.join(tmpdir, basename + '.fobjects')
|
||
|
fake_clib_file = os.path.join(tmpdir, basename + '.cobjects')
|
||
|
with open(fake_lib_file, 'w') as f:
|
||
|
f.write("\n".join(library_paths))
|
||
|
with open(fake_clib_file, 'w') as f:
|
||
|
pass
|
||
|
|
||
|
return info
|
||
|
|
||
|
def check_symbols(self, info):
|
||
|
res = False
|
||
|
c = customized_ccompiler()
|
||
|
|
||
|
tmpdir = tempfile.mkdtemp()
|
||
|
|
||
|
prototypes = "\n".join("void %s%s%s();" % (self.symbol_prefix,
|
||
|
symbol_name,
|
||
|
self.symbol_suffix)
|
||
|
for symbol_name in self._require_symbols)
|
||
|
calls = "\n".join("%s%s%s();" % (self.symbol_prefix,
|
||
|
symbol_name,
|
||
|
self.symbol_suffix)
|
||
|
for symbol_name in self._require_symbols)
|
||
|
s = textwrap.dedent("""\
|
||
|
%(prototypes)s
|
||
|
int main(int argc, const char *argv[])
|
||
|
{
|
||
|
%(calls)s
|
||
|
return 0;
|
||
|
}""") % dict(prototypes=prototypes, calls=calls)
|
||
|
src = os.path.join(tmpdir, 'source.c')
|
||
|
out = os.path.join(tmpdir, 'a.out')
|
||
|
# Add the additional "extra" arguments
|
||
|
try:
|
||
|
extra_args = info['extra_link_args']
|
||
|
except Exception:
|
||
|
extra_args = []
|
||
|
try:
|
||
|
with open(src, 'wt') as f:
|
||
|
f.write(s)
|
||
|
obj = c.compile([src], output_dir=tmpdir)
|
||
|
try:
|
||
|
c.link_executable(obj, out, libraries=info['libraries'],
|
||
|
library_dirs=info['library_dirs'],
|
||
|
extra_postargs=extra_args)
|
||
|
res = True
|
||
|
except distutils.ccompiler.LinkError:
|
||
|
res = False
|
||
|
finally:
|
||
|
shutil.rmtree(tmpdir)
|
||
|
return res
|
||
|
|
||
|
class openblas_lapack_info(openblas_info):
|
||
|
section = 'openblas'
|
||
|
dir_env_var = 'OPENBLAS'
|
||
|
_lib_names = ['openblas']
|
||
|
_require_symbols = ['zungqr_']
|
||
|
notfounderror = BlasNotFoundError
|
||
|
|
||
|
class openblas_clapack_info(openblas_lapack_info):
|
||
|
_lib_names = ['openblas', 'lapack']
|
||
|
|
||
|
class openblas_ilp64_info(openblas_info):
|
||
|
section = 'openblas_ilp64'
|
||
|
dir_env_var = 'OPENBLAS_ILP64'
|
||
|
_lib_names = ['openblas64']
|
||
|
_require_symbols = ['dgemm_', 'cblas_dgemm']
|
||
|
notfounderror = BlasILP64NotFoundError
|
||
|
|
||
|
def _calc_info(self):
|
||
|
info = super()._calc_info()
|
||
|
if info is not None:
|
||
|
info['define_macros'] += [('HAVE_BLAS_ILP64', None)]
|
||
|
return info
|
||
|
|
||
|
class openblas_ilp64_lapack_info(openblas_ilp64_info):
|
||
|
_require_symbols = ['dgemm_', 'cblas_dgemm', 'zungqr_', 'LAPACKE_zungqr']
|
||
|
|
||
|
def _calc_info(self):
|
||
|
info = super()._calc_info()
|
||
|
if info:
|
||
|
info['define_macros'] += [('HAVE_LAPACKE', None)]
|
||
|
return info
|
||
|
|
||
|
class openblas64__info(openblas_ilp64_info):
|
||
|
# ILP64 Openblas, with default symbol suffix
|
||
|
section = 'openblas64_'
|
||
|
dir_env_var = 'OPENBLAS64_'
|
||
|
_lib_names = ['openblas64_']
|
||
|
symbol_suffix = '64_'
|
||
|
symbol_prefix = ''
|
||
|
|
||
|
class openblas64__lapack_info(openblas_ilp64_lapack_info, openblas64__info):
|
||
|
pass
|
||
|
|
||
|
class blis_info(blas_info):
|
||
|
section = 'blis'
|
||
|
dir_env_var = 'BLIS'
|
||
|
_lib_names = ['blis']
|
||
|
notfounderror = BlasNotFoundError
|
||
|
|
||
|
def calc_info(self):
|
||
|
lib_dirs = self.get_lib_dirs()
|
||
|
opt = self.get_option_single('blis_libs', 'libraries')
|
||
|
blis_libs = self.get_libs(opt, self._lib_names)
|
||
|
info = self.check_libs2(lib_dirs, blis_libs, [])
|
||
|
if info is None:
|
||
|
return
|
||
|
|
||
|
# Add include dirs
|
||
|
incl_dirs = self.get_include_dirs()
|
||
|
dict_append(info,
|
||
|
language='c',
|
||
|
define_macros=[('HAVE_CBLAS', None)],
|
||
|
include_dirs=incl_dirs)
|
||
|
self.set_info(**info)
|
||
|
|
||
|
|
||
|
class flame_info(system_info):
|
||
|
""" Usage of libflame for LAPACK operations
|
||
|
|
||
|
This requires libflame to be compiled with lapack wrappers:
|
||
|
|
||
|
./configure --enable-lapack2flame ...
|
||
|
|
||
|
Be aware that libflame 5.1.0 has some missing names in the shared library, so
|
||
|
if you have problems, try the static flame library.
|
||
|
"""
|
||
|
section = 'flame'
|
||
|
_lib_names = ['flame']
|
||
|
notfounderror = FlameNotFoundError
|
||
|
|
||
|
def check_embedded_lapack(self, info):
|
||
|
""" libflame does not necessarily have a wrapper for fortran LAPACK, we need to check """
|
||
|
c = customized_ccompiler()
|
||
|
|
||
|
tmpdir = tempfile.mkdtemp()
|
||
|
s = textwrap.dedent("""\
|
||
|
void zungqr_();
|
||
|
int main(int argc, const char *argv[])
|
||
|
{
|
||
|
zungqr_();
|
||
|
return 0;
|
||
|
}""")
|
||
|
src = os.path.join(tmpdir, 'source.c')
|
||
|
out = os.path.join(tmpdir, 'a.out')
|
||
|
# Add the additional "extra" arguments
|
||
|
extra_args = info.get('extra_link_args', [])
|
||
|
try:
|
||
|
with open(src, 'wt') as f:
|
||
|
f.write(s)
|
||
|
obj = c.compile([src], output_dir=tmpdir)
|
||
|
try:
|
||
|
c.link_executable(obj, out, libraries=info['libraries'],
|
||
|
library_dirs=info['library_dirs'],
|
||
|
extra_postargs=extra_args)
|
||
|
return True
|
||
|
except distutils.ccompiler.LinkError:
|
||
|
return False
|
||
|
finally:
|
||
|
shutil.rmtree(tmpdir)
|
||
|
|
||
|
def calc_info(self):
|
||
|
lib_dirs = self.get_lib_dirs()
|
||
|
flame_libs = self.get_libs('libraries', self._lib_names)
|
||
|
|
||
|
info = self.check_libs2(lib_dirs, flame_libs, [])
|
||
|
if info is None:
|
||
|
return
|
||
|
|
||
|
if self.check_embedded_lapack(info):
|
||
|
# check if the user has supplied all information required
|
||
|
self.set_info(**info)
|
||
|
else:
|
||
|
# Try and get the BLAS lib to see if we can get it to work
|
||
|
blas_info = get_info('blas_opt')
|
||
|
if not blas_info:
|
||
|
# since we already failed once, this ain't going to work either
|
||
|
return
|
||
|
|
||
|
# Now we need to merge the two dictionaries
|
||
|
for key in blas_info:
|
||
|
if isinstance(blas_info[key], list):
|
||
|
info[key] = info.get(key, []) + blas_info[key]
|
||
|
elif isinstance(blas_info[key], tuple):
|
||
|
info[key] = info.get(key, ()) + blas_info[key]
|
||
|
else:
|
||
|
info[key] = info.get(key, '') + blas_info[key]
|
||
|
|
||
|
# Now check again
|
||
|
if self.check_embedded_lapack(info):
|
||
|
self.set_info(**info)
|
||
|
|
||
|
|
||
|
class accelerate_info(system_info):
|
||
|
section = 'accelerate'
|
||
|
_lib_names = ['accelerate', 'veclib']
|
||
|
notfounderror = BlasNotFoundError
|
||
|
|
||
|
def calc_info(self):
|
||
|
# Make possible to enable/disable from config file/env var
|
||
|
libraries = os.environ.get('ACCELERATE')
|
||
|
if libraries:
|
||
|
libraries = [libraries]
|
||
|
else:
|
||
|
libraries = self.get_libs('libraries', self._lib_names)
|
||
|
libraries = [lib.strip().lower() for lib in libraries]
|
||
|
|
||
|
if (sys.platform == 'darwin' and
|
||
|
not os.getenv('_PYTHON_HOST_PLATFORM', None)):
|
||
|
# Use the system BLAS from Accelerate or vecLib under OSX
|
||
|
args = []
|
||
|
link_args = []
|
||
|
if get_platform()[-4:] == 'i386' or 'intel' in get_platform() or \
|
||
|
'x86_64' in get_platform() or \
|
||
|
'i386' in platform.platform():
|
||
|
intel = 1
|
||
|
else:
|
||
|
intel = 0
|
||
|
if (os.path.exists('/System/Library/Frameworks'
|
||
|
'/Accelerate.framework/') and
|
||
|
'accelerate' in libraries):
|
||
|
if intel:
|
||
|
args.extend(['-msse3'])
|
||
|
else:
|
||
|
args.extend(['-faltivec'])
|
||
|
args.extend([
|
||
|
'-I/System/Library/Frameworks/vecLib.framework/Headers'])
|
||
|
link_args.extend(['-Wl,-framework', '-Wl,Accelerate'])
|
||
|
elif (os.path.exists('/System/Library/Frameworks'
|
||
|
'/vecLib.framework/') and
|
||
|
'veclib' in libraries):
|
||
|
if intel:
|
||
|
args.extend(['-msse3'])
|
||
|
else:
|
||
|
args.extend(['-faltivec'])
|
||
|
args.extend([
|
||
|
'-I/System/Library/Frameworks/vecLib.framework/Headers'])
|
||
|
link_args.extend(['-Wl,-framework', '-Wl,vecLib'])
|
||
|
|
||
|
if args:
|
||
|
self.set_info(extra_compile_args=args,
|
||
|
extra_link_args=link_args,
|
||
|
define_macros=[('NO_ATLAS_INFO', 3),
|
||
|
('HAVE_CBLAS', None)])
|
||
|
|
||
|
return
|
||
|
|
||
|
class blas_src_info(system_info):
|
||
|
section = 'blas_src'
|
||
|
dir_env_var = 'BLAS_SRC'
|
||
|
notfounderror = BlasSrcNotFoundError
|
||
|
|
||
|
def get_paths(self, section, key):
|
||
|
pre_dirs = system_info.get_paths(self, section, key)
|
||
|
dirs = []
|
||
|
for d in pre_dirs:
|
||
|
dirs.extend([d] + self.combine_paths(d, ['blas']))
|
||
|
return [d for d in dirs if os.path.isdir(d)]
|
||
|
|
||
|
def calc_info(self):
|
||
|
src_dirs = self.get_src_dirs()
|
||
|
src_dir = ''
|
||
|
for d in src_dirs:
|
||
|
if os.path.isfile(os.path.join(d, 'daxpy.f')):
|
||
|
src_dir = d
|
||
|
break
|
||
|
if not src_dir:
|
||
|
#XXX: Get sources from netlib. May be ask first.
|
||
|
return
|
||
|
blas1 = '''
|
||
|
caxpy csscal dnrm2 dzasum saxpy srotg zdotc ccopy cswap drot
|
||
|
dznrm2 scasum srotm zdotu cdotc dasum drotg icamax scnrm2
|
||
|
srotmg zdrot cdotu daxpy drotm idamax scopy sscal zdscal crotg
|
||
|
dcabs1 drotmg isamax sdot sswap zrotg cscal dcopy dscal izamax
|
||
|
snrm2 zaxpy zscal csrot ddot dswap sasum srot zcopy zswap
|
||
|
scabs1
|
||
|
'''
|
||
|
blas2 = '''
|
||
|
cgbmv chpmv ctrsv dsymv dtrsv sspr2 strmv zhemv ztpmv cgemv
|
||
|
chpr dgbmv dsyr lsame ssymv strsv zher ztpsv cgerc chpr2 dgemv
|
||
|
dsyr2 sgbmv ssyr xerbla zher2 ztrmv cgeru ctbmv dger dtbmv
|
||
|
sgemv ssyr2 zgbmv zhpmv ztrsv chbmv ctbsv dsbmv dtbsv sger
|
||
|
stbmv zgemv zhpr chemv ctpmv dspmv dtpmv ssbmv stbsv zgerc
|
||
|
zhpr2 cher ctpsv dspr dtpsv sspmv stpmv zgeru ztbmv cher2
|
||
|
ctrmv dspr2 dtrmv sspr stpsv zhbmv ztbsv
|
||
|
'''
|
||
|
blas3 = '''
|
||
|
cgemm csymm ctrsm dsyrk sgemm strmm zhemm zsyr2k chemm csyr2k
|
||
|
dgemm dtrmm ssymm strsm zher2k zsyrk cher2k csyrk dsymm dtrsm
|
||
|
ssyr2k zherk ztrmm cherk ctrmm dsyr2k ssyrk zgemm zsymm ztrsm
|
||
|
'''
|
||
|
sources = [os.path.join(src_dir, f + '.f') \
|
||
|
for f in (blas1 + blas2 + blas3).split()]
|
||
|
#XXX: should we check here actual existence of source files?
|
||
|
sources = [f for f in sources if os.path.isfile(f)]
|
||
|
info = {'sources': sources, 'language': 'f77'}
|
||
|
self.set_info(**info)
|
||
|
|
||
|
|
||
|
class x11_info(system_info):
|
||
|
section = 'x11'
|
||
|
notfounderror = X11NotFoundError
|
||
|
_lib_names = ['X11']
|
||
|
|
||
|
def __init__(self):
|
||
|
system_info.__init__(self,
|
||
|
default_lib_dirs=default_x11_lib_dirs,
|
||
|
default_include_dirs=default_x11_include_dirs)
|
||
|
|
||
|
def calc_info(self):
|
||
|
if sys.platform in ['win32']:
|
||
|
return
|
||
|
lib_dirs = self.get_lib_dirs()
|
||
|
include_dirs = self.get_include_dirs()
|
||
|
opt = self.get_option_single('x11_libs', 'libraries')
|
||
|
x11_libs = self.get_libs(opt, self._lib_names)
|
||
|
info = self.check_libs(lib_dirs, x11_libs, [])
|
||
|
if info is None:
|
||
|
return
|
||
|
inc_dir = None
|
||
|
for d in include_dirs:
|
||
|
if self.combine_paths(d, 'X11/X.h'):
|
||
|
inc_dir = d
|
||
|
break
|
||
|
if inc_dir is not None:
|
||
|
dict_append(info, include_dirs=[inc_dir])
|
||
|
self.set_info(**info)
|
||
|
|
||
|
|
||
|
class _numpy_info(system_info):
|
||
|
section = 'Numeric'
|
||
|
modulename = 'Numeric'
|
||
|
notfounderror = NumericNotFoundError
|
||
|
|
||
|
def __init__(self):
|
||
|
include_dirs = []
|
||
|
try:
|
||
|
module = __import__(self.modulename)
|
||
|
prefix = []
|
||
|
for name in module.__file__.split(os.sep):
|
||
|
if name == 'lib':
|
||
|
break
|
||
|
prefix.append(name)
|
||
|
|
||
|
# Ask numpy for its own include path before attempting
|
||
|
# anything else
|
||
|
try:
|
||
|
include_dirs.append(getattr(module, 'get_include')())
|
||
|
except AttributeError:
|
||
|
pass
|
||
|
|
||
|
include_dirs.append(distutils.sysconfig.get_python_inc(
|
||
|
prefix=os.sep.join(prefix)))
|
||
|
except ImportError:
|
||
|
pass
|
||
|
py_incl_dir = distutils.sysconfig.get_python_inc()
|
||
|
include_dirs.append(py_incl_dir)
|
||
|
py_pincl_dir = distutils.sysconfig.get_python_inc(plat_specific=True)
|
||
|
if py_pincl_dir not in include_dirs:
|
||
|
include_dirs.append(py_pincl_dir)
|
||
|
for d in default_include_dirs:
|
||
|
d = os.path.join(d, os.path.basename(py_incl_dir))
|
||
|
if d not in include_dirs:
|
||
|
include_dirs.append(d)
|
||
|
system_info.__init__(self,
|
||
|
default_lib_dirs=[],
|
||
|
default_include_dirs=include_dirs)
|
||
|
|
||
|
def calc_info(self):
|
||
|
try:
|
||
|
module = __import__(self.modulename)
|
||
|
except ImportError:
|
||
|
return
|
||
|
info = {}
|
||
|
macros = []
|
||
|
for v in ['__version__', 'version']:
|
||
|
vrs = getattr(module, v, None)
|
||
|
if vrs is None:
|
||
|
continue
|
||
|
macros = [(self.modulename.upper() + '_VERSION',
|
||
|
_c_string_literal(vrs)),
|
||
|
(self.modulename.upper(), None)]
|
||
|
break
|
||
|
dict_append(info, define_macros=macros)
|
||
|
include_dirs = self.get_include_dirs()
|
||
|
inc_dir = None
|
||
|
for d in include_dirs:
|
||
|
if self.combine_paths(d,
|
||
|
os.path.join(self.modulename,
|
||
|
'arrayobject.h')):
|
||
|
inc_dir = d
|
||
|
break
|
||
|
if inc_dir is not None:
|
||
|
dict_append(info, include_dirs=[inc_dir])
|
||
|
if info:
|
||
|
self.set_info(**info)
|
||
|
return
|
||
|
|
||
|
|
||
|
class numarray_info(_numpy_info):
|
||
|
section = 'numarray'
|
||
|
modulename = 'numarray'
|
||
|
|
||
|
|
||
|
class Numeric_info(_numpy_info):
|
||
|
section = 'Numeric'
|
||
|
modulename = 'Numeric'
|
||
|
|
||
|
|
||
|
class numpy_info(_numpy_info):
|
||
|
section = 'numpy'
|
||
|
modulename = 'numpy'
|
||
|
|
||
|
|
||
|
class numerix_info(system_info):
|
||
|
section = 'numerix'
|
||
|
|
||
|
def calc_info(self):
|
||
|
which = None, None
|
||
|
if os.getenv("NUMERIX"):
|
||
|
which = os.getenv("NUMERIX"), "environment var"
|
||
|
# If all the above fail, default to numpy.
|
||
|
if which[0] is None:
|
||
|
which = "numpy", "defaulted"
|
||
|
try:
|
||
|
import numpy # noqa: F401
|
||
|
which = "numpy", "defaulted"
|
||
|
except ImportError as e:
|
||
|
msg1 = str(e)
|
||
|
try:
|
||
|
import Numeric # noqa: F401
|
||
|
which = "numeric", "defaulted"
|
||
|
except ImportError as e:
|
||
|
msg2 = str(e)
|
||
|
try:
|
||
|
import numarray # noqa: F401
|
||
|
which = "numarray", "defaulted"
|
||
|
except ImportError as e:
|
||
|
msg3 = str(e)
|
||
|
log.info(msg1)
|
||
|
log.info(msg2)
|
||
|
log.info(msg3)
|
||
|
which = which[0].strip().lower(), which[1]
|
||
|
if which[0] not in ["numeric", "numarray", "numpy"]:
|
||
|
raise ValueError("numerix selector must be either 'Numeric' "
|
||
|
"or 'numarray' or 'numpy' but the value obtained"
|
||
|
" from the %s was '%s'." % (which[1], which[0]))
|
||
|
os.environ['NUMERIX'] = which[0]
|
||
|
self.set_info(**get_info(which[0]))
|
||
|
|
||
|
|
||
|
class f2py_info(system_info):
|
||
|
def calc_info(self):
|
||
|
try:
|
||
|
import numpy.f2py as f2py
|
||
|
except ImportError:
|
||
|
return
|
||
|
f2py_dir = os.path.join(os.path.dirname(f2py.__file__), 'src')
|
||
|
self.set_info(sources=[os.path.join(f2py_dir, 'fortranobject.c')],
|
||
|
include_dirs=[f2py_dir])
|
||
|
return
|
||
|
|
||
|
|
||
|
class boost_python_info(system_info):
|
||
|
section = 'boost_python'
|
||
|
dir_env_var = 'BOOST'
|
||
|
|
||
|
def get_paths(self, section, key):
|
||
|
pre_dirs = system_info.get_paths(self, section, key)
|
||
|
dirs = []
|
||
|
for d in pre_dirs:
|
||
|
dirs.extend([d] + self.combine_paths(d, ['boost*']))
|
||
|
return [d for d in dirs if os.path.isdir(d)]
|
||
|
|
||
|
def calc_info(self):
|
||
|
src_dirs = self.get_src_dirs()
|
||
|
src_dir = ''
|
||
|
for d in src_dirs:
|
||
|
if os.path.isfile(os.path.join(d, 'libs', 'python', 'src',
|
||
|
'module.cpp')):
|
||
|
src_dir = d
|
||
|
break
|
||
|
if not src_dir:
|
||
|
return
|
||
|
py_incl_dirs = [distutils.sysconfig.get_python_inc()]
|
||
|
py_pincl_dir = distutils.sysconfig.get_python_inc(plat_specific=True)
|
||
|
if py_pincl_dir not in py_incl_dirs:
|
||
|
py_incl_dirs.append(py_pincl_dir)
|
||
|
srcs_dir = os.path.join(src_dir, 'libs', 'python', 'src')
|
||
|
bpl_srcs = glob(os.path.join(srcs_dir, '*.cpp'))
|
||
|
bpl_srcs += glob(os.path.join(srcs_dir, '*', '*.cpp'))
|
||
|
info = {'libraries': [('boost_python_src',
|
||
|
{'include_dirs': [src_dir] + py_incl_dirs,
|
||
|
'sources':bpl_srcs}
|
||
|
)],
|
||
|
'include_dirs': [src_dir],
|
||
|
}
|
||
|
if info:
|
||
|
self.set_info(**info)
|
||
|
return
|
||
|
|
||
|
|
||
|
class agg2_info(system_info):
|
||
|
section = 'agg2'
|
||
|
dir_env_var = 'AGG2'
|
||
|
|
||
|
def get_paths(self, section, key):
|
||
|
pre_dirs = system_info.get_paths(self, section, key)
|
||
|
dirs = []
|
||
|
for d in pre_dirs:
|
||
|
dirs.extend([d] + self.combine_paths(d, ['agg2*']))
|
||
|
return [d for d in dirs if os.path.isdir(d)]
|
||
|
|
||
|
def calc_info(self):
|
||
|
src_dirs = self.get_src_dirs()
|
||
|
src_dir = ''
|
||
|
for d in src_dirs:
|
||
|
if os.path.isfile(os.path.join(d, 'src', 'agg_affine_matrix.cpp')):
|
||
|
src_dir = d
|
||
|
break
|
||
|
if not src_dir:
|
||
|
return
|
||
|
if sys.platform == 'win32':
|
||
|
agg2_srcs = glob(os.path.join(src_dir, 'src', 'platform',
|
||
|
'win32', 'agg_win32_bmp.cpp'))
|
||
|
else:
|
||
|
agg2_srcs = glob(os.path.join(src_dir, 'src', '*.cpp'))
|
||
|
agg2_srcs += [os.path.join(src_dir, 'src', 'platform',
|
||
|
'X11',
|
||
|
'agg_platform_support.cpp')]
|
||
|
|
||
|
info = {'libraries':
|
||
|
[('agg2_src',
|
||
|
{'sources': agg2_srcs,
|
||
|
'include_dirs': [os.path.join(src_dir, 'include')],
|
||
|
}
|
||
|
)],
|
||
|
'include_dirs': [os.path.join(src_dir, 'include')],
|
||
|
}
|
||
|
if info:
|
||
|
self.set_info(**info)
|
||
|
return
|
||
|
|
||
|
|
||
|
class _pkg_config_info(system_info):
|
||
|
section = None
|
||
|
config_env_var = 'PKG_CONFIG'
|
||
|
default_config_exe = 'pkg-config'
|
||
|
append_config_exe = ''
|
||
|
version_macro_name = None
|
||
|
release_macro_name = None
|
||
|
version_flag = '--modversion'
|
||
|
cflags_flag = '--cflags'
|
||
|
|
||
|
def get_config_exe(self):
|
||
|
if self.config_env_var in os.environ:
|
||
|
return os.environ[self.config_env_var]
|
||
|
return self.default_config_exe
|
||
|
|
||
|
def get_config_output(self, config_exe, option):
|
||
|
cmd = config_exe + ' ' + self.append_config_exe + ' ' + option
|
||
|
try:
|
||
|
o = subprocess.check_output(cmd)
|
||
|
except (OSError, subprocess.CalledProcessError):
|
||
|
pass
|
||
|
else:
|
||
|
o = filepath_from_subprocess_output(o)
|
||
|
return o
|
||
|
|
||
|
def calc_info(self):
|
||
|
config_exe = find_executable(self.get_config_exe())
|
||
|
if not config_exe:
|
||
|
log.warn('File not found: %s. Cannot determine %s info.' \
|
||
|
% (config_exe, self.section))
|
||
|
return
|
||
|
info = {}
|
||
|
macros = []
|
||
|
libraries = []
|
||
|
library_dirs = []
|
||
|
include_dirs = []
|
||
|
extra_link_args = []
|
||
|
extra_compile_args = []
|
||
|
version = self.get_config_output(config_exe, self.version_flag)
|
||
|
if version:
|
||
|
macros.append((self.__class__.__name__.split('.')[-1].upper(),
|
||
|
_c_string_literal(version)))
|
||
|
if self.version_macro_name:
|
||
|
macros.append((self.version_macro_name + '_%s'
|
||
|
% (version.replace('.', '_')), None))
|
||
|
if self.release_macro_name:
|
||
|
release = self.get_config_output(config_exe, '--release')
|
||
|
if release:
|
||
|
macros.append((self.release_macro_name + '_%s'
|
||
|
% (release.replace('.', '_')), None))
|
||
|
opts = self.get_config_output(config_exe, '--libs')
|
||
|
if opts:
|
||
|
for opt in opts.split():
|
||
|
if opt[:2] == '-l':
|
||
|
libraries.append(opt[2:])
|
||
|
elif opt[:2] == '-L':
|
||
|
library_dirs.append(opt[2:])
|
||
|
else:
|
||
|
extra_link_args.append(opt)
|
||
|
opts = self.get_config_output(config_exe, self.cflags_flag)
|
||
|
if opts:
|
||
|
for opt in opts.split():
|
||
|
if opt[:2] == '-I':
|
||
|
include_dirs.append(opt[2:])
|
||
|
elif opt[:2] == '-D':
|
||
|
if '=' in opt:
|
||
|
n, v = opt[2:].split('=')
|
||
|
macros.append((n, v))
|
||
|
else:
|
||
|
macros.append((opt[2:], None))
|
||
|
else:
|
||
|
extra_compile_args.append(opt)
|
||
|
if macros:
|
||
|
dict_append(info, define_macros=macros)
|
||
|
if libraries:
|
||
|
dict_append(info, libraries=libraries)
|
||
|
if library_dirs:
|
||
|
dict_append(info, library_dirs=library_dirs)
|
||
|
if include_dirs:
|
||
|
dict_append(info, include_dirs=include_dirs)
|
||
|
if extra_link_args:
|
||
|
dict_append(info, extra_link_args=extra_link_args)
|
||
|
if extra_compile_args:
|
||
|
dict_append(info, extra_compile_args=extra_compile_args)
|
||
|
if info:
|
||
|
self.set_info(**info)
|
||
|
return
|
||
|
|
||
|
|
||
|
class wx_info(_pkg_config_info):
|
||
|
section = 'wx'
|
||
|
config_env_var = 'WX_CONFIG'
|
||
|
default_config_exe = 'wx-config'
|
||
|
append_config_exe = ''
|
||
|
version_macro_name = 'WX_VERSION'
|
||
|
release_macro_name = 'WX_RELEASE'
|
||
|
version_flag = '--version'
|
||
|
cflags_flag = '--cxxflags'
|
||
|
|
||
|
|
||
|
class gdk_pixbuf_xlib_2_info(_pkg_config_info):
|
||
|
section = 'gdk_pixbuf_xlib_2'
|
||
|
append_config_exe = 'gdk-pixbuf-xlib-2.0'
|
||
|
version_macro_name = 'GDK_PIXBUF_XLIB_VERSION'
|
||
|
|
||
|
|
||
|
class gdk_pixbuf_2_info(_pkg_config_info):
|
||
|
section = 'gdk_pixbuf_2'
|
||
|
append_config_exe = 'gdk-pixbuf-2.0'
|
||
|
version_macro_name = 'GDK_PIXBUF_VERSION'
|
||
|
|
||
|
|
||
|
class gdk_x11_2_info(_pkg_config_info):
|
||
|
section = 'gdk_x11_2'
|
||
|
append_config_exe = 'gdk-x11-2.0'
|
||
|
version_macro_name = 'GDK_X11_VERSION'
|
||
|
|
||
|
|
||
|
class gdk_2_info(_pkg_config_info):
|
||
|
section = 'gdk_2'
|
||
|
append_config_exe = 'gdk-2.0'
|
||
|
version_macro_name = 'GDK_VERSION'
|
||
|
|
||
|
|
||
|
class gdk_info(_pkg_config_info):
|
||
|
section = 'gdk'
|
||
|
append_config_exe = 'gdk'
|
||
|
version_macro_name = 'GDK_VERSION'
|
||
|
|
||
|
|
||
|
class gtkp_x11_2_info(_pkg_config_info):
|
||
|
section = 'gtkp_x11_2'
|
||
|
append_config_exe = 'gtk+-x11-2.0'
|
||
|
version_macro_name = 'GTK_X11_VERSION'
|
||
|
|
||
|
|
||
|
class gtkp_2_info(_pkg_config_info):
|
||
|
section = 'gtkp_2'
|
||
|
append_config_exe = 'gtk+-2.0'
|
||
|
version_macro_name = 'GTK_VERSION'
|
||
|
|
||
|
|
||
|
class xft_info(_pkg_config_info):
|
||
|
section = 'xft'
|
||
|
append_config_exe = 'xft'
|
||
|
version_macro_name = 'XFT_VERSION'
|
||
|
|
||
|
|
||
|
class freetype2_info(_pkg_config_info):
|
||
|
section = 'freetype2'
|
||
|
append_config_exe = 'freetype2'
|
||
|
version_macro_name = 'FREETYPE2_VERSION'
|
||
|
|
||
|
|
||
|
class amd_info(system_info):
|
||
|
section = 'amd'
|
||
|
dir_env_var = 'AMD'
|
||
|
_lib_names = ['amd']
|
||
|
|
||
|
def calc_info(self):
|
||
|
lib_dirs = self.get_lib_dirs()
|
||
|
|
||
|
opt = self.get_option_single('amd_libs', 'libraries')
|
||
|
amd_libs = self.get_libs(opt, self._lib_names)
|
||
|
info = self.check_libs(lib_dirs, amd_libs, [])
|
||
|
if info is None:
|
||
|
return
|
||
|
|
||
|
include_dirs = self.get_include_dirs()
|
||
|
|
||
|
inc_dir = None
|
||
|
for d in include_dirs:
|
||
|
p = self.combine_paths(d, 'amd.h')
|
||
|
if p:
|
||
|
inc_dir = os.path.dirname(p[0])
|
||
|
break
|
||
|
if inc_dir is not None:
|
||
|
dict_append(info, include_dirs=[inc_dir],
|
||
|
define_macros=[('SCIPY_AMD_H', None)],
|
||
|
swig_opts=['-I' + inc_dir])
|
||
|
|
||
|
self.set_info(**info)
|
||
|
return
|
||
|
|
||
|
|
||
|
class umfpack_info(system_info):
|
||
|
section = 'umfpack'
|
||
|
dir_env_var = 'UMFPACK'
|
||
|
notfounderror = UmfpackNotFoundError
|
||
|
_lib_names = ['umfpack']
|
||
|
|
||
|
def calc_info(self):
|
||
|
lib_dirs = self.get_lib_dirs()
|
||
|
|
||
|
opt = self.get_option_single('umfpack_libs', 'libraries')
|
||
|
umfpack_libs = self.get_libs(opt, self._lib_names)
|
||
|
info = self.check_libs(lib_dirs, umfpack_libs, [])
|
||
|
if info is None:
|
||
|
return
|
||
|
|
||
|
include_dirs = self.get_include_dirs()
|
||
|
|
||
|
inc_dir = None
|
||
|
for d in include_dirs:
|
||
|
p = self.combine_paths(d, ['', 'umfpack'], 'umfpack.h')
|
||
|
if p:
|
||
|
inc_dir = os.path.dirname(p[0])
|
||
|
break
|
||
|
if inc_dir is not None:
|
||
|
dict_append(info, include_dirs=[inc_dir],
|
||
|
define_macros=[('SCIPY_UMFPACK_H', None)],
|
||
|
swig_opts=['-I' + inc_dir])
|
||
|
|
||
|
dict_append(info, **get_info('amd'))
|
||
|
|
||
|
self.set_info(**info)
|
||
|
return
|
||
|
|
||
|
|
||
|
def combine_paths(*args, **kws):
|
||
|
""" Return a list of existing paths composed by all combinations of
|
||
|
items from arguments.
|
||
|
"""
|
||
|
r = []
|
||
|
for a in args:
|
||
|
if not a:
|
||
|
continue
|
||
|
if is_string(a):
|
||
|
a = [a]
|
||
|
r.append(a)
|
||
|
args = r
|
||
|
if not args:
|
||
|
return []
|
||
|
if len(args) == 1:
|
||
|
result = reduce(lambda a, b: a + b, map(glob, args[0]), [])
|
||
|
elif len(args) == 2:
|
||
|
result = []
|
||
|
for a0 in args[0]:
|
||
|
for a1 in args[1]:
|
||
|
result.extend(glob(os.path.join(a0, a1)))
|
||
|
else:
|
||
|
result = combine_paths(*(combine_paths(args[0], args[1]) + args[2:]))
|
||
|
log.debug('(paths: %s)', ','.join(result))
|
||
|
return result
|
||
|
|
||
|
language_map = {'c': 0, 'c++': 1, 'f77': 2, 'f90': 3}
|
||
|
inv_language_map = {0: 'c', 1: 'c++', 2: 'f77', 3: 'f90'}
|
||
|
|
||
|
|
||
|
def dict_append(d, **kws):
|
||
|
languages = []
|
||
|
for k, v in kws.items():
|
||
|
if k == 'language':
|
||
|
languages.append(v)
|
||
|
continue
|
||
|
if k in d:
|
||
|
if k in ['library_dirs', 'include_dirs',
|
||
|
'extra_compile_args', 'extra_link_args',
|
||
|
'runtime_library_dirs', 'define_macros']:
|
||
|
[d[k].append(vv) for vv in v if vv not in d[k]]
|
||
|
else:
|
||
|
d[k].extend(v)
|
||
|
else:
|
||
|
d[k] = v
|
||
|
if languages:
|
||
|
l = inv_language_map[max([language_map.get(l, 0) for l in languages])]
|
||
|
d['language'] = l
|
||
|
return
|
||
|
|
||
|
|
||
|
def parseCmdLine(argv=(None,)):
|
||
|
import optparse
|
||
|
parser = optparse.OptionParser("usage: %prog [-v] [info objs]")
|
||
|
parser.add_option('-v', '--verbose', action='store_true', dest='verbose',
|
||
|
default=False,
|
||
|
help='be verbose and print more messages')
|
||
|
|
||
|
opts, args = parser.parse_args(args=argv[1:])
|
||
|
return opts, args
|
||
|
|
||
|
|
||
|
def show_all(argv=None):
|
||
|
import inspect
|
||
|
if argv is None:
|
||
|
argv = sys.argv
|
||
|
opts, args = parseCmdLine(argv)
|
||
|
if opts.verbose:
|
||
|
log.set_threshold(log.DEBUG)
|
||
|
else:
|
||
|
log.set_threshold(log.INFO)
|
||
|
show_only = []
|
||
|
for n in args:
|
||
|
if n[-5:] != '_info':
|
||
|
n = n + '_info'
|
||
|
show_only.append(n)
|
||
|
show_all = not show_only
|
||
|
_gdict_ = globals().copy()
|
||
|
for name, c in _gdict_.items():
|
||
|
if not inspect.isclass(c):
|
||
|
continue
|
||
|
if not issubclass(c, system_info) or c is system_info:
|
||
|
continue
|
||
|
if not show_all:
|
||
|
if name not in show_only:
|
||
|
continue
|
||
|
del show_only[show_only.index(name)]
|
||
|
conf = c()
|
||
|
conf.verbosity = 2
|
||
|
# FIXME: r not used
|
||
|
r = conf.get_info()
|
||
|
if show_only:
|
||
|
log.info('Info classes not defined: %s', ','.join(show_only))
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
show_all()
|