thirdparty:waf: New files for waf 1.9.10
[bbaumbach/samba-autobuild/.git] / third_party / waf / waflib / Tools / c_config.py
diff --git a/third_party/waf/waflib/Tools/c_config.py b/third_party/waf/waflib/Tools/c_config.py
new file mode 100644 (file)
index 0000000..b615600
--- /dev/null
@@ -0,0 +1,1426 @@
+#! /usr/bin/env python
+# encoding: utf-8
+# WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
+
+#!/usr/bin/env python
+# encoding: utf-8
+# Thomas Nagy, 2005-2016 (ita)
+
+"""
+C/C++/D configuration helpers
+"""
+
+from __future__ import with_statement
+
+import os, re, shlex
+from waflib import Build, Utils, Task, Options, Logs, Errors, Runner
+from waflib.TaskGen import after_method, feature
+from waflib.Configure import conf
+
+WAF_CONFIG_H   = 'config.h'
+"""default name for the config.h file"""
+
+DEFKEYS = 'define_key'
+INCKEYS = 'include_key'
+
+cfg_ver = {
+       'atleast-version': '>=',
+       'exact-version': '==',
+       'max-version': '<=',
+}
+
+SNIP_FUNCTION = '''
+int main(int argc, char **argv) {
+       void (*p)();
+       (void)argc; (void)argv;
+       p=(void(*)())(%s);
+       return !p;
+}
+'''
+"""Code template for checking for functions"""
+
+SNIP_TYPE = '''
+int main(int argc, char **argv) {
+       (void)argc; (void)argv;
+       if ((%(type_name)s *) 0) return 0;
+       if (sizeof (%(type_name)s)) return 0;
+       return 1;
+}
+'''
+"""Code template for checking for types"""
+
+SNIP_EMPTY_PROGRAM = '''
+int main(int argc, char **argv) {
+       (void)argc; (void)argv;
+       return 0;
+}
+'''
+
+SNIP_FIELD = '''
+int main(int argc, char **argv) {
+       char *off;
+       (void)argc; (void)argv;
+       off = (char*) &((%(type_name)s*)0)->%(field_name)s;
+       return (size_t) off < sizeof(%(type_name)s);
+}
+'''
+
+MACRO_TO_DESTOS = {
+'__linux__'                                      : 'linux',
+'__GNU__'                                        : 'gnu', # hurd
+'__FreeBSD__'                                    : 'freebsd',
+'__NetBSD__'                                     : 'netbsd',
+'__OpenBSD__'                                    : 'openbsd',
+'__sun'                                          : 'sunos',
+'__hpux'                                         : 'hpux',
+'__sgi'                                          : 'irix',
+'_AIX'                                           : 'aix',
+'__CYGWIN__'                                     : 'cygwin',
+'__MSYS__'                                       : 'cygwin',
+'_UWIN'                                          : 'uwin',
+'_WIN64'                                         : 'win32',
+'_WIN32'                                         : 'win32',
+# Note about darwin: this is also tested with 'defined __APPLE__ && defined __MACH__' somewhere below in this file.
+'__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__'  : 'darwin',
+'__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__' : 'darwin', # iphone
+'__QNX__'                                        : 'qnx',
+'__native_client__'                              : 'nacl' # google native client platform
+}
+
+MACRO_TO_DEST_CPU = {
+'__x86_64__'  : 'x86_64',
+'__amd64__'   : 'x86_64',
+'__i386__'    : 'x86',
+'__ia64__'    : 'ia',
+'__mips__'    : 'mips',
+'__sparc__'   : 'sparc',
+'__alpha__'   : 'alpha',
+'__aarch64__' : 'aarch64',
+'__thumb__'   : 'thumb',
+'__arm__'     : 'arm',
+'__hppa__'    : 'hppa',
+'__powerpc__' : 'powerpc',
+'__ppc__'     : 'powerpc',
+'__convex__'  : 'convex',
+'__m68k__'    : 'm68k',
+'__s390x__'   : 's390x',
+'__s390__'    : 's390',
+'__sh__'      : 'sh',
+'__xtensa__'  : 'xtensa',
+}
+
+@conf
+def parse_flags(self, line, uselib_store, env=None, force_static=False, posix=None):
+       """
+       Parses flags from the input lines, and adds them to the relevant use variables::
+
+               def configure(conf):
+                       conf.parse_flags('-O3', 'FOO')
+                       # conf.env.CXXFLAGS_FOO = ['-O3']
+                       # conf.env.CFLAGS_FOO = ['-O3']
+
+       :param line: flags
+       :type line: string
+       :param uselib_store: where to add the flags
+       :type uselib_store: string
+       :param env: config set or conf.env by default
+       :type env: :py:class:`waflib.ConfigSet.ConfigSet`
+       """
+
+       assert(isinstance(line, str))
+
+       env = env or self.env
+
+       # Issue 811 and 1371
+       if posix is None:
+               posix = True
+               if '\\' in line:
+                       posix = ('\\ ' in line) or ('\\\\' in line)
+
+       lex = shlex.shlex(line, posix=posix)
+       lex.whitespace_split = True
+       lex.commenters = ''
+       lst = list(lex)
+
+       # append_unique is not always possible
+       # for example, apple flags may require both -arch i386 and -arch ppc
+       uselib = uselib_store
+       def app(var, val):
+               env.append_value('%s_%s' % (var, uselib), val)
+       def appu(var, val):
+               env.append_unique('%s_%s' % (var, uselib), val)
+       static = False
+       while lst:
+               x = lst.pop(0)
+               st = x[:2]
+               ot = x[2:]
+
+               if st == '-I' or st == '/I':
+                       if not ot:
+                               ot = lst.pop(0)
+                       appu('INCLUDES', ot)
+               elif st == '-i':
+                       tmp = [x, lst.pop(0)]
+                       app('CFLAGS', tmp)
+                       app('CXXFLAGS', tmp)
+               elif st == '-D' or (env.CXX_NAME == 'msvc' and st == '/D'): # not perfect but..
+                       if not ot:
+                               ot = lst.pop(0)
+                       app('DEFINES', ot)
+               elif st == '-l':
+                       if not ot:
+                               ot = lst.pop(0)
+                       prefix = 'STLIB' if (force_static or static) else 'LIB'
+                       app(prefix, ot)
+               elif st == '-L':
+                       if not ot:
+                               ot = lst.pop(0)
+                       prefix = 'STLIBPATH' if (force_static or static) else 'LIBPATH'
+                       appu(prefix, ot)
+               elif x.startswith('/LIBPATH:'):
+                       prefix = 'STLIBPATH' if (force_static or static) else 'LIBPATH'
+                       appu(prefix, x.replace('/LIBPATH:', ''))
+               elif x.startswith('-std='):
+                       prefix = 'CXXFLAGS' if '++' in x else 'CFLAGS'
+                       app(prefix, x)
+               elif x.startswith('+') or x in ('-pthread', '-fPIC', '-fpic', '-fPIE', '-fpie'):
+                       app('CFLAGS', x)
+                       app('CXXFLAGS', x)
+                       app('LINKFLAGS', x)
+               elif x == '-framework':
+                       appu('FRAMEWORK', lst.pop(0))
+               elif x.startswith('-F'):
+                       appu('FRAMEWORKPATH', x[2:])
+               elif x == '-Wl,-rpath' or x == '-Wl,-R':
+                       app('RPATH', lst.pop(0).lstrip('-Wl,'))
+               elif x.startswith('-Wl,-R,'):
+                       app('RPATH', x[7:])
+               elif x.startswith('-Wl,-R'):
+                       app('RPATH', x[6:])
+               elif x.startswith('-Wl,-rpath,'):
+                       app('RPATH', x[11:])
+               elif x == '-Wl,-Bstatic' or x == '-Bstatic':
+                       static = True
+               elif x == '-Wl,-Bdynamic' or x == '-Bdynamic':
+                       static = False
+               elif x.startswith('-Wl') or x in ('-rdynamic', '-pie'):
+                       app('LINKFLAGS', x)
+               elif x.startswith(('-m', '-f', '-dynamic', '-O')):
+                       app('CFLAGS', x)
+                       app('CXXFLAGS', x)
+               elif x.startswith('-bundle'):
+                       app('LINKFLAGS', x)
+               elif x.startswith(('-undefined', '-Xlinker')):
+                       arg = lst.pop(0)
+                       app('LINKFLAGS', [x, arg])
+               elif x.startswith(('-arch', '-isysroot')):
+                       tmp = [x, lst.pop(0)]
+                       app('CFLAGS', tmp)
+                       app('CXXFLAGS', tmp)
+                       app('LINKFLAGS', tmp)
+               elif x.endswith(('.a', '.so', '.dylib', '.lib')):
+                       appu('LINKFLAGS', x) # not cool, #762
+               else:
+                       self.to_log('Unhandled flag %r' % x)
+
+@conf
+def validate_cfg(self, kw):
+       """
+       Searches for the program *pkg-config* if missing, and validates the
+       parameters to pass to :py:func:`waflib.Tools.c_config.exec_cfg`.
+
+       :param path: the **-config program to use** (default is *pkg-config*)
+       :type path: list of string
+       :param msg: message to display to describe the test executed
+       :type msg: string
+       :param okmsg: message to display when the test is successful
+       :type okmsg: string
+       :param errmsg: message to display in case of error
+       :type errmsg: string
+       """
+       if not 'path' in kw:
+               if not self.env.PKGCONFIG:
+                       self.find_program('pkg-config', var='PKGCONFIG')
+               kw['path'] = self.env.PKGCONFIG
+
+       # pkg-config version
+       if 'atleast_pkgconfig_version' in kw:
+               if not 'msg' in kw:
+                       kw['msg'] = 'Checking for pkg-config version >= %r' % kw['atleast_pkgconfig_version']
+               return
+
+       if not 'okmsg' in kw:
+               kw['okmsg'] = 'yes'
+       if not 'errmsg' in kw:
+               kw['errmsg'] = 'not found'
+
+       if 'modversion' in kw:
+               if not 'msg' in kw:
+                       kw['msg'] = 'Checking for %r version' % kw['modversion']
+               if not 'uselib_store' in kw:
+                       kw['uselib_store'] = kw['modversion']
+               if not 'define_name' in kw:
+                       kw['define_name'] = '%s_VERSION' % Utils.quote_define_name(kw['uselib_store'])
+               return
+
+       if not 'package' in kw:
+               raise ValueError('a package name is required')
+
+       if not 'uselib_store' in kw:
+               kw['uselib_store'] = kw['package'].upper()
+
+       if not 'define_name' in kw:
+               kw['define_name'] = self.have_define(kw['uselib_store'])
+
+       if not 'msg' in kw:
+               kw['msg'] = 'Checking for %r' % (kw['package'] or kw['path'])
+
+       for x in cfg_ver:
+               # Gotcha: only one predicate is allowed at a time
+               # TODO remove in waf 2.0
+               y = x.replace('-', '_')
+               if y in kw:
+                       package = kw['package']
+                       if Logs.verbose:
+                               Logs.warn('Passing %r to conf.check_cfg() is obsolete, pass parameters directly, eg:', y)
+                               Logs.warn(" conf.check_cfg(package='%s', args=['--libs', '--cflags', '%s >= 1.6'])", package, package)
+                       if not 'msg' in kw:
+                               kw['msg'] = 'Checking for %r %s %s' % (package, cfg_ver[x], kw[y])
+                       break
+
+@conf
+def exec_cfg(self, kw):
+       """
+       Executes ``pkg-config`` or other ``-config`` applications to colect configuration flags:
+
+       * if atleast_pkgconfig_version is given, check that pkg-config has the version n and return
+       * if modversion is given, then return the module version
+       * else, execute the *-config* program with the *args* and *variables* given, and set the flags on the *conf.env.FLAGS_name* variable
+
+       :param atleast_pkgconfig_version: minimum pkg-config version to use (disable other tests)
+       :type atleast_pkgconfig_version: string
+       :param package: package name, for example *gtk+-2.0*
+       :type package: string
+       :param uselib_store: if the test is successful, define HAVE\_*name*. It is also used to define *conf.env.FLAGS_name* variables.
+       :type uselib_store: string
+       :param modversion: if provided, return the version of the given module and define *name*\_VERSION
+       :type modversion: string
+       :param args: arguments to give to *package* when retrieving flags
+       :type args: list of string
+       :param variables: return the values of particular variables
+       :type variables: list of string
+       :param define_variable: additional variables to define (also in conf.env.PKG_CONFIG_DEFINES)
+       :type define_variable: dict(string: string)
+       """
+
+       path = Utils.to_list(kw['path'])
+       env = self.env.env or None
+       if kw.get('pkg_config_path'):
+               if not env:
+                       env = dict(self.environ)
+               env['PKG_CONFIG_PATH'] = kw['pkg_config_path']
+
+       def define_it():
+               define_name = kw['define_name']
+               # by default, add HAVE_X to the config.h, else provide DEFINES_X for use=X
+               if kw.get('global_define', 1):
+                       self.define(define_name, 1, False)
+               else:
+                       self.env.append_unique('DEFINES_%s' % kw['uselib_store'], "%s=1" % define_name)
+
+               if kw.get('add_have_to_env', 1):
+                       self.env[define_name] = 1
+
+       # pkg-config version
+       if 'atleast_pkgconfig_version' in kw:
+               cmd = path + ['--atleast-pkgconfig-version=%s' % kw['atleast_pkgconfig_version']]
+               self.cmd_and_log(cmd, env=env)
+               if not 'okmsg' in kw:
+                       kw['okmsg'] = 'yes'
+               return
+
+       for x in cfg_ver:
+               # TODO remove in waf 2.0
+               y = x.replace('-', '_')
+               if y in kw:
+                       self.cmd_and_log(path + ['--%s=%s' % (x, kw[y]), kw['package']], env=env)
+                       if not 'okmsg' in kw:
+                               kw['okmsg'] = 'yes'
+                       define_it()
+                       break
+
+       # single version for a module
+       if 'modversion' in kw:
+               version = self.cmd_and_log(path + ['--modversion', kw['modversion']], env=env).strip()
+               self.define(kw['define_name'], version)
+               return version
+
+       lst = [] + path
+
+       defi = kw.get('define_variable')
+       if not defi:
+               defi = self.env.PKG_CONFIG_DEFINES or {}
+       for key, val in defi.items():
+               lst.append('--define-variable=%s=%s' % (key, val))
+
+       static = kw.get('force_static', False)
+       if 'args' in kw:
+               args = Utils.to_list(kw['args'])
+               if '--static' in args or '--static-libs' in args:
+                       static = True
+               lst += args
+
+       # tools like pkgconf expect the package argument after the -- ones -_-
+       lst.extend(Utils.to_list(kw['package']))
+
+       # retrieving variables of a module
+       if 'variables' in kw:
+               v_env = kw.get('env', self.env)
+               vars = Utils.to_list(kw['variables'])
+               for v in vars:
+                       val = self.cmd_and_log(lst + ['--variable=' + v], env=env).strip()
+                       var = '%s_%s' % (kw['uselib_store'], v)
+                       v_env[var] = val
+               if not 'okmsg' in kw:
+                       kw['okmsg'] = 'yes'
+               return
+
+       # so we assume the command-line will output flags to be parsed afterwards
+       ret = self.cmd_and_log(lst, env=env)
+       if not 'okmsg' in kw:
+               kw['okmsg'] = 'yes'
+
+       define_it()
+       self.parse_flags(ret, kw['uselib_store'], kw.get('env', self.env), force_static=static, posix=kw.get('posix'))
+       return ret
+
+@conf
+def check_cfg(self, *k, **kw):
+       """
+       Checks for configuration flags using a **-config**-like program (pkg-config, sdl-config, etc).
+       This wraps internal calls to :py:func:`waflib.Tools.c_config.validate_cfg` and :py:func:`waflib.Tools.c_config.exec_cfg`
+
+       A few examples::
+
+               def configure(conf):
+                       conf.load('compiler_c')
+                       conf.check_cfg(package='glib-2.0', args='--libs --cflags')
+                       conf.check_cfg(package='glib-2.0', uselib_store='GLIB', atleast_version='2.10.0',
+                               args='--cflags --libs')
+                       conf.check_cfg(package='pango')
+                       conf.check_cfg(package='pango', uselib_store='MYPANGO', args=['--cflags', '--libs'])
+                       conf.check_cfg(package='pango',
+                               args=['pango >= 0.1.0', 'pango < 9.9.9', '--cflags', '--libs'],
+                               msg="Checking for 'pango 0.1.0'")
+                       conf.check_cfg(path='sdl-config', args='--cflags --libs', package='', uselib_store='SDL')
+                       conf.check_cfg(path='mpicc', args='--showme:compile --showme:link',
+                               package='', uselib_store='OPEN_MPI', mandatory=False)
+                       # variables
+                       conf.check_cfg(package='gtk+-2.0', variables=['includedir', 'prefix'], uselib_store='FOO')
+                       print(conf.env.FOO_includedir)
+       """
+       if k:
+               lst = k[0].split()
+               kw['package'] = lst[0]
+               kw['args'] = ' '.join(lst[1:])
+
+       self.validate_cfg(kw)
+       if 'msg' in kw:
+               self.start_msg(kw['msg'], **kw)
+       ret = None
+       try:
+               ret = self.exec_cfg(kw)
+       except self.errors.WafError:
+               if 'errmsg' in kw:
+                       self.end_msg(kw['errmsg'], 'YELLOW', **kw)
+               if Logs.verbose > 1:
+                       raise
+               else:
+                       self.fatal('The configuration failed')
+       else:
+               if not ret:
+                       ret = True
+               kw['success'] = ret
+               if 'okmsg' in kw:
+                       self.end_msg(self.ret_msg(kw['okmsg'], kw), **kw)
+
+       return ret
+
+def build_fun(bld):
+       """
+       Build function that is used for running configuration tests with ``conf.check()``
+       """
+       if bld.kw['compile_filename']:
+               node = bld.srcnode.make_node(bld.kw['compile_filename'])
+               node.write(bld.kw['code'])
+
+       o = bld(features=bld.kw['features'], source=bld.kw['compile_filename'], target='testprog')
+
+       for k, v in bld.kw.items():
+               setattr(o, k, v)
+
+       if not bld.kw.get('quiet'):
+               bld.conf.to_log("==>\n%s\n<==" % bld.kw['code'])
+
+@conf
+def validate_c(self, kw):
+       """
+       Pre-checks the parameters that will be given to :py:func:`waflib.Configure.run_build`
+
+       :param compiler: c or cxx (tries to guess what is best)
+       :type compiler: string
+       :param type: cprogram, cshlib, cstlib - not required if *features are given directly*
+       :type type: binary to create
+       :param feature: desired features for the task generator that will execute the test, for example ``cxx cxxstlib``
+       :type feature: list of string
+       :param fragment: provide a piece of code for the test (default is to let the system create one)
+       :type fragment: string
+       :param uselib_store: define variables after the test is executed (IMPORTANT!)
+       :type uselib_store: string
+       :param use: parameters to use for building (just like the normal *use* keyword)
+       :type use: list of string
+       :param define_name: define to set when the check is over
+       :type define_name: string
+       :param execute: execute the resulting binary
+       :type execute: bool
+       :param define_ret: if execute is set to True, use the execution output in both the define and the return value
+       :type define_ret: bool
+       :param header_name: check for a particular header
+       :type header_name: string
+       :param auto_add_header_name: if header_name was set, add the headers in env.INCKEYS so the next tests will include these headers
+       :type auto_add_header_name: bool
+       """
+
+       if not 'build_fun' in kw:
+               kw['build_fun'] = build_fun
+
+       if not 'env' in kw:
+               kw['env'] = self.env.derive()
+       env = kw['env']
+
+       if not 'compiler' in kw and not 'features' in kw:
+               kw['compiler'] = 'c'
+               if env.CXX_NAME and Task.classes.get('cxx'):
+                       kw['compiler'] = 'cxx'
+                       if not self.env.CXX:
+                               self.fatal('a c++ compiler is required')
+               else:
+                       if not self.env.CC:
+                               self.fatal('a c compiler is required')
+
+       if not 'compile_mode' in kw:
+               kw['compile_mode'] = 'c'
+               if 'cxx' in Utils.to_list(kw.get('features',[])) or kw.get('compiler', '') == 'cxx':
+                       kw['compile_mode'] = 'cxx'
+
+       if not 'type' in kw:
+               kw['type'] = 'cprogram'
+
+       if not 'features' in kw:
+               if not 'header_name' in kw or kw.get('link_header_test', True):
+                       kw['features'] = [kw['compile_mode'], kw['type']] # "c ccprogram"
+               else:
+                       kw['features'] = [kw['compile_mode']]
+       else:
+               kw['features'] = Utils.to_list(kw['features'])
+
+       if not 'compile_filename' in kw:
+               kw['compile_filename'] = 'test.c' + ((kw['compile_mode'] == 'cxx') and 'pp' or '')
+
+       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 ''
+
+       #OSX
+       if 'framework_name' in kw:
+               fwkname = kw['framework_name']
+               if not 'uselib_store' in kw:
+                       kw['uselib_store'] = fwkname.upper()
+               if not kw.get('no_header', False):
+                       if not 'header_name' in kw:
+                               kw['header_name'] = []
+                       fwk = '%s/%s.h' % (fwkname, fwkname)
+                       if kw.get('remove_dot_h'):
+                               fwk = fwk[:-2]
+                       kw['header_name'] = Utils.to_list(kw['header_name']) + [fwk]
+
+               kw['msg'] = 'Checking for framework %s' % fwkname
+               kw['framework'] = fwkname
+               #kw['frameworkpath'] = set it yourself
+
+       if 'function_name' in kw:
+               fu = kw['function_name']
+               if not 'msg' in kw:
+                       kw['msg'] = 'Checking for function %s' % fu
+               kw['code'] = to_header(kw) + SNIP_FUNCTION % fu
+               if not 'uselib_store' in kw:
+                       kw['uselib_store'] = fu.upper()
+               if not 'define_name' in kw:
+                       kw['define_name'] = self.have_define(fu)
+
+       elif 'type_name' in kw:
+               tu = kw['type_name']
+               if not 'header_name' in kw:
+                       kw['header_name'] = 'stdint.h'
+               if 'field_name' in kw:
+                       field = kw['field_name']
+                       kw['code'] = to_header(kw) + SNIP_FIELD % {'type_name' : tu, 'field_name' : field}
+                       if not 'msg' in kw:
+                               kw['msg'] = 'Checking for field %s in %s' % (field, tu)
+                       if not 'define_name' in kw:
+                               kw['define_name'] = self.have_define((tu + '_' + field).upper())
+               else:
+                       kw['code'] = to_header(kw) + SNIP_TYPE % {'type_name' : tu}
+                       if not 'msg' in kw:
+                               kw['msg'] = 'Checking for type %s' % tu
+                       if not 'define_name' in kw:
+                               kw['define_name'] = self.have_define(tu.upper())
+
+       elif 'header_name' in kw:
+               if not 'msg' in kw:
+                       kw['msg'] = 'Checking for header %s' % kw['header_name']
+
+               l = Utils.to_list(kw['header_name'])
+               assert len(l), 'list of headers in header_name is empty'
+
+               kw['code'] = to_header(kw) + SNIP_EMPTY_PROGRAM
+               if not 'uselib_store' in kw:
+                       kw['uselib_store'] = l[0].upper()
+               if not 'define_name' in kw:
+                       kw['define_name'] = self.have_define(l[0])
+
+       if 'lib' in kw:
+               if not 'msg' in kw:
+                       kw['msg'] = 'Checking for library %s' % kw['lib']
+               if not 'uselib_store' in kw:
+                       kw['uselib_store'] = kw['lib'].upper()
+
+       if 'stlib' in kw:
+               if not 'msg' in kw:
+                       kw['msg'] = 'Checking for static library %s' % kw['stlib']
+               if not 'uselib_store' in kw:
+                       kw['uselib_store'] = kw['stlib'].upper()
+
+       if 'fragment' in kw:
+               # an additional code fragment may be provided to replace the predefined code
+               # in custom headers
+               kw['code'] = kw['fragment']
+               if not 'msg' in kw:
+                       kw['msg'] = 'Checking for code snippet'
+               if not 'errmsg' in kw:
+                       kw['errmsg'] = 'no'
+
+       for (flagsname,flagstype) in (('cxxflags','compiler'), ('cflags','compiler'), ('linkflags','linker')):
+               if flagsname in kw:
+                       if not 'msg' in kw:
+                               kw['msg'] = 'Checking for %s flags %s' % (flagstype, kw[flagsname])
+                       if not 'errmsg' in kw:
+                               kw['errmsg'] = 'no'
+
+       if not 'execute' in kw:
+               kw['execute'] = False
+       if kw['execute']:
+               kw['features'].append('test_exec')
+               kw['chmod'] = Utils.O755
+
+       if not 'errmsg' in kw:
+               kw['errmsg'] = 'not found'
+
+       if not 'okmsg' in kw:
+               kw['okmsg'] = 'yes'
+
+       if not 'code' in kw:
+               kw['code'] = SNIP_EMPTY_PROGRAM
+
+       # if there are headers to append automatically to the next tests
+       if self.env[INCKEYS]:
+               kw['code'] = '\n'.join(['#include <%s>' % x for x in self.env[INCKEYS]]) + '\n' + kw['code']
+
+       # in case defines lead to very long command-lines
+       if kw.get('merge_config_header', False) or env.merge_config_header:
+               kw['code'] = '%s\n\n%s' % (self.get_config_header(), kw['code'])
+               env.DEFINES = [] # modify the copy
+
+       if not kw.get('success'): kw['success'] = None
+
+       if 'define_name' in kw:
+               self.undefine(kw['define_name'])
+       if not 'msg' in kw:
+               self.fatal('missing "msg" in conf.check(...)')
+
+@conf
+def post_check(self, *k, **kw):
+       """
+       Sets the variables after a test executed in
+       :py:func:`waflib.Tools.c_config.check` was run successfully
+       """
+       is_success = 0
+       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)
+
+       if kw.get('define_name'):
+               # TODO this is still way too complicated
+               comment = kw.get('comment', '')
+               define_name = kw['define_name']
+               if kw['execute'] and kw.get('define_ret') and isinstance(is_success, str):
+                       if kw.get('global_define', 1):
+                               self.define(define_name, is_success, quote=kw.get('quote', 1), comment=comment)
+                       else:
+                               if kw.get('quote', 1):
+                                       succ = '"%s"' % is_success
+                               else:
+                                       succ = int(is_success)
+                               val = '%s=%s' % (define_name, succ)
+                               var = 'DEFINES_%s' % kw['uselib_store']
+                               self.env.append_value(var, val)
+               else:
+                       if kw.get('global_define', 1):
+                               self.define_cond(define_name, is_success, comment=comment)
+                       else:
+                               var = 'DEFINES_%s' % kw['uselib_store']
+                               self.env.append_value(var, '%s=%s' % (define_name, int(is_success)))
+
+               # define conf.env.HAVE_X to 1
+               if kw.get('add_have_to_env', 1):
+                       if kw.get('uselib_store'):
+                               self.env[self.have_define(kw['uselib_store'])] = 1
+                       elif kw['execute'] and kw.get('define_ret'):
+                               self.env[define_name] = is_success
+                       else:
+                               self.env[define_name] = int(is_success)
+
+       if 'header_name' in kw:
+               if kw.get('auto_add_header_name', False):
+                       self.env.append_value(INCKEYS, Utils.to_list(kw['header_name']))
+
+       if is_success and 'uselib_store' in kw:
+               from waflib.Tools import ccroot
+               # See get_uselib_vars in ccroot.py
+               _vars = set()
+               for x in kw['features']:
+                       if x in ccroot.USELIB_VARS:
+                               _vars |= ccroot.USELIB_VARS[x]
+
+               for k in _vars:
+                       x = k.lower()
+                       if x in kw:
+                               self.env.append_value(k + '_' + kw['uselib_store'], kw[x])
+       return is_success
+
+@conf
+def check(self, *k, **kw):
+       """
+       Performs a configuration test by calling :py:func:`waflib.Configure.run_build`.
+       For the complete list of parameters, see :py:func:`waflib.Tools.c_config.validate_c`.
+       To force a specific compiler, pass ``compiler='c'`` or ``compiler='cxx'`` to the list of arguments
+
+       Besides build targets, complete builds can be given through a build function. All files will
+       be written to a temporary directory::
+
+               def build(bld):
+                       lib_node = bld.srcnode.make_node('libdir/liblc1.c')
+                       lib_node.parent.mkdir()
+                       lib_node.write('#include <stdio.h>\\nint lib_func(void) { FILE *f = fopen("foo", "r");}\\n', 'w')
+                       bld(features='c cshlib', source=[lib_node], linkflags=conf.env.EXTRA_LDFLAGS, target='liblc')
+               conf.check(build_fun=build, msg=msg)
+       """
+       self.validate_c(kw)
+       self.start_msg(kw['msg'], **kw)
+       ret = None
+       try:
+               ret = self.run_build(*k, **kw)
+       except self.errors.ConfigurationError:
+               self.end_msg(kw['errmsg'], 'YELLOW', **kw)
+               if Logs.verbose > 1:
+                       raise
+               else:
+                       self.fatal('The configuration failed')
+       else:
+               kw['success'] = ret
+
+       ret = self.post_check(*k, **kw)
+       if not ret:
+               self.end_msg(kw['errmsg'], 'YELLOW', **kw)
+               self.fatal('The configuration failed %r' % ret)
+       else:
+               self.end_msg(self.ret_msg(kw['okmsg'], kw), **kw)
+       return ret
+
+class test_exec(Task.Task):
+       """
+       A task that runs programs after they are built. See :py:func:`waflib.Tools.c_config.test_exec_fun`.
+       """
+       color = 'PINK'
+       def run(self):
+               if getattr(self.generator, 'rpath', None):
+                       if getattr(self.generator, 'define_ret', False):
+                               self.generator.bld.retval = self.generator.bld.cmd_and_log([self.inputs[0].abspath()])
+                       else:
+                               self.generator.bld.retval = self.generator.bld.exec_command([self.inputs[0].abspath()])
+               else:
+                       env = self.env.env or {}
+                       env.update(dict(os.environ))
+                       for var in ('LD_LIBRARY_PATH', 'DYLD_LIBRARY_PATH', 'PATH'):
+                               env[var] = self.inputs[0].parent.abspath() + os.path.pathsep + env.get(var, '')
+                       if getattr(self.generator, 'define_ret', False):
+                               self.generator.bld.retval = self.generator.bld.cmd_and_log([self.inputs[0].abspath()], env=env)
+                       else:
+                               self.generator.bld.retval = self.generator.bld.exec_command([self.inputs[0].abspath()], env=env)
+
+@feature('test_exec')
+@after_method('apply_link')
+def test_exec_fun(self):
+       """
+       The feature **test_exec** is used to create a task that will to execute the binary
+       created (link task output) during the build. The exit status will be set
+       on the build context, so only one program may have the feature *test_exec*.
+       This is used by configuration tests::
+
+               def configure(conf):
+                       conf.check(execute=True)
+       """
+       self.create_task('test_exec', self.link_task.outputs[0])
+
+@conf
+def check_cxx(self, *k, **kw):
+       """
+       Runs a test with a task generator of the form::
+
+               conf.check(features='cxx cxxprogram', ...)
+       """
+       kw['compiler'] = 'cxx'
+       return self.check(*k, **kw)
+
+@conf
+def check_cc(self, *k, **kw):
+       """
+       Runs a test with a task generator of the form::
+
+               conf.check(features='c cprogram', ...)
+       """
+       kw['compiler'] = 'c'
+       return self.check(*k, **kw)
+
+@conf
+def set_define_comment(self, key, comment):
+       """
+       Sets a comment that will appear in the configuration header
+
+       :type key: string
+       :type comment: string
+       """
+       coms = self.env.DEFINE_COMMENTS
+       if not coms:
+               coms = self.env.DEFINE_COMMENTS = {}
+       coms[key] = comment or ''
+
+@conf
+def get_define_comment(self, key):
+       """
+       Returns the comment associated to a define
+
+       :type key: string
+       """
+       coms = self.env.DEFINE_COMMENTS or {}
+       return coms.get(key, '')
+
+@conf
+def define(self, key, val, quote=True, comment=''):
+       """
+       Stores a single define and its state into ``conf.env.DEFINES``. The value is cast to an integer (0/1).
+
+       :param key: define name
+       :type key: string
+       :param val: value
+       :type val: int or string
+       :param quote: enclose strings in quotes (yes by default)
+       :type quote: bool
+       """
+       assert isinstance(key, str)
+       if not key:
+               return
+       if val is True:
+               val = 1
+       elif val in (False, None):
+               val = 0
+
+       if isinstance(val, int) or isinstance(val, float):
+               s = '%s=%s'
+       else:
+               s = quote and '%s="%s"' or '%s=%s'
+       app = s % (key, str(val))
+
+       ban = key + '='
+       lst = self.env.DEFINES
+       for x in lst:
+               if x.startswith(ban):
+                       lst[lst.index(x)] = app
+                       break
+       else:
+               self.env.append_value('DEFINES', app)
+
+       self.env.append_unique(DEFKEYS, key)
+       self.set_define_comment(key, comment)
+
+@conf
+def undefine(self, key, comment=''):
+       """
+       Removes a global define from ``conf.env.DEFINES``
+
+       :param key: define name
+       :type key: string
+       """
+       assert isinstance(key, str)
+       if not key:
+               return
+       ban = key + '='
+       lst = [x for x in self.env.DEFINES if not x.startswith(ban)]
+       self.env.DEFINES = lst
+       self.env.append_unique(DEFKEYS, key)
+       self.set_define_comment(key, comment)
+
+@conf
+def define_cond(self, key, val, comment=''):
+       """
+       Conditionally defines a name::
+
+               def configure(conf):
+                       conf.define_cond('A', True)
+                       # equivalent to:
+                       # if val: conf.define('A', 1)
+                       # else: conf.undefine('A')
+
+       :param key: define name
+       :type key: string
+       :param val: value
+       :type val: int or string
+       """
+       assert isinstance(key, str)
+       if not key:
+               return
+       if val:
+               self.define(key, 1, comment=comment)
+       else:
+               self.undefine(key, comment=comment)
+
+@conf
+def is_defined(self, key):
+       """
+       Indicates whether a particular define is globally set in ``conf.env.DEFINES``.
+
+       :param key: define name
+       :type key: string
+       :return: True if the define is set
+       :rtype: bool
+       """
+       assert key and isinstance(key, str)
+
+       ban = key + '='
+       for x in self.env.DEFINES:
+               if x.startswith(ban):
+                       return True
+       return False
+
+@conf
+def get_define(self, key):
+       """
+       Returns the value of an existing define, or None if not found
+
+       :param key: define name
+       :type key: string
+       :rtype: string
+       """
+       assert key and isinstance(key, str)
+
+       ban = key + '='
+       for x in self.env.DEFINES:
+               if x.startswith(ban):
+                       return x[len(ban):]
+       return None
+
+@conf
+def have_define(self, key):
+       """
+       Returns a variable suitable for command-line or header use by removing invalid characters
+       and prefixing it with ``HAVE_``
+
+       :param key: define name
+       :type key: string
+       :return: the input key prefixed by *HAVE_* and substitute any invalid characters.
+       :rtype: string
+       """
+       return (self.env.HAVE_PAT or 'HAVE_%s') % Utils.quote_define_name(key)
+
+@conf
+def write_config_header(self, configfile='', guard='', top=False, defines=True, headers=False, remove=True, define_prefix=''):
+       """
+       Writes a configuration header containing defines and includes::
+
+               def configure(cnf):
+                       cnf.define('A', 1)
+                       cnf.write_config_header('config.h')
+
+       This function only adds include guards (if necessary), consult
+       :py:func:`waflib.Tools.c_config.get_config_header` for details on the body.
+
+       :param configfile: path to the file to create (relative or absolute)
+       :type configfile: string
+       :param guard: include guard name to add, by default it is computed from the file name
+       :type guard: string
+       :param top: write the configuration header from the build directory (default is from the current path)
+       :type top: bool
+       :param defines: add the defines (yes by default)
+       :type defines: bool
+       :param headers: add #include in the file
+       :type headers: bool
+       :param remove: remove the defines after they are added (yes by default, works like in autoconf)
+       :type remove: bool
+       :type define_prefix: string
+       :param define_prefix: prefix all the defines in the file with a particular prefix
+       """
+       if not configfile: configfile = WAF_CONFIG_H
+       waf_guard = guard or 'W_%s_WAF' % Utils.quote_define_name(configfile)
+
+       node = top and self.bldnode or self.path.get_bld()
+       node = node.make_node(configfile)
+       node.parent.mkdir()
+
+       lst = ['/* WARNING! All changes made to this file will be lost! */\n']
+       lst.append('#ifndef %s\n#define %s\n' % (waf_guard, waf_guard))
+       lst.append(self.get_config_header(defines, headers, define_prefix=define_prefix))
+       lst.append('\n#endif /* %s */\n' % waf_guard)
+
+       node.write('\n'.join(lst))
+
+       # config files must not be removed on "waf clean"
+       self.env.append_unique(Build.CFG_FILES, [node.abspath()])
+
+       if remove:
+               for key in self.env[DEFKEYS]:
+                       self.undefine(key)
+               self.env[DEFKEYS] = []
+
+@conf
+def get_config_header(self, defines=True, headers=False, define_prefix=''):
+       """
+       Creates 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.
+
+       A prelude will be added from the variable env.WAF_CONFIG_H_PRELUDE if provided. This
+       can be used to insert complex macros or include guards::
+
+               def configure(conf):
+                       conf.env.WAF_CONFIG_H_PRELUDE = '#include <unistd.h>\\n'
+                       conf.write_config_header('config.h')
+
+       :param defines: write the defines values
+       :type defines: bool
+       :param headers: write include entries for each element in self.env.INCKEYS
+       :type headers: bool
+       :type define_prefix: string
+       :param define_prefix: prefix all the defines with a particular prefix
+       :return: the contents of a ``config.h`` file
+       :rtype: string
+       """
+       lst = []
+
+       if self.env.WAF_CONFIG_H_PRELUDE:
+               lst.append(self.env.WAF_CONFIG_H_PRELUDE)
+
+       if headers:
+               for x in self.env[INCKEYS]:
+                       lst.append('#include <%s>' % x)
+
+       if defines:
+               tbl = {}
+               for k in self.env.DEFINES:
+                       a, _, b = k.partition('=')
+                       tbl[a] = b
+
+               for k in self.env[DEFKEYS]:
+                       caption = self.get_define_comment(k)
+                       if caption:
+                               caption = ' /* %s */' % caption
+                       try:
+                               txt = '#define %s%s %s%s' % (define_prefix, k, tbl[k], caption)
+                       except KeyError:
+                               txt = '/* #undef %s%s */%s' % (define_prefix, k, caption)
+                       lst.append(txt)
+       return "\n".join(lst)
+
+@conf
+def cc_add_flags(conf):
+       """
+       Adds CFLAGS / CPPFLAGS from os.environ to conf.env
+       """
+       conf.add_os_flags('CPPFLAGS', dup=False)
+       conf.add_os_flags('CFLAGS', dup=False)
+
+@conf
+def cxx_add_flags(conf):
+       """
+       Adds CXXFLAGS / CPPFLAGS from os.environ to conf.env
+       """
+       conf.add_os_flags('CPPFLAGS', dup=False)
+       conf.add_os_flags('CXXFLAGS', dup=False)
+
+@conf
+def link_add_flags(conf):
+       """
+       Adds LINKFLAGS / LDFLAGS from os.environ to conf.env
+       """
+       conf.add_os_flags('LINKFLAGS', dup=False)
+       conf.add_os_flags('LDFLAGS', dup=False)
+
+@conf
+def cc_load_tools(conf):
+       """
+       Loads the Waf c extensions
+       """
+       if not conf.env.DEST_OS:
+               conf.env.DEST_OS = Utils.unversioned_sys_platform()
+       conf.load('c')
+
+@conf
+def cxx_load_tools(conf):
+       """
+       Loads the Waf c++ extensions
+       """
+       if not conf.env.DEST_OS:
+               conf.env.DEST_OS = Utils.unversioned_sys_platform()
+       conf.load('cxx')
+
+@conf
+def get_cc_version(conf, cc, gcc=False, icc=False, clang=False):
+       """
+       Runs the preprocessor to determine the gcc/icc/clang version
+
+       The variables CC_VERSION, DEST_OS, DEST_BINFMT and DEST_CPU will be set in *conf.env*
+
+       :raise: :py:class:`waflib.Errors.ConfigurationError`
+       """
+       cmd = cc + ['-dM', '-E', '-']
+       env = conf.env.env or None
+       try:
+               out, err = conf.cmd_and_log(cmd, output=0, input='\n', env=env)
+       except Exception:
+               conf.fatal('Could not determine the compiler version %r' % cmd)
+
+       if gcc:
+               if out.find('__INTEL_COMPILER') >= 0:
+                       conf.fatal('The intel compiler pretends to be gcc')
+               if out.find('__GNUC__') < 0 and out.find('__clang__') < 0:
+                       conf.fatal('Could not determine the compiler type')
+
+       if icc and out.find('__INTEL_COMPILER') < 0:
+               conf.fatal('Not icc/icpc')
+
+       if clang and out.find('__clang__') < 0:
+               conf.fatal('Not clang/clang++')
+       if not clang and out.find('__clang__') >= 0:
+               conf.fatal('Could not find gcc/g++ (only Clang), if renamed try eg: CC=gcc48 CXX=g++48 waf configure')
+
+       k = {}
+       if icc or gcc or clang:
+               out = out.splitlines()
+               for line in out:
+                       lst = shlex.split(line)
+                       if len(lst)>2:
+                               key = lst[1]
+                               val = lst[2]
+                               k[key] = val
+
+               def isD(var):
+                       return var in k
+
+               # Some documentation is available at http://predef.sourceforge.net
+               # The names given to DEST_OS must match what Utils.unversioned_sys_platform() returns.
+               if not conf.env.DEST_OS:
+                       conf.env.DEST_OS = ''
+               for i in MACRO_TO_DESTOS:
+                       if isD(i):
+                               conf.env.DEST_OS = MACRO_TO_DESTOS[i]
+                               break
+               else:
+                       if isD('__APPLE__') and isD('__MACH__'):
+                               conf.env.DEST_OS = 'darwin'
+                       elif isD('__unix__'): # unix must be tested last as it's a generic fallback
+                               conf.env.DEST_OS = 'generic'
+
+               if isD('__ELF__'):
+                       conf.env.DEST_BINFMT = 'elf'
+               elif isD('__WINNT__') or isD('__CYGWIN__') or isD('_WIN32'):
+                       conf.env.DEST_BINFMT = 'pe'
+                       conf.env.LIBDIR = conf.env.BINDIR
+               elif isD('__APPLE__'):
+                       conf.env.DEST_BINFMT = 'mac-o'
+
+               if not conf.env.DEST_BINFMT:
+                       # Infer the binary format from the os name.
+                       conf.env.DEST_BINFMT = Utils.destos_to_binfmt(conf.env.DEST_OS)
+
+               for i in MACRO_TO_DEST_CPU:
+                       if isD(i):
+                               conf.env.DEST_CPU = MACRO_TO_DEST_CPU[i]
+                               break
+
+               Logs.debug('ccroot: dest platform: ' + ' '.join([conf.env[x] or '?' for x in ('DEST_OS', 'DEST_BINFMT', 'DEST_CPU')]))
+               if icc:
+                       ver = k['__INTEL_COMPILER']
+                       conf.env.CC_VERSION = (ver[:-2], ver[-2], ver[-1])
+               else:
+                       if isD('__clang__') and isD('__clang_major__'):
+                               conf.env.CC_VERSION = (k['__clang_major__'], k['__clang_minor__'], k['__clang_patchlevel__'])
+                       else:
+                               # older clang versions and gcc
+                               conf.env.CC_VERSION = (k['__GNUC__'], k['__GNUC_MINOR__'], k.get('__GNUC_PATCHLEVEL__', '0'))
+       return k
+
+@conf
+def get_xlc_version(conf, cc):
+       """
+       Returns the Aix compiler version
+
+       :raise: :py:class:`waflib.Errors.ConfigurationError`
+       """
+       cmd = cc + ['-qversion']
+       try:
+               out, err = conf.cmd_and_log(cmd, output=0)
+       except Errors.WafError:
+               conf.fatal('Could not find xlc %r' % cmd)
+
+       # the intention is to catch the 8.0 in "IBM XL C/C++ Enterprise Edition V8.0 for AIX..."
+       for v in (r"IBM XL C/C\+\+.* V(?P<major>\d*)\.(?P<minor>\d*)",):
+               version_re = re.compile(v, re.I).search
+               match = version_re(out or err)
+               if match:
+                       k = match.groupdict()
+                       conf.env.CC_VERSION = (k['major'], k['minor'])
+                       break
+       else:
+               conf.fatal('Could not determine the XLC version.')
+
+@conf
+def get_suncc_version(conf, cc):
+       """
+       Returns the Sun compiler version
+
+       :raise: :py:class:`waflib.Errors.ConfigurationError`
+       """
+       cmd = cc + ['-V']
+       try:
+               out, err = conf.cmd_and_log(cmd, output=0)
+       except Errors.WafError ,e:
+               # Older versions of the compiler exit with non-zero status when reporting their version
+               if not (hasattr(e, 'returncode') and hasattr(e, 'stdout') and hasattr(e, 'stderr')):
+                       conf.fatal('Could not find suncc %r' % cmd)
+               out = e.stdout
+               err = e.stderr
+
+       version = (out or err)
+       version = version.splitlines()[0]
+
+       # cc: Sun C 5.10 SunOS_i386 2009/06/03
+       # cc: Studio 12.5 Sun C++ 5.14 SunOS_sparc Beta 2015/11/17
+       # cc: WorkShop Compilers 5.0 98/12/15 C 5.0
+       version_re = re.compile(r'cc: (studio.*?|\s+)?(sun\s+(c\+\+|c)|(WorkShop\s+Compilers))?\s+(?P<major>\d*)\.(?P<minor>\d*)', re.I).search
+       match = version_re(version)
+       if match:
+               k = match.groupdict()
+               conf.env.CC_VERSION = (k['major'], k['minor'])
+       else:
+               conf.fatal('Could not determine the suncc version.')
+
+# ============ the --as-needed flag should added during the configuration, not at runtime =========
+
+@conf
+def add_as_needed(self):
+       """
+       Adds ``--as-needed`` to the *LINKFLAGS*
+       On some platforms, it is a default flag.  In some cases (e.g., in NS-3) it is necessary to explicitly disable this feature with `-Wl,--no-as-needed` flag.
+       """
+       if self.env.DEST_BINFMT == 'elf' and 'gcc' in (self.env.CXX_NAME, self.env.CC_NAME):
+               self.env.append_unique('LINKFLAGS', '-Wl,--as-needed')
+
+# ============ parallel configuration
+
+class cfgtask(Task.TaskBase):
+       """
+       A task that executes build configuration tests (calls conf.check)
+
+       Make sure to use locks if concurrent access to the same conf.env data is necessary.
+       """
+       def __init__(self, *k, **kw):
+               Task.TaskBase.__init__(self, *k, **kw)
+               self.run_after = set()
+
+       def display(self):
+               return ''
+
+       def runnable_status(self):
+               for x in self.run_after:
+                       if not x.hasrun:
+                               return Task.ASK_LATER
+               return Task.RUN_ME
+
+       def uid(self):
+               return Utils.SIG_NIL
+
+       def run(self):
+               conf = self.conf
+               bld = Build.BuildContext(top_dir=conf.srcnode.abspath(), out_dir=conf.bldnode.abspath())
+               bld.env = conf.env
+               bld.init_dirs()
+               bld.in_msg = 1 # suppress top-level start_msg
+               bld.logger = self.logger
+               bld.multicheck_task = self
+               args = self.args
+               try:
+                       if 'func' in args:
+                               bld.test(build_fun=args['func'],
+                                       msg=args.get('msg', ''),
+                                       okmsg=args.get('okmsg', ''),
+                                       errmsg=args.get('errmsg', ''),
+                                       )
+                       else:
+                               args['multicheck_mandatory'] = args.get('mandatory', True)
+                               args['mandatory'] = True
+                               try:
+                                       bld.check(**args)
+                               finally:
+                                       args['mandatory'] = args['multicheck_mandatory']
+               except Exception:
+                       return 1
+
+       def process(self):
+               Task.TaskBase.process(self)
+               if 'msg' in self.args:
+                       with self.generator.bld.multicheck_lock:
+                               self.conf.start_msg(self.args['msg'])
+                               if self.hasrun == Task.NOT_RUN:
+                                       self.conf.end_msg('test cancelled', 'YELLOW')
+                               elif self.hasrun != Task.SUCCESS:
+                                       self.conf.end_msg(self.args.get('errmsg', 'no'), 'YELLOW')
+                               else:
+                                       self.conf.end_msg(self.args.get('okmsg', 'yes'), 'GREEN')
+
+@conf
+def multicheck(self, *k, **kw):
+       """
+       Runs configuration tests in parallel; results are printed sequentially at the end of the build
+       but each test must provide its own msg value to display a line::
+
+               def test_build(ctx):
+                       ctx.in_msg = True # suppress console outputs
+                       ctx.check_large_file(mandatory=False)
+
+               conf.multicheck(
+                       {'header_name':'stdio.h', 'msg':'... stdio', 'uselib_store':'STDIO', 'global_define':False},
+                       {'header_name':'xyztabcd.h', 'msg':'... optional xyztabcd.h', 'mandatory': False},
+                       {'header_name':'stdlib.h', 'msg':'... stdlib', 'okmsg': 'aye', 'errmsg': 'nope'},
+                       {'func': test_build, 'msg':'... testing an arbitrary build function', 'okmsg':'ok'},
+                       msg       = 'Checking for headers in parallel',
+                       mandatory = True, # mandatory tests raise an error at the end
+                       run_all_tests = True, # try running all tests
+               )
+
+       The configuration tests may modify the values in conf.env in any order, and the define
+       values can affect configuration tests being executed. It is hence recommended
+       to provide `uselib_store` values with `global_define=False` to prevent such issues.
+       """
+       self.start_msg(kw.get('msg', 'Executing %d configuration tests' % len(k)), **kw)
+
+       # Force a copy so that threads append to the same list at least
+       # no order is guaranteed, but the values should not disappear at least
+       for var in ('DEFINES', DEFKEYS):
+               self.env.append_value(var, [])
+       self.env.DEFINE_COMMENTS = self.env.DEFINE_COMMENTS or {}
+
+       # define a task object that will execute our tests
+       class par(object):
+               def __init__(self):
+                       self.keep = False
+                       self.task_sigs = {}
+                       self.progress_bar = 0
+               def total(self):
+                       return len(tasks)
+               def to_log(self, *k, **kw):
+                       return
+
+       bld = par()
+       bld.keep = kw.get('run_all_tests', True)
+       tasks = []
+
+       id_to_task = {}
+       for dct in k:
+               x = Task.classes['cfgtask'](bld=bld)
+               tasks.append(x)
+               x.args = dct
+               x.bld = bld
+               x.conf = self
+               x.args = dct
+
+               # bind a logger that will keep the info in memory
+               x.logger = Logs.make_mem_logger(str(id(x)), self.logger)
+
+               if 'id' in dct:
+                       id_to_task[dct['id']] = x
+
+       # second pass to set dependencies with after_test/before_test
+       for x in tasks:
+               for key in Utils.to_list(x.args.get('before_tests', [])):
+                       tsk = id_to_task[key]
+                       if not tsk:
+                               raise ValueError('No test named %r' % key)
+                       tsk.run_after.add(x)
+               for key in Utils.to_list(x.args.get('after_tests', [])):
+                       tsk = id_to_task[key]
+                       if not tsk:
+                               raise ValueError('No test named %r' % key)
+                       x.run_after.add(tsk)
+
+       def it():
+               yield tasks
+               while 1:
+                       yield []
+       bld.producer = p = Runner.Parallel(bld, Options.options.jobs)
+       bld.multicheck_lock = Utils.threading.Lock()
+       p.biter = it()
+
+       self.end_msg('started')
+       p.start()
+
+       # flush the logs in order into the config.log
+       for x in tasks:
+               x.logger.memhandler.flush()
+
+       self.start_msg('-> processing test results')
+       if p.error:
+               for x in p.error:
+                       if getattr(x, 'err_msg', None):
+                               self.to_log(x.err_msg)
+                               self.end_msg('fail', color='RED')
+                               raise Errors.WafError('There is an error in the library, read config.log for more information')
+
+       failure_count = 0
+       for x in tasks:
+               if x.hasrun not in (Task.SUCCESS, Task.NOT_RUN):
+                       failure_count += 1
+
+       if failure_count:
+               self.end_msg(kw.get('errmsg', '%s test failed' % failure_count), color='YELLOW', **kw)
+       else:
+               self.end_msg('all ok', **kw)
+
+       for x in tasks:
+               if x.hasrun != Task.SUCCESS:
+                       if x.args.get('mandatory', True):
+                               self.fatal(kw.get('fatalmsg') or 'One of the tests has failed, read config.log for more information')