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		cmd = [self.inputs[0].abspath()] + getattr(self.generator, 'test_args', [])
663		if getattr(self.generator, 'rpath', None):
664			if getattr(self.generator, 'define_ret', False):
665				self.generator.bld.retval = self.generator.bld.cmd_and_log(cmd)
666			else:
667				self.generator.bld.retval = self.generator.bld.exec_command(cmd)
668		else:
669			env = self.env.env or {}
670			env.update(dict(os.environ))
671			for var in ('LD_LIBRARY_PATH', 'DYLD_LIBRARY_PATH', 'PATH'):
672				env[var] = self.inputs[0].parent.abspath() + os.path.pathsep + env.get(var, '')
673			if getattr(self.generator, 'define_ret', False):
674				self.generator.bld.retval = self.generator.bld.cmd_and_log(cmd, env=env)
675			else:
676				self.generator.bld.retval = self.generator.bld.exec_command(cmd, env=env)
677
678@feature('test_exec')
679@after_method('apply_link')
680def test_exec_fun(self):
681	"""
682	The feature **test_exec** is used to create a task that will to execute the binary
683	created (link task output) during the build. The exit status will be set
684	on the build context, so only one program may have the feature *test_exec*.
685	This is used by configuration tests::
686
687		def configure(conf):
688			conf.check(execute=True)
689	"""
690	self.create_task('test_exec', self.link_task.outputs[0])
691
692@conf
693def check_cxx(self, *k, **kw):
694	"""
695	Runs a test with a task generator of the form::
696
697		conf.check(features='cxx cxxprogram', ...)
698	"""
699	kw['compiler'] = 'cxx'
700	return self.check(*k, **kw)
701
702@conf
703def check_cc(self, *k, **kw):
704	"""
705	Runs a test with a task generator of the form::
706
707		conf.check(features='c cprogram', ...)
708	"""
709	kw['compiler'] = 'c'
710	return self.check(*k, **kw)
711
712@conf
713def set_define_comment(self, key, comment):
714	"""
715	Sets a comment that will appear in the configuration header
716
717	:type key: string
718	:type comment: string
719	"""
720	coms = self.env.DEFINE_COMMENTS
721	if not coms:
722		coms = self.env.DEFINE_COMMENTS = {}
723	coms[key] = comment or ''
724
725@conf
726def get_define_comment(self, key):
727	"""
728	Returns the comment associated to a define
729
730	:type key: string
731	"""
732	coms = self.env.DEFINE_COMMENTS or {}
733	return coms.get(key, '')
734
735@conf
736def define(self, key, val, quote=True, comment=''):
737	"""
738	Stores a single define and its state into ``conf.env.DEFINES``. The value is cast to an integer (0/1).
739
740	:param key: define name
741	:type key: string
742	:param val: value
743	:type val: int or string
744	:param quote: enclose strings in quotes (yes by default)
745	:type quote: bool
746	"""
747	assert isinstance(key, str)
748	if not key:
749		return
750	if val is True:
751		val = 1
752	elif val in (False, None):
753		val = 0
754
755	if isinstance(val, int) or isinstance(val, float):
756		s = '%s=%s'
757	else:
758		s = quote and '%s="%s"' or '%s=%s'
759	app = s % (key, str(val))
760
761	ban = key + '='
762	lst = self.env.DEFINES
763	for x in lst:
764		if x.startswith(ban):
765			lst[lst.index(x)] = app
766			break
767	else:
768		self.env.append_value('DEFINES', app)
769
770	self.env.append_unique(DEFKEYS, key)
771	self.set_define_comment(key, comment)
772
773@conf
774def undefine(self, key, comment=''):
775	"""
776	Removes a global define from ``conf.env.DEFINES``
777
778	:param key: define name
779	:type key: string
780	"""
781	assert isinstance(key, str)
782	if not key:
783		return
784	ban = key + '='
785	lst = [x for x in self.env.DEFINES if not x.startswith(ban)]
786	self.env.DEFINES = lst
787	self.env.append_unique(DEFKEYS, key)
788	self.set_define_comment(key, comment)
789
790@conf
791def define_cond(self, key, val, comment=''):
792	"""
793	Conditionally defines a name::
794
795		def configure(conf):
796			conf.define_cond('A', True)
797			# equivalent to:
798			# if val: conf.define('A', 1)
799			# else: conf.undefine('A')
800
801	:param key: define name
802	:type key: string
803	:param val: value
804	:type val: int or string
805	"""
806	assert isinstance(key, str)
807	if not key:
808		return
809	if val:
810		self.define(key, 1, comment=comment)
811	else:
812		self.undefine(key, comment=comment)
813
814@conf
815def is_defined(self, key):
816	"""
817	Indicates whether a particular define is globally set in ``conf.env.DEFINES``.
818
819	:param key: define name
820	:type key: string
821	:return: True if the define is set
822	:rtype: bool
823	"""
824	assert key and isinstance(key, str)
825
826	ban = key + '='
827	for x in self.env.DEFINES:
828		if x.startswith(ban):
829			return True
830	return False
831
832@conf
833def get_define(self, key):
834	"""
835	Returns the value of an existing define, or None if not found
836
837	:param key: define name
838	:type key: string
839	:rtype: string
840	"""
841	assert key and isinstance(key, str)
842
843	ban = key + '='
844	for x in self.env.DEFINES:
845		if x.startswith(ban):
846			return x[len(ban):]
847	return None
848
849@conf
850def have_define(self, key):
851	"""
852	Returns a variable suitable for command-line or header use by removing invalid characters
853	and prefixing it with ``HAVE_``
854
855	:param key: define name
856	:type key: string
857	:return: the input key prefixed by *HAVE_* and substitute any invalid characters.
858	:rtype: string
859	"""
860	return (self.env.HAVE_PAT or 'HAVE_%s') % Utils.quote_define_name(key)
861
862@conf
863def write_config_header(self, configfile='', guard='', top=False, defines=True, headers=False, remove=True, define_prefix=''):
864	"""
865	Writes a configuration header containing defines and includes::
866
867		def configure(cnf):
868			cnf.define('A', 1)
869			cnf.write_config_header('config.h')
870
871	This function only adds include guards (if necessary), consult
872	:py:func:`waflib.Tools.c_config.get_config_header` for details on the body.
873
874	:param configfile: path to the file to create (relative or absolute)
875	:type configfile: string
876	:param guard: include guard name to add, by default it is computed from the file name
877	:type guard: string
878	:param top: write the configuration header from the build directory (default is from the current path)
879	:type top: bool
880	:param defines: add the defines (yes by default)
881	:type defines: bool
882	:param headers: add #include in the file
883	:type headers: bool
884	:param remove: remove the defines after they are added (yes by default, works like in autoconf)
885	:type remove: bool
886	:type define_prefix: string
887	:param define_prefix: prefix all the defines in the file with a particular prefix
888	"""
889	if not configfile:
890		configfile = WAF_CONFIG_H
891	waf_guard = guard or 'W_%s_WAF' % Utils.quote_define_name(configfile)
892
893	node = top and self.bldnode or self.path.get_bld()
894	node = node.make_node(configfile)
895	node.parent.mkdir()
896
897	lst = ['/* WARNING! All changes made to this file will be lost! */\n']
898	lst.append('#ifndef %s\n#define %s\n' % (waf_guard, waf_guard))
899	lst.append(self.get_config_header(defines, headers, define_prefix=define_prefix))
900	lst.append('\n#endif /* %s */\n' % waf_guard)
901
902	node.write('\n'.join(lst))
903
904	# config files must not be removed on "waf clean"
905	self.env.append_unique(Build.CFG_FILES, [node.abspath()])
906
907	if remove:
908		for key in self.env[DEFKEYS]:
909			self.undefine(key)
910		self.env[DEFKEYS] = []
911
912@conf
913def get_config_header(self, defines=True, headers=False, define_prefix=''):
914	"""
915	Creates the contents of a ``config.h`` file from the defines and includes
916	set in conf.env.define_key / conf.env.include_key. No include guards are added.
917
918	A prelude will be added from the variable env.WAF_CONFIG_H_PRELUDE if provided. This
919	can be used to insert complex macros or include guards::
920
921		def configure(conf):
922			conf.env.WAF_CONFIG_H_PRELUDE = '#include <unistd.h>\\n'
923			conf.write_config_header('config.h')
924
925	:param defines: write the defines values
926	:type defines: bool
927	:param headers: write include entries for each element in self.env.INCKEYS
928	:type headers: bool
929	:type define_prefix: string
930	:param define_prefix: prefix all the defines with a particular prefix
931	:return: the contents of a ``config.h`` file
932	:rtype: string
933	"""
934	lst = []
935
936	if self.env.WAF_CONFIG_H_PRELUDE:
937		lst.append(self.env.WAF_CONFIG_H_PRELUDE)
938
939	if headers:
940		for x in self.env[INCKEYS]:
941			lst.append('#include <%s>' % x)
942
943	if defines:
944		tbl = {}
945		for k in self.env.DEFINES:
946			a, _, b = k.partition('=')
947			tbl[a] = b
948
949		for k in self.env[DEFKEYS]:
950			caption = self.get_define_comment(k)
951			if caption:
952				caption = ' /* %s */' % caption
953			try:
954				txt = '#define %s%s %s%s' % (define_prefix, k, tbl[k], caption)
955			except KeyError:
956				txt = '/* #undef %s%s */%s' % (define_prefix, k, caption)
957			lst.append(txt)
958	return "\n".join(lst)
959
960@conf
961def cc_add_flags(conf):
962	"""
963	Adds CFLAGS / CPPFLAGS from os.environ to conf.env
964	"""
965	conf.add_os_flags('CPPFLAGS', dup=False)
966	conf.add_os_flags('CFLAGS', dup=False)
967
968@conf
969def cxx_add_flags(conf):
970	"""
971	Adds CXXFLAGS / CPPFLAGS from os.environ to conf.env
972	"""
973	conf.add_os_flags('CPPFLAGS', dup=False)
974	conf.add_os_flags('CXXFLAGS', dup=False)
975
976@conf
977def link_add_flags(conf):
978	"""
979	Adds LINKFLAGS / LDFLAGS from os.environ to conf.env
980	"""
981	conf.add_os_flags('LINKFLAGS', dup=False)
982	conf.add_os_flags('LDFLAGS', dup=False)
983
984@conf
985def cc_load_tools(conf):
986	"""
987	Loads the Waf c extensions
988	"""
989	if not conf.env.DEST_OS:
990		conf.env.DEST_OS = Utils.unversioned_sys_platform()
991	conf.load('c')
992
993@conf
994def cxx_load_tools(conf):
995	"""
996	Loads the Waf c++ extensions
997	"""
998	if not conf.env.DEST_OS:
999		conf.env.DEST_OS = Utils.unversioned_sys_platform()
1000	conf.load('cxx')
1001
1002@conf
1003def get_cc_version(conf, cc, gcc=False, icc=False, clang=False):
1004	"""
1005	Runs the preprocessor to determine the gcc/icc/clang version
1006
1007	The variables CC_VERSION, DEST_OS, DEST_BINFMT and DEST_CPU will be set in *conf.env*
1008
1009	:raise: :py:class:`waflib.Errors.ConfigurationError`
1010	"""
1011	cmd = cc + ['-dM', '-E', '-']
1012	env = conf.env.env or None
1013	try:
1014		out, err = conf.cmd_and_log(cmd, output=0, input='\n'.encode(), env=env)
1015	except Errors.WafError:
1016		conf.fatal('Could not determine the compiler version %r' % cmd)
1017
1018	if gcc:
1019		if out.find('__INTEL_COMPILER') >= 0:
1020			conf.fatal('The intel compiler pretends to be gcc')
1021		if out.find('__GNUC__') < 0 and out.find('__clang__') < 0:
1022			conf.fatal('Could not determine the compiler type')
1023
1024	if icc and out.find('__INTEL_COMPILER') < 0:
1025		conf.fatal('Not icc/icpc')
1026
1027	if clang and out.find('__clang__') < 0:
1028		conf.fatal('Not clang/clang++')
1029	if not clang and out.find('__clang__') >= 0:
1030		conf.fatal('Could not find gcc/g++ (only Clang), if renamed try eg: CC=gcc48 CXX=g++48 waf configure')
1031
1032	k = {}
1033	if icc or gcc or clang:
1034		out = out.splitlines()
1035		for line in out:
1036			lst = shlex.split(line)
1037			if len(lst)>2:
1038				key = lst[1]
1039				val = lst[2]
1040				k[key] = val
1041
1042		def isD(var):
1043			return var in k
1044
1045		# Some documentation is available at http://predef.sourceforge.net
1046		# The names given to DEST_OS must match what Utils.unversioned_sys_platform() returns.
1047		if not conf.env.DEST_OS:
1048			conf.env.DEST_OS = ''
1049		for i in MACRO_TO_DESTOS:
1050			if isD(i):
1051				conf.env.DEST_OS = MACRO_TO_DESTOS[i]
1052				break
1053		else:
1054			if isD('__APPLE__') and isD('__MACH__'):
1055				conf.env.DEST_OS = 'darwin'
1056			elif isD('__unix__'): # unix must be tested last as it's a generic fallback
1057				conf.env.DEST_OS = 'generic'
1058
1059		if isD('__ELF__'):
1060			conf.env.DEST_BINFMT = 'elf'
1061		elif isD('__WINNT__') or isD('__CYGWIN__') or isD('_WIN32'):
1062			conf.env.DEST_BINFMT = 'pe'
1063			if not conf.env.IMPLIBDIR:
1064				conf.env.IMPLIBDIR = conf.env.LIBDIR # for .lib or .dll.a files
1065			conf.env.LIBDIR = conf.env.BINDIR
1066		elif isD('__APPLE__'):
1067			conf.env.DEST_BINFMT = 'mac-o'
1068
1069		if not conf.env.DEST_BINFMT:
1070			# Infer the binary format from the os name.
1071			conf.env.DEST_BINFMT = Utils.destos_to_binfmt(conf.env.DEST_OS)
1072
1073		for i in MACRO_TO_DEST_CPU:
1074			if isD(i):
1075				conf.env.DEST_CPU = MACRO_TO_DEST_CPU[i]
1076				break
1077
1078		Logs.debug('ccroot: dest platform: ' + ' '.join([conf.env[x] or '?' for x in ('DEST_OS', 'DEST_BINFMT', 'DEST_CPU')]))
1079		if icc:
1080			ver = k['__INTEL_COMPILER']
1081			conf.env.CC_VERSION = (ver[:-2], ver[-2], ver[-1])
1082		else:
1083			if isD('__clang__') and isD('__clang_major__'):
1084				conf.env.CC_VERSION = (k['__clang_major__'], k['__clang_minor__'], k['__clang_patchlevel__'])
1085			else:
1086				# older clang versions and gcc
1087				conf.env.CC_VERSION = (k['__GNUC__'], k['__GNUC_MINOR__'], k.get('__GNUC_PATCHLEVEL__', '0'))
1088	return k
1089
1090@conf
1091def get_xlc_version(conf, cc):
1092	"""
1093	Returns the Aix compiler version
1094
1095	:raise: :py:class:`waflib.Errors.ConfigurationError`
1096	"""
1097	cmd = cc + ['-qversion']
1098	try:
1099		out, err = conf.cmd_and_log(cmd, output=0)
1100	except Errors.WafError:
1101		conf.fatal('Could not find xlc %r' % cmd)
1102
1103	# the intention is to catch the 8.0 in "IBM XL C/C++ Enterprise Edition V8.0 for AIX..."
1104	for v in (r"IBM XL C/C\+\+.* V(?P<major>\d*)\.(?P<minor>\d*)",):
1105		version_re = re.compile(v, re.I).search
1106		match = version_re(out or err)
1107		if match:
1108			k = match.groupdict()
1109			conf.env.CC_VERSION = (k['major'], k['minor'])
1110			break
1111	else:
1112		conf.fatal('Could not determine the XLC version.')
1113
1114@conf
1115def get_suncc_version(conf, cc):
1116	"""
1117	Returns the Sun compiler version
1118
1119	:raise: :py:class:`waflib.Errors.ConfigurationError`
1120	"""
1121	cmd = cc + ['-V']
1122	try:
1123		out, err = conf.cmd_and_log(cmd, output=0)
1124	except Errors.WafError as e:
1125		# Older versions of the compiler exit with non-zero status when reporting their version
1126		if not (hasattr(e, 'returncode') and hasattr(e, 'stdout') and hasattr(e, 'stderr')):
1127			conf.fatal('Could not find suncc %r' % cmd)
1128		out = e.stdout
1129		err = e.stderr
1130
1131	version = (out or err)
1132	version = version.splitlines()[0]
1133
1134	# cc: Sun C 5.10 SunOS_i386 2009/06/03
1135	# cc: Studio 12.5 Sun C++ 5.14 SunOS_sparc Beta 2015/11/17
1136	# cc: WorkShop Compilers 5.0 98/12/15 C 5.0
1137	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
1138	match = version_re(version)
1139	if match:
1140		k = match.groupdict()
1141		conf.env.CC_VERSION = (k['major'], k['minor'])
1142	else:
1143		conf.fatal('Could not determine the suncc version.')
1144
1145# ============ the --as-needed flag should added during the configuration, not at runtime =========
1146
1147@conf
1148def add_as_needed(self):
1149	"""
1150	Adds ``--as-needed`` to the *LINKFLAGS*
1151	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.
1152	"""
1153	if self.env.DEST_BINFMT == 'elf' and 'gcc' in (self.env.CXX_NAME, self.env.CC_NAME):
1154		self.env.append_unique('LINKFLAGS', '-Wl,--as-needed')
1155
1156# ============ parallel configuration
1157
1158class cfgtask(Task.Task):
1159	"""
1160	A task that executes build configuration tests (calls conf.check)
1161
1162	Make sure to use locks if concurrent access to the same conf.env data is necessary.
1163	"""
1164	def __init__(self, *k, **kw):
1165		Task.Task.__init__(self, *k, **kw)
1166		self.run_after = set()
1167
1168	def display(self):
1169		return ''
1170
1171	def runnable_status(self):
1172		for x in self.run_after:
1173			if not x.hasrun:
1174				return Task.ASK_LATER
1175		return Task.RUN_ME
1176
1177	def uid(self):
1178		return Utils.SIG_NIL
1179
1180	def signature(self):
1181		return Utils.SIG_NIL
1182
1183	def run(self):
1184		conf = self.conf
1185		bld = Build.BuildContext(top_dir=conf.srcnode.abspath(), out_dir=conf.bldnode.abspath())
1186		bld.env = conf.env
1187		bld.init_dirs()
1188		bld.in_msg = 1 # suppress top-level start_msg
1189		bld.logger = self.logger
1190		bld.multicheck_task = self
1191		args = self.args
1192		try:
1193			if 'func' in args:
1194				bld.test(build_fun=args['func'],
1195					msg=args.get('msg', ''),
1196					okmsg=args.get('okmsg', ''),
1197					errmsg=args.get('errmsg', ''),
1198					)
1199			else:
1200				args['multicheck_mandatory'] = args.get('mandatory', True)
1201				args['mandatory'] = True
1202				try:
1203					bld.check(**args)
1204				finally:
1205					args['mandatory'] = args['multicheck_mandatory']
1206		except Exception:
1207			return 1
1208
1209	def process(self):
1210		Task.Task.process(self)
1211		if 'msg' in self.args:
1212			with self.generator.bld.multicheck_lock:
1213				self.conf.start_msg(self.args['msg'])
1214				if self.hasrun == Task.NOT_RUN:
1215					self.conf.end_msg('test cancelled', 'YELLOW')
1216				elif self.hasrun != Task.SUCCESS:
1217					self.conf.end_msg(self.args.get('errmsg', 'no'), 'YELLOW')
1218				else:
1219					self.conf.end_msg(self.args.get('okmsg', 'yes'), 'GREEN')
1220
1221@conf
1222def multicheck(self, *k, **kw):
1223	"""
1224	Runs configuration tests in parallel; results are printed sequentially at the end of the build
1225	but each test must provide its own msg value to display a line::
1226
1227		def test_build(ctx):
1228			ctx.in_msg = True # suppress console outputs
1229			ctx.check_large_file(mandatory=False)
1230
1231		conf.multicheck(
1232			{'header_name':'stdio.h', 'msg':'... stdio', 'uselib_store':'STDIO', 'global_define':False},
1233			{'header_name':'xyztabcd.h', 'msg':'... optional xyztabcd.h', 'mandatory': False},
1234			{'header_name':'stdlib.h', 'msg':'... stdlib', 'okmsg': 'aye', 'errmsg': 'nope'},
1235			{'func': test_build, 'msg':'... testing an arbitrary build function', 'okmsg':'ok'},
1236			msg       = 'Checking for headers in parallel',
1237			mandatory = True, # mandatory tests raise an error at the end
1238			run_all_tests = True, # try running all tests
1239		)
1240
1241	The configuration tests may modify the values in conf.env in any order, and the define
1242	values can affect configuration tests being executed. It is hence recommended
1243	to provide `uselib_store` values with `global_define=False` to prevent such issues.
1244	"""
1245	self.start_msg(kw.get('msg', 'Executing %d configuration tests' % len(k)), **kw)
1246
1247	# Force a copy so that threads append to the same list at least
1248	# no order is guaranteed, but the values should not disappear at least
1249	for var in ('DEFINES', DEFKEYS):
1250		self.env.append_value(var, [])
1251	self.env.DEFINE_COMMENTS = self.env.DEFINE_COMMENTS or {}
1252
1253	# define a task object that will execute our tests
1254	class par(object):
1255		def __init__(self):
1256			self.keep = False
1257			self.task_sigs = {}
1258			self.progress_bar = 0
1259		def total(self):
1260			return len(tasks)
1261		def to_log(self, *k, **kw):
1262			return
1263
1264	bld = par()
1265	bld.keep = kw.get('run_all_tests', True)
1266	bld.imp_sigs = {}
1267	tasks = []
1268
1269	id_to_task = {}
1270	for dct in k:
1271		x = Task.classes['cfgtask'](bld=bld, env=None)
1272		tasks.append(x)
1273		x.args = dct
1274		x.bld = bld
1275		x.conf = self
1276		x.args = dct
1277
1278		# bind a logger that will keep the info in memory
1279		x.logger = Logs.make_mem_logger(str(id(x)), self.logger)
1280
1281		if 'id' in dct:
1282			id_to_task[dct['id']] = x
1283
1284	# second pass to set dependencies with after_test/before_test
1285	for x in tasks:
1286		for key in Utils.to_list(x.args.get('before_tests', [])):
1287			tsk = id_to_task[key]
1288			if not tsk:
1289				raise ValueError('No test named %r' % key)
1290			tsk.run_after.add(x)
1291		for key in Utils.to_list(x.args.get('after_tests', [])):
1292			tsk = id_to_task[key]
1293			if not tsk:
1294				raise ValueError('No test named %r' % key)
1295			x.run_after.add(tsk)
1296
1297	def it():
1298		yield tasks
1299		while 1:
1300			yield []
1301	bld.producer = p = Runner.Parallel(bld, Options.options.jobs)
1302	bld.multicheck_lock = Utils.threading.Lock()
1303	p.biter = it()
1304
1305	self.end_msg('started')
1306	p.start()
1307
1308	# flush the logs in order into the config.log
1309	for x in tasks:
1310		x.logger.memhandler.flush()
1311
1312	self.start_msg('-> processing test results')
1313	if p.error:
1314		for x in p.error:
1315			if getattr(x, 'err_msg', None):
1316				self.to_log(x.err_msg)
1317				self.end_msg('fail', color='RED')
1318				raise Errors.WafError('There is an error in the library, read config.log for more information')
1319
1320	failure_count = 0
1321	for x in tasks:
1322		if x.hasrun not in (Task.SUCCESS, Task.NOT_RUN):
1323			failure_count += 1
1324
1325	if failure_count:
1326		self.end_msg(kw.get('errmsg', '%s test failed' % failure_count), color='YELLOW', **kw)
1327	else:
1328		self.end_msg('all ok', **kw)
1329
1330	for x in tasks:
1331		if x.hasrun != Task.SUCCESS:
1332			if x.args.get('mandatory', True):
1333				self.fatal(kw.get('fatalmsg') or 'One of the tests has failed, read config.log for more information')
1334
1335@conf
1336def check_gcc_o_space(self, mode='c'):
1337	if int(self.env.CC_VERSION[0]) > 4:
1338		# this is for old compilers
1339		return
1340	self.env.stash()
1341	if mode == 'c':
1342		self.env.CCLNK_TGT_F = ['-o', '']
1343	elif mode == 'cxx':
1344		self.env.CXXLNK_TGT_F = ['-o', '']
1345	features = '%s %sshlib' % (mode, mode)
1346	try:
1347		self.check(msg='Checking if the -o link must be split from arguments', fragment=SNIP_EMPTY_PROGRAM, features=features)
1348	except self.errors.ConfigurationError:
1349		self.env.revert()
1350	else:
1351		self.env.commit()
1352
1353