waf/waflib/Tools/c_config.py

1365 lines
41 KiB
Python
Raw Permalink Normal View History

2011-09-10 11:13:51 +02:00
#!/usr/bin/env python
# encoding: utf-8
2018-01-01 20:53:49 +01:00
# Thomas Nagy, 2005-2018 (ita)
2011-09-10 11:13:51 +02:00
"""
C/C++/D configuration helpers
"""
from __future__ import with_statement
2015-10-06 23:06:22 +02:00
import os, re, shlex
from waflib import Build, Utils, Task, Options, Logs, Errors, Runner
2012-02-04 19:39:45 +01:00
from waflib.TaskGen import after_method, feature
2011-09-10 11:13:51 +02:00
from waflib.Configure import conf
WAF_CONFIG_H = 'config.h'
"""default name for the config.h file"""
DEFKEYS = 'define_key'
INCKEYS = 'include_key'
SNIP_EMPTY_PROGRAM = '''
2012-08-24 22:53:07 +02:00
int main(int argc, char **argv) {
(void)argc; (void)argv;
2011-09-10 11:13:51 +02:00
return 0;
}
'''
MACRO_TO_DESTOS = {
'__linux__' : 'linux',
'__GNU__' : 'gnu', # hurd
'__FreeBSD__' : 'freebsd',
'__NetBSD__' : 'netbsd',
'__OpenBSD__' : 'openbsd',
'__sun' : 'sunos',
'__hpux' : 'hpux',
'__sgi' : 'irix',
'_AIX' : 'aix',
'__CYGWIN__' : 'cygwin',
'__MSYS__' : 'cygwin',
'_UWIN' : 'uwin',
'_WIN64' : 'win32',
'_WIN32' : 'win32',
# Note about darwin: this is also tested with 'defined __APPLE__ && defined __MACH__' somewhere below in this file.
2016-06-25 20:10:04 +02:00
'__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__' : 'darwin',
'__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__' : 'darwin', # iphone
'__QNX__' : 'qnx',
'__native_client__' : 'nacl' # google native client platform
2011-09-10 11:13:51 +02:00
}
MACRO_TO_DEST_CPU = {
'__x86_64__' : 'x86_64',
'__amd64__' : 'x86_64',
2011-09-10 11:13:51 +02:00
'__i386__' : 'x86',
'__ia64__' : 'ia',
'__mips__' : 'mips',
'__sparc__' : 'sparc',
'__alpha__' : 'alpha',
'__aarch64__' : 'aarch64',
'__thumb__' : 'thumb',
2011-09-10 11:13:51 +02:00
'__arm__' : 'arm',
'__hppa__' : 'hppa',
'__powerpc__' : 'powerpc',
'__ppc__' : 'powerpc',
'__convex__' : 'convex',
'__m68k__' : 'm68k',
'__s390x__' : 's390x',
'__s390__' : 's390',
'__sh__' : 'sh',
2016-01-21 17:54:02 +01:00
'__xtensa__' : 'xtensa',
2020-07-12 16:33:33 +02:00
'__e2k__' : 'e2k',
2021-04-15 14:37:06 +02:00
'__riscv' : 'riscv',
2011-09-10 11:13:51 +02:00
}
@conf
2013-11-03 00:14:04 +01:00
def parse_flags(self, line, uselib_store, env=None, force_static=False, posix=None):
2011-09-10 11:13:51 +02:00
"""
2016-06-26 00:15:41 +02:00
Parses flags from the input lines, and adds them to the relevant use variables::
2011-09-10 11:13:51 +02:00
def configure(conf):
2012-07-05 09:57:34 +02:00
conf.parse_flags('-O3', 'FOO')
2011-09-10 11:13:51 +02:00
# conf.env.CXXFLAGS_FOO = ['-O3']
# conf.env.CFLAGS_FOO = ['-O3']
:param line: flags
:type line: string
2012-07-05 09:57:34 +02:00
:param uselib_store: where to add the flags
:type uselib_store: string
2011-09-10 11:13:51 +02:00
:param env: config set or conf.env by default
:type env: :py:class:`waflib.ConfigSet.ConfigSet`
:param force_static: force usage of static libraries
:type force_static: bool default False
:param posix: usage of POSIX mode for shlex lexical analiysis library
:type posix: bool default True
2011-09-10 11:13:51 +02:00
"""
assert(isinstance(line, str))
env = env or self.env
2013-11-03 00:14:04 +01:00
# Issue 811 and 1371
if posix is None:
posix = True
if '\\' in line:
posix = ('\\ ' in line) or ('\\\\' in line)
2011-09-10 11:13:51 +02:00
2013-11-03 00:14:04 +01:00
lex = shlex.shlex(line, posix=posix)
2011-09-10 11:13:51 +02:00
lex.whitespace_split = True
lex.commenters = ''
lst = list(lex)
so_re = re.compile(r"\.so(?:\.[0-9]+)*$")
2013-11-03 00:14:04 +01:00
# append_unique is not always possible
# for example, apple flags may require both -arch i386 and -arch ppc
2012-07-05 09:57:34 +02:00
uselib = uselib_store
2016-03-28 14:46:29 +02:00
def app(var, val):
env.append_value('%s_%s' % (var, uselib), val)
def appu(var, val):
env.append_unique('%s_%s' % (var, uselib), val)
static = False
2011-09-10 11:13:51 +02:00
while lst:
x = lst.pop(0)
st = x[:2]
ot = x[2:]
if st == '-I' or st == '/I':
2016-03-28 14:46:29 +02:00
if not ot:
ot = lst.pop(0)
appu('INCLUDES', ot)
elif st == '-i':
2011-09-10 11:13:51 +02:00
tmp = [x, lst.pop(0)]
app('CFLAGS', tmp)
app('CXXFLAGS', tmp)
elif st == '-D' or (env.CXX_NAME == 'msvc' and st == '/D'): # not perfect but..
2016-03-28 14:46:29 +02:00
if not ot:
ot = lst.pop(0)
app('DEFINES', ot)
2011-09-10 11:13:51 +02:00
elif st == '-l':
2016-03-28 14:46:29 +02:00
if not ot:
ot = lst.pop(0)
prefix = 'STLIB' if (force_static or static) else 'LIB'
app(prefix, ot)
2011-09-10 11:13:51 +02:00
elif st == '-L':
2016-03-28 14:46:29 +02:00
if not ot:
ot = lst.pop(0)
prefix = 'STLIBPATH' if (force_static or static) else 'LIBPATH'
appu(prefix, ot)
elif x.startswith('/LIBPATH:'):
2016-03-28 14:46:29 +02:00
prefix = 'STLIBPATH' if (force_static or static) else 'LIBPATH'
appu(prefix, x.replace('/LIBPATH:', ''))
2016-03-04 19:23:14 +01:00
elif x.startswith('-std='):
2016-03-28 14:46:29 +02:00
prefix = 'CXXFLAGS' if '++' in x else 'CFLAGS'
app(prefix, x)
elif x.startswith('+') or x in ('-pthread', '-fPIC', '-fpic', '-fPIE', '-fpie', '-flto', '-fno-lto'):
2016-03-28 14:46:29 +02:00
app('CFLAGS', x)
app('CXXFLAGS', x)
app('LINKFLAGS', x)
2011-09-10 11:13:51 +02:00
elif x == '-framework':
2016-03-28 14:46:29 +02:00
appu('FRAMEWORK', lst.pop(0))
2011-09-10 11:13:51 +02:00
elif x.startswith('-F'):
2016-03-28 14:46:29 +02:00
appu('FRAMEWORKPATH', x[2:])
elif x == '-Wl,-rpath' or x == '-Wl,-R':
2016-03-28 14:46:29 +02:00
app('RPATH', lst.pop(0).lstrip('-Wl,'))
elif x.startswith('-Wl,-R,'):
2016-03-28 14:46:29 +02:00
app('RPATH', x[7:])
2013-10-27 14:56:04 +01:00
elif x.startswith('-Wl,-R'):
2016-03-28 14:46:29 +02:00
app('RPATH', x[6:])
2013-10-27 14:56:04 +01:00
elif x.startswith('-Wl,-rpath,'):
2016-03-28 14:46:29 +02:00
app('RPATH', x[11:])
elif x == '-Wl,-Bstatic' or x == '-Bstatic':
static = True
elif x == '-Wl,-Bdynamic' or x == '-Bdynamic':
static = False
2017-04-01 09:29:00 +02:00
elif x.startswith('-Wl') or x in ('-rdynamic', '-pie'):
2016-03-28 14:46:29 +02:00
app('LINKFLAGS', x)
elif x.startswith(('-m', '-f', '-dynamic', '-O', '-g')):
# Adding the -W option breaks python builds on Openindiana
2016-03-28 14:46:29 +02:00
app('CFLAGS', x)
app('CXXFLAGS', x)
2011-09-10 11:13:51 +02:00
elif x.startswith('-bundle'):
2016-03-28 14:46:29 +02:00
app('LINKFLAGS', x)
elif x.startswith(('-undefined', '-Xlinker')):
2011-09-10 11:13:51 +02:00
arg = lst.pop(0)
2016-03-28 14:46:29 +02:00
app('LINKFLAGS', [x, arg])
elif x.startswith(('-arch', '-isysroot')):
2011-09-10 11:13:51 +02:00
tmp = [x, lst.pop(0)]
2016-03-28 14:46:29 +02:00
app('CFLAGS', tmp)
app('CXXFLAGS', tmp)
app('LINKFLAGS', tmp)
elif x.endswith(('.a', '.dylib', '.lib')) or so_re.search(x):
2016-03-28 14:46:29 +02:00
appu('LINKFLAGS', x) # not cool, #762
2017-04-01 09:29:00 +02:00
else:
self.to_log('Unhandled flag %r' % x)
2011-09-10 11:13:51 +02:00
@conf
def validate_cfg(self, kw):
"""
2016-06-26 00:15:41 +02:00
Searches for the program *pkg-config* if missing, and validates the
parameters to pass to :py:func:`waflib.Tools.c_config.exec_cfg`.
2011-09-10 11:13:51 +02:00
:param path: the **-config program to use** (default is *pkg-config*)
:type path: list of string
:param msg: message to display to describe the test executed
:type msg: string
:param okmsg: message to display when the test is successful
:type okmsg: string
:param errmsg: message to display in case of error
:type errmsg: string
"""
if not 'path' in kw:
if not self.env.PKGCONFIG:
self.find_program('pkg-config', var='PKGCONFIG')
kw['path'] = self.env.PKGCONFIG
# verify that exactly one action is requested
s = ('atleast_pkgconfig_version' in kw) + ('modversion' in kw) + ('package' in kw)
if s != 1:
raise ValueError('exactly one of atleast_pkgconfig_version, modversion and package must be set')
if not 'msg' in kw:
if 'atleast_pkgconfig_version' in kw:
2011-09-10 11:13:51 +02:00
kw['msg'] = 'Checking for pkg-config version >= %r' % kw['atleast_pkgconfig_version']
elif 'modversion' in kw:
kw['msg'] = 'Checking for %r version' % kw['modversion']
else:
kw['msg'] = 'Checking for %r' %(kw['package'])
2011-09-10 11:13:51 +02:00
# let the modversion check set the okmsg to the detected version
if not 'okmsg' in kw and not 'modversion' in kw:
2011-09-10 11:13:51 +02:00
kw['okmsg'] = 'yes'
if not 'errmsg' in kw:
kw['errmsg'] = 'not found'
# pkg-config version
if 'atleast_pkgconfig_version' in kw:
pass
elif 'modversion' in kw:
if not 'uselib_store' in kw:
kw['uselib_store'] = kw['modversion']
if not 'define_name' in kw:
kw['define_name'] = '%s_VERSION' % Utils.quote_define_name(kw['uselib_store'])
else:
if not 'uselib_store' in kw:
kw['uselib_store'] = Utils.to_list(kw['package'])[0].upper()
if not 'define_name' in kw:
kw['define_name'] = self.have_define(kw['uselib_store'])
2011-09-10 11:13:51 +02:00
@conf
def exec_cfg(self, kw):
2019-01-05 16:16:48 +01:00
"""
2017-08-03 06:43:50 +02:00
Executes ``pkg-config`` or other ``-config`` applications to collect configuration flags:
2011-09-10 11:13:51 +02:00
* if atleast_pkgconfig_version is given, check that pkg-config has the version n and return
* if modversion is given, then return the module version
* else, execute the *-config* program with the *args* and *variables* given, and set the flags on the *conf.env.FLAGS_name* variable
:param path: the **-config program to use**
:type path: list of string
2011-09-10 11:13:51 +02:00
:param atleast_pkgconfig_version: minimum pkg-config version to use (disable other tests)
:type atleast_pkgconfig_version: string
:param package: package name, for example *gtk+-2.0*
:type package: string
2019-01-05 16:16:48 +01:00
:param uselib_store: if the test is successful, define HAVE\\_*name*. It is also used to define *conf.env.FLAGS_name* variables.
2011-09-10 11:13:51 +02:00
:type uselib_store: string
2019-01-05 16:16:48 +01:00
:param modversion: if provided, return the version of the given module and define *name*\\_VERSION
2011-09-10 11:13:51 +02:00
:type modversion: string
:param args: arguments to give to *package* when retrieving flags
:type args: list of string
:param variables: return the values of particular variables
:type variables: list of string
:param define_variable: additional variables to define (also in conf.env.PKG_CONFIG_DEFINES)
:type define_variable: dict(string: string)
:param pkg_config_path: paths where pkg-config should search for .pc config files (overrides env.PKG_CONFIG_PATH if exists)
:type pkg_config_path: string, list of directories separated by colon
:param force_static: force usage of static libraries
:type force_static: bool default False
:param posix: usage of POSIX mode for shlex lexical analiysis library
:type posix: bool default True
2011-09-10 11:13:51 +02:00
"""
path = Utils.to_list(kw['path'])
2015-10-06 23:06:22 +02:00
env = self.env.env or None
if kw.get('pkg_config_path'):
if not env:
env = dict(self.environ)
env['PKG_CONFIG_PATH'] = kw['pkg_config_path']
2012-10-20 23:45:16 +02:00
def define_it():
define_name = kw['define_name']
# by default, add HAVE_X to the config.h, else provide DEFINES_X for use=X
if kw.get('global_define', 1):
self.define(define_name, 1, False)
2014-09-21 12:47:32 +02:00
else:
self.env.append_unique('DEFINES_%s' % kw['uselib_store'], "%s=1" % define_name)
if kw.get('add_have_to_env', 1):
self.env[define_name] = 1
2012-10-20 23:45:16 +02:00
2011-09-10 11:13:51 +02:00
# pkg-config version
if 'atleast_pkgconfig_version' in kw:
cmd = path + ['--atleast-pkgconfig-version=%s' % kw['atleast_pkgconfig_version']]
2015-10-06 23:06:22 +02:00
self.cmd_and_log(cmd, env=env)
2011-09-10 11:13:51 +02:00
return
# single version for a module
2011-09-10 11:13:51 +02:00
if 'modversion' in kw:
2015-10-06 23:06:22 +02:00
version = self.cmd_and_log(path + ['--modversion', kw['modversion']], env=env).strip()
if not 'okmsg' in kw:
kw['okmsg'] = version
self.define(kw['define_name'], version)
2011-09-10 11:13:51 +02:00
return version
lst = [] + path
2011-09-10 11:13:51 +02:00
2016-04-19 22:00:21 +02:00
defi = kw.get('define_variable')
2011-09-10 11:13:51 +02:00
if not defi:
defi = self.env.PKG_CONFIG_DEFINES or {}
for key, val in defi.items():
lst.append('--define-variable=%s=%s' % (key, val))
static = kw.get('force_static', False)
if 'args' in kw:
args = Utils.to_list(kw['args'])
if '--static' in args or '--static-libs' in args:
static = True
lst += args
# tools like pkgconf expect the package argument after the -- ones -_-
lst.extend(Utils.to_list(kw['package']))
2011-09-10 11:13:51 +02:00
# retrieving variables of a module
if 'variables' in kw:
v_env = kw.get('env', self.env)
2011-09-10 11:13:51 +02:00
vars = Utils.to_list(kw['variables'])
for v in vars:
2015-10-06 23:06:22 +02:00
val = self.cmd_and_log(lst + ['--variable=' + v], env=env).strip()
var = '%s_%s' % (kw['uselib_store'], v)
v_env[var] = val
2011-09-10 11:13:51 +02:00
return
# so we assume the command-line will output flags to be parsed afterwards
2015-10-06 23:06:22 +02:00
ret = self.cmd_and_log(lst, env=env)
2011-09-10 11:13:51 +02:00
2012-10-20 23:45:16 +02:00
define_it()
2016-04-19 22:00:21 +02:00
self.parse_flags(ret, kw['uselib_store'], kw.get('env', self.env), force_static=static, posix=kw.get('posix'))
2011-09-10 11:13:51 +02:00
return ret
@conf
def check_cfg(self, *k, **kw):
"""
2016-06-26 00:15:41 +02:00
Checks for configuration flags using a **-config**-like program (pkg-config, sdl-config, etc).
This wraps internal calls to :py:func:`waflib.Tools.c_config.validate_cfg` and :py:func:`waflib.Tools.c_config.exec_cfg`
so check exec_cfg parameters descriptions for more details on kw passed
2011-09-10 11:13:51 +02:00
A few examples::
def configure(conf):
conf.load('compiler_c')
conf.check_cfg(package='glib-2.0', args='--libs --cflags')
conf.check_cfg(package='pango')
conf.check_cfg(package='pango', uselib_store='MYPANGO', args=['--cflags', '--libs'])
conf.check_cfg(package='pango',
args=['pango >= 0.1.0', 'pango < 9.9.9', '--cflags', '--libs'],
msg="Checking for 'pango 0.1.0'")
conf.check_cfg(path='sdl-config', args='--cflags --libs', package='', uselib_store='SDL')
conf.check_cfg(path='mpicc', args='--showme:compile --showme:link',
package='', uselib_store='OPEN_MPI', mandatory=False)
2015-05-03 01:03:11 +02:00
# variables
2015-05-03 10:22:28 +02:00
conf.check_cfg(package='gtk+-2.0', variables=['includedir', 'prefix'], uselib_store='FOO')
2015-05-03 01:03:11 +02:00
print(conf.env.FOO_includedir)
2011-09-10 11:13:51 +02:00
"""
self.validate_cfg(kw)
if 'msg' in kw:
self.start_msg(kw['msg'], **kw)
2011-09-10 11:13:51 +02:00
ret = None
try:
ret = self.exec_cfg(kw)
except self.errors.WafError as e:
2011-09-10 11:13:51 +02:00
if 'errmsg' in kw:
self.end_msg(kw['errmsg'], 'YELLOW', **kw)
2011-09-10 11:13:51 +02:00
if Logs.verbose > 1:
self.to_log('Command failure: %s' % e)
self.fatal('The configuration failed')
2011-09-10 11:13:51 +02:00
else:
if not ret:
ret = True
2011-09-10 11:13:51 +02:00
kw['success'] = ret
if 'okmsg' in kw:
self.end_msg(self.ret_msg(kw['okmsg'], kw), **kw)
2011-09-10 11:13:51 +02:00
return ret
def build_fun(bld):
2016-06-26 00:15:41 +02:00
"""
Build function that is used for running configuration tests with ``conf.check()``
"""
if bld.kw['compile_filename']:
node = bld.srcnode.make_node(bld.kw['compile_filename'])
node.write(bld.kw['code'])
o = bld(features=bld.kw['features'], source=bld.kw['compile_filename'], target='testprog')
for k, v in bld.kw.items():
setattr(o, k, v)
2016-04-19 22:00:21 +02:00
if not bld.kw.get('quiet'):
bld.conf.to_log("==>\n%s\n<==" % bld.kw['code'])
2011-09-10 11:13:51 +02:00
@conf
def validate_c(self, kw):
"""
2016-06-26 00:15:41 +02:00
Pre-checks the parameters that will be given to :py:func:`waflib.Configure.run_build`
2011-09-10 11:13:51 +02:00
:param compiler: c or cxx (tries to guess what is best)
:type compiler: string
:param type: cprogram, cshlib, cstlib - not required if *features are given directly*
:type type: binary to create
:param feature: desired features for the task generator that will execute the test, for example ``cxx cxxstlib``
:type feature: list of string
:param fragment: provide a piece of code for the test (default is to let the system create one)
:type fragment: string
:param uselib_store: define variables after the test is executed (IMPORTANT!)
:type uselib_store: string
:param use: parameters to use for building (just like the normal *use* keyword)
:type use: list of string
:param define_name: define to set when the check is over
:type define_name: string
:param execute: execute the resulting binary
:type execute: bool
:param define_ret: if execute is set to True, use the execution output in both the define and the return value
:type define_ret: bool
:param header_name: check for a particular header
:type header_name: string
:param auto_add_header_name: if header_name was set, add the headers in env.INCKEYS so the next tests will include these headers
:type auto_add_header_name: bool
"""
for x in ('type_name', 'field_name', 'function_name'):
if x in kw:
Logs.warn('Invalid argument %r in test' % x)
2011-09-10 11:13:51 +02:00
if not 'build_fun' in kw:
kw['build_fun'] = build_fun
2011-09-10 11:13:51 +02:00
if not 'env' in kw:
kw['env'] = self.env.derive()
env = kw['env']
if not 'compiler' in kw and not 'features' in kw:
kw['compiler'] = 'c'
2016-06-25 23:54:12 +02:00
if env.CXX_NAME and Task.classes.get('cxx'):
2011-09-10 11:13:51 +02:00
kw['compiler'] = 'cxx'
if not self.env.CXX:
2011-09-10 11:13:51 +02:00
self.fatal('a c++ compiler is required')
else:
if not self.env.CC:
2011-09-10 11:13:51 +02:00
self.fatal('a c compiler is required')
if not 'compile_mode' in kw:
kw['compile_mode'] = 'c'
2017-01-15 01:16:44 +01:00
if 'cxx' in Utils.to_list(kw.get('features', [])) or kw.get('compiler') == 'cxx':
2011-09-10 11:13:51 +02:00
kw['compile_mode'] = 'cxx'
if not 'type' in kw:
kw['type'] = 'cprogram'
if not 'features' in kw:
if not 'header_name' in kw or kw.get('link_header_test', True):
kw['features'] = [kw['compile_mode'], kw['type']] # "c ccprogram"
else:
kw['features'] = [kw['compile_mode']]
2011-09-10 11:13:51 +02:00
else:
kw['features'] = Utils.to_list(kw['features'])
if not 'compile_filename' in kw:
kw['compile_filename'] = 'test.c' + ((kw['compile_mode'] == 'cxx') and 'pp' or '')
def to_header(dct):
if 'header_name' in dct:
dct = Utils.to_list(dct['header_name'])
return ''.join(['#include <%s>\n' % x for x in dct])
return ''
if 'framework_name' in kw:
2017-01-15 01:16:44 +01:00
# OSX, not sure this is used anywhere
2011-09-10 11:13:51 +02:00
fwkname = kw['framework_name']
if not 'uselib_store' in kw:
kw['uselib_store'] = fwkname.upper()
2017-01-15 01:16:44 +01:00
if not kw.get('no_header'):
2011-09-10 11:13:51 +02:00
fwk = '%s/%s.h' % (fwkname, fwkname)
2016-04-19 22:00:21 +02:00
if kw.get('remove_dot_h'):
2011-09-10 11:13:51 +02:00
fwk = fwk[:-2]
2017-01-15 01:16:44 +01:00
val = kw.get('header_name', [])
kw['header_name'] = Utils.to_list(val) + [fwk]
2011-09-10 11:13:51 +02:00
kw['msg'] = 'Checking for framework %s' % fwkname
kw['framework'] = fwkname
elif 'header_name' in kw:
if not 'msg' in kw:
kw['msg'] = 'Checking for header %s' % kw['header_name']
l = Utils.to_list(kw['header_name'])
assert len(l), 'list of headers in header_name is empty'
2011-09-10 11:13:51 +02:00
kw['code'] = to_header(kw) + SNIP_EMPTY_PROGRAM
if not 'uselib_store' in kw:
kw['uselib_store'] = l[0].upper()
if not 'define_name' in kw:
kw['define_name'] = self.have_define(l[0])
if 'lib' in kw:
if not 'msg' in kw:
kw['msg'] = 'Checking for library %s' % kw['lib']
if not 'uselib_store' in kw:
kw['uselib_store'] = kw['lib'].upper()
if 'stlib' in kw:
if not 'msg' in kw:
kw['msg'] = 'Checking for static library %s' % kw['stlib']
if not 'uselib_store' in kw:
kw['uselib_store'] = kw['stlib'].upper()
if 'fragment' in kw:
# an additional code fragment may be provided to replace the predefined code
# in custom headers
kw['code'] = kw['fragment']
if not 'msg' in kw:
kw['msg'] = 'Checking for code snippet'
if not 'errmsg' in kw:
kw['errmsg'] = 'no'
for (flagsname,flagstype) in (('cxxflags','compiler'), ('cflags','compiler'), ('linkflags','linker')):
2011-09-10 11:13:51 +02:00
if flagsname in kw:
if not 'msg' in kw:
kw['msg'] = 'Checking for %s flags %s' % (flagstype, kw[flagsname])
if not 'errmsg' in kw:
kw['errmsg'] = 'no'
if not 'execute' in kw:
kw['execute'] = False
if kw['execute']:
kw['features'].append('test_exec')
kw['chmod'] = Utils.O755
2011-09-10 11:13:51 +02:00
if not 'errmsg' in kw:
kw['errmsg'] = 'not found'
if not 'okmsg' in kw:
kw['okmsg'] = 'yes'
if not 'code' in kw:
kw['code'] = SNIP_EMPTY_PROGRAM
# if there are headers to append automatically to the next tests
if self.env[INCKEYS]:
kw['code'] = '\n'.join(['#include <%s>' % x for x in self.env[INCKEYS]]) + '\n' + kw['code']
# in case defines lead to very long command-lines
2017-01-15 01:16:44 +01:00
if kw.get('merge_config_header') or env.merge_config_header:
kw['code'] = '%s\n\n%s' % (self.get_config_header(), kw['code'])
env.DEFINES = [] # modify the copy
2017-01-15 01:16:44 +01:00
if not kw.get('success'):
kw['success'] = None
2011-09-10 11:13:51 +02:00
if 'define_name' in kw:
self.undefine(kw['define_name'])
2014-01-14 20:10:23 +01:00
if not 'msg' in kw:
self.fatal('missing "msg" in conf.check(...)')
2011-09-10 11:13:51 +02:00
@conf
def post_check(self, *k, **kw):
2016-06-26 00:15:41 +02:00
"""
Sets the variables after a test executed in
:py:func:`waflib.Tools.c_config.check` was run successfully
"""
2011-09-10 11:13:51 +02:00
is_success = 0
if kw['execute']:
if kw['success'] is not None:
2017-01-15 01:16:44 +01:00
if kw.get('define_ret'):
2011-09-10 11:13:51 +02:00
is_success = kw['success']
else:
is_success = (kw['success'] == 0)
else:
is_success = (kw['success'] == 0)
if kw.get('define_name'):
comment = kw.get('comment', '')
define_name = kw['define_name']
2016-04-19 22:00:21 +02:00
if kw['execute'] and kw.get('define_ret') and isinstance(is_success, str):
if kw.get('global_define', 1):
self.define(define_name, is_success, quote=kw.get('quote', 1), comment=comment)
else:
if kw.get('quote', 1):
succ = '"%s"' % is_success
else:
succ = int(is_success)
val = '%s=%s' % (define_name, succ)
var = 'DEFINES_%s' % kw['uselib_store']
self.env.append_value(var, val)
2011-09-10 11:13:51 +02:00
else:
if kw.get('global_define', 1):
self.define_cond(define_name, is_success, comment=comment)
else:
var = 'DEFINES_%s' % kw['uselib_store']
self.env.append_value(var, '%s=%s' % (define_name, int(is_success)))
2011-09-10 11:13:51 +02:00
# define conf.env.HAVE_X to 1
if kw.get('add_have_to_env', 1):
2016-04-19 22:00:21 +02:00
if kw.get('uselib_store'):
self.env[self.have_define(kw['uselib_store'])] = 1
elif kw['execute'] and kw.get('define_ret'):
self.env[define_name] = is_success
else:
self.env[define_name] = int(is_success)
2011-09-10 11:13:51 +02:00
if 'header_name' in kw:
2017-01-15 01:16:44 +01:00
if kw.get('auto_add_header_name'):
2011-09-10 11:13:51 +02:00
self.env.append_value(INCKEYS, Utils.to_list(kw['header_name']))
if is_success and 'uselib_store' in kw:
from waflib.Tools import ccroot
# See get_uselib_vars in ccroot.py
2016-09-02 20:20:43 +02:00
_vars = set()
2011-09-10 11:13:51 +02:00
for x in kw['features']:
if x in ccroot.USELIB_VARS:
_vars |= ccroot.USELIB_VARS[x]
for k in _vars:
x = k.lower()
if x in kw:
self.env.append_value(k + '_' + kw['uselib_store'], kw[x])
2011-09-10 11:13:51 +02:00
return is_success
@conf
def check(self, *k, **kw):
"""
2016-06-26 00:15:41 +02:00
Performs a configuration test by calling :py:func:`waflib.Configure.run_build`.
2011-09-10 11:13:51 +02:00
For the complete list of parameters, see :py:func:`waflib.Tools.c_config.validate_c`.
2016-06-26 00:15:41 +02:00
To force a specific compiler, pass ``compiler='c'`` or ``compiler='cxx'`` to the list of arguments
2015-07-07 22:30:00 +02:00
2017-02-16 07:03:43 +01:00
Besides build targets, complete builds can be given through a build function. All files will
2015-07-07 22:30:00 +02:00
be written to a temporary directory::
def build(bld):
lib_node = bld.srcnode.make_node('libdir/liblc1.c')
lib_node.parent.mkdir()
lib_node.write('#include <stdio.h>\\nint lib_func(void) { FILE *f = fopen("foo", "r");}\\n', 'w')
bld(features='c cshlib', source=[lib_node], linkflags=conf.env.EXTRA_LDFLAGS, target='liblc')
conf.check(build_fun=build, msg=msg)
2011-09-10 11:13:51 +02:00
"""
self.validate_c(kw)
self.start_msg(kw['msg'], **kw)
2011-09-10 11:13:51 +02:00
ret = None
try:
ret = self.run_build(*k, **kw)
2012-02-04 19:39:45 +01:00
except self.errors.ConfigurationError:
self.end_msg(kw['errmsg'], 'YELLOW', **kw)
2011-09-10 11:13:51 +02:00
if Logs.verbose > 1:
raise
else:
self.fatal('The configuration failed')
else:
kw['success'] = ret
ret = self.post_check(*k, **kw)
if not ret:
self.end_msg(kw['errmsg'], 'YELLOW', **kw)
2011-09-10 11:13:51 +02:00
self.fatal('The configuration failed %r' % ret)
2012-02-26 14:32:30 +01:00
else:
self.end_msg(self.ret_msg(kw['okmsg'], kw), **kw)
2011-09-10 11:13:51 +02:00
return ret
class test_exec(Task.Task):
"""
2016-06-26 00:15:41 +02:00
A task that runs programs after they are built. See :py:func:`waflib.Tools.c_config.test_exec_fun`.
2011-09-10 11:13:51 +02:00
"""
color = 'PINK'
def run(self):
cmd = [self.inputs[0].abspath()] + getattr(self.generator, 'test_args', [])
2011-09-10 11:13:51 +02:00
if getattr(self.generator, 'rpath', None):
if getattr(self.generator, 'define_ret', False):
self.generator.bld.retval = self.generator.bld.cmd_and_log(cmd)
2011-09-10 11:13:51 +02:00
else:
self.generator.bld.retval = self.generator.bld.exec_command(cmd)
2011-09-10 11:13:51 +02:00
else:
env = self.env.env or {}
env.update(dict(os.environ))
for var in ('LD_LIBRARY_PATH', 'DYLD_LIBRARY_PATH', 'PATH'):
env[var] = self.inputs[0].parent.abspath() + os.path.pathsep + env.get(var, '')
if getattr(self.generator, 'define_ret', False):
self.generator.bld.retval = self.generator.bld.cmd_and_log(cmd, env=env)
2011-09-10 11:13:51 +02:00
else:
self.generator.bld.retval = self.generator.bld.exec_command(cmd, env=env)
2011-09-10 11:13:51 +02:00
@feature('test_exec')
@after_method('apply_link')
def test_exec_fun(self):
"""
The feature **test_exec** is used to create a task that will to execute the binary
created (link task output) during the build. The exit status will be set
on the build context, so only one program may have the feature *test_exec*.
This is used by configuration tests::
def configure(conf):
conf.check(execute=True)
"""
self.create_task('test_exec', self.link_task.outputs[0])
@conf
def check_cxx(self, *k, **kw):
2016-06-26 00:15:41 +02:00
"""
Runs a test with a task generator of the form::
conf.check(features='cxx cxxprogram', ...)
"""
2011-09-10 11:13:51 +02:00
kw['compiler'] = 'cxx'
return self.check(*k, **kw)
@conf
def check_cc(self, *k, **kw):
2016-06-26 00:15:41 +02:00
"""
Runs a test with a task generator of the form::
conf.check(features='c cprogram', ...)
"""
2011-09-10 11:13:51 +02:00
kw['compiler'] = 'c'
return self.check(*k, **kw)
@conf
def set_define_comment(self, key, comment):
2016-06-26 00:15:41 +02:00
"""
Sets a comment that will appear in the configuration header
:type key: string
:type comment: string
"""
coms = self.env.DEFINE_COMMENTS
if not coms:
coms = self.env.DEFINE_COMMENTS = {}
coms[key] = comment or ''
@conf
def get_define_comment(self, key):
2016-06-26 00:15:41 +02:00
"""
Returns the comment associated to a define
:type key: string
"""
coms = self.env.DEFINE_COMMENTS or {}
return coms.get(key, '')
@conf
def define(self, key, val, quote=True, comment=''):
2011-09-10 11:13:51 +02:00
"""
2016-06-26 00:15:41 +02:00
Stores a single define and its state into ``conf.env.DEFINES``. The value is cast to an integer (0/1).
2011-09-10 11:13:51 +02:00
:param key: define name
:type key: string
:param val: value
:type val: int or string
:param quote: enclose strings in quotes (yes by default)
:type quote: bool
"""
assert isinstance(key, str)
if not key:
return
if val is True:
val = 1
elif val in (False, None):
val = 0
2011-09-10 11:13:51 +02:00
if isinstance(val, int) or isinstance(val, float):
s = '%s=%s'
else:
s = quote and '%s="%s"' or '%s=%s'
app = s % (key, str(val))
ban = key + '='
2016-06-25 23:54:12 +02:00
lst = self.env.DEFINES
2011-09-10 11:13:51 +02:00
for x in lst:
if x.startswith(ban):
lst[lst.index(x)] = app
break
else:
self.env.append_value('DEFINES', app)
self.env.append_unique(DEFKEYS, key)
self.set_define_comment(key, comment)
2011-09-10 11:13:51 +02:00
@conf
def undefine(self, key, comment=''):
2011-09-10 11:13:51 +02:00
"""
2016-06-26 00:15:41 +02:00
Removes a global define from ``conf.env.DEFINES``
2011-09-10 11:13:51 +02:00
:param key: define name
:type key: string
"""
assert isinstance(key, str)
if not key:
return
2011-09-10 11:13:51 +02:00
ban = key + '='
2016-06-25 23:54:12 +02:00
lst = [x for x in self.env.DEFINES if not x.startswith(ban)]
self.env.DEFINES = lst
2011-09-10 11:13:51 +02:00
self.env.append_unique(DEFKEYS, key)
self.set_define_comment(key, comment)
2011-09-10 11:13:51 +02:00
@conf
def define_cond(self, key, val, comment=''):
2011-09-10 11:13:51 +02:00
"""
2016-06-26 00:15:41 +02:00
Conditionally defines a name::
2011-09-10 11:13:51 +02:00
def configure(conf):
conf.define_cond('A', True)
# equivalent to:
# if val: conf.define('A', 1)
# else: conf.undefine('A')
:param key: define name
:type key: string
:param val: value
:type val: int or string
"""
assert isinstance(key, str)
if not key:
return
2011-09-10 11:13:51 +02:00
if val:
self.define(key, 1, comment=comment)
2011-09-10 11:13:51 +02:00
else:
self.undefine(key, comment=comment)
2011-09-10 11:13:51 +02:00
@conf
def is_defined(self, key):
"""
2016-06-26 00:15:41 +02:00
Indicates whether a particular define is globally set in ``conf.env.DEFINES``.
2011-09-10 11:13:51 +02:00
:param key: define name
:type key: string
:return: True if the define is set
:rtype: bool
"""
assert key and isinstance(key, str)
ban = key + '='
2016-06-25 23:54:12 +02:00
for x in self.env.DEFINES:
2011-09-10 11:13:51 +02:00
if x.startswith(ban):
return True
return False
@conf
def get_define(self, key):
"""
2016-06-26 00:15:41 +02:00
Returns the value of an existing define, or None if not found
2011-09-10 11:13:51 +02:00
:param key: define name
:type key: string
2016-06-26 00:15:41 +02:00
:rtype: string
2011-09-10 11:13:51 +02:00
"""
assert key and isinstance(key, str)
ban = key + '='
2016-06-25 23:54:12 +02:00
for x in self.env.DEFINES:
2011-09-10 11:13:51 +02:00
if x.startswith(ban):
return x[len(ban):]
return None
@conf
def have_define(self, key):
"""
2016-06-26 00:15:41 +02:00
Returns a variable suitable for command-line or header use by removing invalid characters
and prefixing it with ``HAVE_``
2011-09-10 11:13:51 +02:00
:param key: define name
:type key: string
:return: the input key prefixed by *HAVE_* and substitute any invalid characters.
:rtype: string
"""
2012-02-22 20:41:09 +01:00
return (self.env.HAVE_PAT or 'HAVE_%s') % Utils.quote_define_name(key)
2011-09-10 11:13:51 +02:00
@conf
def write_config_header(self, configfile='', guard='', top=False, defines=True, headers=False, remove=True, define_prefix=''):
2011-09-10 11:13:51 +02:00
"""
2016-06-26 00:15:41 +02:00
Writes a configuration header containing defines and includes::
2011-09-10 11:13:51 +02:00
def configure(cnf):
cnf.define('A', 1)
cnf.write_config_header('config.h')
This function only adds include guards (if necessary), consult
:py:func:`waflib.Tools.c_config.get_config_header` for details on the body.
:param configfile: path to the file to create (relative or absolute)
2011-09-10 11:13:51 +02:00
:type configfile: string
2012-08-04 02:26:08 +02:00
:param guard: include guard name to add, by default it is computed from the file name
:type guard: string
2011-09-10 11:13:51 +02:00
:param top: write the configuration header from the build directory (default is from the current path)
:type top: bool
:param defines: add the defines (yes by default)
:type defines: bool
:param headers: add #include in the file
:type headers: bool
2012-08-04 02:26:08 +02:00
:param remove: remove the defines after they are added (yes by default, works like in autoconf)
:type remove: bool
2012-04-16 19:54:19 +02:00
:type define_prefix: string
:param define_prefix: prefix all the defines in the file with a particular prefix
2011-09-10 11:13:51 +02:00
"""
if not configfile:
configfile = WAF_CONFIG_H
waf_guard = guard or 'W_%s_WAF' % Utils.quote_define_name(configfile)
2011-09-10 11:13:51 +02:00
node = top and self.bldnode or self.path.get_bld()
node = node.make_node(configfile)
node.parent.mkdir()
lst = ['/* WARNING! All changes made to this file will be lost! */\n']
lst.append('#ifndef %s\n#define %s\n' % (waf_guard, waf_guard))
2012-02-26 13:25:21 +01:00
lst.append(self.get_config_header(defines, headers, define_prefix=define_prefix))
2011-09-10 11:13:51 +02:00
lst.append('\n#endif /* %s */\n' % waf_guard)
node.write('\n'.join(lst))
# config files must not be removed on "waf clean"
2012-05-16 08:29:13 +02:00
self.env.append_unique(Build.CFG_FILES, [node.abspath()])
2011-09-10 11:13:51 +02:00
if remove:
for key in self.env[DEFKEYS]:
self.undefine(key)
self.env[DEFKEYS] = []
@conf
2012-02-26 13:25:21 +01:00
def get_config_header(self, defines=True, headers=False, define_prefix=''):
2011-09-10 11:13:51 +02:00
"""
2016-06-26 00:15:41 +02:00
Creates the contents of a ``config.h`` file from the defines and includes
2011-09-10 11:13:51 +02:00
set in conf.env.define_key / conf.env.include_key. No include guards are added.
A prelude will be added from the variable env.WAF_CONFIG_H_PRELUDE if provided. This
can be used to insert complex macros or include guards::
def configure(conf):
conf.env.WAF_CONFIG_H_PRELUDE = '#include <unistd.h>\\n'
conf.write_config_header('config.h')
2011-09-10 11:13:51 +02:00
:param defines: write the defines values
:type defines: bool
:param headers: write include entries for each element in self.env.INCKEYS
2011-09-10 11:13:51 +02:00
:type headers: bool
2012-04-16 19:54:19 +02:00
:type define_prefix: string
:param define_prefix: prefix all the defines with a particular prefix
2011-09-10 11:13:51 +02:00
:return: the contents of a ``config.h`` file
:rtype: string
"""
lst = []
if self.env.WAF_CONFIG_H_PRELUDE:
lst.append(self.env.WAF_CONFIG_H_PRELUDE)
2011-09-10 11:13:51 +02:00
if headers:
for x in self.env[INCKEYS]:
lst.append('#include <%s>' % x)
if defines:
tbl = {}
2016-06-25 23:54:12 +02:00
for k in self.env.DEFINES:
a, _, b = k.partition('=')
tbl[a] = b
for k in self.env[DEFKEYS]:
caption = self.get_define_comment(k)
if caption:
caption = ' /* %s */' % caption
try:
txt = '#define %s%s %s%s' % (define_prefix, k, tbl[k], caption)
except KeyError:
txt = '/* #undef %s%s */%s' % (define_prefix, k, caption)
lst.append(txt)
2011-09-10 11:13:51 +02:00
return "\n".join(lst)
@conf
def cc_add_flags(conf):
"""
2016-06-26 00:15:41 +02:00
Adds CFLAGS / CPPFLAGS from os.environ to conf.env
2011-09-10 11:13:51 +02:00
"""
conf.add_os_flags('CPPFLAGS', dup=False)
conf.add_os_flags('CFLAGS', dup=False)
2011-09-10 11:13:51 +02:00
@conf
def cxx_add_flags(conf):
"""
2016-06-26 00:15:41 +02:00
Adds CXXFLAGS / CPPFLAGS from os.environ to conf.env
2011-09-10 11:13:51 +02:00
"""
conf.add_os_flags('CPPFLAGS', dup=False)
conf.add_os_flags('CXXFLAGS', dup=False)
2011-09-10 11:13:51 +02:00
@conf
def link_add_flags(conf):
"""
2016-06-26 00:15:41 +02:00
Adds LINKFLAGS / LDFLAGS from os.environ to conf.env
2011-09-10 11:13:51 +02:00
"""
conf.add_os_flags('LINKFLAGS', dup=False)
conf.add_os_flags('LDFLAGS', dup=False)
2011-09-10 11:13:51 +02:00
@conf
def cc_load_tools(conf):
"""
2016-06-26 00:15:41 +02:00
Loads the Waf c extensions
2011-09-10 11:13:51 +02:00
"""
if not conf.env.DEST_OS:
conf.env.DEST_OS = Utils.unversioned_sys_platform()
conf.load('c')
@conf
def cxx_load_tools(conf):
"""
2016-06-26 00:15:41 +02:00
Loads the Waf c++ extensions
2011-09-10 11:13:51 +02:00
"""
if not conf.env.DEST_OS:
conf.env.DEST_OS = Utils.unversioned_sys_platform()
conf.load('cxx')
@conf
def get_cc_version(conf, cc, gcc=False, icc=False, clang=False):
2011-09-10 11:13:51 +02:00
"""
2016-06-26 00:15:41 +02:00
Runs the preprocessor to determine the gcc/icc/clang version
2011-09-10 11:13:51 +02:00
The variables CC_VERSION, DEST_OS, DEST_BINFMT and DEST_CPU will be set in *conf.env*
2016-06-26 00:15:41 +02:00
:raise: :py:class:`waflib.Errors.ConfigurationError`
2011-09-10 11:13:51 +02:00
"""
cmd = cc + ['-dM', '-E', '-']
env = conf.env.env or None
2011-09-10 11:13:51 +02:00
try:
out, err = conf.cmd_and_log(cmd, output=0, input='\n'.encode(), env=env)
except Errors.WafError:
conf.fatal('Could not determine the compiler version %r' % cmd)
2011-09-10 11:13:51 +02:00
if gcc:
if out.find('__INTEL_COMPILER') >= 0:
conf.fatal('The intel compiler pretends to be gcc')
if out.find('__GNUC__') < 0 and out.find('__clang__') < 0:
2011-09-10 11:13:51 +02:00
conf.fatal('Could not determine the compiler type')
if icc and out.find('__INTEL_COMPILER') < 0 and out.find('__INTEL_CLANG_COMPILER') < 0:
conf.fatal('Not icc/icx/icpc/icpx')
2011-09-10 11:13:51 +02:00
if clang and out.find('__clang__') < 0:
conf.fatal('Not clang/clang++')
if not clang and out.find('__clang__') >= 0:
conf.fatal('Could not find gcc/g++ (only Clang), if renamed try eg: CC=gcc48 CXX=g++48 waf configure')
2011-09-10 11:13:51 +02:00
k = {}
if icc or gcc or clang:
2012-02-06 02:13:25 +01:00
out = out.splitlines()
2011-09-10 11:13:51 +02:00
for line in out:
lst = shlex.split(line)
if len(lst)>2:
key = lst[1]
val = lst[2]
k[key] = val
def isD(var):
return var in k
# Some documentation is available at http://predef.sourceforge.net
# The names given to DEST_OS must match what Utils.unversioned_sys_platform() returns.
if not conf.env.DEST_OS:
conf.env.DEST_OS = ''
for i in MACRO_TO_DESTOS:
if isD(i):
conf.env.DEST_OS = MACRO_TO_DESTOS[i]
break
else:
if isD('__APPLE__') and isD('__MACH__'):
conf.env.DEST_OS = 'darwin'
elif isD('__unix__'): # unix must be tested last as it's a generic fallback
conf.env.DEST_OS = 'generic'
if isD('__ELF__'):
conf.env.DEST_BINFMT = 'elf'
elif isD('__WINNT__') or isD('__CYGWIN__') or isD('_WIN32'):
2011-09-10 11:13:51 +02:00
conf.env.DEST_BINFMT = 'pe'
if not conf.env.IMPLIBDIR:
conf.env.IMPLIBDIR = conf.env.LIBDIR # for .lib or .dll.a files
2013-05-08 23:18:53 +02:00
conf.env.LIBDIR = conf.env.BINDIR
2011-09-10 11:13:51 +02:00
elif isD('__APPLE__'):
conf.env.DEST_BINFMT = 'mac-o'
if not conf.env.DEST_BINFMT:
# Infer the binary format from the os name.
conf.env.DEST_BINFMT = Utils.destos_to_binfmt(conf.env.DEST_OS)
for i in MACRO_TO_DEST_CPU:
if isD(i):
conf.env.DEST_CPU = MACRO_TO_DEST_CPU[i]
break
Logs.debug('ccroot: dest platform: ' + ' '.join([conf.env[x] or '?' for x in ('DEST_OS', 'DEST_BINFMT', 'DEST_CPU')]))
if icc:
if isD('__INTEL_CLANG_COMPILER'):
# 20230100
ver = k['__INTEL_CLANG_COMPILER']
conf.env.CC_VERSION = (ver[:4], ver[4:6], ver[-2:])
else:
ver = k['__INTEL_COMPILER']
conf.env.CC_VERSION = (ver[:-2], ver[-2], ver[-1])
2011-09-10 11:13:51 +02:00
else:
2015-08-07 10:51:45 +02:00
if isD('__clang__') and isD('__clang_major__'):
2016-06-25 23:54:12 +02:00
conf.env.CC_VERSION = (k['__clang_major__'], k['__clang_minor__'], k['__clang_patchlevel__'])
2012-08-23 17:33:36 +02:00
else:
2015-08-07 10:51:45 +02:00
# older clang versions and gcc
2016-06-25 23:54:12 +02:00
conf.env.CC_VERSION = (k['__GNUC__'], k['__GNUC_MINOR__'], k.get('__GNUC_PATCHLEVEL__', '0'))
2011-09-10 11:13:51 +02:00
return k
@conf
def get_xlc_version(conf, cc):
2016-06-26 00:15:41 +02:00
"""
Returns the Aix compiler version
2011-09-10 11:13:51 +02:00
2016-06-26 00:15:41 +02:00
:raise: :py:class:`waflib.Errors.ConfigurationError`
"""
2011-09-10 11:13:51 +02:00
cmd = cc + ['-qversion']
try:
out, err = conf.cmd_and_log(cmd, output=0)
except Errors.WafError:
conf.fatal('Could not find xlc %r' % cmd)
2012-07-05 11:24:46 +02:00
# the intention is to catch the 8.0 in "IBM XL C/C++ Enterprise Edition V8.0 for AIX..."
2012-12-05 00:40:50 +01:00
for v in (r"IBM XL C/C\+\+.* V(?P<major>\d*)\.(?P<minor>\d*)",):
version_re = re.compile(v, re.I).search
2012-07-05 11:24:46 +02:00
match = version_re(out or err)
if match:
k = match.groupdict()
2016-06-25 23:54:12 +02:00
conf.env.CC_VERSION = (k['major'], k['minor'])
2012-07-05 11:24:46 +02:00
break
else:
2011-09-10 11:13:51 +02:00
conf.fatal('Could not determine the XLC version.')
@conf
def get_suncc_version(conf, cc):
2016-06-26 00:15:41 +02:00
"""
Returns the Sun compiler version
2016-06-26 00:15:41 +02:00
:raise: :py:class:`waflib.Errors.ConfigurationError`
"""
cmd = cc + ['-V']
try:
out, err = conf.cmd_and_log(cmd, output=0)
2013-11-08 22:46:51 +01:00
except Errors.WafError as e:
# Older versions of the compiler exit with non-zero status when reporting their version
if not (hasattr(e, 'returncode') and hasattr(e, 'stdout') and hasattr(e, 'stderr')):
conf.fatal('Could not find suncc %r' % cmd)
out = e.stdout
err = e.stderr
version = (out or err)
2015-03-03 12:19:25 +01:00
version = version.splitlines()[0]
2016-01-16 21:13:38 +01:00
# cc: Sun C 5.10 SunOS_i386 2009/06/03
# cc: Studio 12.5 Sun C++ 5.14 SunOS_sparc Beta 2015/11/17
2016-02-04 06:42:43 +01:00
# cc: WorkShop Compilers 5.0 98/12/15 C 5.0
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
match = version_re(version)
if match:
k = match.groupdict()
2016-06-25 23:54:12 +02:00
conf.env.CC_VERSION = (k['major'], k['minor'])
else:
conf.fatal('Could not determine the suncc version.')
2011-09-10 11:13:51 +02:00
# ============ parallel configuration
class cfgtask(Task.Task):
2011-09-10 11:13:51 +02:00
"""
2016-06-26 00:15:41 +02:00
A task that executes build configuration tests (calls conf.check)
2011-09-10 11:13:51 +02:00
2016-06-26 00:15:41 +02:00
Make sure to use locks if concurrent access to the same conf.env data is necessary.
2011-09-10 11:13:51 +02:00
"""
def __init__(self, *k, **kw):
Task.Task.__init__(self, *k, **kw)
self.run_after = set()
2011-09-10 11:13:51 +02:00
def display(self):
return ''
def runnable_status(self):
for x in self.run_after:
if not x.hasrun:
return Task.ASK_LATER
2011-09-10 11:13:51 +02:00
return Task.RUN_ME
2012-02-11 14:43:07 +01:00
def uid(self):
return Utils.SIG_NIL
def signature(self):
return Utils.SIG_NIL
2011-09-10 11:13:51 +02:00
def run(self):
conf = self.conf
bld = Build.BuildContext(top_dir=conf.srcnode.abspath(), out_dir=conf.bldnode.abspath())
bld.env = conf.env
bld.init_dirs()
bld.in_msg = 1 # suppress top-level start_msg
bld.logger = self.logger
bld.multicheck_task = self
args = self.args
2011-09-10 11:13:51 +02:00
try:
if 'func' in args:
bld.test(build_fun=args['func'],
msg=args.get('msg', ''),
okmsg=args.get('okmsg', ''),
errmsg=args.get('errmsg', ''),
)
else:
args['multicheck_mandatory'] = args.get('mandatory', True)
args['mandatory'] = True
try:
bld.check(**args)
finally:
args['mandatory'] = args['multicheck_mandatory']
2012-02-11 14:49:27 +01:00
except Exception:
2011-09-10 11:13:51 +02:00
return 1
def process(self):
Task.Task.process(self)
if 'msg' in self.args:
with self.generator.bld.multicheck_lock:
self.conf.start_msg(self.args['msg'])
if self.hasrun == Task.NOT_RUN:
self.conf.end_msg('test cancelled', 'YELLOW')
elif self.hasrun != Task.SUCCESS:
self.conf.end_msg(self.args.get('errmsg', 'no'), 'YELLOW')
else:
self.conf.end_msg(self.args.get('okmsg', 'yes'), 'GREEN')
2011-09-10 11:13:51 +02:00
@conf
def multicheck(self, *k, **kw):
"""
Runs configuration tests in parallel; results are printed sequentially at the end of the build
but each test must provide its own msg value to display a line::
def test_build(ctx):
ctx.in_msg = True # suppress console outputs
ctx.check_large_file(mandatory=False)
conf.multicheck(
2016-08-21 15:31:47 +02:00
{'header_name':'stdio.h', 'msg':'... stdio', 'uselib_store':'STDIO', 'global_define':False},
{'header_name':'xyztabcd.h', 'msg':'... optional xyztabcd.h', 'mandatory': False},
{'header_name':'stdlib.h', 'msg':'... stdlib', 'okmsg': 'aye', 'errmsg': 'nope'},
{'func': test_build, 'msg':'... testing an arbitrary build function', 'okmsg':'ok'},
msg = 'Checking for headers in parallel',
2016-08-21 15:31:47 +02:00
mandatory = True, # mandatory tests raise an error at the end
run_all_tests = True, # try running all tests
)
2016-08-21 15:31:47 +02:00
The configuration tests may modify the values in conf.env in any order, and the define
values can affect configuration tests being executed. It is hence recommended
to provide `uselib_store` values with `global_define=False` to prevent such issues.
2011-09-10 11:13:51 +02:00
"""
self.start_msg(kw.get('msg', 'Executing %d configuration tests' % len(k)), **kw)
2011-09-10 11:13:51 +02:00
# Force a copy so that threads append to the same list at least
# no order is guaranteed, but the values should not disappear at least
for var in ('DEFINES', DEFKEYS):
self.env.append_value(var, [])
self.env.DEFINE_COMMENTS = self.env.DEFINE_COMMENTS or {}
# define a task object that will execute our tests
2011-09-10 11:13:51 +02:00
class par(object):
def __init__(self):
self.keep = False
2012-02-11 14:43:07 +01:00
self.task_sigs = {}
2015-05-23 15:09:37 +02:00
self.progress_bar = 0
2011-09-10 11:13:51 +02:00
def total(self):
return len(tasks)
def to_log(self, *k, **kw):
return
bld = par()
bld.keep = kw.get('run_all_tests', True)
2017-08-25 20:22:46 +02:00
bld.imp_sigs = {}
2011-09-10 11:13:51 +02:00
tasks = []
id_to_task = {}
2019-12-14 23:49:03 +01:00
for counter, dct in enumerate(k):
x = Task.classes['cfgtask'](bld=bld, env=None)
2011-09-10 11:13:51 +02:00
tasks.append(x)
x.args = dct
2019-12-14 23:49:03 +01:00
x.args['multicheck_counter'] = counter
2011-09-10 11:13:51 +02:00
x.bld = bld
x.conf = self
x.args = dct
# bind a logger that will keep the info in memory
x.logger = Logs.make_mem_logger(str(id(x)), self.logger)
if 'id' in dct:
id_to_task[dct['id']] = x
# second pass to set dependencies with after_test/before_test
for x in tasks:
for key in Utils.to_list(x.args.get('before_tests', [])):
tsk = id_to_task[key]
if not tsk:
raise ValueError('No test named %r' % key)
tsk.run_after.add(x)
for key in Utils.to_list(x.args.get('after_tests', [])):
tsk = id_to_task[key]
if not tsk:
raise ValueError('No test named %r' % key)
x.run_after.add(tsk)
2011-09-10 11:13:51 +02:00
def it():
yield tasks
while 1:
yield []
bld.producer = p = Runner.Parallel(bld, Options.options.jobs)
bld.multicheck_lock = Utils.threading.Lock()
2011-09-10 11:13:51 +02:00
p.biter = it()
self.end_msg('started')
2011-09-10 11:13:51 +02:00
p.start()
# flush the logs in order into the config.log
for x in tasks:
x.logger.memhandler.flush()
self.start_msg('-> processing test results')
2015-05-23 15:09:37 +02:00
if p.error:
for x in p.error:
if getattr(x, 'err_msg', None):
self.to_log(x.err_msg)
self.end_msg('fail', color='RED')
raise Errors.WafError('There is an error in the library, read config.log for more information')
failure_count = 0
2011-09-10 11:13:51 +02:00
for x in tasks:
if x.hasrun not in (Task.SUCCESS, Task.NOT_RUN):
failure_count += 1
if failure_count:
self.end_msg(kw.get('errmsg', '%s test failed' % failure_count), color='YELLOW', **kw)
else:
self.end_msg('all ok', **kw)
2011-09-10 11:13:51 +02:00
for x in tasks:
if x.hasrun != Task.SUCCESS:
if x.args.get('mandatory', True):
self.fatal(kw.get('fatalmsg') or 'One of the tests has failed, read config.log for more information')
2011-09-10 11:13:51 +02:00
2017-07-30 17:22:04 +02:00
@conf
def check_gcc_o_space(self, mode='c'):
if int(self.env.CC_VERSION[0]) > 4:
# this is for old compilers
return
self.env.stash()
if mode == 'c':
self.env.CCLNK_TGT_F = ['-o', '']
elif mode == 'cxx':
self.env.CXXLNK_TGT_F = ['-o', '']
features = '%s %sshlib' % (mode, mode)
try:
self.check(msg='Checking if the -o link must be split from arguments', fragment=SNIP_EMPTY_PROGRAM, features=features)
except self.errors.ConfigurationError:
self.env.revert()
else:
self.env.commit()