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