thirdparty:waf: New files for waf 1.9.10
[vlendec/samba-autobuild/.git] / third_party / waf / waflib / Tools / c_config.py
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
4
5 #!/usr/bin/env python
6 # encoding: utf-8
7 # Thomas Nagy, 2005-2016 (ita)
8
9 """
10 C/C++/D configuration helpers
11 """
12
13 from __future__ import with_statement
14
15 import os, re, shlex
16 from waflib import Build, Utils, Task, Options, Logs, Errors, Runner
17 from waflib.TaskGen import after_method, feature
18 from waflib.Configure import conf
19
20 WAF_CONFIG_H   = 'config.h'
21 """default name for the config.h file"""
22
23 DEFKEYS = 'define_key'
24 INCKEYS = 'include_key'
25
26 cfg_ver = {
27         'atleast-version': '>=',
28         'exact-version': '==',
29         'max-version': '<=',
30 }
31
32 SNIP_FUNCTION = '''
33 int main(int argc, char **argv) {
34         void (*p)();
35         (void)argc; (void)argv;
36         p=(void(*)())(%s);
37         return !p;
38 }
39 '''
40 """Code template for checking for functions"""
41
42 SNIP_TYPE = '''
43 int main(int argc, char **argv) {
44         (void)argc; (void)argv;
45         if ((%(type_name)s *) 0) return 0;
46         if (sizeof (%(type_name)s)) return 0;
47         return 1;
48 }
49 '''
50 """Code template for checking for types"""
51
52 SNIP_EMPTY_PROGRAM = '''
53 int main(int argc, char **argv) {
54         (void)argc; (void)argv;
55         return 0;
56 }
57 '''
58
59 SNIP_FIELD = '''
60 int main(int argc, char **argv) {
61         char *off;
62         (void)argc; (void)argv;
63         off = (char*) &((%(type_name)s*)0)->%(field_name)s;
64         return (size_t) off < sizeof(%(type_name)s);
65 }
66 '''
67
68 MACRO_TO_DESTOS = {
69 '__linux__'                                      : 'linux',
70 '__GNU__'                                        : 'gnu', # hurd
71 '__FreeBSD__'                                    : 'freebsd',
72 '__NetBSD__'                                     : 'netbsd',
73 '__OpenBSD__'                                    : 'openbsd',
74 '__sun'                                          : 'sunos',
75 '__hpux'                                         : 'hpux',
76 '__sgi'                                          : 'irix',
77 '_AIX'                                           : 'aix',
78 '__CYGWIN__'                                     : 'cygwin',
79 '__MSYS__'                                       : 'cygwin',
80 '_UWIN'                                          : 'uwin',
81 '_WIN64'                                         : 'win32',
82 '_WIN32'                                         : 'win32',
83 # Note about darwin: this is also tested with 'defined __APPLE__ && defined __MACH__' somewhere below in this file.
84 '__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__'  : 'darwin',
85 '__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__' : 'darwin', # iphone
86 '__QNX__'                                        : 'qnx',
87 '__native_client__'                              : 'nacl' # google native client platform
88 }
89
90 MACRO_TO_DEST_CPU = {
91 '__x86_64__'  : 'x86_64',
92 '__amd64__'   : 'x86_64',
93 '__i386__'    : 'x86',
94 '__ia64__'    : 'ia',
95 '__mips__'    : 'mips',
96 '__sparc__'   : 'sparc',
97 '__alpha__'   : 'alpha',
98 '__aarch64__' : 'aarch64',
99 '__thumb__'   : 'thumb',
100 '__arm__'     : 'arm',
101 '__hppa__'    : 'hppa',
102 '__powerpc__' : 'powerpc',
103 '__ppc__'     : 'powerpc',
104 '__convex__'  : 'convex',
105 '__m68k__'    : 'm68k',
106 '__s390x__'   : 's390x',
107 '__s390__'    : 's390',
108 '__sh__'      : 'sh',
109 '__xtensa__'  : 'xtensa',
110 }
111
112 @conf
113 def parse_flags(self, line, uselib_store, env=None, force_static=False, posix=None):
114         """
115         Parses flags from the input lines, and adds them to the relevant use variables::
116
117                 def configure(conf):
118                         conf.parse_flags('-O3', 'FOO')
119                         # conf.env.CXXFLAGS_FOO = ['-O3']
120                         # conf.env.CFLAGS_FOO = ['-O3']
121
122         :param line: flags
123         :type line: string
124         :param uselib_store: where to add the flags
125         :type uselib_store: string
126         :param env: config set or conf.env by default
127         :type env: :py:class:`waflib.ConfigSet.ConfigSet`
128         """
129
130         assert(isinstance(line, str))
131
132         env = env or self.env
133
134         # Issue 811 and 1371
135         if posix is None:
136                 posix = True
137                 if '\\' in line:
138                         posix = ('\\ ' in line) or ('\\\\' in line)
139
140         lex = shlex.shlex(line, posix=posix)
141         lex.whitespace_split = True
142         lex.commenters = ''
143         lst = list(lex)
144
145         # append_unique is not always possible
146         # for example, apple flags may require both -arch i386 and -arch ppc
147         uselib = uselib_store
148         def app(var, val):
149                 env.append_value('%s_%s' % (var, uselib), val)
150         def appu(var, val):
151                 env.append_unique('%s_%s' % (var, uselib), val)
152         static = False
153         while lst:
154                 x = lst.pop(0)
155                 st = x[:2]
156                 ot = x[2:]
157
158                 if st == '-I' or st == '/I':
159                         if not ot:
160                                 ot = lst.pop(0)
161                         appu('INCLUDES', ot)
162                 elif st == '-i':
163                         tmp = [x, lst.pop(0)]
164                         app('CFLAGS', tmp)
165                         app('CXXFLAGS', tmp)
166                 elif st == '-D' or (env.CXX_NAME == 'msvc' and st == '/D'): # not perfect but..
167                         if not ot:
168                                 ot = lst.pop(0)
169                         app('DEFINES', ot)
170                 elif st == '-l':
171                         if not ot:
172                                 ot = lst.pop(0)
173                         prefix = 'STLIB' if (force_static or static) else 'LIB'
174                         app(prefix, ot)
175                 elif st == '-L':
176                         if not ot:
177                                 ot = lst.pop(0)
178                         prefix = 'STLIBPATH' if (force_static or static) else 'LIBPATH'
179                         appu(prefix, ot)
180                 elif x.startswith('/LIBPATH:'):
181                         prefix = 'STLIBPATH' if (force_static or static) else 'LIBPATH'
182                         appu(prefix, x.replace('/LIBPATH:', ''))
183                 elif x.startswith('-std='):
184                         prefix = 'CXXFLAGS' if '++' in x else 'CFLAGS'
185                         app(prefix, x)
186                 elif x.startswith('+') or x in ('-pthread', '-fPIC', '-fpic', '-fPIE', '-fpie'):
187                         app('CFLAGS', x)
188                         app('CXXFLAGS', x)
189                         app('LINKFLAGS', x)
190                 elif x == '-framework':
191                         appu('FRAMEWORK', lst.pop(0))
192                 elif x.startswith('-F'):
193                         appu('FRAMEWORKPATH', x[2:])
194                 elif x == '-Wl,-rpath' or x == '-Wl,-R':
195                         app('RPATH', lst.pop(0).lstrip('-Wl,'))
196                 elif x.startswith('-Wl,-R,'):
197                         app('RPATH', x[7:])
198                 elif x.startswith('-Wl,-R'):
199                         app('RPATH', x[6:])
200                 elif x.startswith('-Wl,-rpath,'):
201                         app('RPATH', x[11:])
202                 elif x == '-Wl,-Bstatic' or x == '-Bstatic':
203                         static = True
204                 elif x == '-Wl,-Bdynamic' or x == '-Bdynamic':
205                         static = False
206                 elif x.startswith('-Wl') or x in ('-rdynamic', '-pie'):
207                         app('LINKFLAGS', x)
208                 elif x.startswith(('-m', '-f', '-dynamic', '-O')):
209                         app('CFLAGS', x)
210                         app('CXXFLAGS', x)
211                 elif x.startswith('-bundle'):
212                         app('LINKFLAGS', x)
213                 elif x.startswith(('-undefined', '-Xlinker')):
214                         arg = lst.pop(0)
215                         app('LINKFLAGS', [x, arg])
216                 elif x.startswith(('-arch', '-isysroot')):
217                         tmp = [x, lst.pop(0)]
218                         app('CFLAGS', tmp)
219                         app('CXXFLAGS', tmp)
220                         app('LINKFLAGS', tmp)
221                 elif x.endswith(('.a', '.so', '.dylib', '.lib')):
222                         appu('LINKFLAGS', x) # not cool, #762
223                 else:
224                         self.to_log('Unhandled flag %r' % x)
225
226 @conf
227 def validate_cfg(self, kw):
228         """
229         Searches for the program *pkg-config* if missing, and validates the
230         parameters to pass to :py:func:`waflib.Tools.c_config.exec_cfg`.
231
232         :param path: the **-config program to use** (default is *pkg-config*)
233         :type path: list of string
234         :param msg: message to display to describe the test executed
235         :type msg: string
236         :param okmsg: message to display when the test is successful
237         :type okmsg: string
238         :param errmsg: message to display in case of error
239         :type errmsg: string
240         """
241         if not 'path' in kw:
242                 if not self.env.PKGCONFIG:
243                         self.find_program('pkg-config', var='PKGCONFIG')
244                 kw['path'] = self.env.PKGCONFIG
245
246         # pkg-config version
247         if 'atleast_pkgconfig_version' in kw:
248                 if not 'msg' in kw:
249                         kw['msg'] = 'Checking for pkg-config version >= %r' % kw['atleast_pkgconfig_version']
250                 return
251
252         if not 'okmsg' in kw:
253                 kw['okmsg'] = 'yes'
254         if not 'errmsg' in kw:
255                 kw['errmsg'] = 'not found'
256
257         if 'modversion' in kw:
258                 if not 'msg' in kw:
259                         kw['msg'] = 'Checking for %r version' % kw['modversion']
260                 if not 'uselib_store' in kw:
261                         kw['uselib_store'] = kw['modversion']
262                 if not 'define_name' in kw:
263                         kw['define_name'] = '%s_VERSION' % Utils.quote_define_name(kw['uselib_store'])
264                 return
265
266         if not 'package' in kw:
267                 raise ValueError('a package name is required')
268
269         if not 'uselib_store' in kw:
270                 kw['uselib_store'] = kw['package'].upper()
271
272         if not 'define_name' in kw:
273                 kw['define_name'] = self.have_define(kw['uselib_store'])
274
275         if not 'msg' in kw:
276                 kw['msg'] = 'Checking for %r' % (kw['package'] or kw['path'])
277
278         for x in cfg_ver:
279                 # Gotcha: only one predicate is allowed at a time
280                 # TODO remove in waf 2.0
281                 y = x.replace('-', '_')
282                 if y in kw:
283                         package = kw['package']
284                         if Logs.verbose:
285                                 Logs.warn('Passing %r to conf.check_cfg() is obsolete, pass parameters directly, eg:', y)
286                                 Logs.warn(" conf.check_cfg(package='%s', args=['--libs', '--cflags', '%s >= 1.6'])", package, package)
287                         if not 'msg' in kw:
288                                 kw['msg'] = 'Checking for %r %s %s' % (package, cfg_ver[x], kw[y])
289                         break
290
291 @conf
292 def exec_cfg(self, kw):
293         """
294         Executes ``pkg-config`` or other ``-config`` applications to colect configuration flags:
295
296         * if atleast_pkgconfig_version is given, check that pkg-config has the version n and return
297         * if modversion is given, then return the module version
298         * else, execute the *-config* program with the *args* and *variables* given, and set the flags on the *conf.env.FLAGS_name* variable
299
300         :param atleast_pkgconfig_version: minimum pkg-config version to use (disable other tests)
301         :type atleast_pkgconfig_version: string
302         :param package: package name, for example *gtk+-2.0*
303         :type package: string
304         :param uselib_store: if the test is successful, define HAVE\_*name*. It is also used to define *conf.env.FLAGS_name* variables.
305         :type uselib_store: string
306         :param modversion: if provided, return the version of the given module and define *name*\_VERSION
307         :type modversion: string
308         :param args: arguments to give to *package* when retrieving flags
309         :type args: list of string
310         :param variables: return the values of particular variables
311         :type variables: list of string
312         :param define_variable: additional variables to define (also in conf.env.PKG_CONFIG_DEFINES)
313         :type define_variable: dict(string: string)
314         """
315
316         path = Utils.to_list(kw['path'])
317         env = self.env.env or None
318         if kw.get('pkg_config_path'):
319                 if not env:
320                         env = dict(self.environ)
321                 env['PKG_CONFIG_PATH'] = kw['pkg_config_path']
322
323         def define_it():
324                 define_name = kw['define_name']
325                 # by default, add HAVE_X to the config.h, else provide DEFINES_X for use=X
326                 if kw.get('global_define', 1):
327                         self.define(define_name, 1, False)
328                 else:
329                         self.env.append_unique('DEFINES_%s' % kw['uselib_store'], "%s=1" % define_name)
330
331                 if kw.get('add_have_to_env', 1):
332                         self.env[define_name] = 1
333
334         # pkg-config version
335         if 'atleast_pkgconfig_version' in kw:
336                 cmd = path + ['--atleast-pkgconfig-version=%s' % kw['atleast_pkgconfig_version']]
337                 self.cmd_and_log(cmd, env=env)
338                 if not 'okmsg' in kw:
339                         kw['okmsg'] = 'yes'
340                 return
341
342         for x in cfg_ver:
343                 # TODO remove in waf 2.0
344                 y = x.replace('-', '_')
345                 if y in kw:
346                         self.cmd_and_log(path + ['--%s=%s' % (x, kw[y]), kw['package']], env=env)
347                         if not 'okmsg' in kw:
348                                 kw['okmsg'] = 'yes'
349                         define_it()
350                         break
351
352         # single version for a module
353         if 'modversion' in kw:
354                 version = self.cmd_and_log(path + ['--modversion', kw['modversion']], env=env).strip()
355                 self.define(kw['define_name'], version)
356                 return version
357
358         lst = [] + path
359
360         defi = kw.get('define_variable')
361         if not defi:
362                 defi = self.env.PKG_CONFIG_DEFINES or {}
363         for key, val in defi.items():
364                 lst.append('--define-variable=%s=%s' % (key, val))
365
366         static = kw.get('force_static', False)
367         if 'args' in kw:
368                 args = Utils.to_list(kw['args'])
369                 if '--static' in args or '--static-libs' in args:
370                         static = True
371                 lst += args
372
373         # tools like pkgconf expect the package argument after the -- ones -_-
374         lst.extend(Utils.to_list(kw['package']))
375
376         # retrieving variables of a module
377         if 'variables' in kw:
378                 v_env = kw.get('env', self.env)
379                 vars = Utils.to_list(kw['variables'])
380                 for v in vars:
381                         val = self.cmd_and_log(lst + ['--variable=' + v], env=env).strip()
382                         var = '%s_%s' % (kw['uselib_store'], v)
383                         v_env[var] = val
384                 if not 'okmsg' in kw:
385                         kw['okmsg'] = 'yes'
386                 return
387
388         # so we assume the command-line will output flags to be parsed afterwards
389         ret = self.cmd_and_log(lst, env=env)
390         if not 'okmsg' in kw:
391                 kw['okmsg'] = 'yes'
392
393         define_it()
394         self.parse_flags(ret, kw['uselib_store'], kw.get('env', self.env), force_static=static, posix=kw.get('posix'))
395         return ret
396
397 @conf
398 def check_cfg(self, *k, **kw):
399         """
400         Checks for configuration flags using a **-config**-like program (pkg-config, sdl-config, etc).
401         This wraps internal calls to :py:func:`waflib.Tools.c_config.validate_cfg` and :py:func:`waflib.Tools.c_config.exec_cfg`
402
403         A few examples::
404
405                 def configure(conf):
406                         conf.load('compiler_c')
407                         conf.check_cfg(package='glib-2.0', args='--libs --cflags')
408                         conf.check_cfg(package='glib-2.0', uselib_store='GLIB', atleast_version='2.10.0',
409                                 args='--cflags --libs')
410                         conf.check_cfg(package='pango')
411                         conf.check_cfg(package='pango', uselib_store='MYPANGO', args=['--cflags', '--libs'])
412                         conf.check_cfg(package='pango',
413                                 args=['pango >= 0.1.0', 'pango < 9.9.9', '--cflags', '--libs'],
414                                 msg="Checking for 'pango 0.1.0'")
415                         conf.check_cfg(path='sdl-config', args='--cflags --libs', package='', uselib_store='SDL')
416                         conf.check_cfg(path='mpicc', args='--showme:compile --showme:link',
417                                 package='', uselib_store='OPEN_MPI', mandatory=False)
418                         # variables
419                         conf.check_cfg(package='gtk+-2.0', variables=['includedir', 'prefix'], uselib_store='FOO')
420                         print(conf.env.FOO_includedir)
421         """
422         if k:
423                 lst = k[0].split()
424                 kw['package'] = lst[0]
425                 kw['args'] = ' '.join(lst[1:])
426
427         self.validate_cfg(kw)
428         if 'msg' in kw:
429                 self.start_msg(kw['msg'], **kw)
430         ret = None
431         try:
432                 ret = self.exec_cfg(kw)
433         except self.errors.WafError:
434                 if 'errmsg' in kw:
435                         self.end_msg(kw['errmsg'], 'YELLOW', **kw)
436                 if Logs.verbose > 1:
437                         raise
438                 else:
439                         self.fatal('The configuration failed')
440         else:
441                 if not ret:
442                         ret = True
443                 kw['success'] = ret
444                 if 'okmsg' in kw:
445                         self.end_msg(self.ret_msg(kw['okmsg'], kw), **kw)
446
447         return ret
448
449 def build_fun(bld):
450         """
451         Build function that is used for running configuration tests with ``conf.check()``
452         """
453         if bld.kw['compile_filename']:
454                 node = bld.srcnode.make_node(bld.kw['compile_filename'])
455                 node.write(bld.kw['code'])
456
457         o = bld(features=bld.kw['features'], source=bld.kw['compile_filename'], target='testprog')
458
459         for k, v in bld.kw.items():
460                 setattr(o, k, v)
461
462         if not bld.kw.get('quiet'):
463                 bld.conf.to_log("==>\n%s\n<==" % bld.kw['code'])
464
465 @conf
466 def validate_c(self, kw):
467         """
468         Pre-checks the parameters that will be given to :py:func:`waflib.Configure.run_build`
469
470         :param compiler: c or cxx (tries to guess what is best)
471         :type compiler: string
472         :param type: cprogram, cshlib, cstlib - not required if *features are given directly*
473         :type type: binary to create
474         :param feature: desired features for the task generator that will execute the test, for example ``cxx cxxstlib``
475         :type feature: list of string
476         :param fragment: provide a piece of code for the test (default is to let the system create one)
477         :type fragment: string
478         :param uselib_store: define variables after the test is executed (IMPORTANT!)
479         :type uselib_store: string
480         :param use: parameters to use for building (just like the normal *use* keyword)
481         :type use: list of string
482         :param define_name: define to set when the check is over
483         :type define_name: string
484         :param execute: execute the resulting binary
485         :type execute: bool
486         :param define_ret: if execute is set to True, use the execution output in both the define and the return value
487         :type define_ret: bool
488         :param header_name: check for a particular header
489         :type header_name: string
490         :param auto_add_header_name: if header_name was set, add the headers in env.INCKEYS so the next tests will include these headers
491         :type auto_add_header_name: bool
492         """
493
494         if not 'build_fun' in kw:
495                 kw['build_fun'] = build_fun
496
497         if not 'env' in kw:
498                 kw['env'] = self.env.derive()
499         env = kw['env']
500
501         if not 'compiler' in kw and not 'features' in kw:
502                 kw['compiler'] = 'c'
503                 if env.CXX_NAME and Task.classes.get('cxx'):
504                         kw['compiler'] = 'cxx'
505                         if not self.env.CXX:
506                                 self.fatal('a c++ compiler is required')
507                 else:
508                         if not self.env.CC:
509                                 self.fatal('a c compiler is required')
510
511         if not 'compile_mode' in kw:
512                 kw['compile_mode'] = 'c'
513                 if 'cxx' in Utils.to_list(kw.get('features',[])) or kw.get('compiler', '') == 'cxx':
514                         kw['compile_mode'] = 'cxx'
515
516         if not 'type' in kw:
517                 kw['type'] = 'cprogram'
518
519         if not 'features' in kw:
520                 if not 'header_name' in kw or kw.get('link_header_test', True):
521                         kw['features'] = [kw['compile_mode'], kw['type']] # "c ccprogram"
522                 else:
523                         kw['features'] = [kw['compile_mode']]
524         else:
525                 kw['features'] = Utils.to_list(kw['features'])
526
527         if not 'compile_filename' in kw:
528                 kw['compile_filename'] = 'test.c' + ((kw['compile_mode'] == 'cxx') and 'pp' or '')
529
530         def to_header(dct):
531                 if 'header_name' in dct:
532                         dct = Utils.to_list(dct['header_name'])
533                         return ''.join(['#include <%s>\n' % x for x in dct])
534                 return ''
535
536         #OSX
537         if 'framework_name' in kw:
538                 fwkname = kw['framework_name']
539                 if not 'uselib_store' in kw:
540                         kw['uselib_store'] = fwkname.upper()
541                 if not kw.get('no_header', False):
542                         if not 'header_name' in kw:
543                                 kw['header_name'] = []
544                         fwk = '%s/%s.h' % (fwkname, fwkname)
545                         if kw.get('remove_dot_h'):
546                                 fwk = fwk[:-2]
547                         kw['header_name'] = Utils.to_list(kw['header_name']) + [fwk]
548
549                 kw['msg'] = 'Checking for framework %s' % fwkname
550                 kw['framework'] = fwkname
551                 #kw['frameworkpath'] = set it yourself
552
553         if 'function_name' in kw:
554                 fu = kw['function_name']
555                 if not 'msg' in kw:
556                         kw['msg'] = 'Checking for function %s' % fu
557                 kw['code'] = to_header(kw) + SNIP_FUNCTION % fu
558                 if not 'uselib_store' in kw:
559                         kw['uselib_store'] = fu.upper()
560                 if not 'define_name' in kw:
561                         kw['define_name'] = self.have_define(fu)
562
563         elif 'type_name' in kw:
564                 tu = kw['type_name']
565                 if not 'header_name' in kw:
566                         kw['header_name'] = 'stdint.h'
567                 if 'field_name' in kw:
568                         field = kw['field_name']
569                         kw['code'] = to_header(kw) + SNIP_FIELD % {'type_name' : tu, 'field_name' : field}
570                         if not 'msg' in kw:
571                                 kw['msg'] = 'Checking for field %s in %s' % (field, tu)
572                         if not 'define_name' in kw:
573                                 kw['define_name'] = self.have_define((tu + '_' + field).upper())
574                 else:
575                         kw['code'] = to_header(kw) + SNIP_TYPE % {'type_name' : tu}
576                         if not 'msg' in kw:
577                                 kw['msg'] = 'Checking for type %s' % tu
578                         if not 'define_name' in kw:
579                                 kw['define_name'] = self.have_define(tu.upper())
580
581         elif 'header_name' in kw:
582                 if not 'msg' in kw:
583                         kw['msg'] = 'Checking for header %s' % kw['header_name']
584
585                 l = Utils.to_list(kw['header_name'])
586                 assert len(l), 'list of headers in header_name is empty'
587
588                 kw['code'] = to_header(kw) + SNIP_EMPTY_PROGRAM
589                 if not 'uselib_store' in kw:
590                         kw['uselib_store'] = l[0].upper()
591                 if not 'define_name' in kw:
592                         kw['define_name'] = self.have_define(l[0])
593
594         if 'lib' in kw:
595                 if not 'msg' in kw:
596                         kw['msg'] = 'Checking for library %s' % kw['lib']
597                 if not 'uselib_store' in kw:
598                         kw['uselib_store'] = kw['lib'].upper()
599
600         if 'stlib' in kw:
601                 if not 'msg' in kw:
602                         kw['msg'] = 'Checking for static library %s' % kw['stlib']
603                 if not 'uselib_store' in kw:
604                         kw['uselib_store'] = kw['stlib'].upper()
605
606         if 'fragment' in kw:
607                 # an additional code fragment may be provided to replace the predefined code
608                 # in custom headers
609                 kw['code'] = kw['fragment']
610                 if not 'msg' in kw:
611                         kw['msg'] = 'Checking for code snippet'
612                 if not 'errmsg' in kw:
613                         kw['errmsg'] = 'no'
614
615         for (flagsname,flagstype) in (('cxxflags','compiler'), ('cflags','compiler'), ('linkflags','linker')):
616                 if flagsname in kw:
617                         if not 'msg' in kw:
618                                 kw['msg'] = 'Checking for %s flags %s' % (flagstype, kw[flagsname])
619                         if not 'errmsg' in kw:
620                                 kw['errmsg'] = 'no'
621
622         if not 'execute' in kw:
623                 kw['execute'] = False
624         if kw['execute']:
625                 kw['features'].append('test_exec')
626                 kw['chmod'] = Utils.O755
627
628         if not 'errmsg' in kw:
629                 kw['errmsg'] = 'not found'
630
631         if not 'okmsg' in kw:
632                 kw['okmsg'] = 'yes'
633
634         if not 'code' in kw:
635                 kw['code'] = SNIP_EMPTY_PROGRAM
636
637         # if there are headers to append automatically to the next tests
638         if self.env[INCKEYS]:
639                 kw['code'] = '\n'.join(['#include <%s>' % x for x in self.env[INCKEYS]]) + '\n' + kw['code']
640
641         # in case defines lead to very long command-lines
642         if kw.get('merge_config_header', False) or env.merge_config_header:
643                 kw['code'] = '%s\n\n%s' % (self.get_config_header(), kw['code'])
644                 env.DEFINES = [] # modify the copy
645
646         if not kw.get('success'): kw['success'] = None
647
648         if 'define_name' in kw:
649                 self.undefine(kw['define_name'])
650         if not 'msg' in kw:
651                 self.fatal('missing "msg" in conf.check(...)')
652
653 @conf
654 def post_check(self, *k, **kw):
655         """
656         Sets the variables after a test executed in
657         :py:func:`waflib.Tools.c_config.check` was run successfully
658         """
659         is_success = 0
660         if kw['execute']:
661                 if kw['success'] is not None:
662                         if kw.get('define_ret', False):
663                                 is_success = kw['success']
664                         else:
665                                 is_success = (kw['success'] == 0)
666         else:
667                 is_success = (kw['success'] == 0)
668
669         if kw.get('define_name'):
670                 # TODO this is still way too complicated
671                 comment = kw.get('comment', '')
672                 define_name = kw['define_name']
673                 if kw['execute'] and kw.get('define_ret') and isinstance(is_success, str):
674                         if kw.get('global_define', 1):
675                                 self.define(define_name, is_success, quote=kw.get('quote', 1), comment=comment)
676                         else:
677                                 if kw.get('quote', 1):
678                                         succ = '"%s"' % is_success
679                                 else:
680                                         succ = int(is_success)
681                                 val = '%s=%s' % (define_name, succ)
682                                 var = 'DEFINES_%s' % kw['uselib_store']
683                                 self.env.append_value(var, val)
684                 else:
685                         if kw.get('global_define', 1):
686                                 self.define_cond(define_name, is_success, comment=comment)
687                         else:
688                                 var = 'DEFINES_%s' % kw['uselib_store']
689                                 self.env.append_value(var, '%s=%s' % (define_name, int(is_success)))
690
691                 # define conf.env.HAVE_X to 1
692                 if kw.get('add_have_to_env', 1):
693                         if kw.get('uselib_store'):
694                                 self.env[self.have_define(kw['uselib_store'])] = 1
695                         elif kw['execute'] and kw.get('define_ret'):
696                                 self.env[define_name] = is_success
697                         else:
698                                 self.env[define_name] = int(is_success)
699
700         if 'header_name' in kw:
701                 if kw.get('auto_add_header_name', False):
702                         self.env.append_value(INCKEYS, Utils.to_list(kw['header_name']))
703
704         if is_success and 'uselib_store' in kw:
705                 from waflib.Tools import ccroot
706                 # See get_uselib_vars in ccroot.py
707                 _vars = set()
708                 for x in kw['features']:
709                         if x in ccroot.USELIB_VARS:
710                                 _vars |= ccroot.USELIB_VARS[x]
711
712                 for k in _vars:
713                         x = k.lower()
714                         if x in kw:
715                                 self.env.append_value(k + '_' + kw['uselib_store'], kw[x])
716         return is_success
717
718 @conf
719 def check(self, *k, **kw):
720         """
721         Performs a configuration test by calling :py:func:`waflib.Configure.run_build`.
722         For the complete list of parameters, see :py:func:`waflib.Tools.c_config.validate_c`.
723         To force a specific compiler, pass ``compiler='c'`` or ``compiler='cxx'`` to the list of arguments
724
725         Besides build targets, complete builds can be given through a build function. All files will
726         be written to a temporary directory::
727
728                 def build(bld):
729                         lib_node = bld.srcnode.make_node('libdir/liblc1.c')
730                         lib_node.parent.mkdir()
731                         lib_node.write('#include <stdio.h>\\nint lib_func(void) { FILE *f = fopen("foo", "r");}\\n', 'w')
732                         bld(features='c cshlib', source=[lib_node], linkflags=conf.env.EXTRA_LDFLAGS, target='liblc')
733                 conf.check(build_fun=build, msg=msg)
734         """
735         self.validate_c(kw)
736         self.start_msg(kw['msg'], **kw)
737         ret = None
738         try:
739                 ret = self.run_build(*k, **kw)
740         except self.errors.ConfigurationError:
741                 self.end_msg(kw['errmsg'], 'YELLOW', **kw)
742                 if Logs.verbose > 1:
743                         raise
744                 else:
745                         self.fatal('The configuration failed')
746         else:
747                 kw['success'] = ret
748
749         ret = self.post_check(*k, **kw)
750         if not ret:
751                 self.end_msg(kw['errmsg'], 'YELLOW', **kw)
752                 self.fatal('The configuration failed %r' % ret)
753         else:
754                 self.end_msg(self.ret_msg(kw['okmsg'], kw), **kw)
755         return ret
756
757 class test_exec(Task.Task):
758         """
759         A task that runs programs after they are built. See :py:func:`waflib.Tools.c_config.test_exec_fun`.
760         """
761         color = 'PINK'
762         def run(self):
763                 if getattr(self.generator, 'rpath', None):
764                         if getattr(self.generator, 'define_ret', False):
765                                 self.generator.bld.retval = self.generator.bld.cmd_and_log([self.inputs[0].abspath()])
766                         else:
767                                 self.generator.bld.retval = self.generator.bld.exec_command([self.inputs[0].abspath()])
768                 else:
769                         env = self.env.env or {}
770                         env.update(dict(os.environ))
771                         for var in ('LD_LIBRARY_PATH', 'DYLD_LIBRARY_PATH', 'PATH'):
772                                 env[var] = self.inputs[0].parent.abspath() + os.path.pathsep + env.get(var, '')
773                         if getattr(self.generator, 'define_ret', False):
774                                 self.generator.bld.retval = self.generator.bld.cmd_and_log([self.inputs[0].abspath()], env=env)
775                         else:
776                                 self.generator.bld.retval = self.generator.bld.exec_command([self.inputs[0].abspath()], env=env)
777
778 @feature('test_exec')
779 @after_method('apply_link')
780 def test_exec_fun(self):
781         """
782         The feature **test_exec** is used to create a task that will to execute the binary
783         created (link task output) during the build. The exit status will be set
784         on the build context, so only one program may have the feature *test_exec*.
785         This is used by configuration tests::
786
787                 def configure(conf):
788                         conf.check(execute=True)
789         """
790         self.create_task('test_exec', self.link_task.outputs[0])
791
792 @conf
793 def check_cxx(self, *k, **kw):
794         """
795         Runs a test with a task generator of the form::
796
797                 conf.check(features='cxx cxxprogram', ...)
798         """
799         kw['compiler'] = 'cxx'
800         return self.check(*k, **kw)
801
802 @conf
803 def check_cc(self, *k, **kw):
804         """
805         Runs a test with a task generator of the form::
806
807                 conf.check(features='c cprogram', ...)
808         """
809         kw['compiler'] = 'c'
810         return self.check(*k, **kw)
811
812 @conf
813 def set_define_comment(self, key, comment):
814         """
815         Sets a comment that will appear in the configuration header
816
817         :type key: string
818         :type comment: string
819         """
820         coms = self.env.DEFINE_COMMENTS
821         if not coms:
822                 coms = self.env.DEFINE_COMMENTS = {}
823         coms[key] = comment or ''
824
825 @conf
826 def get_define_comment(self, key):
827         """
828         Returns the comment associated to a define
829
830         :type key: string
831         """
832         coms = self.env.DEFINE_COMMENTS or {}
833         return coms.get(key, '')
834
835 @conf
836 def define(self, key, val, quote=True, comment=''):
837         """
838         Stores a single define and its state into ``conf.env.DEFINES``. The value is cast to an integer (0/1).
839
840         :param key: define name
841         :type key: string
842         :param val: value
843         :type val: int or string
844         :param quote: enclose strings in quotes (yes by default)
845         :type quote: bool
846         """
847         assert isinstance(key, str)
848         if not key:
849                 return
850         if val is True:
851                 val = 1
852         elif val in (False, None):
853                 val = 0
854
855         if isinstance(val, int) or isinstance(val, float):
856                 s = '%s=%s'
857         else:
858                 s = quote and '%s="%s"' or '%s=%s'
859         app = s % (key, str(val))
860
861         ban = key + '='
862         lst = self.env.DEFINES
863         for x in lst:
864                 if x.startswith(ban):
865                         lst[lst.index(x)] = app
866                         break
867         else:
868                 self.env.append_value('DEFINES', app)
869
870         self.env.append_unique(DEFKEYS, key)
871         self.set_define_comment(key, comment)
872
873 @conf
874 def undefine(self, key, comment=''):
875         """
876         Removes a global define from ``conf.env.DEFINES``
877
878         :param key: define name
879         :type key: string
880         """
881         assert isinstance(key, str)
882         if not key:
883                 return
884         ban = key + '='
885         lst = [x for x in self.env.DEFINES if not x.startswith(ban)]
886         self.env.DEFINES = lst
887         self.env.append_unique(DEFKEYS, key)
888         self.set_define_comment(key, comment)
889
890 @conf
891 def define_cond(self, key, val, comment=''):
892         """
893         Conditionally defines a name::
894
895                 def configure(conf):
896                         conf.define_cond('A', True)
897                         # equivalent to:
898                         # if val: conf.define('A', 1)
899                         # else: conf.undefine('A')
900
901         :param key: define name
902         :type key: string
903         :param val: value
904         :type val: int or string
905         """
906         assert isinstance(key, str)
907         if not key:
908                 return
909         if val:
910                 self.define(key, 1, comment=comment)
911         else:
912                 self.undefine(key, comment=comment)
913
914 @conf
915 def is_defined(self, key):
916         """
917         Indicates whether a particular define is globally set in ``conf.env.DEFINES``.
918
919         :param key: define name
920         :type key: string
921         :return: True if the define is set
922         :rtype: bool
923         """
924         assert key and isinstance(key, str)
925
926         ban = key + '='
927         for x in self.env.DEFINES:
928                 if x.startswith(ban):
929                         return True
930         return False
931
932 @conf
933 def get_define(self, key):
934         """
935         Returns the value of an existing define, or None if not found
936
937         :param key: define name
938         :type key: string
939         :rtype: string
940         """
941         assert key and isinstance(key, str)
942
943         ban = key + '='
944         for x in self.env.DEFINES:
945                 if x.startswith(ban):
946                         return x[len(ban):]
947         return None
948
949 @conf
950 def have_define(self, key):
951         """
952         Returns a variable suitable for command-line or header use by removing invalid characters
953         and prefixing it with ``HAVE_``
954
955         :param key: define name
956         :type key: string
957         :return: the input key prefixed by *HAVE_* and substitute any invalid characters.
958         :rtype: string
959         """
960         return (self.env.HAVE_PAT or 'HAVE_%s') % Utils.quote_define_name(key)
961
962 @conf
963 def write_config_header(self, configfile='', guard='', top=False, defines=True, headers=False, remove=True, define_prefix=''):
964         """
965         Writes a configuration header containing defines and includes::
966
967                 def configure(cnf):
968                         cnf.define('A', 1)
969                         cnf.write_config_header('config.h')
970
971         This function only adds include guards (if necessary), consult
972         :py:func:`waflib.Tools.c_config.get_config_header` for details on the body.
973
974         :param configfile: path to the file to create (relative or absolute)
975         :type configfile: string
976         :param guard: include guard name to add, by default it is computed from the file name
977         :type guard: string
978         :param top: write the configuration header from the build directory (default is from the current path)
979         :type top: bool
980         :param defines: add the defines (yes by default)
981         :type defines: bool
982         :param headers: add #include in the file
983         :type headers: bool
984         :param remove: remove the defines after they are added (yes by default, works like in autoconf)
985         :type remove: bool
986         :type define_prefix: string
987         :param define_prefix: prefix all the defines in the file with a particular prefix
988         """
989         if not configfile: configfile = WAF_CONFIG_H
990         waf_guard = guard or 'W_%s_WAF' % Utils.quote_define_name(configfile)
991
992         node = top and self.bldnode or self.path.get_bld()
993         node = node.make_node(configfile)
994         node.parent.mkdir()
995
996         lst = ['/* WARNING! All changes made to this file will be lost! */\n']
997         lst.append('#ifndef %s\n#define %s\n' % (waf_guard, waf_guard))
998         lst.append(self.get_config_header(defines, headers, define_prefix=define_prefix))
999         lst.append('\n#endif /* %s */\n' % waf_guard)
1000
1001         node.write('\n'.join(lst))
1002
1003         # config files must not be removed on "waf clean"
1004         self.env.append_unique(Build.CFG_FILES, [node.abspath()])
1005
1006         if remove:
1007                 for key in self.env[DEFKEYS]:
1008                         self.undefine(key)
1009                 self.env[DEFKEYS] = []
1010
1011 @conf
1012 def get_config_header(self, defines=True, headers=False, define_prefix=''):
1013         """
1014         Creates the contents of a ``config.h`` file from the defines and includes
1015         set in conf.env.define_key / conf.env.include_key. No include guards are added.
1016
1017         A prelude will be added from the variable env.WAF_CONFIG_H_PRELUDE if provided. This
1018         can be used to insert complex macros or include guards::
1019
1020                 def configure(conf):
1021                         conf.env.WAF_CONFIG_H_PRELUDE = '#include <unistd.h>\\n'
1022                         conf.write_config_header('config.h')
1023
1024         :param defines: write the defines values
1025         :type defines: bool
1026         :param headers: write include entries for each element in self.env.INCKEYS
1027         :type headers: bool
1028         :type define_prefix: string
1029         :param define_prefix: prefix all the defines with a particular prefix
1030         :return: the contents of a ``config.h`` file
1031         :rtype: string
1032         """
1033         lst = []
1034
1035         if self.env.WAF_CONFIG_H_PRELUDE:
1036                 lst.append(self.env.WAF_CONFIG_H_PRELUDE)
1037
1038         if headers:
1039                 for x in self.env[INCKEYS]:
1040                         lst.append('#include <%s>' % x)
1041
1042         if defines:
1043                 tbl = {}
1044                 for k in self.env.DEFINES:
1045                         a, _, b = k.partition('=')
1046                         tbl[a] = b
1047
1048                 for k in self.env[DEFKEYS]:
1049                         caption = self.get_define_comment(k)
1050                         if caption:
1051                                 caption = ' /* %s */' % caption
1052                         try:
1053                                 txt = '#define %s%s %s%s' % (define_prefix, k, tbl[k], caption)
1054                         except KeyError:
1055                                 txt = '/* #undef %s%s */%s' % (define_prefix, k, caption)
1056                         lst.append(txt)
1057         return "\n".join(lst)
1058
1059 @conf
1060 def cc_add_flags(conf):
1061         """
1062         Adds CFLAGS / CPPFLAGS from os.environ to conf.env
1063         """
1064         conf.add_os_flags('CPPFLAGS', dup=False)
1065         conf.add_os_flags('CFLAGS', dup=False)
1066
1067 @conf
1068 def cxx_add_flags(conf):
1069         """
1070         Adds CXXFLAGS / CPPFLAGS from os.environ to conf.env
1071         """
1072         conf.add_os_flags('CPPFLAGS', dup=False)
1073         conf.add_os_flags('CXXFLAGS', dup=False)
1074
1075 @conf
1076 def link_add_flags(conf):
1077         """
1078         Adds LINKFLAGS / LDFLAGS from os.environ to conf.env
1079         """
1080         conf.add_os_flags('LINKFLAGS', dup=False)
1081         conf.add_os_flags('LDFLAGS', dup=False)
1082
1083 @conf
1084 def cc_load_tools(conf):
1085         """
1086         Loads the Waf c extensions
1087         """
1088         if not conf.env.DEST_OS:
1089                 conf.env.DEST_OS = Utils.unversioned_sys_platform()
1090         conf.load('c')
1091
1092 @conf
1093 def cxx_load_tools(conf):
1094         """
1095         Loads the Waf c++ extensions
1096         """
1097         if not conf.env.DEST_OS:
1098                 conf.env.DEST_OS = Utils.unversioned_sys_platform()
1099         conf.load('cxx')
1100
1101 @conf
1102 def get_cc_version(conf, cc, gcc=False, icc=False, clang=False):
1103         """
1104         Runs the preprocessor to determine the gcc/icc/clang version
1105
1106         The variables CC_VERSION, DEST_OS, DEST_BINFMT and DEST_CPU will be set in *conf.env*
1107
1108         :raise: :py:class:`waflib.Errors.ConfigurationError`
1109         """
1110         cmd = cc + ['-dM', '-E', '-']
1111         env = conf.env.env or None
1112         try:
1113                 out, err = conf.cmd_and_log(cmd, output=0, input='\n', env=env)
1114         except Exception:
1115                 conf.fatal('Could not determine the compiler version %r' % cmd)
1116
1117         if gcc:
1118                 if out.find('__INTEL_COMPILER') >= 0:
1119                         conf.fatal('The intel compiler pretends to be gcc')
1120                 if out.find('__GNUC__') < 0 and out.find('__clang__') < 0:
1121                         conf.fatal('Could not determine the compiler type')
1122
1123         if icc and out.find('__INTEL_COMPILER') < 0:
1124                 conf.fatal('Not icc/icpc')
1125
1126         if clang and out.find('__clang__') < 0:
1127                 conf.fatal('Not clang/clang++')
1128         if not clang and out.find('__clang__') >= 0:
1129                 conf.fatal('Could not find gcc/g++ (only Clang), if renamed try eg: CC=gcc48 CXX=g++48 waf configure')
1130
1131         k = {}
1132         if icc or gcc or clang:
1133                 out = out.splitlines()
1134                 for line in out:
1135                         lst = shlex.split(line)
1136                         if len(lst)>2:
1137                                 key = lst[1]
1138                                 val = lst[2]
1139                                 k[key] = val
1140
1141                 def isD(var):
1142                         return var in k
1143
1144                 # Some documentation is available at http://predef.sourceforge.net
1145                 # The names given to DEST_OS must match what Utils.unversioned_sys_platform() returns.
1146                 if not conf.env.DEST_OS:
1147                         conf.env.DEST_OS = ''
1148                 for i in MACRO_TO_DESTOS:
1149                         if isD(i):
1150                                 conf.env.DEST_OS = MACRO_TO_DESTOS[i]
1151                                 break
1152                 else:
1153                         if isD('__APPLE__') and isD('__MACH__'):
1154                                 conf.env.DEST_OS = 'darwin'
1155                         elif isD('__unix__'): # unix must be tested last as it's a generic fallback
1156                                 conf.env.DEST_OS = 'generic'
1157
1158                 if isD('__ELF__'):
1159                         conf.env.DEST_BINFMT = 'elf'
1160                 elif isD('__WINNT__') or isD('__CYGWIN__') or isD('_WIN32'):
1161                         conf.env.DEST_BINFMT = 'pe'
1162                         conf.env.LIBDIR = conf.env.BINDIR
1163                 elif isD('__APPLE__'):
1164                         conf.env.DEST_BINFMT = 'mac-o'
1165
1166                 if not conf.env.DEST_BINFMT:
1167                         # Infer the binary format from the os name.
1168                         conf.env.DEST_BINFMT = Utils.destos_to_binfmt(conf.env.DEST_OS)
1169
1170                 for i in MACRO_TO_DEST_CPU:
1171                         if isD(i):
1172                                 conf.env.DEST_CPU = MACRO_TO_DEST_CPU[i]
1173                                 break
1174
1175                 Logs.debug('ccroot: dest platform: ' + ' '.join([conf.env[x] or '?' for x in ('DEST_OS', 'DEST_BINFMT', 'DEST_CPU')]))
1176                 if icc:
1177                         ver = k['__INTEL_COMPILER']
1178                         conf.env.CC_VERSION = (ver[:-2], ver[-2], ver[-1])
1179                 else:
1180                         if isD('__clang__') and isD('__clang_major__'):
1181                                 conf.env.CC_VERSION = (k['__clang_major__'], k['__clang_minor__'], k['__clang_patchlevel__'])
1182                         else:
1183                                 # older clang versions and gcc
1184                                 conf.env.CC_VERSION = (k['__GNUC__'], k['__GNUC_MINOR__'], k.get('__GNUC_PATCHLEVEL__', '0'))
1185         return k
1186
1187 @conf
1188 def get_xlc_version(conf, cc):
1189         """
1190         Returns the Aix compiler version
1191
1192         :raise: :py:class:`waflib.Errors.ConfigurationError`
1193         """
1194         cmd = cc + ['-qversion']
1195         try:
1196                 out, err = conf.cmd_and_log(cmd, output=0)
1197         except Errors.WafError:
1198                 conf.fatal('Could not find xlc %r' % cmd)
1199
1200         # the intention is to catch the 8.0 in "IBM XL C/C++ Enterprise Edition V8.0 for AIX..."
1201         for v in (r"IBM XL C/C\+\+.* V(?P<major>\d*)\.(?P<minor>\d*)",):
1202                 version_re = re.compile(v, re.I).search
1203                 match = version_re(out or err)
1204                 if match:
1205                         k = match.groupdict()
1206                         conf.env.CC_VERSION = (k['major'], k['minor'])
1207                         break
1208         else:
1209                 conf.fatal('Could not determine the XLC version.')
1210
1211 @conf
1212 def get_suncc_version(conf, cc):
1213         """
1214         Returns the Sun compiler version
1215
1216         :raise: :py:class:`waflib.Errors.ConfigurationError`
1217         """
1218         cmd = cc + ['-V']
1219         try:
1220                 out, err = conf.cmd_and_log(cmd, output=0)
1221         except Errors.WafError ,e:
1222                 # Older versions of the compiler exit with non-zero status when reporting their version
1223                 if not (hasattr(e, 'returncode') and hasattr(e, 'stdout') and hasattr(e, 'stderr')):
1224                         conf.fatal('Could not find suncc %r' % cmd)
1225                 out = e.stdout
1226                 err = e.stderr
1227
1228         version = (out or err)
1229         version = version.splitlines()[0]
1230
1231         # cc: Sun C 5.10 SunOS_i386 2009/06/03
1232         # cc: Studio 12.5 Sun C++ 5.14 SunOS_sparc Beta 2015/11/17
1233         # cc: WorkShop Compilers 5.0 98/12/15 C 5.0
1234         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
1235         match = version_re(version)
1236         if match:
1237                 k = match.groupdict()
1238                 conf.env.CC_VERSION = (k['major'], k['minor'])
1239         else:
1240                 conf.fatal('Could not determine the suncc version.')
1241
1242 # ============ the --as-needed flag should added during the configuration, not at runtime =========
1243
1244 @conf
1245 def add_as_needed(self):
1246         """
1247         Adds ``--as-needed`` to the *LINKFLAGS*
1248         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.
1249         """
1250         if self.env.DEST_BINFMT == 'elf' and 'gcc' in (self.env.CXX_NAME, self.env.CC_NAME):
1251                 self.env.append_unique('LINKFLAGS', '-Wl,--as-needed')
1252
1253 # ============ parallel configuration
1254
1255 class cfgtask(Task.TaskBase):
1256         """
1257         A task that executes build configuration tests (calls conf.check)
1258
1259         Make sure to use locks if concurrent access to the same conf.env data is necessary.
1260         """
1261         def __init__(self, *k, **kw):
1262                 Task.TaskBase.__init__(self, *k, **kw)
1263                 self.run_after = set()
1264
1265         def display(self):
1266                 return ''
1267
1268         def runnable_status(self):
1269                 for x in self.run_after:
1270                         if not x.hasrun:
1271                                 return Task.ASK_LATER
1272                 return Task.RUN_ME
1273
1274         def uid(self):
1275                 return Utils.SIG_NIL
1276
1277         def run(self):
1278                 conf = self.conf
1279                 bld = Build.BuildContext(top_dir=conf.srcnode.abspath(), out_dir=conf.bldnode.abspath())
1280                 bld.env = conf.env
1281                 bld.init_dirs()
1282                 bld.in_msg = 1 # suppress top-level start_msg
1283                 bld.logger = self.logger
1284                 bld.multicheck_task = self
1285                 args = self.args
1286                 try:
1287                         if 'func' in args:
1288                                 bld.test(build_fun=args['func'],
1289                                         msg=args.get('msg', ''),
1290                                         okmsg=args.get('okmsg', ''),
1291                                         errmsg=args.get('errmsg', ''),
1292                                         )
1293                         else:
1294                                 args['multicheck_mandatory'] = args.get('mandatory', True)
1295                                 args['mandatory'] = True
1296                                 try:
1297                                         bld.check(**args)
1298                                 finally:
1299                                         args['mandatory'] = args['multicheck_mandatory']
1300                 except Exception:
1301                         return 1
1302
1303         def process(self):
1304                 Task.TaskBase.process(self)
1305                 if 'msg' in self.args:
1306                         with self.generator.bld.multicheck_lock:
1307                                 self.conf.start_msg(self.args['msg'])
1308                                 if self.hasrun == Task.NOT_RUN:
1309                                         self.conf.end_msg('test cancelled', 'YELLOW')
1310                                 elif self.hasrun != Task.SUCCESS:
1311                                         self.conf.end_msg(self.args.get('errmsg', 'no'), 'YELLOW')
1312                                 else:
1313                                         self.conf.end_msg(self.args.get('okmsg', 'yes'), 'GREEN')
1314
1315 @conf
1316 def multicheck(self, *k, **kw):
1317         """
1318         Runs configuration tests in parallel; results are printed sequentially at the end of the build
1319         but each test must provide its own msg value to display a line::
1320
1321                 def test_build(ctx):
1322                         ctx.in_msg = True # suppress console outputs
1323                         ctx.check_large_file(mandatory=False)
1324
1325                 conf.multicheck(
1326                         {'header_name':'stdio.h', 'msg':'... stdio', 'uselib_store':'STDIO', 'global_define':False},
1327                         {'header_name':'xyztabcd.h', 'msg':'... optional xyztabcd.h', 'mandatory': False},
1328                         {'header_name':'stdlib.h', 'msg':'... stdlib', 'okmsg': 'aye', 'errmsg': 'nope'},
1329                         {'func': test_build, 'msg':'... testing an arbitrary build function', 'okmsg':'ok'},
1330                         msg       = 'Checking for headers in parallel',
1331                         mandatory = True, # mandatory tests raise an error at the end
1332                         run_all_tests = True, # try running all tests
1333                 )
1334
1335         The configuration tests may modify the values in conf.env in any order, and the define
1336         values can affect configuration tests being executed. It is hence recommended
1337         to provide `uselib_store` values with `global_define=False` to prevent such issues.
1338         """
1339         self.start_msg(kw.get('msg', 'Executing %d configuration tests' % len(k)), **kw)
1340
1341         # Force a copy so that threads append to the same list at least
1342         # no order is guaranteed, but the values should not disappear at least
1343         for var in ('DEFINES', DEFKEYS):
1344                 self.env.append_value(var, [])
1345         self.env.DEFINE_COMMENTS = self.env.DEFINE_COMMENTS or {}
1346
1347         # define a task object that will execute our tests
1348         class par(object):
1349                 def __init__(self):
1350                         self.keep = False
1351                         self.task_sigs = {}
1352                         self.progress_bar = 0
1353                 def total(self):
1354                         return len(tasks)
1355                 def to_log(self, *k, **kw):
1356                         return
1357
1358         bld = par()
1359         bld.keep = kw.get('run_all_tests', True)
1360         tasks = []
1361
1362         id_to_task = {}
1363         for dct in k:
1364                 x = Task.classes['cfgtask'](bld=bld)
1365                 tasks.append(x)
1366                 x.args = dct
1367                 x.bld = bld
1368                 x.conf = self
1369                 x.args = dct
1370
1371                 # bind a logger that will keep the info in memory
1372                 x.logger = Logs.make_mem_logger(str(id(x)), self.logger)
1373
1374                 if 'id' in dct:
1375                         id_to_task[dct['id']] = x
1376
1377         # second pass to set dependencies with after_test/before_test
1378         for x in tasks:
1379                 for key in Utils.to_list(x.args.get('before_tests', [])):
1380                         tsk = id_to_task[key]
1381                         if not tsk:
1382                                 raise ValueError('No test named %r' % key)
1383                         tsk.run_after.add(x)
1384                 for key in Utils.to_list(x.args.get('after_tests', [])):
1385                         tsk = id_to_task[key]
1386                         if not tsk:
1387                                 raise ValueError('No test named %r' % key)
1388                         x.run_after.add(tsk)
1389
1390         def it():
1391                 yield tasks
1392                 while 1:
1393                         yield []
1394         bld.producer = p = Runner.Parallel(bld, Options.options.jobs)
1395         bld.multicheck_lock = Utils.threading.Lock()
1396         p.biter = it()
1397
1398         self.end_msg('started')
1399         p.start()
1400
1401         # flush the logs in order into the config.log
1402         for x in tasks:
1403                 x.logger.memhandler.flush()
1404
1405         self.start_msg('-> processing test results')
1406         if p.error:
1407                 for x in p.error:
1408                         if getattr(x, 'err_msg', None):
1409                                 self.to_log(x.err_msg)
1410                                 self.end_msg('fail', color='RED')
1411                                 raise Errors.WafError('There is an error in the library, read config.log for more information')
1412
1413         failure_count = 0
1414         for x in tasks:
1415                 if x.hasrun not in (Task.SUCCESS, Task.NOT_RUN):
1416                         failure_count += 1
1417
1418         if failure_count:
1419                 self.end_msg(kw.get('errmsg', '%s test failed' % failure_count), color='YELLOW', **kw)
1420         else:
1421                 self.end_msg('all ok', **kw)
1422
1423         for x in tasks:
1424                 if x.hasrun != Task.SUCCESS:
1425                         if x.args.get('mandatory', True):
1426                                 self.fatal(kw.get('fatalmsg') or 'One of the tests has failed, read config.log for more information')