1#!/usr/bin/env python3
2"""
3This file defines a set of system_info classes for getting
4information about various resources (libraries, library directories,
5include directories, etc.) in the system. Usage:
6    info_dict = get_info(<name>)
7  where <name> is a string 'atlas','x11','fftw','lapack','blas',
8  'lapack_src', 'blas_src', etc. For a complete list of allowed names,
9  see the definition of get_info() function below.
10
11  Returned info_dict is a dictionary which is compatible with
12  distutils.setup keyword arguments. If info_dict == {}, then the
13  asked resource is not available (system_info could not find it).
14
15  Several *_info classes specify an environment variable to specify
16  the locations of software. When setting the corresponding environment
17  variable to 'None' then the software will be ignored, even when it
18  is available in system.
19
20Global parameters:
21  system_info.search_static_first - search static libraries (.a)
22             in precedence to shared ones (.so, .sl) if enabled.
23  system_info.verbosity - output the results to stdout if enabled.
24
25The file 'site.cfg' is looked for in
26
271) Directory of main setup.py file being run.
282) Home directory of user running the setup.py file as ~/.numpy-site.cfg
293) System wide directory (location of this file...)
30
31The first one found is used to get system configuration options The
32format is that used by ConfigParser (i.e., Windows .INI style). The
33section ALL is not intended for general use.
34
35Appropriate defaults are used if nothing is specified.
36
37The order of finding the locations of resources is the following:
38 1. environment variable
39 2. section in site.cfg
40 3. DEFAULT section in site.cfg
41 4. System default search paths (see ``default_*`` variables below).
42Only the first complete match is returned.
43
44Currently, the following classes are available, along with their section names:
45
46    Numeric_info:Numeric
47    _numpy_info:Numeric
48    _pkg_config_info:None
49    accelerate_info:accelerate
50    agg2_info:agg2
51    amd_info:amd
52    atlas_3_10_blas_info:atlas
53    atlas_3_10_blas_threads_info:atlas
54    atlas_3_10_info:atlas
55    atlas_3_10_threads_info:atlas
56    atlas_blas_info:atlas
57    atlas_blas_threads_info:atlas
58    atlas_info:atlas
59    atlas_threads_info:atlas
60    blas64__opt_info:ALL               # usage recommended (general ILP64 BLAS, 64_ symbol suffix)
61    blas_ilp64_opt_info:ALL            # usage recommended (general ILP64 BLAS)
62    blas_ilp64_plain_opt_info:ALL      # usage recommended (general ILP64 BLAS, no symbol suffix)
63    blas_info:blas
64    blas_mkl_info:mkl
65    blas_opt_info:ALL                  # usage recommended
66    blas_src_info:blas_src
67    blis_info:blis
68    boost_python_info:boost_python
69    dfftw_info:fftw
70    dfftw_threads_info:fftw
71    djbfft_info:djbfft
72    f2py_info:ALL
73    fft_opt_info:ALL
74    fftw2_info:fftw
75    fftw3_info:fftw3
76    fftw_info:fftw
77    fftw_threads_info:fftw
78    flame_info:flame
79    freetype2_info:freetype2
80    gdk_2_info:gdk_2
81    gdk_info:gdk
82    gdk_pixbuf_2_info:gdk_pixbuf_2
83    gdk_pixbuf_xlib_2_info:gdk_pixbuf_xlib_2
84    gdk_x11_2_info:gdk_x11_2
85    gtkp_2_info:gtkp_2
86    gtkp_x11_2_info:gtkp_x11_2
87    lapack64__opt_info:ALL             # usage recommended (general ILP64 LAPACK, 64_ symbol suffix)
88    lapack_atlas_3_10_info:atlas
89    lapack_atlas_3_10_threads_info:atlas
90    lapack_atlas_info:atlas
91    lapack_atlas_threads_info:atlas
92    lapack_ilp64_opt_info:ALL          # usage recommended (general ILP64 LAPACK)
93    lapack_ilp64_plain_opt_info:ALL    # usage recommended (general ILP64 LAPACK, no symbol suffix)
94    lapack_info:lapack
95    lapack_mkl_info:mkl
96    lapack_opt_info:ALL                # usage recommended
97    lapack_src_info:lapack_src
98    mkl_info:mkl
99    numarray_info:numarray
100    numerix_info:numerix
101    numpy_info:numpy
102    openblas64__info:openblas64_
103    openblas64__lapack_info:openblas64_
104    openblas_clapack_info:openblas
105    openblas_ilp64_info:openblas_ilp64
106    openblas_ilp64_lapack_info:openblas_ilp64
107    openblas_info:openblas
108    openblas_lapack_info:openblas
109    sfftw_info:fftw
110    sfftw_threads_info:fftw
111    system_info:ALL
112    umfpack_info:umfpack
113    wx_info:wx
114    x11_info:x11
115    xft_info:xft
116
117Example:
118----------
119[DEFAULT]
120# default section
121library_dirs = /usr/lib:/usr/local/lib:/opt/lib
122include_dirs = /usr/include:/usr/local/include:/opt/include
123src_dirs = /usr/local/src:/opt/src
124# search static libraries (.a) in preference to shared ones (.so)
125search_static_first = 0
126
127[fftw]
128libraries = rfftw, fftw
129
130[atlas]
131library_dirs = /usr/lib/3dnow:/usr/lib/3dnow/atlas
132# for overriding the names of the atlas libraries
133libraries = lapack, f77blas, cblas, atlas
134
135[x11]
136library_dirs = /usr/X11R6/lib
137include_dirs = /usr/X11R6/include
138----------
139
140Note that the ``libraries`` key is the default setting for libraries.
141
142Authors:
143  Pearu Peterson <pearu@cens.ioc.ee>, February 2002
144  David M. Cooke <cookedm@physics.mcmaster.ca>, April 2002
145
146Copyright 2002 Pearu Peterson all rights reserved,
147Pearu Peterson <pearu@cens.ioc.ee>
148Permission to use, modify, and distribute this software is given under the
149terms of the NumPy (BSD style) license.  See LICENSE.txt that came with
150this distribution for specifics.
151
152NO WARRANTY IS EXPRESSED OR IMPLIED.  USE AT YOUR OWN RISK.
153
154"""
155import sys
156import os
157import re
158import copy
159import warnings
160import subprocess
161import textwrap
162
163from glob import glob
164from functools import reduce
165from configparser import NoOptionError
166from configparser import RawConfigParser as ConfigParser
167# It seems that some people are importing ConfigParser from here so is
168# good to keep its class name. Use of RawConfigParser is needed in
169# order to be able to load path names with percent in them, like
170# `feature%2Fcool` which is common on git flow branch names.
171
172from distutils.errors import DistutilsError
173from distutils.dist import Distribution
174import sysconfig
175from numpy.distutils import log
176from distutils.util import get_platform
177
178from numpy.distutils.exec_command import (
179    find_executable, filepath_from_subprocess_output,
180    )
181from numpy.distutils.misc_util import (is_sequence, is_string,
182                                       get_shared_lib_extension)
183from numpy.distutils.command.config import config as cmd_config
184from numpy.distutils import customized_ccompiler as _customized_ccompiler
185from numpy.distutils import _shell_utils
186import distutils.ccompiler
187import tempfile
188import shutil
189
190__all__ = ['system_info']
191
192# Determine number of bits
193import platform
194_bits = {'32bit': 32, '64bit': 64}
195platform_bits = _bits[platform.architecture()[0]]
196
197
198global_compiler = None
199
200def customized_ccompiler():
201    global global_compiler
202    if not global_compiler:
203        global_compiler = _customized_ccompiler()
204    return global_compiler
205
206
207def _c_string_literal(s):
208    """
209    Convert a python string into a literal suitable for inclusion into C code
210    """
211    # only these three characters are forbidden in C strings
212    if s is None:
213        return '"None"'
214    s = s.replace('\\', r'\\')
215    s = s.replace('"',  r'\"')
216    s = s.replace('\n', r'\n')
217    return '"{}"'.format(s)
218
219
220def libpaths(paths, bits):
221    """Return a list of library paths valid on 32 or 64 bit systems.
222
223    Inputs:
224      paths : sequence
225        A sequence of strings (typically paths)
226      bits : int
227        An integer, the only valid values are 32 or 64.  A ValueError exception
228      is raised otherwise.
229
230    Examples:
231
232    Consider a list of directories
233    >>> paths = ['/usr/X11R6/lib','/usr/X11/lib','/usr/lib']
234
235    For a 32-bit platform, this is already valid:
236    >>> np.distutils.system_info.libpaths(paths,32)
237    ['/usr/X11R6/lib', '/usr/X11/lib', '/usr/lib']
238
239    On 64 bits, we prepend the '64' postfix
240    >>> np.distutils.system_info.libpaths(paths,64)
241    ['/usr/X11R6/lib64', '/usr/X11R6/lib', '/usr/X11/lib64', '/usr/X11/lib',
242    '/usr/lib64', '/usr/lib']
243    """
244    if bits not in (32, 64):
245        raise ValueError("Invalid bit size in libpaths: 32 or 64 only")
246
247    # Handle 32bit case
248    if bits == 32:
249        return paths
250
251    # Handle 64bit case
252    out = []
253    for p in paths:
254        out.extend([p + '64', p])
255
256    return out
257
258
259if sys.platform == 'win32':
260    default_lib_dirs = ['C:\\',
261                        os.path.join(sysconfig.get_config_var('exec_prefix'),
262                                     'libs')]
263    default_runtime_dirs = []
264    default_include_dirs = []
265    default_src_dirs = ['.']
266    default_x11_lib_dirs = []
267    default_x11_include_dirs = []
268    _include_dirs = [
269        'include',
270        'include/suitesparse',
271    ]
272    _lib_dirs = [
273        'lib',
274    ]
275
276    _include_dirs = [d.replace('/', os.sep) for d in _include_dirs]
277    _lib_dirs = [d.replace('/', os.sep) for d in _lib_dirs]
278    def add_system_root(library_root):
279        """Add a package manager root to the include directories"""
280        global default_lib_dirs
281        global default_include_dirs
282
283        library_root = os.path.normpath(library_root)
284
285        default_lib_dirs.extend(
286            os.path.join(library_root, d) for d in _lib_dirs)
287        default_include_dirs.extend(
288            os.path.join(library_root, d) for d in _include_dirs)
289
290    # VCpkg is the de-facto package manager on windows for C/C++
291    # libraries. If it is on the PATH, then we append its paths here.
292    vcpkg = shutil.which('vcpkg')
293    if vcpkg:
294        vcpkg_dir = os.path.dirname(vcpkg)
295        if platform.architecture()[0] == '32bit':
296            specifier = 'x86'
297        else:
298            specifier = 'x64'
299
300        vcpkg_installed = os.path.join(vcpkg_dir, 'installed')
301        for vcpkg_root in [
302            os.path.join(vcpkg_installed, specifier + '-windows'),
303            os.path.join(vcpkg_installed, specifier + '-windows-static'),
304        ]:
305            add_system_root(vcpkg_root)
306
307    # Conda is another popular package manager that provides libraries
308    conda = shutil.which('conda')
309    if conda:
310        conda_dir = os.path.dirname(conda)
311        add_system_root(os.path.join(conda_dir, '..', 'Library'))
312        add_system_root(os.path.join(conda_dir, 'Library'))
313
314else:
315    default_lib_dirs = libpaths(['/usr/local/lib', '/opt/lib', '/usr/lib',
316                                 '/opt/local/lib', '/sw/lib'], platform_bits)
317    default_runtime_dirs = []
318    default_include_dirs = ['/usr/local/include',
319                            '/opt/include', '/usr/include',
320                            # path of umfpack under macports
321                            '/opt/local/include/ufsparse',
322                            '/opt/local/include', '/sw/include',
323                            '/usr/include/suitesparse']
324    default_src_dirs = ['.', '/usr/local/src', '/opt/src', '/sw/src']
325
326    default_x11_lib_dirs = libpaths(['/usr/X11R6/lib', '/usr/X11/lib',
327                                     '/usr/lib'], platform_bits)
328    default_x11_include_dirs = ['/usr/X11R6/include', '/usr/X11/include',
329                                '/usr/include']
330
331    if os.path.exists('/usr/lib/X11'):
332        globbed_x11_dir = glob('/usr/lib/*/libX11.so')
333        if globbed_x11_dir:
334            x11_so_dir = os.path.split(globbed_x11_dir[0])[0]
335            default_x11_lib_dirs.extend([x11_so_dir, '/usr/lib/X11'])
336            default_x11_include_dirs.extend(['/usr/lib/X11/include',
337                                             '/usr/include/X11'])
338
339    with open(os.devnull, 'w') as tmp:
340        try:
341            p = subprocess.Popen(["gcc", "-print-multiarch"], stdout=subprocess.PIPE,
342                         stderr=tmp)
343        except (OSError, DistutilsError):
344            # OSError if gcc is not installed, or SandboxViolation (DistutilsError
345            # subclass) if an old setuptools bug is triggered (see gh-3160).
346            pass
347        else:
348            triplet = str(p.communicate()[0].decode().strip())
349            if p.returncode == 0:
350                # gcc supports the "-print-multiarch" option
351                default_x11_lib_dirs += [os.path.join("/usr/lib/", triplet)]
352                default_lib_dirs += [os.path.join("/usr/lib/", triplet)]
353
354
355if os.path.join(sys.prefix, 'lib') not in default_lib_dirs:
356    default_lib_dirs.insert(0, os.path.join(sys.prefix, 'lib'))
357    default_include_dirs.append(os.path.join(sys.prefix, 'include'))
358    default_src_dirs.append(os.path.join(sys.prefix, 'src'))
359
360default_lib_dirs = [_m for _m in default_lib_dirs if os.path.isdir(_m)]
361default_runtime_dirs = [_m for _m in default_runtime_dirs if os.path.isdir(_m)]
362default_include_dirs = [_m for _m in default_include_dirs if os.path.isdir(_m)]
363default_src_dirs = [_m for _m in default_src_dirs if os.path.isdir(_m)]
364
365so_ext = get_shared_lib_extension()
366
367
368def is_symlink_to_accelerate(filename):
369    accelpath = '/System/Library/Frameworks/Accelerate.framework'
370    return (sys.platform == 'darwin' and os.path.islink(filename) and
371            os.path.realpath(filename).startswith(accelpath))
372
373
374_accel_msg = (
375    'Found {filename}, but that file is a symbolic link to the '
376    'MacOS Accelerate framework, which is not supported by NumPy. '
377    'You must configure the build to use a different optimized library, '
378    'or disable the use of optimized BLAS and LAPACK by setting the '
379    'environment variables NPY_BLAS_ORDER="" and NPY_LAPACK_ORDER="" '
380    'before building NumPy.'
381)
382
383
384def get_standard_file(fname):
385    """Returns a list of files named 'fname' from
386    1) System-wide directory (directory-location of this module)
387    2) Users HOME directory (os.environ['HOME'])
388    3) Local directory
389    """
390    # System-wide file
391    filenames = []
392    try:
393        f = __file__
394    except NameError:
395        f = sys.argv[0]
396    else:
397        sysfile = os.path.join(os.path.split(os.path.abspath(f))[0],
398                               fname)
399        if os.path.isfile(sysfile):
400            filenames.append(sysfile)
401
402    # Home directory
403    # And look for the user config file
404    try:
405        f = os.path.expanduser('~')
406    except KeyError:
407        pass
408    else:
409        user_file = os.path.join(f, fname)
410        if os.path.isfile(user_file):
411            filenames.append(user_file)
412
413    # Local file
414    if os.path.isfile(fname):
415        filenames.append(os.path.abspath(fname))
416
417    return filenames
418
419
420def _parse_env_order(base_order, env):
421    """ Parse an environment variable `env` by splitting with "," and only returning elements from `base_order`
422
423    This method will sequence the environment variable and check for their invidual elements in `base_order`.
424
425    The items in the environment variable may be negated via '^item' or '!itema,itemb'.
426    It must start with ^/! to negate all options.
427
428    Raises
429    ------
430    ValueError: for mixed negated and non-negated orders or multiple negated orders
431
432    Parameters
433    ----------
434    base_order : list of str
435       the base list of orders
436    env : str
437       the environment variable to be parsed, if none is found, `base_order` is returned
438
439    Returns
440    -------
441    allow_order : list of str
442        allowed orders in lower-case
443    unknown_order : list of str
444        for values not overlapping with `base_order`
445    """
446    order_str = os.environ.get(env, None)
447
448    # ensure all base-orders are lower-case (for easier comparison)
449    base_order = [order.lower() for order in base_order]
450    if order_str is None:
451        return base_order, []
452
453    neg = order_str.startswith('^') or order_str.startswith('!')
454    # Check format
455    order_str_l = list(order_str)
456    sum_neg = order_str_l.count('^') + order_str_l.count('!')
457    if neg:
458        if sum_neg > 1:
459            raise ValueError(f"Environment variable '{env}' may only contain a single (prefixed) negation: {order_str}")
460        # remove prefix
461        order_str = order_str[1:]
462    elif sum_neg > 0:
463        raise ValueError(f"Environment variable '{env}' may not mix negated an non-negated items: {order_str}")
464
465    # Split and lower case
466    orders = order_str.lower().split(',')
467
468    # to inform callee about non-overlapping elements
469    unknown_order = []
470
471    # if negated, we have to remove from the order
472    if neg:
473        allow_order = base_order.copy()
474
475        for order in orders:
476            if not order:
477                continue
478
479            if order not in base_order:
480                unknown_order.append(order)
481                continue
482
483            if order in allow_order:
484                allow_order.remove(order)
485
486    else:
487        allow_order = []
488
489        for order in orders:
490            if not order:
491                continue
492
493            if order not in base_order:
494                unknown_order.append(order)
495                continue
496
497            if order not in allow_order:
498                allow_order.append(order)
499
500    return allow_order, unknown_order
501
502
503def get_info(name, notfound_action=0):
504    """
505    notfound_action:
506      0 - do nothing
507      1 - display warning message
508      2 - raise error
509    """
510    cl = {'atlas': atlas_info,  # use lapack_opt or blas_opt instead
511          'atlas_threads': atlas_threads_info,                # ditto
512          'atlas_blas': atlas_blas_info,
513          'atlas_blas_threads': atlas_blas_threads_info,
514          'lapack_atlas': lapack_atlas_info,  # use lapack_opt instead
515          'lapack_atlas_threads': lapack_atlas_threads_info,  # ditto
516          'atlas_3_10': atlas_3_10_info,  # use lapack_opt or blas_opt instead
517          'atlas_3_10_threads': atlas_3_10_threads_info,                # ditto
518          'atlas_3_10_blas': atlas_3_10_blas_info,
519          'atlas_3_10_blas_threads': atlas_3_10_blas_threads_info,
520          'lapack_atlas_3_10': lapack_atlas_3_10_info,  # use lapack_opt instead
521          'lapack_atlas_3_10_threads': lapack_atlas_3_10_threads_info,  # ditto
522          'flame': flame_info,          # use lapack_opt instead
523          'mkl': mkl_info,
524          # openblas which may or may not have embedded lapack
525          'openblas': openblas_info,          # use blas_opt instead
526          # openblas with embedded lapack
527          'openblas_lapack': openblas_lapack_info, # use blas_opt instead
528          'openblas_clapack': openblas_clapack_info, # use blas_opt instead
529          'blis': blis_info,                  # use blas_opt instead
530          'lapack_mkl': lapack_mkl_info,      # use lapack_opt instead
531          'blas_mkl': blas_mkl_info,          # use blas_opt instead
532          'openblas64_': openblas64__info,
533          'openblas64__lapack': openblas64__lapack_info,
534          'openblas_ilp64': openblas_ilp64_info,
535          'openblas_ilp64_lapack': openblas_ilp64_lapack_info,
536          'x11': x11_info,
537          'fft_opt': fft_opt_info,
538          'fftw': fftw_info,
539          'fftw2': fftw2_info,
540          'fftw3': fftw3_info,
541          'dfftw': dfftw_info,
542          'sfftw': sfftw_info,
543          'fftw_threads': fftw_threads_info,
544          'dfftw_threads': dfftw_threads_info,
545          'sfftw_threads': sfftw_threads_info,
546          'djbfft': djbfft_info,
547          'blas': blas_info,                  # use blas_opt instead
548          'lapack': lapack_info,              # use lapack_opt instead
549          'lapack_src': lapack_src_info,
550          'blas_src': blas_src_info,
551          'numpy': numpy_info,
552          'f2py': f2py_info,
553          'Numeric': Numeric_info,
554          'numeric': Numeric_info,
555          'numarray': numarray_info,
556          'numerix': numerix_info,
557          'lapack_opt': lapack_opt_info,
558          'lapack_ilp64_opt': lapack_ilp64_opt_info,
559          'lapack_ilp64_plain_opt': lapack_ilp64_plain_opt_info,
560          'lapack64__opt': lapack64__opt_info,
561          'blas_opt': blas_opt_info,
562          'blas_ilp64_opt': blas_ilp64_opt_info,
563          'blas_ilp64_plain_opt': blas_ilp64_plain_opt_info,
564          'blas64__opt': blas64__opt_info,
565          'boost_python': boost_python_info,
566          'agg2': agg2_info,
567          'wx': wx_info,
568          'gdk_pixbuf_xlib_2': gdk_pixbuf_xlib_2_info,
569          'gdk-pixbuf-xlib-2.0': gdk_pixbuf_xlib_2_info,
570          'gdk_pixbuf_2': gdk_pixbuf_2_info,
571          'gdk-pixbuf-2.0': gdk_pixbuf_2_info,
572          'gdk': gdk_info,
573          'gdk_2': gdk_2_info,
574          'gdk-2.0': gdk_2_info,
575          'gdk_x11_2': gdk_x11_2_info,
576          'gdk-x11-2.0': gdk_x11_2_info,
577          'gtkp_x11_2': gtkp_x11_2_info,
578          'gtk+-x11-2.0': gtkp_x11_2_info,
579          'gtkp_2': gtkp_2_info,
580          'gtk+-2.0': gtkp_2_info,
581          'xft': xft_info,
582          'freetype2': freetype2_info,
583          'umfpack': umfpack_info,
584          'amd': amd_info,
585          }.get(name.lower(), system_info)
586    return cl().get_info(notfound_action)
587
588
589class NotFoundError(DistutilsError):
590    """Some third-party program or library is not found."""
591
592
593class AliasedOptionError(DistutilsError):
594    """
595    Aliases entries in config files should not be existing.
596    In section '{section}' we found multiple appearances of options {options}."""
597
598
599class AtlasNotFoundError(NotFoundError):
600    """
601    Atlas (http://github.com/math-atlas/math-atlas) libraries not found.
602    Directories to search for the libraries can be specified in the
603    numpy/distutils/site.cfg file (section [atlas]) or by setting
604    the ATLAS environment variable."""
605
606
607class FlameNotFoundError(NotFoundError):
608    """
609    FLAME (http://www.cs.utexas.edu/~flame/web/) libraries not found.
610    Directories to search for the libraries can be specified in the
611    numpy/distutils/site.cfg file (section [flame])."""
612
613
614class LapackNotFoundError(NotFoundError):
615    """
616    Lapack (http://www.netlib.org/lapack/) libraries not found.
617    Directories to search for the libraries can be specified in the
618    numpy/distutils/site.cfg file (section [lapack]) or by setting
619    the LAPACK environment variable."""
620
621
622class LapackSrcNotFoundError(LapackNotFoundError):
623    """
624    Lapack (http://www.netlib.org/lapack/) sources not found.
625    Directories to search for the sources can be specified in the
626    numpy/distutils/site.cfg file (section [lapack_src]) or by setting
627    the LAPACK_SRC environment variable."""
628
629
630class LapackILP64NotFoundError(NotFoundError):
631    """
632    64-bit Lapack libraries not found.
633    Known libraries in numpy/distutils/site.cfg file are:
634    openblas64_, openblas_ilp64
635    """
636
637class BlasOptNotFoundError(NotFoundError):
638    """
639    Optimized (vendor) Blas libraries are not found.
640    Falls back to netlib Blas library which has worse performance.
641    A better performance should be easily gained by switching
642    Blas library."""
643
644class BlasNotFoundError(NotFoundError):
645    """
646    Blas (http://www.netlib.org/blas/) libraries not found.
647    Directories to search for the libraries can be specified in the
648    numpy/distutils/site.cfg file (section [blas]) or by setting
649    the BLAS environment variable."""
650
651class BlasILP64NotFoundError(NotFoundError):
652    """
653    64-bit Blas libraries not found.
654    Known libraries in numpy/distutils/site.cfg file are:
655    openblas64_, openblas_ilp64
656    """
657
658class BlasSrcNotFoundError(BlasNotFoundError):
659    """
660    Blas (http://www.netlib.org/blas/) sources not found.
661    Directories to search for the sources can be specified in the
662    numpy/distutils/site.cfg file (section [blas_src]) or by setting
663    the BLAS_SRC environment variable."""
664
665
666class FFTWNotFoundError(NotFoundError):
667    """
668    FFTW (http://www.fftw.org/) libraries not found.
669    Directories to search for the libraries can be specified in the
670    numpy/distutils/site.cfg file (section [fftw]) or by setting
671    the FFTW environment variable."""
672
673
674class DJBFFTNotFoundError(NotFoundError):
675    """
676    DJBFFT (https://cr.yp.to/djbfft.html) libraries not found.
677    Directories to search for the libraries can be specified in the
678    numpy/distutils/site.cfg file (section [djbfft]) or by setting
679    the DJBFFT environment variable."""
680
681
682class NumericNotFoundError(NotFoundError):
683    """
684    Numeric (https://www.numpy.org/) module not found.
685    Get it from above location, install it, and retry setup.py."""
686
687
688class X11NotFoundError(NotFoundError):
689    """X11 libraries not found."""
690
691
692class UmfpackNotFoundError(NotFoundError):
693    """
694    UMFPACK sparse solver (https://www.cise.ufl.edu/research/sparse/umfpack/)
695    not found. Directories to search for the libraries can be specified in the
696    numpy/distutils/site.cfg file (section [umfpack]) or by setting
697    the UMFPACK environment variable."""
698
699
700class system_info:
701
702    """ get_info() is the only public method. Don't use others.
703    """
704    dir_env_var = None
705    # XXX: search_static_first is disabled by default, may disappear in
706    # future unless it is proved to be useful.
707    search_static_first = 0
708    # The base-class section name is a random word "ALL" and is not really
709    # intended for general use. It cannot be None nor can it be DEFAULT as
710    # these break the ConfigParser. See gh-15338
711    section = 'ALL'
712    saved_results = {}
713
714    notfounderror = NotFoundError
715
716    def __init__(self,
717                  default_lib_dirs=default_lib_dirs,
718                  default_include_dirs=default_include_dirs,
719                  ):
720        self.__class__.info = {}
721        self.local_prefixes = []
722        defaults = {'library_dirs': os.pathsep.join(default_lib_dirs),
723                    'include_dirs': os.pathsep.join(default_include_dirs),
724                    'runtime_library_dirs': os.pathsep.join(default_runtime_dirs),
725                    'rpath': '',
726                    'src_dirs': os.pathsep.join(default_src_dirs),
727                    'search_static_first': str(self.search_static_first),
728                    'extra_compile_args': '', 'extra_link_args': ''}
729        self.cp = ConfigParser(defaults)
730        self.files = []
731        self.files.extend(get_standard_file('.numpy-site.cfg'))
732        self.files.extend(get_standard_file('site.cfg'))
733        self.parse_config_files()
734
735        if self.section is not None:
736            self.search_static_first = self.cp.getboolean(
737                self.section, 'search_static_first')
738        assert isinstance(self.search_static_first, int)
739
740    def parse_config_files(self):
741        self.cp.read(self.files)
742        if not self.cp.has_section(self.section):
743            if self.section is not None:
744                self.cp.add_section(self.section)
745
746    def calc_libraries_info(self):
747        libs = self.get_libraries()
748        dirs = self.get_lib_dirs()
749        # The extensions use runtime_library_dirs
750        r_dirs = self.get_runtime_lib_dirs()
751        # Intrinsic distutils use rpath, we simply append both entries
752        # as though they were one entry
753        r_dirs.extend(self.get_runtime_lib_dirs(key='rpath'))
754        info = {}
755        for lib in libs:
756            i = self.check_libs(dirs, [lib])
757            if i is not None:
758                dict_append(info, **i)
759            else:
760                log.info('Library %s was not found. Ignoring' % (lib))
761
762            if r_dirs:
763                i = self.check_libs(r_dirs, [lib])
764                if i is not None:
765                    # Swap library keywords found to runtime_library_dirs
766                    # the libraries are insisting on the user having defined
767                    # them using the library_dirs, and not necessarily by
768                    # runtime_library_dirs
769                    del i['libraries']
770                    i['runtime_library_dirs'] = i.pop('library_dirs')
771                    dict_append(info, **i)
772                else:
773                    log.info('Runtime library %s was not found. Ignoring' % (lib))
774
775        return info
776
777    def set_info(self, **info):
778        if info:
779            lib_info = self.calc_libraries_info()
780            dict_append(info, **lib_info)
781            # Update extra information
782            extra_info = self.calc_extra_info()
783            dict_append(info, **extra_info)
784        self.saved_results[self.__class__.__name__] = info
785
786    def get_option_single(self, *options):
787        """ Ensure that only one of `options` are found in the section
788
789        Parameters
790        ----------
791        *options : list of str
792           a list of options to be found in the section (``self.section``)
793
794        Returns
795        -------
796        str :
797            the option that is uniquely found in the section
798
799        Raises
800        ------
801        AliasedOptionError :
802            in case more than one of the options are found
803        """
804        found = [self.cp.has_option(self.section, opt) for opt in options]
805        if sum(found) == 1:
806            return options[found.index(True)]
807        elif sum(found) == 0:
808            # nothing is found anyways
809            return options[0]
810
811        # Else we have more than 1 key found
812        if AliasedOptionError.__doc__ is None:
813            raise AliasedOptionError()
814        raise AliasedOptionError(AliasedOptionError.__doc__.format(
815            section=self.section, options='[{}]'.format(', '.join(options))))
816
817
818    def has_info(self):
819        return self.__class__.__name__ in self.saved_results
820
821    def calc_extra_info(self):
822        """ Updates the information in the current information with
823        respect to these flags:
824          extra_compile_args
825          extra_link_args
826        """
827        info = {}
828        for key in ['extra_compile_args', 'extra_link_args']:
829            # Get values
830            opt = self.cp.get(self.section, key)
831            opt = _shell_utils.NativeParser.split(opt)
832            if opt:
833                tmp = {key: opt}
834                dict_append(info, **tmp)
835        return info
836
837    def get_info(self, notfound_action=0):
838        """ Return a dictionary with items that are compatible
839            with numpy.distutils.setup keyword arguments.
840        """
841        flag = 0
842        if not self.has_info():
843            flag = 1
844            log.info(self.__class__.__name__ + ':')
845            if hasattr(self, 'calc_info'):
846                self.calc_info()
847            if notfound_action:
848                if not self.has_info():
849                    if notfound_action == 1:
850                        warnings.warn(self.notfounderror.__doc__, stacklevel=2)
851                    elif notfound_action == 2:
852                        raise self.notfounderror(self.notfounderror.__doc__)
853                    else:
854                        raise ValueError(repr(notfound_action))
855
856            if not self.has_info():
857                log.info('  NOT AVAILABLE')
858                self.set_info()
859            else:
860                log.info('  FOUND:')
861
862        res = self.saved_results.get(self.__class__.__name__)
863        if log.get_threshold() <= log.INFO and flag:
864            for k, v in res.items():
865                v = str(v)
866                if k in ['sources', 'libraries'] and len(v) > 270:
867                    v = v[:120] + '...\n...\n...' + v[-120:]
868                log.info('    %s = %s', k, v)
869            log.info('')
870
871        return copy.deepcopy(res)
872
873    def get_paths(self, section, key):
874        dirs = self.cp.get(section, key).split(os.pathsep)
875        env_var = self.dir_env_var
876        if env_var:
877            if is_sequence(env_var):
878                e0 = env_var[-1]
879                for e in env_var:
880                    if e in os.environ:
881                        e0 = e
882                        break
883                if not env_var[0] == e0:
884                    log.info('Setting %s=%s' % (env_var[0], e0))
885                env_var = e0
886        if env_var and env_var in os.environ:
887            d = os.environ[env_var]
888            if d == 'None':
889                log.info('Disabled %s: %s',
890                         self.__class__.__name__, '(%s is None)'
891                         % (env_var,))
892                return []
893            if os.path.isfile(d):
894                dirs = [os.path.dirname(d)] + dirs
895                l = getattr(self, '_lib_names', [])
896                if len(l) == 1:
897                    b = os.path.basename(d)
898                    b = os.path.splitext(b)[0]
899                    if b[:3] == 'lib':
900                        log.info('Replacing _lib_names[0]==%r with %r' \
901                              % (self._lib_names[0], b[3:]))
902                        self._lib_names[0] = b[3:]
903            else:
904                ds = d.split(os.pathsep)
905                ds2 = []
906                for d in ds:
907                    if os.path.isdir(d):
908                        ds2.append(d)
909                        for dd in ['include', 'lib']:
910                            d1 = os.path.join(d, dd)
911                            if os.path.isdir(d1):
912                                ds2.append(d1)
913                dirs = ds2 + dirs
914        default_dirs = self.cp.get(self.section, key).split(os.pathsep)
915        dirs.extend(default_dirs)
916        ret = []
917        for d in dirs:
918            if len(d) > 0 and not os.path.isdir(d):
919                warnings.warn('Specified path %s is invalid.' % d, stacklevel=2)
920                continue
921
922            if d not in ret:
923                ret.append(d)
924
925        log.debug('( %s = %s )', key, ':'.join(ret))
926        return ret
927
928    def get_lib_dirs(self, key='library_dirs'):
929        return self.get_paths(self.section, key)
930
931    def get_runtime_lib_dirs(self, key='runtime_library_dirs'):
932        path = self.get_paths(self.section, key)
933        if path == ['']:
934            path = []
935        return path
936
937    def get_include_dirs(self, key='include_dirs'):
938        return self.get_paths(self.section, key)
939
940    def get_src_dirs(self, key='src_dirs'):
941        return self.get_paths(self.section, key)
942
943    def get_libs(self, key, default):
944        try:
945            libs = self.cp.get(self.section, key)
946        except NoOptionError:
947            if not default:
948                return []
949            if is_string(default):
950                return [default]
951            return default
952        return [b for b in [a.strip() for a in libs.split(',')] if b]
953
954    def get_libraries(self, key='libraries'):
955        if hasattr(self, '_lib_names'):
956            return self.get_libs(key, default=self._lib_names)
957        else:
958            return self.get_libs(key, '')
959
960    def library_extensions(self):
961        c = customized_ccompiler()
962        static_exts = []
963        if c.compiler_type != 'msvc':
964            # MSVC doesn't understand binutils
965            static_exts.append('.a')
966        if sys.platform == 'win32':
967            static_exts.append('.lib')  # .lib is used by MSVC and others
968        if self.search_static_first:
969            exts = static_exts + [so_ext]
970        else:
971            exts = [so_ext] + static_exts
972        if sys.platform == 'cygwin':
973            exts.append('.dll.a')
974        if sys.platform == 'darwin':
975            exts.append('.dylib')
976        return exts
977
978    def check_libs(self, lib_dirs, libs, opt_libs=[]):
979        """If static or shared libraries are available then return
980        their info dictionary.
981
982        Checks for all libraries as shared libraries first, then
983        static (or vice versa if self.search_static_first is True).
984        """
985        exts = self.library_extensions()
986        info = None
987        for ext in exts:
988            info = self._check_libs(lib_dirs, libs, opt_libs, [ext])
989            if info is not None:
990                break
991        if not info:
992            log.info('  libraries %s not found in %s', ','.join(libs),
993                     lib_dirs)
994        return info
995
996    def check_libs2(self, lib_dirs, libs, opt_libs=[]):
997        """If static or shared libraries are available then return
998        their info dictionary.
999
1000        Checks each library for shared or static.
1001        """
1002        exts = self.library_extensions()
1003        info = self._check_libs(lib_dirs, libs, opt_libs, exts)
1004        if not info:
1005            log.info('  libraries %s not found in %s', ','.join(libs),
1006                     lib_dirs)
1007
1008        return info
1009
1010    def _find_lib(self, lib_dir, lib, exts):
1011        assert is_string(lib_dir)
1012        # under windows first try without 'lib' prefix
1013        if sys.platform == 'win32':
1014            lib_prefixes = ['', 'lib']
1015        else:
1016            lib_prefixes = ['lib']
1017        # for each library name, see if we can find a file for it.
1018        for ext in exts:
1019            for prefix in lib_prefixes:
1020                p = self.combine_paths(lib_dir, prefix + lib + ext)
1021                if p:
1022                    # p[0] is the full path to the binary library file.
1023                    if is_symlink_to_accelerate(p[0]):
1024                        raise RuntimeError(_accel_msg.format(filename=p[0]))
1025                    break
1026            if p:
1027                assert len(p) == 1
1028                # ??? splitext on p[0] would do this for cygwin
1029                # doesn't seem correct
1030                if ext == '.dll.a':
1031                    lib += '.dll'
1032                if ext == '.lib':
1033                    lib = prefix + lib
1034                return lib
1035
1036        return False
1037
1038    def _find_libs(self, lib_dirs, libs, exts):
1039        # make sure we preserve the order of libs, as it can be important
1040        found_dirs, found_libs = [], []
1041        for lib in libs:
1042            for lib_dir in lib_dirs:
1043                found_lib = self._find_lib(lib_dir, lib, exts)
1044                if found_lib:
1045                    found_libs.append(found_lib)
1046                    if lib_dir not in found_dirs:
1047                        found_dirs.append(lib_dir)
1048                    break
1049        return found_dirs, found_libs
1050
1051    def _check_libs(self, lib_dirs, libs, opt_libs, exts):
1052        """Find mandatory and optional libs in expected paths.
1053
1054        Missing optional libraries are silently forgotten.
1055        """
1056        if not is_sequence(lib_dirs):
1057            lib_dirs = [lib_dirs]
1058        # First, try to find the mandatory libraries
1059        found_dirs, found_libs = self._find_libs(lib_dirs, libs, exts)
1060        if len(found_libs) > 0 and len(found_libs) == len(libs):
1061            # Now, check for optional libraries
1062            opt_found_dirs, opt_found_libs = self._find_libs(lib_dirs, opt_libs, exts)
1063            found_libs.extend(opt_found_libs)
1064            for lib_dir in opt_found_dirs:
1065                if lib_dir not in found_dirs:
1066                    found_dirs.append(lib_dir)
1067            info = {'libraries': found_libs, 'library_dirs': found_dirs}
1068            return info
1069        else:
1070            return None
1071
1072    def combine_paths(self, *args):
1073        """Return a list of existing paths composed by all combinations
1074        of items from the arguments.
1075        """
1076        return combine_paths(*args)
1077
1078
1079class fft_opt_info(system_info):
1080
1081    def calc_info(self):
1082        info = {}
1083        fftw_info = get_info('fftw3') or get_info('fftw2') or get_info('dfftw')
1084        djbfft_info = get_info('djbfft')
1085        if fftw_info:
1086            dict_append(info, **fftw_info)
1087            if djbfft_info:
1088                dict_append(info, **djbfft_info)
1089            self.set_info(**info)
1090            return
1091
1092
1093class fftw_info(system_info):
1094    #variables to override
1095    section = 'fftw'
1096    dir_env_var = 'FFTW'
1097    notfounderror = FFTWNotFoundError
1098    ver_info = [{'name':'fftw3',
1099                    'libs':['fftw3'],
1100                    'includes':['fftw3.h'],
1101                    'macros':[('SCIPY_FFTW3_H', None)]},
1102                  {'name':'fftw2',
1103                    'libs':['rfftw', 'fftw'],
1104                    'includes':['fftw.h', 'rfftw.h'],
1105                    'macros':[('SCIPY_FFTW_H', None)]}]
1106
1107    def calc_ver_info(self, ver_param):
1108        """Returns True on successful version detection, else False"""
1109        lib_dirs = self.get_lib_dirs()
1110        incl_dirs = self.get_include_dirs()
1111
1112        opt = self.get_option_single(self.section + '_libs', 'libraries')
1113        libs = self.get_libs(opt, ver_param['libs'])
1114        info = self.check_libs(lib_dirs, libs)
1115        if info is not None:
1116            flag = 0
1117            for d in incl_dirs:
1118                if len(self.combine_paths(d, ver_param['includes'])) \
1119                   == len(ver_param['includes']):
1120                    dict_append(info, include_dirs=[d])
1121                    flag = 1
1122                    break
1123            if flag:
1124                dict_append(info, define_macros=ver_param['macros'])
1125            else:
1126                info = None
1127        if info is not None:
1128            self.set_info(**info)
1129            return True
1130        else:
1131            log.info('  %s not found' % (ver_param['name']))
1132            return False
1133
1134    def calc_info(self):
1135        for i in self.ver_info:
1136            if self.calc_ver_info(i):
1137                break
1138
1139
1140class fftw2_info(fftw_info):
1141    #variables to override
1142    section = 'fftw'
1143    dir_env_var = 'FFTW'
1144    notfounderror = FFTWNotFoundError
1145    ver_info = [{'name':'fftw2',
1146                    'libs':['rfftw', 'fftw'],
1147                    'includes':['fftw.h', 'rfftw.h'],
1148                    'macros':[('SCIPY_FFTW_H', None)]}
1149                  ]
1150
1151
1152class fftw3_info(fftw_info):
1153    #variables to override
1154    section = 'fftw3'
1155    dir_env_var = 'FFTW3'
1156    notfounderror = FFTWNotFoundError
1157    ver_info = [{'name':'fftw3',
1158                    'libs':['fftw3'],
1159                    'includes':['fftw3.h'],
1160                    'macros':[('SCIPY_FFTW3_H', None)]},
1161                  ]
1162
1163
1164class dfftw_info(fftw_info):
1165    section = 'fftw'
1166    dir_env_var = 'FFTW'
1167    ver_info = [{'name':'dfftw',
1168                    'libs':['drfftw', 'dfftw'],
1169                    'includes':['dfftw.h', 'drfftw.h'],
1170                    'macros':[('SCIPY_DFFTW_H', None)]}]
1171
1172
1173class sfftw_info(fftw_info):
1174    section = 'fftw'
1175    dir_env_var = 'FFTW'
1176    ver_info = [{'name':'sfftw',
1177                    'libs':['srfftw', 'sfftw'],
1178                    'includes':['sfftw.h', 'srfftw.h'],
1179                    'macros':[('SCIPY_SFFTW_H', None)]}]
1180
1181
1182class fftw_threads_info(fftw_info):
1183    section = 'fftw'
1184    dir_env_var = 'FFTW'
1185    ver_info = [{'name':'fftw threads',
1186                    'libs':['rfftw_threads', 'fftw_threads'],
1187                    'includes':['fftw_threads.h', 'rfftw_threads.h'],
1188                    'macros':[('SCIPY_FFTW_THREADS_H', None)]}]
1189
1190
1191class dfftw_threads_info(fftw_info):
1192    section = 'fftw'
1193    dir_env_var = 'FFTW'
1194    ver_info = [{'name':'dfftw threads',
1195                    'libs':['drfftw_threads', 'dfftw_threads'],
1196                    'includes':['dfftw_threads.h', 'drfftw_threads.h'],
1197                    'macros':[('SCIPY_DFFTW_THREADS_H', None)]}]
1198
1199
1200class sfftw_threads_info(fftw_info):
1201    section = 'fftw'
1202    dir_env_var = 'FFTW'
1203    ver_info = [{'name':'sfftw threads',
1204                    'libs':['srfftw_threads', 'sfftw_threads'],
1205                    'includes':['sfftw_threads.h', 'srfftw_threads.h'],
1206                    'macros':[('SCIPY_SFFTW_THREADS_H', None)]}]
1207
1208
1209class djbfft_info(system_info):
1210    section = 'djbfft'
1211    dir_env_var = 'DJBFFT'
1212    notfounderror = DJBFFTNotFoundError
1213
1214    def get_paths(self, section, key):
1215        pre_dirs = system_info.get_paths(self, section, key)
1216        dirs = []
1217        for d in pre_dirs:
1218            dirs.extend(self.combine_paths(d, ['djbfft']) + [d])
1219        return [d for d in dirs if os.path.isdir(d)]
1220
1221    def calc_info(self):
1222        lib_dirs = self.get_lib_dirs()
1223        incl_dirs = self.get_include_dirs()
1224        info = None
1225        for d in lib_dirs:
1226            p = self.combine_paths(d, ['djbfft.a'])
1227            if p:
1228                info = {'extra_objects': p}
1229                break
1230            p = self.combine_paths(d, ['libdjbfft.a', 'libdjbfft' + so_ext])
1231            if p:
1232                info = {'libraries': ['djbfft'], 'library_dirs': [d]}
1233                break
1234        if info is None:
1235            return
1236        for d in incl_dirs:
1237            if len(self.combine_paths(d, ['fftc8.h', 'fftfreq.h'])) == 2:
1238                dict_append(info, include_dirs=[d],
1239                            define_macros=[('SCIPY_DJBFFT_H', None)])
1240                self.set_info(**info)
1241                return
1242        return
1243
1244
1245class mkl_info(system_info):
1246    section = 'mkl'
1247    dir_env_var = 'MKLROOT'
1248    _lib_mkl = ['mkl_rt']
1249
1250    def get_mkl_rootdir(self):
1251        mklroot = os.environ.get('MKLROOT', None)
1252        if mklroot is not None:
1253            return mklroot
1254        paths = os.environ.get('LD_LIBRARY_PATH', '').split(os.pathsep)
1255        ld_so_conf = '/etc/ld.so.conf'
1256        if os.path.isfile(ld_so_conf):
1257            with open(ld_so_conf, 'r') as f:
1258                for d in f:
1259                    d = d.strip()
1260                    if d:
1261                        paths.append(d)
1262        intel_mkl_dirs = []
1263        for path in paths:
1264            path_atoms = path.split(os.sep)
1265            for m in path_atoms:
1266                if m.startswith('mkl'):
1267                    d = os.sep.join(path_atoms[:path_atoms.index(m) + 2])
1268                    intel_mkl_dirs.append(d)
1269                    break
1270        for d in paths:
1271            dirs = glob(os.path.join(d, 'mkl', '*'))
1272            dirs += glob(os.path.join(d, 'mkl*'))
1273            for sub_dir in dirs:
1274                if os.path.isdir(os.path.join(sub_dir, 'lib')):
1275                    return sub_dir
1276        return None
1277
1278    def __init__(self):
1279        mklroot = self.get_mkl_rootdir()
1280        if mklroot is None:
1281            system_info.__init__(self)
1282        else:
1283            from .cpuinfo import cpu
1284            if cpu.is_Itanium():
1285                plt = '64'
1286            elif cpu.is_Intel() and cpu.is_64bit():
1287                plt = 'intel64'
1288            else:
1289                plt = '32'
1290            system_info.__init__(
1291                self,
1292                default_lib_dirs=[os.path.join(mklroot, 'lib', plt)],
1293                default_include_dirs=[os.path.join(mklroot, 'include')])
1294
1295    def calc_info(self):
1296        lib_dirs = self.get_lib_dirs()
1297        incl_dirs = self.get_include_dirs()
1298        opt = self.get_option_single('mkl_libs', 'libraries')
1299        mkl_libs = self.get_libs(opt, self._lib_mkl)
1300        info = self.check_libs2(lib_dirs, mkl_libs)
1301        if info is None:
1302            return
1303        dict_append(info,
1304                    define_macros=[('SCIPY_MKL_H', None),
1305                                   ('HAVE_CBLAS', None)],
1306                    include_dirs=incl_dirs)
1307        if sys.platform == 'win32':
1308            pass  # win32 has no pthread library
1309        else:
1310            dict_append(info, libraries=['pthread'])
1311        self.set_info(**info)
1312
1313
1314class lapack_mkl_info(mkl_info):
1315    pass
1316
1317
1318class blas_mkl_info(mkl_info):
1319    pass
1320
1321
1322class atlas_info(system_info):
1323    section = 'atlas'
1324    dir_env_var = 'ATLAS'
1325    _lib_names = ['f77blas', 'cblas']
1326    if sys.platform[:7] == 'freebsd':
1327        _lib_atlas = ['atlas']
1328        _lib_lapack = ['alapack']
1329    else:
1330        _lib_atlas = ['atlas']
1331        _lib_lapack = ['lapack']
1332
1333    notfounderror = AtlasNotFoundError
1334
1335    def get_paths(self, section, key):
1336        pre_dirs = system_info.get_paths(self, section, key)
1337        dirs = []
1338        for d in pre_dirs:
1339            dirs.extend(self.combine_paths(d, ['atlas*', 'ATLAS*',
1340                                         'sse', '3dnow', 'sse2']) + [d])
1341        return [d for d in dirs if os.path.isdir(d)]
1342
1343    def calc_info(self):
1344        lib_dirs = self.get_lib_dirs()
1345        info = {}
1346        opt = self.get_option_single('atlas_libs', 'libraries')
1347        atlas_libs = self.get_libs(opt, self._lib_names + self._lib_atlas)
1348        lapack_libs = self.get_libs('lapack_libs', self._lib_lapack)
1349        atlas = None
1350        lapack = None
1351        atlas_1 = None
1352        for d in lib_dirs:
1353            # FIXME: lapack_atlas is unused
1354            lapack_atlas = self.check_libs2(d, ['lapack_atlas'], [])
1355            atlas = self.check_libs2(d, atlas_libs, [])
1356            if atlas is not None:
1357                lib_dirs2 = [d] + self.combine_paths(d, ['atlas*', 'ATLAS*'])
1358                lapack = self.check_libs2(lib_dirs2, lapack_libs, [])
1359                if lapack is not None:
1360                    break
1361            if atlas:
1362                atlas_1 = atlas
1363        log.info(self.__class__)
1364        if atlas is None:
1365            atlas = atlas_1
1366        if atlas is None:
1367            return
1368        include_dirs = self.get_include_dirs()
1369        h = (self.combine_paths(lib_dirs + include_dirs, 'cblas.h') or [None])
1370        h = h[0]
1371        if h:
1372            h = os.path.dirname(h)
1373            dict_append(info, include_dirs=[h])
1374        info['language'] = 'c'
1375        if lapack is not None:
1376            dict_append(info, **lapack)
1377            dict_append(info, **atlas)
1378        elif 'lapack_atlas' in atlas['libraries']:
1379            dict_append(info, **atlas)
1380            dict_append(info,
1381                        define_macros=[('ATLAS_WITH_LAPACK_ATLAS', None)])
1382            self.set_info(**info)
1383            return
1384        else:
1385            dict_append(info, **atlas)
1386            dict_append(info, define_macros=[('ATLAS_WITHOUT_LAPACK', None)])
1387            message = textwrap.dedent("""
1388                *********************************************************************
1389                    Could not find lapack library within the ATLAS installation.
1390                *********************************************************************
1391                """)
1392            warnings.warn(message, stacklevel=2)
1393            self.set_info(**info)
1394            return
1395
1396        # Check if lapack library is complete, only warn if it is not.
1397        lapack_dir = lapack['library_dirs'][0]
1398        lapack_name = lapack['libraries'][0]
1399        lapack_lib = None
1400        lib_prefixes = ['lib']
1401        if sys.platform == 'win32':
1402            lib_prefixes.append('')
1403        for e in self.library_extensions():
1404            for prefix in lib_prefixes:
1405                fn = os.path.join(lapack_dir, prefix + lapack_name + e)
1406                if os.path.exists(fn):
1407                    lapack_lib = fn
1408                    break
1409            if lapack_lib:
1410                break
1411        if lapack_lib is not None:
1412            sz = os.stat(lapack_lib)[6]
1413            if sz <= 4000 * 1024:
1414                message = textwrap.dedent("""
1415                    *********************************************************************
1416                        Lapack library (from ATLAS) is probably incomplete:
1417                          size of %s is %sk (expected >4000k)
1418
1419                        Follow the instructions in the KNOWN PROBLEMS section of the file
1420                        numpy/INSTALL.txt.
1421                    *********************************************************************
1422                    """) % (lapack_lib, sz / 1024)
1423                warnings.warn(message, stacklevel=2)
1424            else:
1425                info['language'] = 'f77'
1426
1427        atlas_version, atlas_extra_info = get_atlas_version(**atlas)
1428        dict_append(info, **atlas_extra_info)
1429
1430        self.set_info(**info)
1431
1432
1433class atlas_blas_info(atlas_info):
1434    _lib_names = ['f77blas', 'cblas']
1435
1436    def calc_info(self):
1437        lib_dirs = self.get_lib_dirs()
1438        info = {}
1439        opt = self.get_option_single('atlas_libs', 'libraries')
1440        atlas_libs = self.get_libs(opt, self._lib_names + self._lib_atlas)
1441        atlas = self.check_libs2(lib_dirs, atlas_libs, [])
1442        if atlas is None:
1443            return
1444        include_dirs = self.get_include_dirs()
1445        h = (self.combine_paths(lib_dirs + include_dirs, 'cblas.h') or [None])
1446        h = h[0]
1447        if h:
1448            h = os.path.dirname(h)
1449            dict_append(info, include_dirs=[h])
1450        info['language'] = 'c'
1451        info['define_macros'] = [('HAVE_CBLAS', None)]
1452
1453        atlas_version, atlas_extra_info = get_atlas_version(**atlas)
1454        dict_append(atlas, **atlas_extra_info)
1455
1456        dict_append(info, **atlas)
1457
1458        self.set_info(**info)
1459        return
1460
1461
1462class atlas_threads_info(atlas_info):
1463    dir_env_var = ['PTATLAS', 'ATLAS']
1464    _lib_names = ['ptf77blas', 'ptcblas']
1465
1466
1467class atlas_blas_threads_info(atlas_blas_info):
1468    dir_env_var = ['PTATLAS', 'ATLAS']
1469    _lib_names = ['ptf77blas', 'ptcblas']
1470
1471
1472class lapack_atlas_info(atlas_info):
1473    _lib_names = ['lapack_atlas'] + atlas_info._lib_names
1474
1475
1476class lapack_atlas_threads_info(atlas_threads_info):
1477    _lib_names = ['lapack_atlas'] + atlas_threads_info._lib_names
1478
1479
1480class atlas_3_10_info(atlas_info):
1481    _lib_names = ['satlas']
1482    _lib_atlas = _lib_names
1483    _lib_lapack = _lib_names
1484
1485
1486class atlas_3_10_blas_info(atlas_3_10_info):
1487    _lib_names = ['satlas']
1488
1489    def calc_info(self):
1490        lib_dirs = self.get_lib_dirs()
1491        info = {}
1492        opt = self.get_option_single('atlas_lib', 'libraries')
1493        atlas_libs = self.get_libs(opt, self._lib_names)
1494        atlas = self.check_libs2(lib_dirs, atlas_libs, [])
1495        if atlas is None:
1496            return
1497        include_dirs = self.get_include_dirs()
1498        h = (self.combine_paths(lib_dirs + include_dirs, 'cblas.h') or [None])
1499        h = h[0]
1500        if h:
1501            h = os.path.dirname(h)
1502            dict_append(info, include_dirs=[h])
1503        info['language'] = 'c'
1504        info['define_macros'] = [('HAVE_CBLAS', None)]
1505
1506        atlas_version, atlas_extra_info = get_atlas_version(**atlas)
1507        dict_append(atlas, **atlas_extra_info)
1508
1509        dict_append(info, **atlas)
1510
1511        self.set_info(**info)
1512        return
1513
1514
1515class atlas_3_10_threads_info(atlas_3_10_info):
1516    dir_env_var = ['PTATLAS', 'ATLAS']
1517    _lib_names = ['tatlas']
1518    _lib_atlas = _lib_names
1519    _lib_lapack = _lib_names
1520
1521
1522class atlas_3_10_blas_threads_info(atlas_3_10_blas_info):
1523    dir_env_var = ['PTATLAS', 'ATLAS']
1524    _lib_names = ['tatlas']
1525
1526
1527class lapack_atlas_3_10_info(atlas_3_10_info):
1528    pass
1529
1530
1531class lapack_atlas_3_10_threads_info(atlas_3_10_threads_info):
1532    pass
1533
1534
1535class lapack_info(system_info):
1536    section = 'lapack'
1537    dir_env_var = 'LAPACK'
1538    _lib_names = ['lapack']
1539    notfounderror = LapackNotFoundError
1540
1541    def calc_info(self):
1542        lib_dirs = self.get_lib_dirs()
1543
1544        opt = self.get_option_single('lapack_libs', 'libraries')
1545        lapack_libs = self.get_libs(opt, self._lib_names)
1546        info = self.check_libs(lib_dirs, lapack_libs, [])
1547        if info is None:
1548            return
1549        info['language'] = 'f77'
1550        self.set_info(**info)
1551
1552
1553class lapack_src_info(system_info):
1554    section = 'lapack_src'
1555    dir_env_var = 'LAPACK_SRC'
1556    notfounderror = LapackSrcNotFoundError
1557
1558    def get_paths(self, section, key):
1559        pre_dirs = system_info.get_paths(self, section, key)
1560        dirs = []
1561        for d in pre_dirs:
1562            dirs.extend([d] + self.combine_paths(d, ['LAPACK*/SRC', 'SRC']))
1563        return [d for d in dirs if os.path.isdir(d)]
1564
1565    def calc_info(self):
1566        src_dirs = self.get_src_dirs()
1567        src_dir = ''
1568        for d in src_dirs:
1569            if os.path.isfile(os.path.join(d, 'dgesv.f')):
1570                src_dir = d
1571                break
1572        if not src_dir:
1573            #XXX: Get sources from netlib. May be ask first.
1574            return
1575        # The following is extracted from LAPACK-3.0/SRC/Makefile.
1576        # Added missing names from lapack-lite-3.1.1/SRC/Makefile
1577        # while keeping removed names for Lapack-3.0 compatibility.
1578        allaux = '''
1579        ilaenv ieeeck lsame lsamen xerbla
1580        iparmq
1581        '''  # *.f
1582        laux = '''
1583        bdsdc bdsqr disna labad lacpy ladiv lae2 laebz laed0 laed1
1584        laed2 laed3 laed4 laed5 laed6 laed7 laed8 laed9 laeda laev2
1585        lagtf lagts lamch lamrg lanst lapy2 lapy3 larnv larrb larre
1586        larrf lartg laruv las2 lascl lasd0 lasd1 lasd2 lasd3 lasd4
1587        lasd5 lasd6 lasd7 lasd8 lasd9 lasda lasdq lasdt laset lasq1
1588        lasq2 lasq3 lasq4 lasq5 lasq6 lasr lasrt lassq lasv2 pttrf
1589        stebz stedc steqr sterf
1590
1591        larra larrc larrd larr larrk larrj larrr laneg laisnan isnan
1592        lazq3 lazq4
1593        '''  # [s|d]*.f
1594        lasrc = '''
1595        gbbrd gbcon gbequ gbrfs gbsv gbsvx gbtf2 gbtrf gbtrs gebak
1596        gebal gebd2 gebrd gecon geequ gees geesx geev geevx gegs gegv
1597        gehd2 gehrd gelq2 gelqf gels gelsd gelss gelsx gelsy geql2
1598        geqlf geqp3 geqpf geqr2 geqrf gerfs gerq2 gerqf gesc2 gesdd
1599        gesv gesvd gesvx getc2 getf2 getrf getri getrs ggbak ggbal
1600        gges ggesx ggev ggevx ggglm gghrd gglse ggqrf ggrqf ggsvd
1601        ggsvp gtcon gtrfs gtsv gtsvx gttrf gttrs gtts2 hgeqz hsein
1602        hseqr labrd lacon laein lags2 lagtm lahqr lahrd laic1 lals0
1603        lalsa lalsd langb lange langt lanhs lansb lansp lansy lantb
1604        lantp lantr lapll lapmt laqgb laqge laqp2 laqps laqsb laqsp
1605        laqsy lar1v lar2v larf larfb larfg larft larfx largv larrv
1606        lartv larz larzb larzt laswp lasyf latbs latdf latps latrd
1607        latrs latrz latzm lauu2 lauum pbcon pbequ pbrfs pbstf pbsv
1608        pbsvx pbtf2 pbtrf pbtrs pocon poequ porfs posv posvx potf2
1609        potrf potri potrs ppcon ppequ pprfs ppsv ppsvx pptrf pptri
1610        pptrs ptcon pteqr ptrfs ptsv ptsvx pttrs ptts2 spcon sprfs
1611        spsv spsvx sptrf sptri sptrs stegr stein sycon syrfs sysv
1612        sysvx sytf2 sytrf sytri sytrs tbcon tbrfs tbtrs tgevc tgex2
1613        tgexc tgsen tgsja tgsna tgsy2 tgsyl tpcon tprfs tptri tptrs
1614        trcon trevc trexc trrfs trsen trsna trsyl trti2 trtri trtrs
1615        tzrqf tzrzf
1616
1617        lacn2 lahr2 stemr laqr0 laqr1 laqr2 laqr3 laqr4 laqr5
1618        '''  # [s|c|d|z]*.f
1619        sd_lasrc = '''
1620        laexc lag2 lagv2 laln2 lanv2 laqtr lasy2 opgtr opmtr org2l
1621        org2r orgbr orghr orgl2 orglq orgql orgqr orgr2 orgrq orgtr
1622        orm2l orm2r ormbr ormhr orml2 ormlq ormql ormqr ormr2 ormr3
1623        ormrq ormrz ormtr rscl sbev sbevd sbevx sbgst sbgv sbgvd sbgvx
1624        sbtrd spev spevd spevx spgst spgv spgvd spgvx sptrd stev stevd
1625        stevr stevx syev syevd syevr syevx sygs2 sygst sygv sygvd
1626        sygvx sytd2 sytrd
1627        '''  # [s|d]*.f
1628        cz_lasrc = '''
1629        bdsqr hbev hbevd hbevx hbgst hbgv hbgvd hbgvx hbtrd hecon heev
1630        heevd heevr heevx hegs2 hegst hegv hegvd hegvx herfs hesv
1631        hesvx hetd2 hetf2 hetrd hetrf hetri hetrs hpcon hpev hpevd
1632        hpevx hpgst hpgv hpgvd hpgvx hprfs hpsv hpsvx hptrd hptrf
1633        hptri hptrs lacgv lacp2 lacpy lacrm lacrt ladiv laed0 laed7
1634        laed8 laesy laev2 lahef lanhb lanhe lanhp lanht laqhb laqhe
1635        laqhp larcm larnv lartg lascl laset lasr lassq pttrf rot spmv
1636        spr stedc steqr symv syr ung2l ung2r ungbr unghr ungl2 unglq
1637        ungql ungqr ungr2 ungrq ungtr unm2l unm2r unmbr unmhr unml2
1638        unmlq unmql unmqr unmr2 unmr3 unmrq unmrz unmtr upgtr upmtr
1639        '''  # [c|z]*.f
1640        #######
1641        sclaux = laux + ' econd '                  # s*.f
1642        dzlaux = laux + ' secnd '                  # d*.f
1643        slasrc = lasrc + sd_lasrc                  # s*.f
1644        dlasrc = lasrc + sd_lasrc                  # d*.f
1645        clasrc = lasrc + cz_lasrc + ' srot srscl '  # c*.f
1646        zlasrc = lasrc + cz_lasrc + ' drot drscl '  # z*.f
1647        oclasrc = ' icmax1 scsum1 '                # *.f
1648        ozlasrc = ' izmax1 dzsum1 '                # *.f
1649        sources = ['s%s.f' % f for f in (sclaux + slasrc).split()] \
1650                  + ['d%s.f' % f for f in (dzlaux + dlasrc).split()] \
1651                  + ['c%s.f' % f for f in (clasrc).split()] \
1652                  + ['z%s.f' % f for f in (zlasrc).split()] \
1653                  + ['%s.f' % f for f in (allaux + oclasrc + ozlasrc).split()]
1654        sources = [os.path.join(src_dir, f) for f in sources]
1655        # Lapack 3.1:
1656        src_dir2 = os.path.join(src_dir, '..', 'INSTALL')
1657        sources += [os.path.join(src_dir2, p + 'lamch.f') for p in 'sdcz']
1658        # Lapack 3.2.1:
1659        sources += [os.path.join(src_dir, p + 'larfp.f') for p in 'sdcz']
1660        sources += [os.path.join(src_dir, 'ila' + p + 'lr.f') for p in 'sdcz']
1661        sources += [os.path.join(src_dir, 'ila' + p + 'lc.f') for p in 'sdcz']
1662        # Should we check here actual existence of source files?
1663        # Yes, the file listing is different between 3.0 and 3.1
1664        # versions.
1665        sources = [f for f in sources if os.path.isfile(f)]
1666        info = {'sources': sources, 'language': 'f77'}
1667        self.set_info(**info)
1668
1669atlas_version_c_text = r'''
1670/* This file is generated from numpy/distutils/system_info.py */
1671void ATL_buildinfo(void);
1672int main(void) {
1673  ATL_buildinfo();
1674  return 0;
1675}
1676'''
1677
1678_cached_atlas_version = {}
1679
1680
1681def get_atlas_version(**config):
1682    libraries = config.get('libraries', [])
1683    library_dirs = config.get('library_dirs', [])
1684    key = (tuple(libraries), tuple(library_dirs))
1685    if key in _cached_atlas_version:
1686        return _cached_atlas_version[key]
1687    c = cmd_config(Distribution())
1688    atlas_version = None
1689    info = {}
1690    try:
1691        s, o = c.get_output(atlas_version_c_text,
1692                            libraries=libraries, library_dirs=library_dirs,
1693                           )
1694        if s and re.search(r'undefined reference to `_gfortran', o, re.M):
1695            s, o = c.get_output(atlas_version_c_text,
1696                                libraries=libraries + ['gfortran'],
1697                                library_dirs=library_dirs,
1698                               )
1699            if not s:
1700                warnings.warn(textwrap.dedent("""
1701                    *****************************************************
1702                    Linkage with ATLAS requires gfortran. Use
1703
1704                      python setup.py config_fc --fcompiler=gnu95 ...
1705
1706                    when building extension libraries that use ATLAS.
1707                    Make sure that -lgfortran is used for C++ extensions.
1708                    *****************************************************
1709                    """), stacklevel=2)
1710                dict_append(info, language='f90',
1711                            define_macros=[('ATLAS_REQUIRES_GFORTRAN', None)])
1712    except Exception:  # failed to get version from file -- maybe on Windows
1713        # look at directory name
1714        for o in library_dirs:
1715            m = re.search(r'ATLAS_(?P<version>\d+[.]\d+[.]\d+)_', o)
1716            if m:
1717                atlas_version = m.group('version')
1718            if atlas_version is not None:
1719                break
1720
1721        # final choice --- look at ATLAS_VERSION environment
1722        #   variable
1723        if atlas_version is None:
1724            atlas_version = os.environ.get('ATLAS_VERSION', None)
1725        if atlas_version:
1726            dict_append(info, define_macros=[(
1727                'ATLAS_INFO', _c_string_literal(atlas_version))
1728            ])
1729        else:
1730            dict_append(info, define_macros=[('NO_ATLAS_INFO', -1)])
1731        return atlas_version or '?.?.?', info
1732
1733    if not s:
1734        m = re.search(r'ATLAS version (?P<version>\d+[.]\d+[.]\d+)', o)
1735        if m:
1736            atlas_version = m.group('version')
1737    if atlas_version is None:
1738        if re.search(r'undefined symbol: ATL_buildinfo', o, re.M):
1739            atlas_version = '3.2.1_pre3.3.6'
1740        else:
1741            log.info('Status: %d', s)
1742            log.info('Output: %s', o)
1743
1744    elif atlas_version == '3.2.1_pre3.3.6':
1745        dict_append(info, define_macros=[('NO_ATLAS_INFO', -2)])
1746    else:
1747        dict_append(info, define_macros=[(
1748            'ATLAS_INFO', _c_string_literal(atlas_version))
1749        ])
1750    result = _cached_atlas_version[key] = atlas_version, info
1751    return result
1752
1753
1754class lapack_opt_info(system_info):
1755    notfounderror = LapackNotFoundError
1756    # List of all known LAPACK libraries, in the default order
1757    lapack_order = ['mkl', 'openblas', 'flame', 'atlas', 'lapack']
1758    order_env_var_name = 'NPY_LAPACK_ORDER'
1759
1760    def _calc_info_mkl(self):
1761        info = get_info('lapack_mkl')
1762        if info:
1763            self.set_info(**info)
1764            return True
1765        return False
1766
1767    def _calc_info_openblas(self):
1768        info = get_info('openblas_lapack')
1769        if info:
1770            self.set_info(**info)
1771            return True
1772        info = get_info('openblas_clapack')
1773        if info:
1774            self.set_info(**info)
1775            return True
1776        return False
1777
1778    def _calc_info_flame(self):
1779        info = get_info('flame')
1780        if info:
1781            self.set_info(**info)
1782            return True
1783        return False
1784
1785    def _calc_info_atlas(self):
1786        info = get_info('atlas_3_10_threads')
1787        if not info:
1788            info = get_info('atlas_3_10')
1789        if not info:
1790            info = get_info('atlas_threads')
1791        if not info:
1792            info = get_info('atlas')
1793        if info:
1794            # Figure out if ATLAS has lapack...
1795            # If not we need the lapack library, but not BLAS!
1796            l = info.get('define_macros', [])
1797            if ('ATLAS_WITH_LAPACK_ATLAS', None) in l \
1798               or ('ATLAS_WITHOUT_LAPACK', None) in l:
1799                # Get LAPACK (with possible warnings)
1800                # If not found we don't accept anything
1801                # since we can't use ATLAS with LAPACK!
1802                lapack_info = self._get_info_lapack()
1803                if not lapack_info:
1804                    return False
1805                dict_append(info, **lapack_info)
1806            self.set_info(**info)
1807            return True
1808        return False
1809
1810    def _calc_info_accelerate(self):
1811        info = get_info('accelerate')
1812        if info:
1813            self.set_info(**info)
1814            return True
1815        return False
1816
1817    def _get_info_blas(self):
1818        # Default to get the optimized BLAS implementation
1819        info = get_info('blas_opt')
1820        if not info:
1821            warnings.warn(BlasNotFoundError.__doc__ or '', stacklevel=3)
1822            info_src = get_info('blas_src')
1823            if not info_src:
1824                warnings.warn(BlasSrcNotFoundError.__doc__ or '', stacklevel=3)
1825                return {}
1826            dict_append(info, libraries=[('fblas_src', info_src)])
1827        return info
1828
1829    def _get_info_lapack(self):
1830        info = get_info('lapack')
1831        if not info:
1832            warnings.warn(LapackNotFoundError.__doc__ or '', stacklevel=3)
1833            info_src = get_info('lapack_src')
1834            if not info_src:
1835                warnings.warn(LapackSrcNotFoundError.__doc__ or '', stacklevel=3)
1836                return {}
1837            dict_append(info, libraries=[('flapack_src', info_src)])
1838        return info
1839
1840    def _calc_info_lapack(self):
1841        info = self._get_info_lapack()
1842        if info:
1843            info_blas = self._get_info_blas()
1844            dict_append(info, **info_blas)
1845            dict_append(info, define_macros=[('NO_ATLAS_INFO', 1)])
1846            self.set_info(**info)
1847            return True
1848        return False
1849
1850    def _calc_info(self, name):
1851        return getattr(self, '_calc_info_{}'.format(name))()
1852
1853    def calc_info(self):
1854        lapack_order, unknown_order = _parse_env_order(self.lapack_order, self.order_env_var_name)
1855        if len(unknown_order) > 0:
1856            raise ValueError("lapack_opt_info user defined "
1857                             "LAPACK order has unacceptable "
1858                             "values: {}".format(unknown_order))
1859
1860        for lapack in lapack_order:
1861            if self._calc_info(lapack):
1862                return
1863
1864        if 'lapack' not in lapack_order:
1865            # Since the user may request *not* to use any library, we still need
1866            # to raise warnings to signal missing packages!
1867            warnings.warn(LapackNotFoundError.__doc__ or '', stacklevel=2)
1868            warnings.warn(LapackSrcNotFoundError.__doc__ or '', stacklevel=2)
1869
1870
1871class _ilp64_opt_info_mixin:
1872    symbol_suffix = None
1873    symbol_prefix = None
1874
1875    def _check_info(self, info):
1876        macros = dict(info.get('define_macros', []))
1877        prefix = macros.get('BLAS_SYMBOL_PREFIX', '')
1878        suffix = macros.get('BLAS_SYMBOL_SUFFIX', '')
1879
1880        if self.symbol_prefix not in (None, prefix):
1881            return False
1882
1883        if self.symbol_suffix not in (None, suffix):
1884            return False
1885
1886        return bool(info)
1887
1888
1889class lapack_ilp64_opt_info(lapack_opt_info, _ilp64_opt_info_mixin):
1890    notfounderror = LapackILP64NotFoundError
1891    lapack_order = ['openblas64_', 'openblas_ilp64']
1892    order_env_var_name = 'NPY_LAPACK_ILP64_ORDER'
1893
1894    def _calc_info(self, name):
1895        info = get_info(name + '_lapack')
1896        if self._check_info(info):
1897            self.set_info(**info)
1898            return True
1899        return False
1900
1901
1902class lapack_ilp64_plain_opt_info(lapack_ilp64_opt_info):
1903    # Same as lapack_ilp64_opt_info, but fix symbol names
1904    symbol_prefix = ''
1905    symbol_suffix = ''
1906
1907
1908class lapack64__opt_info(lapack_ilp64_opt_info):
1909    symbol_prefix = ''
1910    symbol_suffix = '64_'
1911
1912
1913class blas_opt_info(system_info):
1914    notfounderror = BlasNotFoundError
1915    # List of all known BLAS libraries, in the default order
1916    blas_order = ['mkl', 'blis', 'openblas', 'atlas', 'blas']
1917    order_env_var_name = 'NPY_BLAS_ORDER'
1918
1919    def _calc_info_mkl(self):
1920        info = get_info('blas_mkl')
1921        if info:
1922            self.set_info(**info)
1923            return True
1924        return False
1925
1926    def _calc_info_blis(self):
1927        info = get_info('blis')
1928        if info:
1929            self.set_info(**info)
1930            return True
1931        return False
1932
1933    def _calc_info_openblas(self):
1934        info = get_info('openblas')
1935        if info:
1936            self.set_info(**info)
1937            return True
1938        return False
1939
1940    def _calc_info_atlas(self):
1941        info = get_info('atlas_3_10_blas_threads')
1942        if not info:
1943            info = get_info('atlas_3_10_blas')
1944        if not info:
1945            info = get_info('atlas_blas_threads')
1946        if not info:
1947            info = get_info('atlas_blas')
1948        if info:
1949            self.set_info(**info)
1950            return True
1951        return False
1952
1953    def _calc_info_accelerate(self):
1954        info = get_info('accelerate')
1955        if info:
1956            self.set_info(**info)
1957            return True
1958        return False
1959
1960    def _calc_info_blas(self):
1961        # Warn about a non-optimized BLAS library
1962        warnings.warn(BlasOptNotFoundError.__doc__ or '', stacklevel=3)
1963        info = {}
1964        dict_append(info, define_macros=[('NO_ATLAS_INFO', 1)])
1965
1966        blas = get_info('blas')
1967        if blas:
1968            dict_append(info, **blas)
1969        else:
1970            # Not even BLAS was found!
1971            warnings.warn(BlasNotFoundError.__doc__ or '', stacklevel=3)
1972
1973            blas_src = get_info('blas_src')
1974            if not blas_src:
1975                warnings.warn(BlasSrcNotFoundError.__doc__ or '', stacklevel=3)
1976                return False
1977            dict_append(info, libraries=[('fblas_src', blas_src)])
1978
1979        self.set_info(**info)
1980        return True
1981
1982    def _calc_info(self, name):
1983        return getattr(self, '_calc_info_{}'.format(name))()
1984
1985    def calc_info(self):
1986        blas_order, unknown_order = _parse_env_order(self.blas_order, self.order_env_var_name)
1987        if len(unknown_order) > 0:
1988            raise ValueError("blas_opt_info user defined BLAS order has unacceptable values: {}".format(unknown_order))
1989
1990        for blas in blas_order:
1991            if self._calc_info(blas):
1992                return
1993
1994        if 'blas' not in blas_order:
1995            # Since the user may request *not* to use any library, we still need
1996            # to raise warnings to signal missing packages!
1997            warnings.warn(BlasNotFoundError.__doc__ or '', stacklevel=2)
1998            warnings.warn(BlasSrcNotFoundError.__doc__ or '', stacklevel=2)
1999
2000
2001class blas_ilp64_opt_info(blas_opt_info, _ilp64_opt_info_mixin):
2002    notfounderror = BlasILP64NotFoundError
2003    blas_order = ['openblas64_', 'openblas_ilp64']
2004    order_env_var_name = 'NPY_BLAS_ILP64_ORDER'
2005
2006    def _calc_info(self, name):
2007        info = get_info(name)
2008        if self._check_info(info):
2009            self.set_info(**info)
2010            return True
2011        return False
2012
2013
2014class blas_ilp64_plain_opt_info(blas_ilp64_opt_info):
2015    symbol_prefix = ''
2016    symbol_suffix = ''
2017
2018
2019class blas64__opt_info(blas_ilp64_opt_info):
2020    symbol_prefix = ''
2021    symbol_suffix = '64_'
2022
2023
2024class cblas_info(system_info):
2025    section = 'cblas'
2026    dir_env_var = 'CBLAS'
2027    # No default as it's used only in blas_info
2028    _lib_names = []
2029    notfounderror = BlasNotFoundError
2030
2031
2032class blas_info(system_info):
2033    section = 'blas'
2034    dir_env_var = 'BLAS'
2035    _lib_names = ['blas']
2036    notfounderror = BlasNotFoundError
2037
2038    def calc_info(self):
2039        lib_dirs = self.get_lib_dirs()
2040        opt = self.get_option_single('blas_libs', 'libraries')
2041        blas_libs = self.get_libs(opt, self._lib_names)
2042        info = self.check_libs(lib_dirs, blas_libs, [])
2043        if info is None:
2044            return
2045        else:
2046            info['include_dirs'] = self.get_include_dirs()
2047        if platform.system() == 'Windows':
2048            # The check for windows is needed because get_cblas_libs uses the
2049            # same compiler that was used to compile Python and msvc is
2050            # often not installed when mingw is being used. This rough
2051            # treatment is not desirable, but windows is tricky.
2052            info['language'] = 'f77'  # XXX: is it generally true?
2053            # If cblas is given as an option, use those
2054            cblas_info_obj = cblas_info()
2055            cblas_opt = cblas_info_obj.get_option_single('cblas_libs', 'libraries')
2056            cblas_libs = cblas_info_obj.get_libs(cblas_opt, None)
2057            if cblas_libs:
2058                info['libraries'] = cblas_libs + blas_libs
2059                info['define_macros'] = [('HAVE_CBLAS', None)]
2060        else:
2061            lib = self.get_cblas_libs(info)
2062            if lib is not None:
2063                info['language'] = 'c'
2064                info['libraries'] = lib
2065                info['define_macros'] = [('HAVE_CBLAS', None)]
2066        self.set_info(**info)
2067
2068    def get_cblas_libs(self, info):
2069        """ Check whether we can link with CBLAS interface
2070
2071        This method will search through several combinations of libraries
2072        to check whether CBLAS is present:
2073
2074        1. Libraries in ``info['libraries']``, as is
2075        2. As 1. but also explicitly adding ``'cblas'`` as a library
2076        3. As 1. but also explicitly adding ``'blas'`` as a library
2077        4. Check only library ``'cblas'``
2078        5. Check only library ``'blas'``
2079
2080        Parameters
2081        ----------
2082        info : dict
2083           system information dictionary for compilation and linking
2084
2085        Returns
2086        -------
2087        libraries : list of str or None
2088            a list of libraries that enables the use of CBLAS interface.
2089            Returns None if not found or a compilation error occurs.
2090
2091            Since 1.17 returns a list.
2092        """
2093        # primitive cblas check by looking for the header and trying to link
2094        # cblas or blas
2095        c = customized_ccompiler()
2096        tmpdir = tempfile.mkdtemp()
2097        s = textwrap.dedent("""\
2098            #include <cblas.h>
2099            int main(int argc, const char *argv[])
2100            {
2101                double a[4] = {1,2,3,4};
2102                double b[4] = {5,6,7,8};
2103                return cblas_ddot(4, a, 1, b, 1) > 10;
2104            }""")
2105        src = os.path.join(tmpdir, 'source.c')
2106        try:
2107            with open(src, 'wt') as f:
2108                f.write(s)
2109
2110            try:
2111                # check we can compile (find headers)
2112                obj = c.compile([src], output_dir=tmpdir,
2113                                include_dirs=self.get_include_dirs())
2114            except (distutils.ccompiler.CompileError, distutils.ccompiler.LinkError):
2115                return None
2116
2117            # check we can link (find library)
2118            # some systems have separate cblas and blas libs.
2119            for libs in [info['libraries'], ['cblas'] + info['libraries'],
2120                         ['blas'] + info['libraries'], ['cblas'], ['blas']]:
2121                try:
2122                    c.link_executable(obj, os.path.join(tmpdir, "a.out"),
2123                                      libraries=libs,
2124                                      library_dirs=info['library_dirs'],
2125                                      extra_postargs=info.get('extra_link_args', []))
2126                    return libs
2127                except distutils.ccompiler.LinkError:
2128                    pass
2129        finally:
2130            shutil.rmtree(tmpdir)
2131        return None
2132
2133
2134class openblas_info(blas_info):
2135    section = 'openblas'
2136    dir_env_var = 'OPENBLAS'
2137    _lib_names = ['openblas']
2138    _require_symbols = []
2139    notfounderror = BlasNotFoundError
2140
2141    @property
2142    def symbol_prefix(self):
2143        try:
2144            return self.cp.get(self.section, 'symbol_prefix')
2145        except NoOptionError:
2146            return ''
2147
2148    @property
2149    def symbol_suffix(self):
2150        try:
2151            return self.cp.get(self.section, 'symbol_suffix')
2152        except NoOptionError:
2153            return ''
2154
2155    def _calc_info(self):
2156        c = customized_ccompiler()
2157
2158        lib_dirs = self.get_lib_dirs()
2159
2160        # Prefer to use libraries over openblas_libs
2161        opt = self.get_option_single('openblas_libs', 'libraries')
2162        openblas_libs = self.get_libs(opt, self._lib_names)
2163
2164        info = self.check_libs(lib_dirs, openblas_libs, [])
2165
2166        if c.compiler_type == "msvc" and info is None:
2167            from numpy.distutils.fcompiler import new_fcompiler
2168            f = new_fcompiler(c_compiler=c)
2169            if f and f.compiler_type == 'gnu95':
2170                # Try gfortran-compatible library files
2171                info = self.check_msvc_gfortran_libs(lib_dirs, openblas_libs)
2172                # Skip lapack check, we'd need build_ext to do it
2173                skip_symbol_check = True
2174        elif info:
2175            skip_symbol_check = False
2176            info['language'] = 'c'
2177
2178        if info is None:
2179            return None
2180
2181        # Add extra info for OpenBLAS
2182        extra_info = self.calc_extra_info()
2183        dict_append(info, **extra_info)
2184
2185        if not (skip_symbol_check or self.check_symbols(info)):
2186            return None
2187
2188        info['define_macros'] = [('HAVE_CBLAS', None)]
2189        if self.symbol_prefix:
2190            info['define_macros'] += [('BLAS_SYMBOL_PREFIX', self.symbol_prefix)]
2191        if self.symbol_suffix:
2192            info['define_macros'] += [('BLAS_SYMBOL_SUFFIX', self.symbol_suffix)]
2193
2194        return info
2195
2196    def calc_info(self):
2197        info = self._calc_info()
2198        if info is not None:
2199            self.set_info(**info)
2200
2201    def check_msvc_gfortran_libs(self, library_dirs, libraries):
2202        # First, find the full path to each library directory
2203        library_paths = []
2204        for library in libraries:
2205            for library_dir in library_dirs:
2206                # MinGW static ext will be .a
2207                fullpath = os.path.join(library_dir, library + '.a')
2208                if os.path.isfile(fullpath):
2209                    library_paths.append(fullpath)
2210                    break
2211            else:
2212                return None
2213
2214        # Generate numpy.distutils virtual static library file
2215        basename = self.__class__.__name__
2216        tmpdir = os.path.join(os.getcwd(), 'build', basename)
2217        if not os.path.isdir(tmpdir):
2218            os.makedirs(tmpdir)
2219
2220        info = {'library_dirs': [tmpdir],
2221                'libraries': [basename],
2222                'language': 'f77'}
2223
2224        fake_lib_file = os.path.join(tmpdir, basename + '.fobjects')
2225        fake_clib_file = os.path.join(tmpdir, basename + '.cobjects')
2226        with open(fake_lib_file, 'w') as f:
2227            f.write("\n".join(library_paths))
2228        with open(fake_clib_file, 'w') as f:
2229            pass
2230
2231        return info
2232
2233    def check_symbols(self, info):
2234        res = False
2235        c = customized_ccompiler()
2236
2237        tmpdir = tempfile.mkdtemp()
2238
2239        prototypes = "\n".join("void %s%s%s();" % (self.symbol_prefix,
2240                                                   symbol_name,
2241                                                   self.symbol_suffix)
2242                               for symbol_name in self._require_symbols)
2243        calls = "\n".join("%s%s%s();" % (self.symbol_prefix,
2244                                         symbol_name,
2245                                         self.symbol_suffix)
2246                          for symbol_name in self._require_symbols)
2247        s = textwrap.dedent("""\
2248            %(prototypes)s
2249            int main(int argc, const char *argv[])
2250            {
2251                %(calls)s
2252                return 0;
2253            }""") % dict(prototypes=prototypes, calls=calls)
2254        src = os.path.join(tmpdir, 'source.c')
2255        out = os.path.join(tmpdir, 'a.out')
2256        # Add the additional "extra" arguments
2257        try:
2258            extra_args = info['extra_link_args']
2259        except Exception:
2260            extra_args = []
2261        try:
2262            with open(src, 'wt') as f:
2263                f.write(s)
2264            obj = c.compile([src], output_dir=tmpdir)
2265            try:
2266                c.link_executable(obj, out, libraries=info['libraries'],
2267                                  library_dirs=info['library_dirs'],
2268                                  extra_postargs=extra_args)
2269                res = True
2270            except distutils.ccompiler.LinkError:
2271                res = False
2272        finally:
2273            shutil.rmtree(tmpdir)
2274        return res
2275
2276class openblas_lapack_info(openblas_info):
2277    section = 'openblas'
2278    dir_env_var = 'OPENBLAS'
2279    _lib_names = ['openblas']
2280    _require_symbols = ['zungqr_']
2281    notfounderror = BlasNotFoundError
2282
2283class openblas_clapack_info(openblas_lapack_info):
2284    _lib_names = ['openblas', 'lapack']
2285
2286class openblas_ilp64_info(openblas_info):
2287    section = 'openblas_ilp64'
2288    dir_env_var = 'OPENBLAS_ILP64'
2289    _lib_names = ['openblas64']
2290    _require_symbols = ['dgemm_', 'cblas_dgemm']
2291    notfounderror = BlasILP64NotFoundError
2292
2293    def _calc_info(self):
2294        info = super()._calc_info()
2295        if info is not None:
2296            info['define_macros'] += [('HAVE_BLAS_ILP64', None)]
2297        return info
2298
2299class openblas_ilp64_lapack_info(openblas_ilp64_info):
2300    _require_symbols = ['dgemm_', 'cblas_dgemm', 'zungqr_', 'LAPACKE_zungqr']
2301
2302    def _calc_info(self):
2303        info = super()._calc_info()
2304        if info:
2305            info['define_macros'] += [('HAVE_LAPACKE', None)]
2306        return info
2307
2308class openblas64__info(openblas_ilp64_info):
2309    # ILP64 Openblas, with default symbol suffix
2310    section = 'openblas64_'
2311    dir_env_var = 'OPENBLAS64_'
2312    _lib_names = ['openblas64_']
2313    symbol_suffix = '64_'
2314    symbol_prefix = ''
2315
2316class openblas64__lapack_info(openblas_ilp64_lapack_info, openblas64__info):
2317    pass
2318
2319class blis_info(blas_info):
2320    section = 'blis'
2321    dir_env_var = 'BLIS'
2322    _lib_names = ['blis']
2323    notfounderror = BlasNotFoundError
2324
2325    def calc_info(self):
2326        lib_dirs = self.get_lib_dirs()
2327        opt = self.get_option_single('blis_libs', 'libraries')
2328        blis_libs = self.get_libs(opt, self._lib_names)
2329        info = self.check_libs2(lib_dirs, blis_libs, [])
2330        if info is None:
2331            return
2332
2333        # Add include dirs
2334        incl_dirs = self.get_include_dirs()
2335        dict_append(info,
2336                    language='c',
2337                    define_macros=[('HAVE_CBLAS', None)],
2338                    include_dirs=incl_dirs)
2339        self.set_info(**info)
2340
2341
2342class flame_info(system_info):
2343    """ Usage of libflame for LAPACK operations
2344
2345    This requires libflame to be compiled with lapack wrappers:
2346
2347    ./configure --enable-lapack2flame ...
2348
2349    Be aware that libflame 5.1.0 has some missing names in the shared library, so
2350    if you have problems, try the static flame library.
2351    """
2352    section = 'flame'
2353    _lib_names = ['flame']
2354    notfounderror = FlameNotFoundError
2355
2356    def check_embedded_lapack(self, info):
2357        """ libflame does not necessarily have a wrapper for fortran LAPACK, we need to check """
2358        c = customized_ccompiler()
2359
2360        tmpdir = tempfile.mkdtemp()
2361        s = textwrap.dedent("""\
2362            void zungqr_();
2363            int main(int argc, const char *argv[])
2364            {
2365                zungqr_();
2366                return 0;
2367            }""")
2368        src = os.path.join(tmpdir, 'source.c')
2369        out = os.path.join(tmpdir, 'a.out')
2370        # Add the additional "extra" arguments
2371        extra_args = info.get('extra_link_args', [])
2372        try:
2373            with open(src, 'wt') as f:
2374                f.write(s)
2375            obj = c.compile([src], output_dir=tmpdir)
2376            try:
2377                c.link_executable(obj, out, libraries=info['libraries'],
2378                                  library_dirs=info['library_dirs'],
2379                                  extra_postargs=extra_args)
2380                return True
2381            except distutils.ccompiler.LinkError:
2382                return False
2383        finally:
2384            shutil.rmtree(tmpdir)
2385
2386    def calc_info(self):
2387        lib_dirs = self.get_lib_dirs()
2388        flame_libs = self.get_libs('libraries', self._lib_names)
2389
2390        info = self.check_libs2(lib_dirs, flame_libs, [])
2391        if info is None:
2392            return
2393
2394        if self.check_embedded_lapack(info):
2395            # check if the user has supplied all information required
2396            self.set_info(**info)
2397        else:
2398            # Try and get the BLAS lib to see if we can get it to work
2399            blas_info = get_info('blas_opt')
2400            if not blas_info:
2401                # since we already failed once, this ain't going to work either
2402                return
2403
2404            # Now we need to merge the two dictionaries
2405            for key in blas_info:
2406                if isinstance(blas_info[key], list):
2407                    info[key] = info.get(key, []) + blas_info[key]
2408                elif isinstance(blas_info[key], tuple):
2409                    info[key] = info.get(key, ()) + blas_info[key]
2410                else:
2411                    info[key] = info.get(key, '') + blas_info[key]
2412
2413            # Now check again
2414            if self.check_embedded_lapack(info):
2415                self.set_info(**info)
2416
2417
2418class accelerate_info(system_info):
2419    section = 'accelerate'
2420    _lib_names = ['accelerate', 'veclib']
2421    notfounderror = BlasNotFoundError
2422
2423    def calc_info(self):
2424        # Make possible to enable/disable from config file/env var
2425        libraries = os.environ.get('ACCELERATE')
2426        if libraries:
2427            libraries = [libraries]
2428        else:
2429            libraries = self.get_libs('libraries', self._lib_names)
2430        libraries = [lib.strip().lower() for lib in libraries]
2431
2432        if (sys.platform == 'darwin' and
2433                not os.getenv('_PYTHON_HOST_PLATFORM', None)):
2434            # Use the system BLAS from Accelerate or vecLib under OSX
2435            args = []
2436            link_args = []
2437            if get_platform()[-4:] == 'i386' or 'intel' in get_platform() or \
2438               'x86_64' in get_platform() or \
2439               'i386' in platform.platform():
2440                intel = 1
2441            else:
2442                intel = 0
2443            if (os.path.exists('/System/Library/Frameworks'
2444                              '/Accelerate.framework/') and
2445                    'accelerate' in libraries):
2446                if intel:
2447                    args.extend(['-msse3'])
2448                args.extend([
2449                    '-I/System/Library/Frameworks/vecLib.framework/Headers'])
2450                link_args.extend(['-Wl,-framework', '-Wl,Accelerate'])
2451            elif (os.path.exists('/System/Library/Frameworks'
2452                                 '/vecLib.framework/') and
2453                      'veclib' in libraries):
2454                if intel:
2455                    args.extend(['-msse3'])
2456                args.extend([
2457                    '-I/System/Library/Frameworks/vecLib.framework/Headers'])
2458                link_args.extend(['-Wl,-framework', '-Wl,vecLib'])
2459
2460            if args:
2461                self.set_info(extra_compile_args=args,
2462                              extra_link_args=link_args,
2463                              define_macros=[('NO_ATLAS_INFO', 3),
2464                                             ('HAVE_CBLAS', None)])
2465
2466        return
2467
2468class blas_src_info(system_info):
2469    section = 'blas_src'
2470    dir_env_var = 'BLAS_SRC'
2471    notfounderror = BlasSrcNotFoundError
2472
2473    def get_paths(self, section, key):
2474        pre_dirs = system_info.get_paths(self, section, key)
2475        dirs = []
2476        for d in pre_dirs:
2477            dirs.extend([d] + self.combine_paths(d, ['blas']))
2478        return [d for d in dirs if os.path.isdir(d)]
2479
2480    def calc_info(self):
2481        src_dirs = self.get_src_dirs()
2482        src_dir = ''
2483        for d in src_dirs:
2484            if os.path.isfile(os.path.join(d, 'daxpy.f')):
2485                src_dir = d
2486                break
2487        if not src_dir:
2488            #XXX: Get sources from netlib. May be ask first.
2489            return
2490        blas1 = '''
2491        caxpy csscal dnrm2 dzasum saxpy srotg zdotc ccopy cswap drot
2492        dznrm2 scasum srotm zdotu cdotc dasum drotg icamax scnrm2
2493        srotmg zdrot cdotu daxpy drotm idamax scopy sscal zdscal crotg
2494        dcabs1 drotmg isamax sdot sswap zrotg cscal dcopy dscal izamax
2495        snrm2 zaxpy zscal csrot ddot dswap sasum srot zcopy zswap
2496        scabs1
2497        '''
2498        blas2 = '''
2499        cgbmv chpmv ctrsv dsymv dtrsv sspr2 strmv zhemv ztpmv cgemv
2500        chpr dgbmv dsyr lsame ssymv strsv zher ztpsv cgerc chpr2 dgemv
2501        dsyr2 sgbmv ssyr xerbla zher2 ztrmv cgeru ctbmv dger dtbmv
2502        sgemv ssyr2 zgbmv zhpmv ztrsv chbmv ctbsv dsbmv dtbsv sger
2503        stbmv zgemv zhpr chemv ctpmv dspmv dtpmv ssbmv stbsv zgerc
2504        zhpr2 cher ctpsv dspr dtpsv sspmv stpmv zgeru ztbmv cher2
2505        ctrmv dspr2 dtrmv sspr stpsv zhbmv ztbsv
2506        '''
2507        blas3 = '''
2508        cgemm csymm ctrsm dsyrk sgemm strmm zhemm zsyr2k chemm csyr2k
2509        dgemm dtrmm ssymm strsm zher2k zsyrk cher2k csyrk dsymm dtrsm
2510        ssyr2k zherk ztrmm cherk ctrmm dsyr2k ssyrk zgemm zsymm ztrsm
2511        '''
2512        sources = [os.path.join(src_dir, f + '.f') \
2513                   for f in (blas1 + blas2 + blas3).split()]
2514        #XXX: should we check here actual existence of source files?
2515        sources = [f for f in sources if os.path.isfile(f)]
2516        info = {'sources': sources, 'language': 'f77'}
2517        self.set_info(**info)
2518
2519
2520class x11_info(system_info):
2521    section = 'x11'
2522    notfounderror = X11NotFoundError
2523    _lib_names = ['X11']
2524
2525    def __init__(self):
2526        system_info.__init__(self,
2527                             default_lib_dirs=default_x11_lib_dirs,
2528                             default_include_dirs=default_x11_include_dirs)
2529
2530    def calc_info(self):
2531        if sys.platform  in ['win32']:
2532            return
2533        lib_dirs = self.get_lib_dirs()
2534        include_dirs = self.get_include_dirs()
2535        opt = self.get_option_single('x11_libs', 'libraries')
2536        x11_libs = self.get_libs(opt, self._lib_names)
2537        info = self.check_libs(lib_dirs, x11_libs, [])
2538        if info is None:
2539            return
2540        inc_dir = None
2541        for d in include_dirs:
2542            if self.combine_paths(d, 'X11/X.h'):
2543                inc_dir = d
2544                break
2545        if inc_dir is not None:
2546            dict_append(info, include_dirs=[inc_dir])
2547        self.set_info(**info)
2548
2549
2550class _numpy_info(system_info):
2551    section = 'Numeric'
2552    modulename = 'Numeric'
2553    notfounderror = NumericNotFoundError
2554
2555    def __init__(self):
2556        include_dirs = []
2557        try:
2558            module = __import__(self.modulename)
2559            prefix = []
2560            for name in module.__file__.split(os.sep):
2561                if name == 'lib':
2562                    break
2563                prefix.append(name)
2564
2565            # Ask numpy for its own include path before attempting
2566            # anything else
2567            try:
2568                include_dirs.append(getattr(module, 'get_include')())
2569            except AttributeError:
2570                pass
2571
2572            include_dirs.append(sysconfig.get_path('include'))
2573        except ImportError:
2574            pass
2575        py_incl_dir = sysconfig.get_path('include')
2576        include_dirs.append(py_incl_dir)
2577        py_pincl_dir = sysconfig.get_path('platinclude')
2578        if py_pincl_dir not in include_dirs:
2579            include_dirs.append(py_pincl_dir)
2580        for d in default_include_dirs:
2581            d = os.path.join(d, os.path.basename(py_incl_dir))
2582            if d not in include_dirs:
2583                include_dirs.append(d)
2584        system_info.__init__(self,
2585                             default_lib_dirs=[],
2586                             default_include_dirs=include_dirs)
2587
2588    def calc_info(self):
2589        try:
2590            module = __import__(self.modulename)
2591        except ImportError:
2592            return
2593        info = {}
2594        macros = []
2595        for v in ['__version__', 'version']:
2596            vrs = getattr(module, v, None)
2597            if vrs is None:
2598                continue
2599            macros = [(self.modulename.upper() + '_VERSION',
2600                      _c_string_literal(vrs)),
2601                      (self.modulename.upper(), None)]
2602            break
2603        dict_append(info, define_macros=macros)
2604        include_dirs = self.get_include_dirs()
2605        inc_dir = None
2606        for d in include_dirs:
2607            if self.combine_paths(d,
2608                                  os.path.join(self.modulename,
2609                                               'arrayobject.h')):
2610                inc_dir = d
2611                break
2612        if inc_dir is not None:
2613            dict_append(info, include_dirs=[inc_dir])
2614        if info:
2615            self.set_info(**info)
2616        return
2617
2618
2619class numarray_info(_numpy_info):
2620    section = 'numarray'
2621    modulename = 'numarray'
2622
2623
2624class Numeric_info(_numpy_info):
2625    section = 'Numeric'
2626    modulename = 'Numeric'
2627
2628
2629class numpy_info(_numpy_info):
2630    section = 'numpy'
2631    modulename = 'numpy'
2632
2633
2634class numerix_info(system_info):
2635    section = 'numerix'
2636
2637    def calc_info(self):
2638        which = None, None
2639        if os.getenv("NUMERIX"):
2640            which = os.getenv("NUMERIX"), "environment var"
2641        # If all the above fail, default to numpy.
2642        if which[0] is None:
2643            which = "numpy", "defaulted"
2644            try:
2645                import numpy  # noqa: F401
2646                which = "numpy", "defaulted"
2647            except ImportError as e:
2648                msg1 = str(e)
2649                try:
2650                    import Numeric  # noqa: F401
2651                    which = "numeric", "defaulted"
2652                except ImportError as e:
2653                    msg2 = str(e)
2654                    try:
2655                        import numarray  # noqa: F401
2656                        which = "numarray", "defaulted"
2657                    except ImportError as e:
2658                        msg3 = str(e)
2659                        log.info(msg1)
2660                        log.info(msg2)
2661                        log.info(msg3)
2662        which = which[0].strip().lower(), which[1]
2663        if which[0] not in ["numeric", "numarray", "numpy"]:
2664            raise ValueError("numerix selector must be either 'Numeric' "
2665                             "or 'numarray' or 'numpy' but the value obtained"
2666                             " from the %s was '%s'." % (which[1], which[0]))
2667        os.environ['NUMERIX'] = which[0]
2668        self.set_info(**get_info(which[0]))
2669
2670
2671class f2py_info(system_info):
2672    def calc_info(self):
2673        try:
2674            import numpy.f2py as f2py
2675        except ImportError:
2676            return
2677        f2py_dir = os.path.join(os.path.dirname(f2py.__file__), 'src')
2678        self.set_info(sources=[os.path.join(f2py_dir, 'fortranobject.c')],
2679                      include_dirs=[f2py_dir])
2680        return
2681
2682
2683class boost_python_info(system_info):
2684    section = 'boost_python'
2685    dir_env_var = 'BOOST'
2686
2687    def get_paths(self, section, key):
2688        pre_dirs = system_info.get_paths(self, section, key)
2689        dirs = []
2690        for d in pre_dirs:
2691            dirs.extend([d] + self.combine_paths(d, ['boost*']))
2692        return [d for d in dirs if os.path.isdir(d)]
2693
2694    def calc_info(self):
2695        src_dirs = self.get_src_dirs()
2696        src_dir = ''
2697        for d in src_dirs:
2698            if os.path.isfile(os.path.join(d, 'libs', 'python', 'src',
2699                                           'module.cpp')):
2700                src_dir = d
2701                break
2702        if not src_dir:
2703            return
2704        py_incl_dirs = [sysconfig.get_path('include')]
2705        py_pincl_dir = sysconfig.get_path('platinclude')
2706        if py_pincl_dir not in py_incl_dirs:
2707            py_incl_dirs.append(py_pincl_dir)
2708        srcs_dir = os.path.join(src_dir, 'libs', 'python', 'src')
2709        bpl_srcs = glob(os.path.join(srcs_dir, '*.cpp'))
2710        bpl_srcs += glob(os.path.join(srcs_dir, '*', '*.cpp'))
2711        info = {'libraries': [('boost_python_src',
2712                               {'include_dirs': [src_dir] + py_incl_dirs,
2713                                'sources':bpl_srcs}
2714                              )],
2715                'include_dirs': [src_dir],
2716                }
2717        if info:
2718            self.set_info(**info)
2719        return
2720
2721
2722class agg2_info(system_info):
2723    section = 'agg2'
2724    dir_env_var = 'AGG2'
2725
2726    def get_paths(self, section, key):
2727        pre_dirs = system_info.get_paths(self, section, key)
2728        dirs = []
2729        for d in pre_dirs:
2730            dirs.extend([d] + self.combine_paths(d, ['agg2*']))
2731        return [d for d in dirs if os.path.isdir(d)]
2732
2733    def calc_info(self):
2734        src_dirs = self.get_src_dirs()
2735        src_dir = ''
2736        for d in src_dirs:
2737            if os.path.isfile(os.path.join(d, 'src', 'agg_affine_matrix.cpp')):
2738                src_dir = d
2739                break
2740        if not src_dir:
2741            return
2742        if sys.platform == 'win32':
2743            agg2_srcs = glob(os.path.join(src_dir, 'src', 'platform',
2744                                          'win32', 'agg_win32_bmp.cpp'))
2745        else:
2746            agg2_srcs = glob(os.path.join(src_dir, 'src', '*.cpp'))
2747            agg2_srcs += [os.path.join(src_dir, 'src', 'platform',
2748                                       'X11',
2749                                       'agg_platform_support.cpp')]
2750
2751        info = {'libraries':
2752                [('agg2_src',
2753                  {'sources': agg2_srcs,
2754                   'include_dirs': [os.path.join(src_dir, 'include')],
2755                  }
2756                 )],
2757                'include_dirs': [os.path.join(src_dir, 'include')],
2758                }
2759        if info:
2760            self.set_info(**info)
2761        return
2762
2763
2764class _pkg_config_info(system_info):
2765    section = None
2766    config_env_var = 'PKG_CONFIG'
2767    default_config_exe = 'pkg-config'
2768    append_config_exe = ''
2769    version_macro_name = None
2770    release_macro_name = None
2771    version_flag = '--modversion'
2772    cflags_flag = '--cflags'
2773
2774    def get_config_exe(self):
2775        if self.config_env_var in os.environ:
2776            return os.environ[self.config_env_var]
2777        return self.default_config_exe
2778
2779    def get_config_output(self, config_exe, option):
2780        cmd = config_exe + ' ' + self.append_config_exe + ' ' + option
2781        try:
2782            o = subprocess.check_output(cmd)
2783        except (OSError, subprocess.CalledProcessError):
2784            pass
2785        else:
2786            o = filepath_from_subprocess_output(o)
2787            return o
2788
2789    def calc_info(self):
2790        config_exe = find_executable(self.get_config_exe())
2791        if not config_exe:
2792            log.warn('File not found: %s. Cannot determine %s info.' \
2793                  % (config_exe, self.section))
2794            return
2795        info = {}
2796        macros = []
2797        libraries = []
2798        library_dirs = []
2799        include_dirs = []
2800        extra_link_args = []
2801        extra_compile_args = []
2802        version = self.get_config_output(config_exe, self.version_flag)
2803        if version:
2804            macros.append((self.__class__.__name__.split('.')[-1].upper(),
2805                           _c_string_literal(version)))
2806            if self.version_macro_name:
2807                macros.append((self.version_macro_name + '_%s'
2808                               % (version.replace('.', '_')), None))
2809        if self.release_macro_name:
2810            release = self.get_config_output(config_exe, '--release')
2811            if release:
2812                macros.append((self.release_macro_name + '_%s'
2813                               % (release.replace('.', '_')), None))
2814        opts = self.get_config_output(config_exe, '--libs')
2815        if opts:
2816            for opt in opts.split():
2817                if opt[:2] == '-l':
2818                    libraries.append(opt[2:])
2819                elif opt[:2] == '-L':
2820                    library_dirs.append(opt[2:])
2821                else:
2822                    extra_link_args.append(opt)
2823        opts = self.get_config_output(config_exe, self.cflags_flag)
2824        if opts:
2825            for opt in opts.split():
2826                if opt[:2] == '-I':
2827                    include_dirs.append(opt[2:])
2828                elif opt[:2] == '-D':
2829                    if '=' in opt:
2830                        n, v = opt[2:].split('=')
2831                        macros.append((n, v))
2832                    else:
2833                        macros.append((opt[2:], None))
2834                else:
2835                    extra_compile_args.append(opt)
2836        if macros:
2837            dict_append(info, define_macros=macros)
2838        if libraries:
2839            dict_append(info, libraries=libraries)
2840        if library_dirs:
2841            dict_append(info, library_dirs=library_dirs)
2842        if include_dirs:
2843            dict_append(info, include_dirs=include_dirs)
2844        if extra_link_args:
2845            dict_append(info, extra_link_args=extra_link_args)
2846        if extra_compile_args:
2847            dict_append(info, extra_compile_args=extra_compile_args)
2848        if info:
2849            self.set_info(**info)
2850        return
2851
2852
2853class wx_info(_pkg_config_info):
2854    section = 'wx'
2855    config_env_var = 'WX_CONFIG'
2856    default_config_exe = 'wx-config'
2857    append_config_exe = ''
2858    version_macro_name = 'WX_VERSION'
2859    release_macro_name = 'WX_RELEASE'
2860    version_flag = '--version'
2861    cflags_flag = '--cxxflags'
2862
2863
2864class gdk_pixbuf_xlib_2_info(_pkg_config_info):
2865    section = 'gdk_pixbuf_xlib_2'
2866    append_config_exe = 'gdk-pixbuf-xlib-2.0'
2867    version_macro_name = 'GDK_PIXBUF_XLIB_VERSION'
2868
2869
2870class gdk_pixbuf_2_info(_pkg_config_info):
2871    section = 'gdk_pixbuf_2'
2872    append_config_exe = 'gdk-pixbuf-2.0'
2873    version_macro_name = 'GDK_PIXBUF_VERSION'
2874
2875
2876class gdk_x11_2_info(_pkg_config_info):
2877    section = 'gdk_x11_2'
2878    append_config_exe = 'gdk-x11-2.0'
2879    version_macro_name = 'GDK_X11_VERSION'
2880
2881
2882class gdk_2_info(_pkg_config_info):
2883    section = 'gdk_2'
2884    append_config_exe = 'gdk-2.0'
2885    version_macro_name = 'GDK_VERSION'
2886
2887
2888class gdk_info(_pkg_config_info):
2889    section = 'gdk'
2890    append_config_exe = 'gdk'
2891    version_macro_name = 'GDK_VERSION'
2892
2893
2894class gtkp_x11_2_info(_pkg_config_info):
2895    section = 'gtkp_x11_2'
2896    append_config_exe = 'gtk+-x11-2.0'
2897    version_macro_name = 'GTK_X11_VERSION'
2898
2899
2900class gtkp_2_info(_pkg_config_info):
2901    section = 'gtkp_2'
2902    append_config_exe = 'gtk+-2.0'
2903    version_macro_name = 'GTK_VERSION'
2904
2905
2906class xft_info(_pkg_config_info):
2907    section = 'xft'
2908    append_config_exe = 'xft'
2909    version_macro_name = 'XFT_VERSION'
2910
2911
2912class freetype2_info(_pkg_config_info):
2913    section = 'freetype2'
2914    append_config_exe = 'freetype2'
2915    version_macro_name = 'FREETYPE2_VERSION'
2916
2917
2918class amd_info(system_info):
2919    section = 'amd'
2920    dir_env_var = 'AMD'
2921    _lib_names = ['amd']
2922
2923    def calc_info(self):
2924        lib_dirs = self.get_lib_dirs()
2925
2926        opt = self.get_option_single('amd_libs', 'libraries')
2927        amd_libs = self.get_libs(opt, self._lib_names)
2928        info = self.check_libs(lib_dirs, amd_libs, [])
2929        if info is None:
2930            return
2931
2932        include_dirs = self.get_include_dirs()
2933
2934        inc_dir = None
2935        for d in include_dirs:
2936            p = self.combine_paths(d, 'amd.h')
2937            if p:
2938                inc_dir = os.path.dirname(p[0])
2939                break
2940        if inc_dir is not None:
2941            dict_append(info, include_dirs=[inc_dir],
2942                        define_macros=[('SCIPY_AMD_H', None)],
2943                        swig_opts=['-I' + inc_dir])
2944
2945        self.set_info(**info)
2946        return
2947
2948
2949class umfpack_info(system_info):
2950    section = 'umfpack'
2951    dir_env_var = 'UMFPACK'
2952    notfounderror = UmfpackNotFoundError
2953    _lib_names = ['umfpack']
2954
2955    def calc_info(self):
2956        lib_dirs = self.get_lib_dirs()
2957
2958        opt = self.get_option_single('umfpack_libs', 'libraries')
2959        umfpack_libs = self.get_libs(opt, self._lib_names)
2960        info = self.check_libs(lib_dirs, umfpack_libs, [])
2961        if info is None:
2962            return
2963
2964        include_dirs = self.get_include_dirs()
2965
2966        inc_dir = None
2967        for d in include_dirs:
2968            p = self.combine_paths(d, ['', 'umfpack'], 'umfpack.h')
2969            if p:
2970                inc_dir = os.path.dirname(p[0])
2971                break
2972        if inc_dir is not None:
2973            dict_append(info, include_dirs=[inc_dir],
2974                        define_macros=[('SCIPY_UMFPACK_H', None)],
2975                        swig_opts=['-I' + inc_dir])
2976
2977        dict_append(info, **get_info('amd'))
2978
2979        self.set_info(**info)
2980        return
2981
2982
2983def combine_paths(*args, **kws):
2984    """ Return a list of existing paths composed by all combinations of
2985        items from arguments.
2986    """
2987    r = []
2988    for a in args:
2989        if not a:
2990            continue
2991        if is_string(a):
2992            a = [a]
2993        r.append(a)
2994    args = r
2995    if not args:
2996        return []
2997    if len(args) == 1:
2998        result = reduce(lambda a, b: a + b, map(glob, args[0]), [])
2999    elif len(args) == 2:
3000        result = []
3001        for a0 in args[0]:
3002            for a1 in args[1]:
3003                result.extend(glob(os.path.join(a0, a1)))
3004    else:
3005        result = combine_paths(*(combine_paths(args[0], args[1]) + args[2:]))
3006    log.debug('(paths: %s)', ','.join(result))
3007    return result
3008
3009language_map = {'c': 0, 'c++': 1, 'f77': 2, 'f90': 3}
3010inv_language_map = {0: 'c', 1: 'c++', 2: 'f77', 3: 'f90'}
3011
3012
3013def dict_append(d, **kws):
3014    languages = []
3015    for k, v in kws.items():
3016        if k == 'language':
3017            languages.append(v)
3018            continue
3019        if k in d:
3020            if k in ['library_dirs', 'include_dirs',
3021                     'extra_compile_args', 'extra_link_args',
3022                     'runtime_library_dirs', 'define_macros']:
3023                [d[k].append(vv) for vv in v if vv not in d[k]]
3024            else:
3025                d[k].extend(v)
3026        else:
3027            d[k] = v
3028    if languages:
3029        l = inv_language_map[max([language_map.get(l, 0) for l in languages])]
3030        d['language'] = l
3031    return
3032
3033
3034def parseCmdLine(argv=(None,)):
3035    import optparse
3036    parser = optparse.OptionParser("usage: %prog [-v] [info objs]")
3037    parser.add_option('-v', '--verbose', action='store_true', dest='verbose',
3038                      default=False,
3039                      help='be verbose and print more messages')
3040
3041    opts, args = parser.parse_args(args=argv[1:])
3042    return opts, args
3043
3044
3045def show_all(argv=None):
3046    import inspect
3047    if argv is None:
3048        argv = sys.argv
3049    opts, args = parseCmdLine(argv)
3050    if opts.verbose:
3051        log.set_threshold(log.DEBUG)
3052    else:
3053        log.set_threshold(log.INFO)
3054    show_only = []
3055    for n in args:
3056        if n[-5:] != '_info':
3057            n = n + '_info'
3058        show_only.append(n)
3059    show_all = not show_only
3060    _gdict_ = globals().copy()
3061    for name, c in _gdict_.items():
3062        if not inspect.isclass(c):
3063            continue
3064        if not issubclass(c, system_info) or c is system_info:
3065            continue
3066        if not show_all:
3067            if name not in show_only:
3068                continue
3069            del show_only[show_only.index(name)]
3070        conf = c()
3071        conf.verbosity = 2
3072        # FIXME: r not used
3073        r = conf.get_info()
3074    if show_only:
3075        log.info('Info classes not defined: %s', ','.join(show_only))
3076
3077if __name__ == "__main__":
3078    show_all()
3079