Merge pull request #1511 from centricular/get-define
New compiler function: cc.get_define()
This commit is contained in:
commit
b42adc8a54
|
@ -550,11 +550,11 @@ class Compiler:
|
|||
def get_exelist(self):
|
||||
return self.exelist[:]
|
||||
|
||||
def get_define(self, *args, **kwargs):
|
||||
raise EnvironmentException('%s does not support get_define.' % self.id)
|
||||
def get_builtin_define(self, *args, **kwargs):
|
||||
raise EnvironmentException('%s does not support get_builtin_define.' % self.id)
|
||||
|
||||
def has_define(self, *args, **kwargs):
|
||||
raise EnvironmentException('%s does not support has_define.' % self.id)
|
||||
def has_builtin_define(self, *args, **kwargs):
|
||||
raise EnvironmentException('%s does not support has_builtin_define.' % self.id)
|
||||
|
||||
def get_always_args(self):
|
||||
return []
|
||||
|
@ -906,8 +906,6 @@ class CCompiler(Compiler):
|
|||
return self.sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code)
|
||||
|
||||
def has_header(self, hname, prefix, env, extra_args=None, dependencies=None):
|
||||
if extra_args is None:
|
||||
extra_args = []
|
||||
fargs = {'prefix': prefix, 'header': hname}
|
||||
code = '''{prefix}
|
||||
#ifdef __has_include
|
||||
|
@ -921,8 +919,6 @@ class CCompiler(Compiler):
|
|||
dependencies, 'preprocess')
|
||||
|
||||
def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None):
|
||||
if extra_args is None:
|
||||
extra_args = []
|
||||
fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol}
|
||||
t = '''{prefix}
|
||||
#include <{header}>
|
||||
|
@ -934,7 +930,7 @@ class CCompiler(Compiler):
|
|||
}}'''
|
||||
return self.compiles(t.format(**fargs), env, extra_args, dependencies)
|
||||
|
||||
def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile'):
|
||||
def _get_compiler_check_args(self, env, extra_args, dependencies, mode='compile'):
|
||||
if extra_args is None:
|
||||
extra_args = []
|
||||
elif isinstance(extra_args, str):
|
||||
|
@ -943,49 +939,43 @@ class CCompiler(Compiler):
|
|||
dependencies = []
|
||||
elif not isinstance(dependencies, list):
|
||||
dependencies = [dependencies]
|
||||
# Add compile flags needed by dependencies
|
||||
# Collect compiler arguments
|
||||
args = CompilerArgs(self)
|
||||
for d in dependencies:
|
||||
# Add compile flags needed by dependencies
|
||||
args += d.get_compile_args()
|
||||
if mode == 'link':
|
||||
# Add link flags needed to find dependencies
|
||||
args += d.get_link_args()
|
||||
# Select a CRT if needed since we're linking
|
||||
if mode == 'link':
|
||||
args += self.get_linker_debug_crt_args()
|
||||
# Read c_args/cpp_args/etc from the cross-info file (if needed)
|
||||
args += self.get_cross_extra_flags(env, compile=True, link=False)
|
||||
# Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS from the env
|
||||
# We assume that the user has ensured these are compiler-specific
|
||||
args += env.coredata.external_args[self.language]
|
||||
args += self.get_cross_extra_flags(env, compile=(mode != 'preprocess'),
|
||||
link=(mode == 'link'))
|
||||
if mode == 'preprocess':
|
||||
# Add CPPFLAGS from the env.
|
||||
args += env.coredata.external_preprocess_args[self.language]
|
||||
elif mode == 'compile':
|
||||
# Add CFLAGS/CXXFLAGS/OBJCFLAGS/OBJCXXFLAGS from the env
|
||||
args += env.coredata.external_args[self.language]
|
||||
elif mode == 'link':
|
||||
# Add LDFLAGS from the env
|
||||
args += env.coredata.external_link_args[self.language]
|
||||
args += self.get_compiler_check_args()
|
||||
# extra_args must override all other arguments, so we add them last
|
||||
args += extra_args
|
||||
return args
|
||||
|
||||
def compiles(self, code, env, extra_args=None, dependencies=None, mode='compile'):
|
||||
args = self._get_compiler_check_args(env, extra_args, dependencies, mode)
|
||||
# We only want to compile; not link
|
||||
with self.compile(code, args.to_native(), mode) as p:
|
||||
return p.returncode == 0
|
||||
|
||||
def _links_wrapper(self, code, env, extra_args, dependencies):
|
||||
"Shares common code between self.links and self.run"
|
||||
if extra_args is None:
|
||||
extra_args = []
|
||||
elif isinstance(extra_args, str):
|
||||
extra_args = [extra_args]
|
||||
if dependencies is None:
|
||||
dependencies = []
|
||||
elif not isinstance(dependencies, list):
|
||||
dependencies = [dependencies]
|
||||
# Add compile and link flags needed by dependencies
|
||||
args = CompilerArgs(self)
|
||||
for d in dependencies:
|
||||
args += d.get_compile_args()
|
||||
args += d.get_link_args()
|
||||
# Select a CRT if needed since we're linking
|
||||
args += self.get_linker_debug_crt_args()
|
||||
# Read c_args/c_link_args/cpp_args/cpp_link_args/etc from the
|
||||
# cross-info file (if needed)
|
||||
args += self.get_cross_extra_flags(env, compile=True, link=True)
|
||||
# Add LDFLAGS from the env. We assume that the user has ensured these
|
||||
# are compiler-specific
|
||||
args += env.coredata.external_link_args[self.language]
|
||||
# Add compiler check args such that they override
|
||||
args += self.get_compiler_check_args()
|
||||
# extra_args must override all other arguments, so we add them last
|
||||
args += extra_args
|
||||
args = self._get_compiler_check_args(env, extra_args, dependencies, mode='link')
|
||||
return self.compile(code, args.to_native())
|
||||
|
||||
def links(self, code, env, extra_args=None, dependencies=None):
|
||||
|
@ -1141,6 +1131,24 @@ class CCompiler(Compiler):
|
|||
raise EnvironmentException('Could not determine alignment of %s. Sorry. You might want to file a bug.' % typename)
|
||||
return align
|
||||
|
||||
def get_define(self, dname, prefix, env, extra_args, dependencies):
|
||||
delim = '"MESON_GET_DEFINE_DELIMITER"'
|
||||
fargs = {'prefix': prefix, 'define': dname, 'delim': delim}
|
||||
code = '''
|
||||
#ifndef {define}
|
||||
# define {define}
|
||||
#endif
|
||||
{prefix}
|
||||
{delim}\n{define}'''
|
||||
args = self._get_compiler_check_args(env, extra_args, dependencies,
|
||||
mode='preprocess').to_native()
|
||||
with self.compile(code.format(**fargs), args, 'preprocess') as p:
|
||||
if p.returncode != 0:
|
||||
raise EnvironmentException('Could not get define {!r}'.format(dname))
|
||||
# Get the preprocessed value after the delimiter,
|
||||
# minus the extra newline at the end
|
||||
return p.stdo.split(delim + '\n')[-1][:-1]
|
||||
|
||||
@staticmethod
|
||||
def _no_prototype_templ():
|
||||
"""
|
||||
|
@ -2382,10 +2390,10 @@ class GnuCompiler:
|
|||
args[args.index('-Wpedantic')] = '-pedantic'
|
||||
return args
|
||||
|
||||
def has_define(self, define):
|
||||
def has_builtin_define(self, define):
|
||||
return define in self.defines
|
||||
|
||||
def get_define(self, define):
|
||||
def get_builtin_define(self, define):
|
||||
if define in self.defines:
|
||||
return self.defines[define]
|
||||
|
||||
|
@ -2896,10 +2904,10 @@ class GnuFortranCompiler(FortranCompiler):
|
|||
self.defines = defines or {}
|
||||
self.id = 'gcc'
|
||||
|
||||
def has_define(self, define):
|
||||
def has_builtin_define(self, define):
|
||||
return define in self.defines
|
||||
|
||||
def get_define(self, define):
|
||||
def get_builtin_define(self, define):
|
||||
if define in self.defines:
|
||||
return self.defines[define]
|
||||
|
||||
|
|
|
@ -148,10 +148,11 @@ class CoreData:
|
|||
self.user_options = {}
|
||||
self.compiler_options = {}
|
||||
self.base_options = {}
|
||||
# These two, external_*args, are set via env vars CFLAGS, LDFLAGS, etc
|
||||
# These external_*args, are set via env vars CFLAGS, LDFLAGS, etc
|
||||
# but only when not cross-compiling.
|
||||
self.external_args = {}
|
||||
self.external_link_args = {}
|
||||
self.external_preprocess_args = {} # CPPFLAGS only
|
||||
self.external_args = {} # CPPFLAGS + CFLAGS
|
||||
self.external_link_args = {} # CFLAGS + LDFLAGS (with MSVC: only LDFLAGS)
|
||||
if options.cross_file is not None:
|
||||
self.cross_file = os.path.join(os.getcwd(), options.cross_file)
|
||||
else:
|
||||
|
|
|
@ -102,7 +102,7 @@ def detect_windows_arch(compilers):
|
|||
platform = os.environ.get('Platform', 'x86').lower()
|
||||
if platform == 'x86':
|
||||
return platform
|
||||
if compiler.id == 'gcc' and compiler.has_define('__i386__'):
|
||||
if compiler.id == 'gcc' and compiler.has_builtin_define('__i386__'):
|
||||
return 'x86'
|
||||
return os_arch
|
||||
|
||||
|
@ -129,10 +129,10 @@ def detect_cpu_family(compilers):
|
|||
# to know is to check the compiler defines.
|
||||
for c in compilers.values():
|
||||
try:
|
||||
if c.has_define('__i386__'):
|
||||
if c.has_builtin_define('__i386__'):
|
||||
return 'x86'
|
||||
except mesonlib.MesonException:
|
||||
# Ignore compilers that do not support has_define.
|
||||
# Ignore compilers that do not support has_builtin_define.
|
||||
pass
|
||||
return 'x86_64'
|
||||
# Add fixes here as bugs are reported.
|
||||
|
@ -149,7 +149,7 @@ def detect_cpu(compilers):
|
|||
# Same check as above for cpu_family
|
||||
for c in compilers.values():
|
||||
try:
|
||||
if c.has_define('__i386__'):
|
||||
if c.has_builtin_define('__i386__'):
|
||||
return 'i686' # All 64 bit cpus have at least this level of x86 support.
|
||||
except mesonlib.MesonException:
|
||||
pass
|
||||
|
@ -770,7 +770,7 @@ def get_args_from_envvars(compiler):
|
|||
compiler_is_linker = (compiler.get_exelist() == compiler.get_linker_exelist())
|
||||
|
||||
if lang not in ('c', 'cpp', 'objc', 'objcpp', 'fortran', 'd'):
|
||||
return [], []
|
||||
return [], [], []
|
||||
|
||||
# Compile flags
|
||||
cflags_mapping = {'c': 'CFLAGS',
|
||||
|
@ -781,12 +781,12 @@ def get_args_from_envvars(compiler):
|
|||
'd': 'DFLAGS'}
|
||||
compile_flags = os.environ.get(cflags_mapping[lang], '')
|
||||
log_var(cflags_mapping[lang], compile_flags)
|
||||
compile_flags = compile_flags.split()
|
||||
compile_flags = shlex.split(compile_flags)
|
||||
|
||||
# Link flags (same for all languages)
|
||||
link_flags = os.environ.get('LDFLAGS', '')
|
||||
log_var('LDFLAGS', link_flags)
|
||||
link_flags = link_flags.split()
|
||||
link_flags = shlex.split(link_flags)
|
||||
if compiler_is_linker:
|
||||
# When the compiler is used as a wrapper around the linker (such as
|
||||
# with GCC and Clang), the compile flags can be needed while linking
|
||||
|
@ -794,14 +794,15 @@ def get_args_from_envvars(compiler):
|
|||
# this when the linker is stand-alone such as with MSVC C/C++, etc.
|
||||
link_flags = compile_flags + link_flags
|
||||
|
||||
# Pre-processof rlags (not for fortran)
|
||||
# Pre-processor flags (not for fortran or D)
|
||||
preproc_flags = ''
|
||||
if lang in ('c', 'cpp', 'objc', 'objcpp'):
|
||||
preproc_flags = os.environ.get('CPPFLAGS', '')
|
||||
log_var('CPPFLAGS', preproc_flags)
|
||||
compile_flags += preproc_flags.split()
|
||||
preproc_flags = shlex.split(preproc_flags)
|
||||
compile_flags += preproc_flags
|
||||
|
||||
return compile_flags, link_flags
|
||||
return preproc_flags, compile_flags, link_flags
|
||||
|
||||
class CrossBuildInfo:
|
||||
def __init__(self, filename):
|
||||
|
|
|
@ -634,6 +634,7 @@ class CompilerHolder(InterpreterObject):
|
|||
'get_id': self.get_id_method,
|
||||
'compute_int': self.compute_int_method,
|
||||
'sizeof': self.sizeof_method,
|
||||
'get_define': self.get_define_method,
|
||||
'has_header': self.has_header_method,
|
||||
'has_header_symbol': self.has_header_symbol_method,
|
||||
'run': self.run_method,
|
||||
|
@ -865,6 +866,20 @@ class CompilerHolder(InterpreterObject):
|
|||
mlog.log('Checking for size of "%s": %d' % (element, esize))
|
||||
return esize
|
||||
|
||||
def get_define_method(self, args, kwargs):
|
||||
if len(args) != 1:
|
||||
raise InterpreterException('get_define() takes exactly one argument.')
|
||||
check_stringlist(args)
|
||||
element = args[0]
|
||||
prefix = kwargs.get('prefix', '')
|
||||
if not isinstance(prefix, str):
|
||||
raise InterpreterException('Prefix argument of get_define() must be a string.')
|
||||
extra_args = self.determine_args(kwargs)
|
||||
deps = self.determine_dependencies(kwargs)
|
||||
value = self.compiler.get_define(element, prefix, self.environment, extra_args, deps)
|
||||
mlog.log('Checking for value of define "%s": %s' % (element, value))
|
||||
return value
|
||||
|
||||
def compiles_method(self, args, kwargs):
|
||||
if len(args) != 1:
|
||||
raise InterpreterException('compiles method takes exactly one argument.')
|
||||
|
@ -1615,25 +1630,29 @@ class Interpreter(InterpreterBase):
|
|||
def func_project(self, node, args, kwargs):
|
||||
if len(args) < 1:
|
||||
raise InvalidArguments('Not enough arguments to project(). Needs at least the project name.')
|
||||
proj_name = args[0]
|
||||
proj_langs = args[1:]
|
||||
if ':' in proj_name:
|
||||
raise InvalidArguments("Project name {!r} must not contain ':'".format(proj_name))
|
||||
default_options = kwargs.get('default_options', [])
|
||||
if self.environment.first_invocation and (len(default_options) > 0 or
|
||||
len(self.default_project_options) > 0):
|
||||
self.parse_default_options(default_options)
|
||||
if not self.is_subproject():
|
||||
self.build.project_name = args[0]
|
||||
self.build.project_name = proj_name
|
||||
if os.path.exists(self.option_file):
|
||||
oi = optinterpreter.OptionInterpreter(self.subproject,
|
||||
self.build.environment.cmd_line_options.projectoptions,
|
||||
)
|
||||
oi.process(self.option_file)
|
||||
self.build.environment.merge_options(oi.options)
|
||||
self.active_projectname = args[0]
|
||||
self.active_projectname = proj_name
|
||||
self.project_version = kwargs.get('version', 'undefined')
|
||||
if self.build.project_version is None:
|
||||
self.build.project_version = self.project_version
|
||||
proj_license = mesonlib.stringlistify(kwargs.get('license', 'unknown'))
|
||||
self.build.dep_manifest[args[0]] = {'version': self.project_version,
|
||||
'license': proj_license}
|
||||
self.build.dep_manifest[proj_name] = {'version': self.project_version,
|
||||
'license': proj_license}
|
||||
if self.subproject in self.build.projects:
|
||||
raise InvalidCode('Second call to project().')
|
||||
if not self.is_subproject() and 'subproject_dir' in kwargs:
|
||||
|
@ -1644,9 +1663,9 @@ class Interpreter(InterpreterBase):
|
|||
pv = kwargs['meson_version']
|
||||
if not mesonlib.version_compare(cv, pv):
|
||||
raise InterpreterException('Meson version is %s but project requires %s.' % (cv, pv))
|
||||
self.build.projects[self.subproject] = args[0]
|
||||
mlog.log('Project name: ', mlog.bold(args[0]), sep='')
|
||||
self.add_languages(args[1:], True)
|
||||
self.build.projects[self.subproject] = proj_name
|
||||
mlog.log('Project name: ', mlog.bold(proj_name), sep='')
|
||||
self.add_languages(proj_langs, True)
|
||||
langs = self.coredata.compilers.keys()
|
||||
if 'vala' in langs:
|
||||
if 'c' not in langs:
|
||||
|
@ -1772,9 +1791,10 @@ class Interpreter(InterpreterBase):
|
|||
raise
|
||||
mlog.log('Native %s compiler: ' % lang, mlog.bold(' '.join(comp.get_exelist())), ' (%s %s)' % (comp.id, comp.version), sep='')
|
||||
if not comp.get_language() in self.coredata.external_args:
|
||||
(ext_compile_args, ext_link_args) = environment.get_args_from_envvars(comp)
|
||||
self.coredata.external_args[comp.get_language()] = ext_compile_args
|
||||
self.coredata.external_link_args[comp.get_language()] = ext_link_args
|
||||
(preproc_args, compile_args, link_args) = environment.get_args_from_envvars(comp)
|
||||
self.coredata.external_preprocess_args[comp.get_language()] = preproc_args
|
||||
self.coredata.external_args[comp.get_language()] = compile_args
|
||||
self.coredata.external_link_args[comp.get_language()] = link_args
|
||||
self.build.add_compiler(comp)
|
||||
if need_cross_compiler:
|
||||
mlog.log('Cross %s compiler: ' % lang, mlog.bold(' '.join(cross_comp.get_exelist())), ' (%s %s)' % (cross_comp.id, cross_comp.version), sep='')
|
||||
|
|
|
@ -75,15 +75,16 @@ class OptionInterpreter:
|
|||
self.cmd_line_options = {}
|
||||
for o in command_line_options:
|
||||
if self.subproject != '': # Strip the beginning.
|
||||
# Ignore options that aren't for this subproject
|
||||
if not o.startswith(self.sbprefix):
|
||||
continue
|
||||
else:
|
||||
if ':' in o:
|
||||
continue
|
||||
try:
|
||||
(key, value) = o.split('=', 1)
|
||||
except ValueError:
|
||||
raise OptionException('Option {!r} must have a value separated by equals sign.'.format(o))
|
||||
# Ignore subproject options if not fetching subproject options
|
||||
if self.subproject == '' and ':' in key:
|
||||
continue
|
||||
self.cmd_line_options[key] = value
|
||||
|
||||
def process(self, option_file):
|
||||
|
|
|
@ -947,6 +947,21 @@ class AllPlatformTests(BasePlatformTests):
|
|||
m = re.search('build c-asm.*: c_LINKER', contents)
|
||||
self.assertIsNotNone(m, msg=contents)
|
||||
|
||||
def test_preprocessor_checks_CPPFLAGS(self):
|
||||
'''
|
||||
Test that preprocessor compiler checks read CPPFLAGS but not CFLAGS
|
||||
'''
|
||||
testdir = os.path.join(self.common_test_dir, '140 get define')
|
||||
define = 'MESON_TEST_DEFINE_VALUE'
|
||||
# NOTE: this list can't have \n, ' or "
|
||||
# \n is never substituted by the GNU pre-processor via a -D define
|
||||
# ' and " confuse shlex.split() even when they are escaped
|
||||
# % and # confuse the MSVC preprocessor
|
||||
value = 'spaces and fun!@$^&*()-=_+{}[]:;<>?,./~`'
|
||||
os.environ['CPPFLAGS'] = '-D{}="{}"'.format(define, value)
|
||||
os.environ['CFLAGS'] = '-DMESON_FAIL_VALUE=cflags-read'.format(define)
|
||||
self.init(testdir, ['-D{}={}'.format(define, value)])
|
||||
|
||||
|
||||
class WindowsTests(BasePlatformTests):
|
||||
'''
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
project('get define', 'c', 'cpp')
|
||||
|
||||
host_system = host_machine.system()
|
||||
|
||||
foreach lang : ['c', 'cpp']
|
||||
cc = meson.get_compiler(lang)
|
||||
if host_system == 'linux'
|
||||
d = cc.get_define('__linux__')
|
||||
assert(d == '1', '__linux__ value is @0@ instead of 1'.format(d))
|
||||
elif host_system == 'darwin'
|
||||
d = cc.get_define('__APPLE__')
|
||||
assert(d == '1', '__APPLE__ value is @0@ instead of 1'.format(d))
|
||||
elif host_system == 'windows'
|
||||
d = cc.get_define('_WIN32')
|
||||
assert(d == '1', '_WIN32 value is @0@ instead of 1'.format(d))
|
||||
else
|
||||
error('Please report a bug and help us improve support for this platform')
|
||||
endif
|
||||
|
||||
# Check that an undefined value is empty.
|
||||
have = cc.get_define('MESON_FAIL_VALUE')
|
||||
assert(have == '', 'MESON_FAIL_VALUE value is "@0@" instead of ""'.format(have))
|
||||
|
||||
# This is used in the test_preprocessor_checks_CPPFLAGS() unit test.
|
||||
have = cc.get_define('MESON_TEST_DEFINE_VALUE')
|
||||
expect = get_option('MESON_TEST_DEFINE_VALUE')
|
||||
assert(have == expect, 'MESON_TEST_DEFINE_VALUE value is "@0@" instead of "@1@"'.format(have, expect))
|
||||
endforeach
|
|
@ -0,0 +1 @@
|
|||
option('MESON_TEST_DEFINE_VALUE', type : 'string', default : '')
|
|
@ -1 +1 @@
|
|||
option('invalid/name', type : 'boolean', value : false)
|
||||
option('invalid:name', type : 'boolean', value : false)
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
project('name with :')
|
Loading…
Reference in New Issue