diff options
author | Alex Willmer <alex@moreati.org.uk> | 2015-08-05 17:21:33 +0100 |
---|---|---|
committer | Alex Willmer <alex@moreati.org.uk> | 2015-08-05 17:21:33 +0100 |
commit | ff668ba0d7652c12b28a6b6f9dbb3b581a383833 (patch) | |
tree | 7a4033c5be2deefac3812c7a221d8c9a741454e4 /numpy/_build_utils | |
parent | f179ec92d8ddb0dc5f7445255022be5c4765a704 (diff) | |
download | numpy-ff668ba0d7652c12b28a6b6f9dbb3b581a383833.tar.gz |
BLD: Move numpy.build_utils -> numpy._build_utils, add to MANIFEST.in
This fixes the distutils built from an sdist (e.g. under tox),
without including _build_utils in binary distributions or the installed numpy.
Diffstat (limited to 'numpy/_build_utils')
-rw-r--r-- | numpy/_build_utils/__init__.py | 1 | ||||
-rw-r--r-- | numpy/_build_utils/apple_accelerate.py | 21 | ||||
-rw-r--r-- | numpy/_build_utils/common.py | 138 | ||||
-rw-r--r-- | numpy/_build_utils/src/apple_sgemv_fix.c | 227 | ||||
-rw-r--r-- | numpy/_build_utils/waf.py | 531 |
5 files changed, 918 insertions, 0 deletions
diff --git a/numpy/_build_utils/__init__.py b/numpy/_build_utils/__init__.py new file mode 100644 index 000000000..1d0f69b67 --- /dev/null +++ b/numpy/_build_utils/__init__.py @@ -0,0 +1 @@ +from __future__ import division, absolute_import, print_function diff --git a/numpy/_build_utils/apple_accelerate.py b/numpy/_build_utils/apple_accelerate.py new file mode 100644 index 000000000..d7351f4c5 --- /dev/null +++ b/numpy/_build_utils/apple_accelerate.py @@ -0,0 +1,21 @@ +import os +import sys +import re + +__all__ = ['uses_accelerate_framework', 'get_sgemv_fix'] + +def uses_accelerate_framework(info): + """ Returns True if Accelerate framework is used for BLAS/LAPACK """ + if sys.platform != "darwin": + return False + r_accelerate = re.compile("Accelerate") + extra_link_args = info.get('extra_link_args', '') + for arg in extra_link_args: + if r_accelerate.search(arg): + return True + return False + +def get_sgemv_fix(): + """ Returns source file needed to correct SGEMV """ + path = os.path.abspath(os.path.dirname(__file__)) + return [os.path.join(path, 'src', 'apple_sgemv_fix.c')] diff --git a/numpy/_build_utils/common.py b/numpy/_build_utils/common.py new file mode 100644 index 000000000..8435c462c --- /dev/null +++ b/numpy/_build_utils/common.py @@ -0,0 +1,138 @@ +from __future__ import division, absolute_import, print_function + +import sys +import copy +import binascii + +LONG_DOUBLE_REPRESENTATION_SRC = r""" +/* "before" is 16 bytes to ensure there's no padding between it and "x". + * We're not expecting any "long double" bigger than 16 bytes or with + * alignment requirements stricter than 16 bytes. */ +typedef %(type)s test_type; + +struct { + char before[16]; + test_type x; + char after[8]; +} foo = { + { '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', + '\001', '\043', '\105', '\147', '\211', '\253', '\315', '\357' }, + -123456789.0, + { '\376', '\334', '\272', '\230', '\166', '\124', '\062', '\020' } +}; +""" + +def pyod(filename): + """Python implementation of the od UNIX utility (od -b, more exactly). + + Parameters + ---------- + filename : str + name of the file to get the dump from. + + Returns + ------- + out : seq + list of lines of od output + + Note + ---- + We only implement enough to get the necessary information for long double + representation, this is not intended as a compatible replacement for od. + """ + def _pyod2(): + out = [] + + fid = open(filename, 'r') + try: + yo = [int(oct(int(binascii.b2a_hex(o), 16))) for o in fid.read()] + for i in range(0, len(yo), 16): + line = ['%07d' % int(oct(i))] + line.extend(['%03d' % c for c in yo[i:i+16]]) + out.append(" ".join(line)) + return out + finally: + fid.close() + + def _pyod3(): + out = [] + + fid = open(filename, 'rb') + try: + yo2 = [oct(o)[2:] for o in fid.read()] + for i in range(0, len(yo2), 16): + line = ['%07d' % int(oct(i)[2:])] + line.extend(['%03d' % int(c) for c in yo2[i:i+16]]) + out.append(" ".join(line)) + return out + finally: + fid.close() + + if sys.version_info[0] < 3: + return _pyod2() + else: + return _pyod3() + +_BEFORE_SEQ = ['000', '000', '000', '000', '000', '000', '000', '000', + '001', '043', '105', '147', '211', '253', '315', '357'] +_AFTER_SEQ = ['376', '334', '272', '230', '166', '124', '062', '020'] + +_IEEE_DOUBLE_BE = ['301', '235', '157', '064', '124', '000', '000', '000'] +_IEEE_DOUBLE_LE = _IEEE_DOUBLE_BE[::-1] +_INTEL_EXTENDED_12B = ['000', '000', '000', '000', '240', '242', '171', '353', + '031', '300', '000', '000'] +_INTEL_EXTENDED_16B = ['000', '000', '000', '000', '240', '242', '171', '353', + '031', '300', '000', '000', '000', '000', '000', '000'] +_IEEE_QUAD_PREC_BE = ['300', '031', '326', '363', '105', '100', '000', '000', + '000', '000', '000', '000', '000', '000', '000', '000'] +_IEEE_QUAD_PREC_LE = _IEEE_QUAD_PREC_BE[::-1] +_DOUBLE_DOUBLE_BE = ['301', '235', '157', '064', '124', '000', '000', '000'] + \ + ['000'] * 8 + +def long_double_representation(lines): + """Given a binary dump as given by GNU od -b, look for long double + representation.""" + + # Read contains a list of 32 items, each item is a byte (in octal + # representation, as a string). We 'slide' over the output until read is of + # the form before_seq + content + after_sequence, where content is the long double + # representation: + # - content is 12 bytes: 80 bits Intel representation + # - content is 16 bytes: 80 bits Intel representation (64 bits) or quad precision + # - content is 8 bytes: same as double (not implemented yet) + read = [''] * 32 + saw = None + for line in lines: + # we skip the first word, as od -b output an index at the beginning of + # each line + for w in line.split()[1:]: + read.pop(0) + read.append(w) + + # If the end of read is equal to the after_sequence, read contains + # the long double + if read[-8:] == _AFTER_SEQ: + saw = copy.copy(read) + if read[:12] == _BEFORE_SEQ[4:]: + if read[12:-8] == _INTEL_EXTENDED_12B: + return 'INTEL_EXTENDED_12_BYTES_LE' + elif read[:8] == _BEFORE_SEQ[8:]: + if read[8:-8] == _INTEL_EXTENDED_16B: + return 'INTEL_EXTENDED_16_BYTES_LE' + elif read[8:-8] == _IEEE_QUAD_PREC_BE: + return 'IEEE_QUAD_BE' + elif read[8:-8] == _IEEE_QUAD_PREC_LE: + return 'IEEE_QUAD_LE' + elif read[8:-8] == _DOUBLE_DOUBLE_BE: + return 'DOUBLE_DOUBLE_BE' + elif read[:16] == _BEFORE_SEQ: + if read[16:-8] == _IEEE_DOUBLE_LE: + return 'IEEE_DOUBLE_LE' + elif read[16:-8] == _IEEE_DOUBLE_BE: + return 'IEEE_DOUBLE_BE' + + if saw is not None: + raise ValueError("Unrecognized format (%s)" % saw) + else: + # We never detected the after_sequence + raise ValueError("Could not lock sequences (%s)" % saw) diff --git a/numpy/_build_utils/src/apple_sgemv_fix.c b/numpy/_build_utils/src/apple_sgemv_fix.c new file mode 100644 index 000000000..ffdfb81f7 --- /dev/null +++ b/numpy/_build_utils/src/apple_sgemv_fix.c @@ -0,0 +1,227 @@ +/* This is a collection of ugly hacks to circumvent a bug in + * Apple Accelerate framework's SGEMV subroutine. + * + * See: https://github.com/numpy/numpy/issues/4007 + * + * SGEMV in Accelerate framework will segfault on MacOS X version 10.9 + * (aka Mavericks) if arrays are not aligned to 32 byte boundaries + * and the CPU supports AVX instructions. This can produce segfaults + * in np.dot. + * + * This patch overshadows the symbols cblas_sgemv, sgemv_ and sgemv + * exported by Accelerate to produce the correct behavior. The MacOS X + * version and CPU specs are checked on module import. If Mavericks and + * AVX are detected the call to SGEMV is emulated with a call to SGEMM + * if the arrays are not 32 byte aligned. If the exported symbols cannot + * be overshadowed on module import, a fatal error is produced and the + * process aborts. All the fixes are in a self-contained C file + * and do not alter the multiarray C code. The patch is not applied + * unless NumPy is configured to link with Apple's Accelerate + * framework. + * + */ + +#define NPY_NO_DEPRECATED_API NPY_API_VERSION +#include "Python.h" +#include "numpy/arrayobject.h" + +#include <string.h> +#include <dlfcn.h> +#include <stdlib.h> +#include <stdio.h> + +/* ----------------------------------------------------------------- */ +/* Original cblas_sgemv */ + +#define VECLIB_FILE "/System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/vecLib" + +enum CBLAS_ORDER {CblasRowMajor=101, CblasColMajor=102}; +enum CBLAS_TRANSPOSE {CblasNoTrans=111, CblasTrans=112, CblasConjTrans=113}; +extern void cblas_xerbla(int info, const char *rout, const char *form, ...); + +typedef void cblas_sgemv_t(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const int M, const int N, + const float alpha, const float *A, const int lda, + const float *X, const int incX, + const float beta, float *Y, const int incY); + +typedef void cblas_sgemm_t(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const enum CBLAS_TRANSPOSE TransB, + const int M, const int N, const int K, + const float alpha, const float *A, const int lda, + const float *B, const int ldb, + const float beta, float *C, const int incC); + +typedef void fortran_sgemv_t( const char* trans, const int* m, const int* n, + const float* alpha, const float* A, const int* ldA, + const float* X, const int* incX, + const float* beta, float* Y, const int* incY ); + +static void *veclib = NULL; +static cblas_sgemv_t *accelerate_cblas_sgemv = NULL; +static cblas_sgemm_t *accelerate_cblas_sgemm = NULL; +static fortran_sgemv_t *accelerate_sgemv = NULL; +static int AVX_and_10_9 = 0; + +/* Dynamic check for AVX support + * __builtin_cpu_supports("avx") is available in gcc 4.8, + * but clang and icc do not currently support it. */ +#define cpu_supports_avx()\ +(system("sysctl -n machdep.cpu.features | grep -q AVX") == 0) + +/* Check if we are using MacOS X version 10.9 */ +#define using_mavericks()\ +(system("sw_vers -productVersion | grep -q 10\\.9\\.") == 0) + +__attribute__((destructor)) +static void unloadlib(void) +{ + if (veclib) dlclose(veclib); +} + +__attribute__((constructor)) +static void loadlib() +/* automatically executed on module import */ +{ + char errormsg[1024]; + int AVX, MAVERICKS; + memset((void*)errormsg, 0, sizeof(errormsg)); + /* check if the CPU supports AVX */ + AVX = cpu_supports_avx(); + /* check if the OS is MacOS X Mavericks */ + MAVERICKS = using_mavericks(); + /* we need the workaround when the CPU supports + * AVX and the OS version is Mavericks */ + AVX_and_10_9 = AVX && MAVERICKS; + /* load vecLib */ + veclib = dlopen(VECLIB_FILE, RTLD_LOCAL | RTLD_FIRST); + if (!veclib) { + veclib = NULL; + snprintf(errormsg, sizeof(errormsg), + "Failed to open vecLib from location '%s'.", VECLIB_FILE); + Py_FatalError(errormsg); /* calls abort() and dumps core */ + } + /* resolve Fortran SGEMV from Accelerate */ + accelerate_sgemv = (fortran_sgemv_t*) dlsym(veclib, "sgemv_"); + if (!accelerate_sgemv) { + unloadlib(); + Py_FatalError("Failed to resolve symbol 'sgemv_'."); + } + /* resolve cblas_sgemv from Accelerate */ + accelerate_cblas_sgemv = (cblas_sgemv_t*) dlsym(veclib, "cblas_sgemv"); + if (!accelerate_cblas_sgemv) { + unloadlib(); + Py_FatalError("Failed to resolve symbol 'cblas_sgemv'."); + } + /* resolve cblas_sgemm from Accelerate */ + accelerate_cblas_sgemm = (cblas_sgemm_t*) dlsym(veclib, "cblas_sgemm"); + if (!accelerate_cblas_sgemm) { + unloadlib(); + Py_FatalError("Failed to resolve symbol 'cblas_sgemm'."); + } +} + +/* ----------------------------------------------------------------- */ +/* Fortran SGEMV override */ + +void sgemv_( const char* trans, const int* m, const int* n, + const float* alpha, const float* A, const int* ldA, + const float* X, const int* incX, + const float* beta, float* Y, const int* incY ) +{ + /* It is safe to use the original SGEMV if we are not using AVX on Mavericks + * or the input arrays A, X and Y are all aligned on 32 byte boundaries. */ + #define BADARRAY(x) (((npy_intp)(void*)x) % 32) + const int use_sgemm = AVX_and_10_9 && (BADARRAY(A) || BADARRAY(X) || BADARRAY(Y)); + if (!use_sgemm) { + accelerate_sgemv(trans,m,n,alpha,A,ldA,X,incX,beta,Y,incY); + return; + } + + /* Arrays are misaligned, the CPU supports AVX, and we are running + * Mavericks. + * + * Emulation of SGEMV with SGEMM: + * + * SGEMV allows vectors to be strided. SGEMM requires all arrays to be + * contiguous along the leading dimension. To emulate striding in SGEMV + * with the leading dimension arguments in SGEMM we compute + * + * Y = alpha * op(A) @ X + beta * Y + * + * as + * + * Y.T = alpha * X.T @ op(A).T + beta * Y.T + * + * Because Fortran uses column major order and X.T and Y.T are row vectors, + * the leading dimensions of X.T and Y.T in SGEMM become equal to the + * strides of the the column vectors X and Y in SGEMV. */ + + switch (*trans) { + case 'T': + case 't': + case 'C': + case 'c': + accelerate_cblas_sgemm( CblasColMajor, CblasNoTrans, CblasNoTrans, + 1, *n, *m, *alpha, X, *incX, A, *ldA, *beta, Y, *incY ); + break; + case 'N': + case 'n': + accelerate_cblas_sgemm( CblasColMajor, CblasNoTrans, CblasTrans, + 1, *m, *n, *alpha, X, *incX, A, *ldA, *beta, Y, *incY ); + break; + default: + cblas_xerbla(1, "SGEMV", "Illegal transpose setting: %c\n", *trans); + } +} + +/* ----------------------------------------------------------------- */ +/* Override for an alias symbol for sgemv_ in Accelerate */ + +void sgemv (char *trans, + const int *m, const int *n, + const float *alpha, + const float *A, const int *lda, + const float *B, const int *incB, + const float *beta, + float *C, const int *incC) +{ + sgemv_(trans,m,n,alpha,A,lda,B,incB,beta,C,incC); +} + +/* ----------------------------------------------------------------- */ +/* cblas_sgemv override, based on Netlib CBLAS code */ + +void cblas_sgemv(const enum CBLAS_ORDER order, + const enum CBLAS_TRANSPOSE TransA, const int M, const int N, + const float alpha, const float *A, const int lda, + const float *X, const int incX, const float beta, + float *Y, const int incY) +{ + char TA; + if (order == CblasColMajor) + { + if (TransA == CblasNoTrans) TA = 'N'; + else if (TransA == CblasTrans) TA = 'T'; + else if (TransA == CblasConjTrans) TA = 'C'; + else + { + cblas_xerbla(2, "cblas_sgemv","Illegal TransA setting, %d\n", TransA); + } + sgemv_(&TA, &M, &N, &alpha, A, &lda, X, &incX, &beta, Y, &incY); + } + else if (order == CblasRowMajor) + { + if (TransA == CblasNoTrans) TA = 'T'; + else if (TransA == CblasTrans) TA = 'N'; + else if (TransA == CblasConjTrans) TA = 'N'; + else + { + cblas_xerbla(2, "cblas_sgemv", "Illegal TransA setting, %d\n", TransA); + return; + } + sgemv_(&TA, &N, &M, &alpha, A, &lda, X, &incX, &beta, Y, &incY); + } + else + cblas_xerbla(1, "cblas_sgemv", "Illegal Order setting, %d\n", order); +} diff --git a/numpy/_build_utils/waf.py b/numpy/_build_utils/waf.py new file mode 100644 index 000000000..263640d9e --- /dev/null +++ b/numpy/_build_utils/waf.py @@ -0,0 +1,531 @@ +from __future__ import division, absolute_import, print_function + +import os +import re + +import waflib.Configure +import waflib.Tools.c_config +from waflib import Logs, Utils + +from .common \ + import \ + LONG_DOUBLE_REPRESENTATION_SRC, pyod, \ + long_double_representation + +DEFKEYS = waflib.Tools.c_config.DEFKEYS +DEFINE_COMMENTS = "define_commentz" + +def to_header(dct): + if 'header_name' in dct: + dct = Utils.to_list(dct['header_name']) + return ''.join(['#include <%s>\n' % x for x in dct]) + return '' + +# Make the given string safe to be used as a CPP macro +def sanitize_string(s): + key_up = s.upper() + return re.sub('[^A-Z0-9_]', '_', key_up) + +def validate_arguments(self, kw): + if not 'env' in kw: + kw['env'] = self.env.derive() + if not "compile_mode" in kw: + kw["compile_mode"] = "c" + if not 'compile_filename' in kw: + kw['compile_filename'] = 'test.c' + \ + ((kw['compile_mode'] == 'cxx') and 'pp' or '') + if not 'features' in kw: + kw['features'] = [kw['compile_mode']] + if not 'execute' in kw: + kw['execute'] = False + if not 'okmsg' in kw: + kw['okmsg'] = 'yes' + if not 'errmsg' in kw: + kw['errmsg'] = 'no !' + + if 'define_name' in kw: + comment = kw.get('define_comment', None) + self.undefine_with_comment(kw['define_name'], comment) + +def try_compile(self, kw): + self.start_msg(kw["msg"]) + ret = None + try: + ret = self.run_c_code(**kw) + except self.errors.ConfigurationError as e: + self.end_msg(kw['errmsg'], 'YELLOW') + if Logs.verbose > 1: + raise + else: + self.fatal('The configuration failed') + else: + kw['success'] = ret + self.end_msg(self.ret_msg(kw['okmsg'], kw)) + +@waflib.Configure.conf +def check_header(self, header_name, **kw): + code = """ +%s + +int main() +{ +} +""" % to_header({"header_name": header_name}) + + kw["code"] = code + kw["define_comment"] = "/* Define to 1 if you have the <%s> header file. */" % header_name + kw["define_name"] = "HAVE_%s" % sanitize_string(header_name) + if not "features" in kw: + kw["features"] = ["c"] + kw["msg"] = "Checking for header %r" % header_name + + validate_arguments(self, kw) + try_compile(self, kw) + ret = kw["success"] + if ret == 0: + kw["define_value"] = 1 + else: + kw["define_value"] = 0 + + self.post_check(**kw) + if not kw.get('execute', False): + return ret == 0 + return ret + +@waflib.Configure.conf +def check_declaration(self, symbol, **kw): + code = r""" +int main() +{ +#ifndef %s + (void) %s; +#endif + ; + return 0; +} +""" % (symbol, symbol) + + kw["code"] = to_header(kw) + code + kw["msg"] = "Checking for macro %r" % symbol + kw["errmsg"] = "not found" + kw["okmsg"] = "yes" + + validate_arguments(self, kw) + try_compile(self, kw) + ret = kw["success"] + + kw["define_name"] = "HAVE_DECL_%s" % sanitize_string(symbol) + kw["define_comment"] = "/* Set to 1 if %s is defined. */" % symbol + self.post_check(**kw) + if not kw.get('execute', False): + return ret == 0 + return ret + +@waflib.Configure.conf +def check_type(self, type_name, **kw): + code = r""" +int main() { + if ((%(type_name)s *) 0) + return 0; + if (sizeof (%(type_name)s)) + return 0; +} +""" % {"type_name": type_name} + + kw["code"] = to_header(kw) + code + kw["msg"] = "Checking for type %r" % type_name + kw["errmsg"] = "not found" + kw["okmsg"] = "yes" + + validate_arguments(self, kw) + try_compile(self, kw) + ret = kw["success"] + if ret == 0: + kw["define_value"] = 1 + else: + kw["define_value"] = 0 + + kw["define_name"] = "HAVE_%s" % sanitize_string(type_name) + kw["define_comment"] = "/* Define to 1 if the system has the type `%s'. */" % type_name + self.post_check(**kw) + if not kw.get('execute', False): + return ret == 0 + return ret + +def do_binary_search(conf, type_name, kw): + code = """\ +typedef %(type)s waf_check_sizeof_type; +int main () +{ + static int test_array [1 - 2 * !(((long) (sizeof (waf_check_sizeof_type))) >= 0)]; + test_array [0] = 0 + + ; + return 0; +} +""" % {"type": type_name} + kw["code"] = to_header(kw) + code + + try: + conf.run_c_code(**kw) + except conf.errors.ConfigurationError as e: + conf.end_msg("failed !") + if waflib.Logs.verbose > 1: + raise + else: + conf.fatal("The configuration failed !") + + body = r""" +typedef %(type)s waf_check_sizeof_type; +int main () +{ + static int test_array [1 - 2 * !(((long) (sizeof (waf_check_sizeof_type))) <= %(size)s)]; + test_array [0] = 0 + + ; + return 0; +} +""" + # The principle is simple: we first find low and high bounds + # of size for the type, where low/high are looked up on a log + # scale. Then, we do a binary search to find the exact size + # between low and high + low = 0 + mid = 0 + while True: + try: + kw["code"] = to_header(kw) + body % {"type": type_name, "size": mid} + validate_arguments(conf, kw) + conf.run_c_code(**kw) + break + except conf.errors.ConfigurationError: + #log.info("failure to test for bound %d" % mid) + low = mid + 1 + mid = 2 * mid + 1 + + high = mid + ret = None + # Binary search: + while low != high: + mid = (high - low) / 2 + low + try: + kw["code"] = to_header(kw) + body % {"type": type_name, "size": mid} + validate_arguments(conf, kw) + ret = conf.run_c_code(**kw) + high = mid + except conf.errors.ConfigurationError: + low = mid + 1 + + return low + +@waflib.Configure.conf +def check_type_size(conf, type_name, expected_sizes=None, **kw): + kw["define_name"] = "SIZEOF_%s" % sanitize_string(type_name) + kw["define_comment"] = "/* The size of `%s', as computed by sizeof. */" % type_name + kw["msg"] = "Checking sizeof(%s)" % type_name + + validate_arguments(conf, kw) + conf.start_msg(kw["msg"]) + + if expected_sizes is not None: + try: + val = int(expected_sizes) + except TypeError: + values = expected_sizes + else: + values = [val] + + size = None + for value in values: + code = """\ + typedef %(type)s waf_check_sizeof_type; + int main () + { + static int test_array [1 - 2 * !(((long) (sizeof (waf_check_sizeof_type))) == %(size)d)]; + test_array [0] = 0 + + ; + return 0; + } + """ % {"type": type_name, "size": value} + kw["code"] = to_header(kw) + code + try: + conf.run_c_code(**kw) + size = value + break + except conf.errors.ConfigurationError: + pass + if size is None: + size = do_binary_search(conf, type_name, kw) + else: + size = do_binary_search(conf, type_name, kw) + + kw["define_value"] = size + kw["success"] = 0 + conf.end_msg(size) + conf.post_check(**kw) + return size + +@waflib.Configure.conf +def check_functions_at_once(self, funcs, **kw): + header = [] + header = ['#ifdef __cplusplus'] + header.append('extern "C" {') + header.append('#endif') + for f in funcs: + header.append("\tchar %s();" % f) + # Handle MSVC intrinsics: force MS compiler to make a function + # call. Useful to test for some functions when built with + # optimization on, to avoid build error because the intrinsic + # and our 'fake' test declaration do not match. + header.append("#ifdef _MSC_VER") + header.append("#pragma function(%s)" % f) + header.append("#endif") + header.append('#ifdef __cplusplus') + header.append('};') + header.append('#endif') + funcs_decl = "\n".join(header) + + tmp = [] + for f in funcs: + tmp.append("\t%s();" % f) + tmp = "\n".join(tmp) + + code = r""" +%(include)s +%(funcs_decl)s + +int main (void) +{ + %(tmp)s + return 0; +} +""" % {"tmp": tmp, "include": to_header(kw), "funcs_decl": funcs_decl} + kw["code"] = code + if not "features" in kw: + kw["features"] = ["c", "cprogram"] + + msg = ", ".join(funcs) + if len(msg) > 30: + _funcs = list(funcs) + msg = [] + while len(", ".join(msg)) < 30 and _funcs: + msg.append(_funcs.pop(0)) + msg = ", ".join(msg) + ",..." + if "lib" in kw: + kw["msg"] = "Checking for functions %s in library %r" % (msg, kw["lib"]) + else: + kw["msg"] = "Checking for functions %s" % msg + + validate_arguments(self, kw) + try_compile(self, kw) + ret = kw["success"] + + # We set the config.h define here because we need to define several of them + # in one shot + if ret == 0: + for f in funcs: + self.define_with_comment("HAVE_%s" % sanitize_string(f), 1, + "/* Define to 1 if you have the `%s' function. */" % f) + + self.post_check(**kw) + if not kw.get('execute', False): + return ret == 0 + return ret + +@waflib.Configure.conf +def check_inline(conf, **kw): + validate_arguments(conf, kw) + + code = """ +#ifndef __cplusplus +static %(inline)s int static_func (void) +{ + return 0; +} +%(inline)s int nostatic_func (void) +{ + return 0; +} +#endif""" + + conf.start_msg("Checking for inline support") + inline = None + for k in ['inline', '__inline__', '__inline']: + try: + kw["code"] = code % {"inline": k} + ret = conf.run_c_code(**kw) + inline = k + break + except conf.errors.ConfigurationError: + pass + + if inline is None: + conf.end_msg("failed", 'YELLOW') + if Logs.verbose > 1: + raise + else: + conf.fatal('The configuration failed') + else: + kw['success'] = ret + conf.end_msg(inline) + return inline + +@waflib.Configure.conf +def check_ldouble_representation(conf, **kw): + msg = { + 'INTEL_EXTENDED_12_BYTES_LE': "Intel extended, little endian", + 'INTEL_EXTENDED_16_BYTES_LE': "Intel extended, little endian", + 'IEEE_QUAD_BE': "IEEE Quad precision, big endian", + 'IEEE_QUAD_LE': "IEEE Quad precision, little endian", + 'IEEE_DOUBLE_LE': "IEEE Double precision, little endian", + 'IEEE_DOUBLE_BE': "IEEE Double precision, big endian" + } + + code = LONG_DOUBLE_REPRESENTATION_SRC % {'type': 'long double'} + validate_arguments(conf, kw) + + conf.start_msg("Checking for long double representation... ") + try: + kw["code"] = code + ret = conf.run_c_code(**kw) + except conf.errors.ConfigurationError as e: + conf.end_msg(kw['errmsg'], 'YELLOW') + if Logs.verbose > 1: + raise + else: + conf.fatal('The configuration failed') + else: + task_gen = conf.test_bld.groups[0][0] + obj_filename = task_gen.tasks[0].outputs[0].abspath() + tp = long_double_representation(pyod(obj_filename)) + kw['success'] = ret + conf.end_msg(msg[tp]) + kw["define_name"] = "HAVE_LDOUBLE_%s" % tp + kw["define_comment"] = "/* Define for arch-specific long double representation */" + ret = kw["success"] + + conf.post_check(**kw) + if not kw.get('execute', False): + return ret == 0 + return ret + +@waflib.Configure.conf +def post_check(self, *k, **kw): + "set the variables after a test was run successfully" + + is_success = False + if kw['execute']: + if kw['success'] is not None: + if kw.get('define_ret', False): + is_success = kw['success'] + else: + is_success = (kw['success'] == 0) + else: + is_success = (kw['success'] == 0) + + def define_or_stuff(): + nm = kw['define_name'] + cmt = kw.get('define_comment', None) + value = kw.get("define_value", is_success) + if kw['execute'] and kw.get('define_ret', None) and isinstance(is_success, str): + self.define_with_comment(kw['define_name'], value, cmt, quote=kw.get('quote', 1)) + else: + self.define_cond(kw['define_name'], value, cmt) + + if 'define_name' in kw: + define_or_stuff() + + if is_success and 'uselib_store' in kw: + from waflib.Tools import ccroot + + # TODO see get_uselib_vars from ccroot.py + _vars = set([]) + for x in kw['features']: + if x in ccroot.USELIB_VARS: + _vars |= ccroot.USELIB_VARS[x] + + for k in _vars: + lk = k.lower() + if k == 'INCLUDES': lk = 'includes' + if k == 'DEFKEYS': lk = 'defines' + if lk in kw: + val = kw[lk] + # remove trailing slash + if isinstance(val, str): + val = val.rstrip(os.path.sep) + self.env.append_unique(k + '_' + kw['uselib_store'], val) + return is_success + +@waflib.Configure.conf +def define_with_comment(conf, define, value, comment=None, quote=True): + if comment is None: + return conf.define(define, value, quote) + + assert define and isinstance(define, str) + + comment_tbl = conf.env[DEFINE_COMMENTS] or {} + comment_tbl[define] = comment + conf.env[DEFINE_COMMENTS] = comment_tbl + + return conf.define(define, value, quote) + +@waflib.Configure.conf +def undefine_with_comment(conf, define, comment=None): + if comment is None: + return conf.undefine(define) + + comment_tbl = conf.env[DEFINE_COMMENTS] or {} + comment_tbl[define] = comment + conf.env[DEFINE_COMMENTS] = comment_tbl + + conf.undefine(define) + +@waflib.Configure.conf +def get_comment(self, key): + assert key and isinstance(key, str) + + if key in self.env[DEFINE_COMMENTS]: + return self.env[DEFINE_COMMENTS][key] + return None + +@waflib.Configure.conf +def define_cond(self, name, value, comment): + """Conditionally define a name. + Formally equivalent to: if value: define(name, 1) else: undefine(name)""" + if value: + self.define_with_comment(name, value, comment) + else: + self.undefine(name) + +@waflib.Configure.conf +def get_config_header(self, defines=True, headers=False, define_prefix=None): + """ + Create the contents of a ``config.h`` file from the defines and includes + set in conf.env.define_key / conf.env.include_key. No include guards are added. + + :param defines: write the defines values + :type defines: bool + :param headers: write the headers + :type headers: bool + :return: the contents of a ``config.h`` file + :rtype: string + """ + tpl = self.env["CONFIG_HEADER_TEMPLATE"] or "%(content)s" + + lst = [] + if headers: + for x in self.env[INCKEYS]: + lst.append('#include <%s>' % x) + + if defines: + for x in self.env[DEFKEYS]: + cmt = self.get_comment(x) + if cmt is not None: + lst.append(cmt) + if self.is_defined(x): + val = self.get_define(x) + lst.append('#define %s %s\n' % (x, val)) + else: + lst.append('/* #undef %s */\n' % x) + return tpl % {"content": "\n".join(lst)} |