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