| # Last Change: Sun Apr 26 05:00 PM 2009 J |
| # vim:syntax=python |
| import os |
| import sys |
| from os.path import join as pjoin, basename as pbasename, dirname as pdirname |
| from copy import deepcopy |
| |
| from numscons import get_pythonlib_dir |
| from numscons import GetNumpyEnvironment |
| from numscons import CheckCBLAS |
| from numscons import write_info |
| |
| from code_generators.numpy_api import \ |
| multiarray_api as multiarray_api_dict, \ |
| ufunc_api as ufunc_api_dict |
| |
| from setup_common import * |
| from scons_support import CheckBrokenMathlib, define_no_smp, \ |
| check_mlib, check_mlibs, is_npy_no_signal, CheckInline |
| from scons_support import array_api_gen_bld, ufunc_api_gen_bld, template_bld, \ |
| umath_bld, CheckGCC4, check_api_version, \ |
| CheckLongDoubleRepresentation |
| |
| import SCons |
| |
| # Set to True to enable multiple file compilations (experimental) |
| try: |
| os.environ['NPY_SEPARATE_COMPILATION'] |
| ENABLE_SEPARATE_COMPILATION = True |
| except KeyError: |
| ENABLE_SEPARATE_COMPILATION = False |
| try: |
| os.environ['NPY_BYPASS_SINGLE_EXTENDED'] |
| BYPASS_SINGLE_EXTENDED = True |
| except KeyError: |
| BYPASS_SINGLE_EXTENDED = False |
| |
| env = GetNumpyEnvironment(ARGUMENTS) |
| env.Append(CPPPATH = env["PYEXTCPPPATH"]) |
| if os.name == 'nt': |
| # NT needs the pythonlib to run any code importing Python.h, including |
| # simple code using only typedef and so on, so we need it for configuration |
| # checks |
| env.AppendUnique(LIBPATH = [get_pythonlib_dir()]) |
| |
| # Check whether we have a mismatch between the set C API VERSION and the |
| # actual C API VERSION |
| check_api_version(C_API_VERSION) |
| |
| #======================= |
| # Starting Configuration |
| #======================= |
| config = env.NumpyConfigure(custom_tests = {'CheckBrokenMathlib' : CheckBrokenMathlib, |
| 'CheckCBLAS' : CheckCBLAS, 'CheckInline': CheckInline, 'CheckGCC4' : CheckGCC4, |
| 'CheckLongDoubleRepresentation': CheckLongDoubleRepresentation}, |
| config_h = pjoin('config.h')) |
| |
| # numpyconfig_sym will keep the values of some configuration variables, the one |
| # needed for the public numpy API. |
| |
| # Convention: list of tuples (definition, value). value: |
| # - 0: #undef definition |
| # - 1: #define definition |
| # - string: #define definition value |
| numpyconfig_sym = [] |
| |
| #--------------- |
| # Checking Types |
| #--------------- |
| if not config.CheckHeader("Python.h"): |
| errmsg = [] |
| for line in config.GetLastError(): |
| errmsg.append("%s " % line) |
| print """ |
| Error: Python.h header cannot be compiled (or cannot be found). |
| On linux, check that you have python-dev/python-devel packages. On windows, |
| check that you have he platform SDK. You may also use unsupported cflags. |
| Configuration error log says: \n\n%s""" % ''.join(errmsg) |
| Exit(-1) |
| |
| st = config.CheckHeader("endian.h") |
| if st: |
| numpyconfig_sym.append(('DEFINE_NPY_HAVE_ENDIAN_H', '#define NPY_HAVE_ENDIAN_H 1')) |
| else: |
| numpyconfig_sym.append(('DEFINE_NPY_HAVE_ENDIAN_H', '')) |
| |
| def check_type(type, include = None): |
| st = config.CheckTypeSize(type, includes = include) |
| type = type.replace(' ', '_') |
| if st: |
| numpyconfig_sym.append(('SIZEOF_%s' % type.upper(), '%d' % st)) |
| else: |
| numpyconfig_sym.append(('SIZEOF_%s' % type.upper(), 0)) |
| |
| for type in ('short', 'int', 'long'): |
| # SIZEOF_LONG defined on darwin |
| if type == "long": |
| if not config.CheckDeclaration("SIZEOF_LONG", includes="#include <Python.h>"): |
| check_type(type) |
| else: |
| numpyconfig_sym.append(('SIZEOF_LONG', 'SIZEOF_LONG')) |
| else: |
| check_type(type) |
| |
| for type in ('float', 'double', 'long double'): |
| sz = config.CheckTypeSize(type) |
| numpyconfig_sym.append(('SIZEOF_%s' % type2def(type), str(sz))) |
| |
| # Compute size of corresponding complex type: used to check that our |
| # definition is binary compatible with C99 complex type (check done at |
| # build time in npy_common.h) |
| complex_def = "struct {%s __x; %s __y;}" % (type, type) |
| sz = config.CheckTypeSize(complex_def) |
| numpyconfig_sym.append(('SIZEOF_COMPLEX_%s' % type2def(type), str(sz))) |
| |
| if sys.platform != 'darwin': |
| tp = config.CheckLongDoubleRepresentation() |
| config.Define("HAVE_LDOUBLE_%s" % tp, 1, |
| "Define for arch-specific long double representation") |
| |
| for type in ('Py_intptr_t',): |
| check_type(type, include = "#include <Python.h>\n") |
| |
| # We check declaration AND type because that's how distutils does it. |
| if config.CheckDeclaration('PY_LONG_LONG', includes = '#include <Python.h>\n'): |
| st = config.CheckTypeSize('PY_LONG_LONG', |
| includes = '#include <Python.h>\n') |
| assert not st == 0 |
| numpyconfig_sym.append(('DEFINE_NPY_SIZEOF_LONGLONG', |
| '#define NPY_SIZEOF_LONGLONG %d' % st)) |
| numpyconfig_sym.append(('DEFINE_NPY_SIZEOF_PY_LONG_LONG', |
| '#define NPY_SIZEOF_PY_LONG_LONG %d' % st)) |
| else: |
| numpyconfig_sym.append(('DEFINE_NPY_SIZEOF_LONGLONG', '')) |
| numpyconfig_sym.append(('DEFINE_NPY_SIZEOF_PY_LONG_LONG', '')) |
| |
| if not config.CheckDeclaration('CHAR_BIT', includes= '#include <Python.h>\n'): |
| raise RuntimeError(\ |
| """Config wo CHAR_BIT is not supported with scons: please contact the |
| maintainer (cdavid)""") |
| |
| #---------------------- |
| # Checking signal stuff |
| #---------------------- |
| if is_npy_no_signal(): |
| numpyconfig_sym.append(('DEFINE_NPY_NO_SIGNAL', '#define NPY_NO_SIGNAL\n')) |
| config.Define('__NPY_PRIVATE_NO_SIGNAL', |
| comment = "define to 1 to disable SMP support ") |
| else: |
| numpyconfig_sym.append(('DEFINE_NPY_NO_SIGNAL', '')) |
| |
| #--------------------- |
| # Checking SMP option |
| #--------------------- |
| if define_no_smp(): |
| nosmp = 1 |
| else: |
| nosmp = 0 |
| numpyconfig_sym.append(('NPY_NO_SMP', nosmp)) |
| |
| #---------------------------------------------- |
| # Check whether we can use C99 printing formats |
| #---------------------------------------------- |
| if config.CheckDeclaration(('PRIdPTR'), includes = '#include <inttypes.h>'): |
| numpyconfig_sym.append(('DEFINE_NPY_USE_C99_FORMATS', '#define NPY_USE_C99_FORMATS 1')) |
| else: |
| numpyconfig_sym.append(('DEFINE_NPY_USE_C99_FORMATS', '')) |
| |
| #---------------------- |
| # Checking the mathlib |
| #---------------------- |
| mlibs = [[], ['m'], ['cpml']] |
| mathlib = os.environ.get('MATHLIB') |
| if mathlib: |
| mlibs.insert(0, mathlib) |
| |
| mlib = check_mlibs(config, mlibs) |
| |
| # XXX: this is ugly: mathlib has nothing to do in a public header file |
| numpyconfig_sym.append(('MATHLIB', ','.join(mlib))) |
| |
| #---------------------------------- |
| # Checking the math funcs available |
| #---------------------------------- |
| # Function to check: |
| mfuncs = ('expl', 'expf', 'log1p', 'expm1', 'asinh', 'atanhf', 'atanhl', |
| 'rint', 'trunc') |
| |
| # Set value to 1 for each defined function (in math lib) |
| mfuncs_defined = dict([(f, 0) for f in mfuncs]) |
| |
| # Check for mandatory funcs: we barf if a single one of those is not there |
| if not config.CheckFuncsAtOnce(MANDATORY_FUNCS): |
| raise SystemError("One of the required function to build numpy is not" |
| " available (the list is %s)." % str(MANDATORY_FUNCS)) |
| |
| # Standard functions which may not be available and for which we have a |
| # replacement implementation |
| # |
| def check_funcs(funcs): |
| # Use check_funcs_once first, and if it does not work, test func per |
| # func. Return success only if all the functions are available |
| st = config.CheckFuncsAtOnce(funcs) |
| if not st: |
| # Global check failed, check func per func |
| for f in funcs: |
| st = config.CheckFunc(f, language = 'C') |
| |
| for f in OPTIONAL_STDFUNCS_MAYBE: |
| if config.CheckDeclaration(fname2def(f), |
| includes="#include <Python.h>\n#include <math.h>"): |
| OPTIONAL_STDFUNCS.remove(f) |
| check_funcs(OPTIONAL_STDFUNCS) |
| |
| # C99 functions: float and long double versions |
| if not BYPASS_SINGLE_EXTENDED: |
| check_funcs(C99_FUNCS_SINGLE) |
| check_funcs(C99_FUNCS_EXTENDED) |
| |
| # Normally, isnan and isinf are macro (C99), but some platforms only have |
| # func, or both func and macro version. Check for macro only, and define |
| # replacement ones if not found. |
| # Note: including Python.h is necessary because it modifies some math.h |
| # definitions |
| for f in ["isnan", "isinf", "signbit", "isfinite"]: |
| includes = """\ |
| #include <Python.h> |
| #include <math.h> |
| """ |
| st = config.CheckDeclaration(f, includes=includes) |
| if st: |
| numpyconfig_sym.append(('DEFINE_NPY_HAVE_DECL_%s' % f.upper(), |
| '#define NPY_HAVE_DECL_%s' % f.upper())) |
| else: |
| numpyconfig_sym.append(('DEFINE_NPY_HAVE_DECL_%s' % f.upper(), '')) |
| |
| inline = config.CheckInline() |
| config.Define('inline', inline) |
| |
| |
| if ENABLE_SEPARATE_COMPILATION: |
| config.Define("ENABLE_SEPARATE_COMPILATION", 1) |
| numpyconfig_sym.append(('DEFINE_NPY_ENABLE_SEPARATE_COMPILATION', '#define NPY_ENABLE_SEPARATE_COMPILATION 1')) |
| else: |
| numpyconfig_sym.append(('DEFINE_NPY_ENABLE_SEPARATE_COMPILATION', '')) |
| |
| #----------------------------- |
| # Checking for complex support |
| #----------------------------- |
| if config.CheckHeader('complex.h'): |
| numpyconfig_sym.append(('DEFINE_NPY_USE_C99_COMPLEX', '#define NPY_USE_C99_COMPLEX 1')) |
| |
| for t in C99_COMPLEX_TYPES: |
| st = config.CheckType(t, includes='#include <complex.h>') |
| if st: |
| numpyconfig_sym.append(('DEFINE_NPY_HAVE_%s' % type2def(t), |
| '#define NPY_HAVE_%s' % type2def(t))) |
| else: |
| numpyconfig_sym.append(('DEFINE_NPY_HAVE_%s' % type2def(t), '')) |
| |
| def check_prec(prec): |
| flist = [f + prec for f in C99_COMPLEX_FUNCS] |
| st = config.CheckFuncsAtOnce(flist) |
| if not st: |
| # Global check failed, check func per func |
| for f in flist: |
| config.CheckFunc(f, language='C') |
| |
| check_prec('') |
| check_prec('f') |
| check_prec('l') |
| |
| else: |
| numpyconfig_sym.append(('DEFINE_NPY_USE_C99_COMPLEX', '')) |
| for t in C99_COMPLEX_TYPES: |
| numpyconfig_sym.append(('DEFINE_NPY_HAVE_%s' % type2def(t), '')) |
| |
| def visibility_define(): |
| if config.CheckGCC4(): |
| return '__attribute__((visibility("hidden")))' |
| else: |
| return '' |
| |
| numpyconfig_sym.append(('VISIBILITY_HIDDEN', visibility_define())) |
| |
| # Add the C API/ABI versions |
| numpyconfig_sym.append(('NPY_ABI_VERSION', '0x%.8X' % C_ABI_VERSION)) |
| numpyconfig_sym.append(('NPY_API_VERSION', '0x%.8X' % C_API_VERSION)) |
| |
| # Check whether we need our own wide character support |
| if not config.CheckDeclaration('Py_UNICODE_WIDE', includes='#include <Python.h>'): |
| PYTHON_HAS_UNICODE_WIDE = True |
| else: |
| PYTHON_HAS_UNICODE_WIDE = False |
| |
| #------------------------------------------------------- |
| # Define the function PyOS_ascii_strod if not available |
| #------------------------------------------------------- |
| if not config.CheckDeclaration('PyOS_ascii_strtod', |
| includes = "#include <Python.h>"): |
| if config.CheckFunc('strtod'): |
| config.Define('PyOS_ascii_strtod', 'strtod', |
| "Define to a function to use as a replacement for "\ |
| "PyOS_ascii_strtod if not available in python header") |
| |
| #------------------------------------ |
| # DISTUTILS Hack on AMD64 on windows |
| #------------------------------------ |
| # XXX: this is ugly |
| if sys.platform=='win32' or os.name=='nt': |
| from distutils.msvccompiler import get_build_architecture |
| a = get_build_architecture() |
| print 'BUILD_ARCHITECTURE: %r, os.name=%r, sys.platform=%r' % \ |
| (a, os.name, sys.platform) |
| if a == 'AMD64': |
| distutils_use_sdk = 1 |
| config.Define('DISTUTILS_USE_SDK', distutils_use_sdk, |
| "define to 1 to disable SMP support ") |
| |
| if a == "Intel": |
| config.Define('FORCE_NO_LONG_DOUBLE_FORMATTING', 1, |
| "define to 1 to force long double format string to the" \ |
| " same as double (Lg -> g)") |
| #-------------- |
| # Checking Blas |
| #-------------- |
| if config.CheckCBLAS(): |
| build_blasdot = 1 |
| else: |
| build_blasdot = 0 |
| |
| config.config_h_text += """ |
| #ifndef _NPY_NPY_CONFIG_H_ |
| #error config.h should never be included directly, include npy_config.h instead |
| #endif |
| """ |
| |
| config.Finish() |
| write_info(env) |
| |
| #========== |
| # Build |
| #========== |
| |
| # List of headers which need to be "installed " into the build directory for |
| # proper in-place build support |
| generated_headers = [] |
| |
| #--------------------------------------- |
| # Generate the public configuration file |
| #--------------------------------------- |
| config_dict = {} |
| # XXX: this is ugly, make the API for config.h and numpyconfig.h similar |
| for key, value in numpyconfig_sym: |
| config_dict['@%s@' % key] = str(value) |
| env['SUBST_DICT'] = config_dict |
| |
| include_dir = 'include/numpy' |
| target = env.SubstInFile(pjoin(include_dir, '_numpyconfig.h'), |
| pjoin(include_dir, '_numpyconfig.h.in')) |
| generated_headers.append(target[0]) |
| |
| env['CONFIG_H_GEN'] = numpyconfig_sym |
| |
| #--------------------------- |
| # Builder for generated code |
| #--------------------------- |
| env.Append(BUILDERS = {'GenerateMultiarrayApi' : array_api_gen_bld, |
| 'GenerateUfuncApi' : ufunc_api_gen_bld, |
| 'GenerateFromTemplate' : template_bld, |
| 'GenerateUmath' : umath_bld}) |
| |
| #------------------------ |
| # Generate generated code |
| #------------------------ |
| scalartypes_src = env.GenerateFromTemplate( |
| pjoin('src', 'multiarray', 'scalartypes.c.src')) |
| umath_funcs_src = env.GenerateFromTemplate(pjoin('src', 'umath', 'funcs.inc.src')) |
| umath_loops_src = env.GenerateFromTemplate(pjoin('src', 'umath', 'loops.c.src')) |
| arraytypes_src = env.GenerateFromTemplate( |
| pjoin('src', 'multiarray', 'arraytypes.c.src')) |
| sortmodule_src = env.GenerateFromTemplate(pjoin('src', '_sortmodule.c.src')) |
| umathmodule_src = env.GenerateFromTemplate(pjoin('src', 'umath', |
| 'umathmodule.c.src')) |
| umath_tests_src = env.GenerateFromTemplate(pjoin('src', 'umath', |
| 'umath_tests.c.src')) |
| multiarray_tests_src = env.GenerateFromTemplate(pjoin('src', 'multiarray', |
| 'multiarray_tests.c.src')) |
| scalarmathmodule_src = env.GenerateFromTemplate( |
| pjoin('src', 'scalarmathmodule.c.src')) |
| |
| umath = env.GenerateUmath('__umath_generated', |
| pjoin('code_generators', 'generate_umath.py')) |
| |
| multiarray_api = env.GenerateMultiarrayApi('include/numpy/multiarray_api', |
| [SCons.Node.Python.Value(d) for d in multiarray_api_dict]) |
| generated_headers.append(multiarray_api[0]) |
| |
| ufunc_api = env.GenerateUfuncApi('include/numpy/ufunc_api', |
| [SCons.Node.Python.Value(d) for d in ufunc_api_dict]) |
| generated_headers.append(ufunc_api[0]) |
| |
| # include/numpy is added for compatibility reasons with distutils: this is |
| # needed for __multiarray_api.c and __ufunc_api.c included from multiarray and |
| # ufunc. |
| env.Prepend(CPPPATH = ['src/private', 'include', '.', 'include/numpy']) |
| |
| # npymath core lib |
| npymath_src = [env.GenerateFromTemplate(pjoin('src', 'npymath', 'npy_math.c.src')), |
| env.GenerateFromTemplate(pjoin('src', 'npymath', 'npy_math_complex.c.src')), |
| env.GenerateFromTemplate(pjoin('src', 'npymath', 'ieee754.c.src'))] |
| env.DistutilsInstalledStaticExtLibrary("npymath", npymath_src, install_dir='lib') |
| env.Prepend(LIBS=["npymath"]) |
| env.Prepend(LIBPATH=["."]) |
| |
| subst_dict = {'@prefix@': '$distutils_install_prefix', |
| '@pkgname@': 'numpy.core', '@sep@': os.path.sep} |
| npymath_ini = env.SubstInFile(pjoin('lib', 'npy-pkg-config', 'npymath.ini'), |
| 'npymath.ini.in', SUBST_DICT=subst_dict) |
| |
| subst_dict = {'@posix_mathlib@': " ".join(['-l%s' % l for l in mlib]), |
| '@msvc_mathlib@': " ".join(['%s.mlib' % l for l in mlib])} |
| mlib_ini = env.SubstInFile(pjoin('lib', 'npy-pkg-config', 'mlib.ini'), |
| 'mlib.ini.in', SUBST_DICT=subst_dict) |
| env.Install('$distutils_installdir/lib/npy-pkg-config', mlib_ini) |
| env.Install('$distutils_installdir/lib/npy-pkg-config', npymath_ini) |
| |
| #----------------- |
| # Build multiarray |
| #----------------- |
| if ENABLE_SEPARATE_COMPILATION: |
| multiarray_src = [pjoin('src', 'multiarray', 'multiarraymodule.c'), |
| pjoin('src', 'multiarray', 'hashdescr.c'), |
| pjoin('src', 'multiarray', 'arrayobject.c'), |
| pjoin('src', 'multiarray', 'numpyos.c'), |
| pjoin('src', 'multiarray', 'flagsobject.c'), |
| pjoin('src', 'multiarray', 'descriptor.c'), |
| pjoin('src', 'multiarray', 'iterators.c'), |
| pjoin('src', 'multiarray', 'mapping.c'), |
| pjoin('src', 'multiarray', 'number.c'), |
| pjoin('src', 'multiarray', 'getset.c'), |
| pjoin('src', 'multiarray', 'sequence.c'), |
| pjoin('src', 'multiarray', 'methods.c'), |
| pjoin('src', 'multiarray', 'ctors.c'), |
| pjoin('src', 'multiarray', 'convert_datatype.c'), |
| pjoin('src', 'multiarray', 'convert.c'), |
| pjoin('src', 'multiarray', 'shape.c'), |
| pjoin('src', 'multiarray', 'item_selection.c'), |
| pjoin('src', 'multiarray', 'calculation.c'), |
| pjoin('src', 'multiarray', 'common.c'), |
| pjoin('src', 'multiarray', 'refcount.c'), |
| pjoin('src', 'multiarray', 'conversion_utils.c'), |
| pjoin('src', 'multiarray', 'usertypes.c'), |
| pjoin('src', 'multiarray', 'buffer.c'), |
| pjoin('src', 'multiarray', 'numpymemoryview.c'), |
| pjoin('src', 'multiarray', 'scalarapi.c')] |
| multiarray_src.extend(arraytypes_src) |
| multiarray_src.extend(scalartypes_src) |
| if PYTHON_HAS_UNICODE_WIDE: |
| multiarray_src.extend([pjoin("src", "multiarray", "ucsnarrow.c")]) |
| else: |
| multiarray_src = [pjoin('src', 'multiarray', 'multiarraymodule_onefile.c')] |
| multiarray = env.DistutilsPythonExtension('multiarray', source = multiarray_src) |
| env.DistutilsPythonExtension('multiarray_tests', source=multiarray_tests_src) |
| |
| #------------------ |
| # Build sort module |
| #------------------ |
| sort = env.DistutilsPythonExtension('_sort', source = sortmodule_src) |
| |
| #------------------- |
| # Build umath module |
| #------------------- |
| if ENABLE_SEPARATE_COMPILATION: |
| umathmodule_src.extend([pjoin('src', 'umath', 'ufunc_object.c')]) |
| umathmodule_src.extend(umath_loops_src) |
| else: |
| umathmodule_src = [pjoin('src', 'umath', 'umathmodule_onefile.c')] |
| umathmodule = env.DistutilsPythonExtension('umath', source = umathmodule_src) |
| |
| #------------------------ |
| # Build scalarmath module |
| #------------------------ |
| scalarmathmodule = env.DistutilsPythonExtension('scalarmath', |
| source = scalarmathmodule_src) |
| |
| #------------------------ |
| # Build scalarmath module |
| #------------------------ |
| umath_tests = env.DistutilsPythonExtension('umath_tests', |
| source=umath_tests_src) |
| |
| #---------------------- |
| # Build _dotblas module |
| #---------------------- |
| if build_blasdot: |
| dotblas_src = [pjoin('blasdot', i) for i in ['_dotblas.c']] |
| # because _dotblas does #include CBLAS_HEADER instead of #include |
| # "cblas.h", scons does not detect the dependency |
| # XXX: PythonExtension builder does not take the Depends on extension into |
| # account for some reason, so we first build the object, with forced |
| # dependency, and then builds the extension. This is more likely a bug in |
| # our PythonExtension builder, but I cannot see how to solve it. |
| dotblas_o = env.PythonObject('_dotblas', source = dotblas_src) |
| env.Depends(dotblas_o, pjoin("blasdot", "cblas.h")) |
| dotblas = env.DistutilsPythonExtension('_dotblas', dotblas_o) |
| |
| # "Install" the header in the build directory, so that in-place build works |
| for h in generated_headers: |
| env.Install(pjoin('$distutils_installdir', include_dir), h) |