waf

FORK: waf with some random patches
git clone https://git.neptards.moe/neptards/waf.git
Log | Files | Refs | README

c_config.py (42032B)


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