summaryrefslogtreecommitdiff
path: root/numpy/_build_utils
diff options
context:
space:
mode:
authorAlex Willmer <alex@moreati.org.uk>2015-08-05 17:21:33 +0100
committerAlex Willmer <alex@moreati.org.uk>2015-08-05 17:21:33 +0100
commitff668ba0d7652c12b28a6b6f9dbb3b581a383833 (patch)
tree7a4033c5be2deefac3812c7a221d8c9a741454e4 /numpy/_build_utils
parentf179ec92d8ddb0dc5f7445255022be5c4765a704 (diff)
downloadnumpy-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__.py1
-rw-r--r--numpy/_build_utils/apple_accelerate.py21
-rw-r--r--numpy/_build_utils/common.py138
-rw-r--r--numpy/_build_utils/src/apple_sgemv_fix.c227
-rw-r--r--numpy/_build_utils/waf.py531
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)}