CI: Bogus install prefix in FreeBSD Cirrus CI.
[jack2.git] / waflib / Tools / c_config.py
blobd2b3c0d8fabac43d9dfdc4bb991d0c589d5e8d17
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2005-2018 (ita)
5 """
6 C/C++/D configuration helpers
7 """
9 from __future__ import with_statement
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
16 WAF_CONFIG_H = 'config.h'
17 """default name for the config.h file"""
19 DEFKEYS = 'define_key'
20 INCKEYS = 'include_key'
22 SNIP_EMPTY_PROGRAM = '''
23 int main(int argc, char **argv) {
24 (void)argc; (void)argv;
25 return 0;
27 '''
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
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',
73 @conf
74 def 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::
78 def configure(conf):
79 conf.parse_flags('-O3', 'FOO')
80 # conf.env.CXXFLAGS_FOO = ['-O3']
81 # conf.env.CFLAGS_FOO = ['-O3']
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 """
91 assert(isinstance(line, str))
93 env = env or self.env
95 # Issue 811 and 1371
96 if posix is None:
97 posix = True
98 if '\\' in line:
99 posix = ('\\ ' in line) or ('\\\\' in line)
101 lex = shlex.shlex(line, posix=posix)
102 lex.whitespace_split = True
103 lex.commenters = ''
104 lst = list(lex)
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:]
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)
188 @conf
189 def validate_cfg(self, kw):
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`.
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
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
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'])
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'
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'])
240 @conf
241 def exec_cfg(self, kw):
243 Executes ``pkg-config`` or other ``-config`` applications to collect configuration flags:
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
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)
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']
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)
280 if kw.get('add_have_to_env', 1):
281 self.env[define_name] = 1
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
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
297 lst = [] + path
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))
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
312 # tools like pkgconf expect the package argument after the -- ones -_-
313 lst.extend(Utils.to_list(kw['package']))
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
325 # so we assume the command-line will output flags to be parsed afterwards
326 ret = self.cmd_and_log(lst, env=env)
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
332 @conf
333 def check_cfg(self, *k, **kw):
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`
338 A few examples::
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)
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)
374 return ret
376 def build_fun(bld):
378 Build function that is used for running configuration tests with ``conf.check()``
380 if bld.kw['compile_filename']:
381 node = bld.srcnode.make_node(bld.kw['compile_filename'])
382 node.write(bld.kw['code'])
384 o = bld(features=bld.kw['features'], source=bld.kw['compile_filename'], target='testprog')
386 for k, v in bld.kw.items():
387 setattr(o, k, v)
389 if not bld.kw.get('quiet'):
390 bld.conf.to_log("==>\n%s\n<==" % bld.kw['code'])
392 @conf
393 def validate_c(self, kw):
395 Pre-checks the parameters that will be given to :py:func:`waflib.Configure.run_build`
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
420 for x in ('type_name', 'field_name', 'function_name'):
421 if x in kw:
422 Logs.warn('Invalid argument %r in test' % x)
424 if not 'build_fun' in kw:
425 kw['build_fun'] = build_fun
427 if not 'env' in kw:
428 kw['env'] = self.env.derive()
429 env = kw['env']
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')
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'
446 if not 'type' in kw:
447 kw['type'] = 'cprogram'
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'])
457 if not 'compile_filename' in kw:
458 kw['compile_filename'] = 'test.c' + ((kw['compile_mode'] == 'cxx') and 'pp' or '')
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 ''
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
480 elif 'header_name' in kw:
481 if not 'msg' in kw:
482 kw['msg'] = 'Checking for header %s' % kw['header_name']
484 l = Utils.to_list(kw['header_name'])
485 assert len(l), 'list of headers in header_name is empty'
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])
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()
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()
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'
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'
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
527 if not 'errmsg' in kw:
528 kw['errmsg'] = 'not found'
530 if not 'okmsg' in kw:
531 kw['okmsg'] = 'yes'
533 if not 'code' in kw:
534 kw['code'] = SNIP_EMPTY_PROGRAM
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']
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
545 if not kw.get('success'):
546 kw['success'] = None
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(...)')
553 @conf
554 def post_check(self, *k, **kw):
556 Sets the variables after a test executed in
557 :py:func:`waflib.Tools.c_config.check` was run successfully
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)
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)))
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)
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']))
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]
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
617 @conf
618 def check(self, *k, **kw):
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
624 Besides build targets, complete builds can be given through a build function. All files will
625 be written to a temporary directory::
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)
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
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
656 class test_exec(Task.Task):
658 A task that runs programs after they are built. See :py:func:`waflib.Tools.c_config.test_exec_fun`.
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)
677 @feature('test_exec')
678 @after_method('apply_link')
679 def test_exec_fun(self):
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::
686 def configure(conf):
687 conf.check(execute=True)
689 self.create_task('test_exec', self.link_task.outputs[0])
691 @conf
692 def check_cxx(self, *k, **kw):
694 Runs a test with a task generator of the form::
696 conf.check(features='cxx cxxprogram', ...)
698 kw['compiler'] = 'cxx'
699 return self.check(*k, **kw)
701 @conf
702 def check_cc(self, *k, **kw):
704 Runs a test with a task generator of the form::
706 conf.check(features='c cprogram', ...)
708 kw['compiler'] = 'c'
709 return self.check(*k, **kw)
711 @conf
712 def set_define_comment(self, key, comment):
714 Sets a comment that will appear in the configuration header
716 :type key: string
717 :type comment: string
719 coms = self.env.DEFINE_COMMENTS
720 if not coms:
721 coms = self.env.DEFINE_COMMENTS = {}
722 coms[key] = comment or ''
724 @conf
725 def get_define_comment(self, key):
727 Returns the comment associated to a define
729 :type key: string
731 coms = self.env.DEFINE_COMMENTS or {}
732 return coms.get(key, '')
734 @conf
735 def define(self, key, val, quote=True, comment=''):
737 Stores a single define and its state into ``conf.env.DEFINES``. The value is cast to an integer (0/1).
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
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
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))
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)
769 self.env.append_unique(DEFKEYS, key)
770 self.set_define_comment(key, comment)
772 @conf
773 def undefine(self, key, comment=''):
775 Removes a global define from ``conf.env.DEFINES``
777 :param key: define name
778 :type key: string
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)
789 @conf
790 def define_cond(self, key, val, comment=''):
792 Conditionally defines a name::
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')
800 :param key: define name
801 :type key: string
802 :param val: value
803 :type val: int or string
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)
813 @conf
814 def is_defined(self, key):
816 Indicates whether a particular define is globally set in ``conf.env.DEFINES``.
818 :param key: define name
819 :type key: string
820 :return: True if the define is set
821 :rtype: bool
823 assert key and isinstance(key, str)
825 ban = key + '='
826 for x in self.env.DEFINES:
827 if x.startswith(ban):
828 return True
829 return False
831 @conf
832 def get_define(self, key):
834 Returns the value of an existing define, or None if not found
836 :param key: define name
837 :type key: string
838 :rtype: string
840 assert key and isinstance(key, str)
842 ban = key + '='
843 for x in self.env.DEFINES:
844 if x.startswith(ban):
845 return x[len(ban):]
846 return None
848 @conf
849 def have_define(self, key):
851 Returns a variable suitable for command-line or header use by removing invalid characters
852 and prefixing it with ``HAVE_``
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
859 return (self.env.HAVE_PAT or 'HAVE_%s') % Utils.quote_define_name(key)
861 @conf
862 def write_config_header(self, configfile='', guard='', top=False, defines=True, headers=False, remove=True, define_prefix=''):
864 Writes a configuration header containing defines and includes::
866 def configure(cnf):
867 cnf.define('A', 1)
868 cnf.write_config_header('config.h')
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.
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
888 if not configfile:
889 configfile = WAF_CONFIG_H
890 waf_guard = guard or 'W_%s_WAF' % Utils.quote_define_name(configfile)
892 node = top and self.bldnode or self.path.get_bld()
893 node = node.make_node(configfile)
894 node.parent.mkdir()
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)
901 node.write('\n'.join(lst))
903 # config files must not be removed on "waf clean"
904 self.env.append_unique(Build.CFG_FILES, [node.abspath()])
906 if remove:
907 for key in self.env[DEFKEYS]:
908 self.undefine(key)
909 self.env[DEFKEYS] = []
911 @conf
912 def get_config_header(self, defines=True, headers=False, define_prefix=''):
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.
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::
920 def configure(conf):
921 conf.env.WAF_CONFIG_H_PRELUDE = '#include <unistd.h>\\n'
922 conf.write_config_header('config.h')
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
933 lst = []
935 if self.env.WAF_CONFIG_H_PRELUDE:
936 lst.append(self.env.WAF_CONFIG_H_PRELUDE)
938 if headers:
939 for x in self.env[INCKEYS]:
940 lst.append('#include <%s>' % x)
942 if defines:
943 tbl = {}
944 for k in self.env.DEFINES:
945 a, _, b = k.partition('=')
946 tbl[a] = b
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)
959 @conf
960 def cc_add_flags(conf):
962 Adds CFLAGS / CPPFLAGS from os.environ to conf.env
964 conf.add_os_flags('CPPFLAGS', dup=False)
965 conf.add_os_flags('CFLAGS', dup=False)
967 @conf
968 def cxx_add_flags(conf):
970 Adds CXXFLAGS / CPPFLAGS from os.environ to conf.env
972 conf.add_os_flags('CPPFLAGS', dup=False)
973 conf.add_os_flags('CXXFLAGS', dup=False)
975 @conf
976 def link_add_flags(conf):
978 Adds LINKFLAGS / LDFLAGS from os.environ to conf.env
980 conf.add_os_flags('LINKFLAGS', dup=False)
981 conf.add_os_flags('LDFLAGS', dup=False)
983 @conf
984 def cc_load_tools(conf):
986 Loads the Waf c extensions
988 if not conf.env.DEST_OS:
989 conf.env.DEST_OS = Utils.unversioned_sys_platform()
990 conf.load('c')
992 @conf
993 def cxx_load_tools(conf):
995 Loads the Waf c++ extensions
997 if not conf.env.DEST_OS:
998 conf.env.DEST_OS = Utils.unversioned_sys_platform()
999 conf.load('cxx')
1001 @conf
1002 def get_cc_version(conf, cc, gcc=False, icc=False, clang=False):
1004 Runs the preprocessor to determine the gcc/icc/clang version
1006 The variables CC_VERSION, DEST_OS, DEST_BINFMT and DEST_CPU will be set in *conf.env*
1008 :raise: :py:class:`waflib.Errors.ConfigurationError`
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)
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')
1023 if icc and out.find('__INTEL_COMPILER') < 0:
1024 conf.fatal('Not icc/icpc')
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')
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
1041 def isD(var):
1042 return var in k
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'
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'
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)
1072 for i in MACRO_TO_DEST_CPU:
1073 if isD(i):
1074 conf.env.DEST_CPU = MACRO_TO_DEST_CPU[i]
1075 break
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
1089 @conf
1090 def get_xlc_version(conf, cc):
1092 Returns the Aix compiler version
1094 :raise: :py:class:`waflib.Errors.ConfigurationError`
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)
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.')
1113 @conf
1114 def get_suncc_version(conf, cc):
1116 Returns the Sun compiler version
1118 :raise: :py:class:`waflib.Errors.ConfigurationError`
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
1130 version = (out or err)
1131 version = version.splitlines()[0]
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.')
1144 # ============ the --as-needed flag should added during the configuration, not at runtime =========
1146 @conf
1147 def add_as_needed(self):
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.
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')
1155 # ============ parallel configuration
1157 class cfgtask(Task.Task):
1159 A task that executes build configuration tests (calls conf.check)
1161 Make sure to use locks if concurrent access to the same conf.env data is necessary.
1163 def __init__(self, *k, **kw):
1164 Task.Task.__init__(self, *k, **kw)
1165 self.run_after = set()
1167 def display(self):
1168 return ''
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
1176 def uid(self):
1177 return Utils.SIG_NIL
1179 def signature(self):
1180 return Utils.SIG_NIL
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', ''),
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
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')
1220 @conf
1221 def multicheck(self, *k, **kw):
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::
1226 def test_build(ctx):
1227 ctx.in_msg = True # suppress console outputs
1228 ctx.check_large_file(mandatory=False)
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
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.
1244 self.start_msg(kw.get('msg', 'Executing %d configuration tests' % len(k)), **kw)
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 {}
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
1263 bld = par()
1264 bld.keep = kw.get('run_all_tests', True)
1265 bld.imp_sigs = {}
1266 tasks = []
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
1277 # bind a logger that will keep the info in memory
1278 x.logger = Logs.make_mem_logger(str(id(x)), self.logger)
1280 if 'id' in dct:
1281 id_to_task[dct['id']] = x
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)
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()
1304 self.end_msg('started')
1305 p.start()
1307 # flush the logs in order into the config.log
1308 for x in tasks:
1309 x.logger.memhandler.flush()
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')
1319 failure_count = 0
1320 for x in tasks:
1321 if x.hasrun not in (Task.SUCCESS, Task.NOT_RUN):
1322 failure_count += 1
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)
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')
1334 @conf
1335 def 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()