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