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