Merge pull request #1511 from centricular/get-define

New compiler function: cc.get_define()
pull/1466/head
Jussi Pakkanen 8 years ago committed by GitHub
commit b42adc8a54
  1. 90
      mesonbuild/compilers.py
  2. 7
      mesonbuild/coredata.py
  3. 21
      mesonbuild/environment.py
  4. 38
      mesonbuild/interpreter.py
  5. 7
      mesonbuild/optinterpreter.py
  6. 15
      run_unittests.py
  7. 28
      test cases/common/140 get define/meson.build
  8. 1
      test cases/common/140 get define/meson_options.txt
  9. 2
      test cases/failing/14 invalid option name/meson_options.txt
  10. 1
      test cases/failing/43 project name colon/meson.build

@ -550,11 +550,11 @@ class Compiler:
def get_exelist(self): def get_exelist(self):
return self.exelist[:] return self.exelist[:]
def get_define(self, *args, **kwargs): def get_builtin_define(self, *args, **kwargs):
raise EnvironmentException('%s does not support get_define.' % self.id) raise EnvironmentException('%s does not support get_builtin_define.' % self.id)
def has_define(self, *args, **kwargs): def has_builtin_define(self, *args, **kwargs):
raise EnvironmentException('%s does not support has_define.' % self.id) raise EnvironmentException('%s does not support has_builtin_define.' % self.id)
def get_always_args(self): def get_always_args(self):
return [] return []
@ -906,8 +906,6 @@ class CCompiler(Compiler):
return self.sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code) return self.sanity_check_impl(work_dir, environment, 'sanitycheckc.c', code)
def has_header(self, hname, prefix, env, extra_args=None, dependencies=None): def has_header(self, hname, prefix, env, extra_args=None, dependencies=None):
if extra_args is None:
extra_args = []
fargs = {'prefix': prefix, 'header': hname} fargs = {'prefix': prefix, 'header': hname}
code = '''{prefix} code = '''{prefix}
#ifdef __has_include #ifdef __has_include
@ -921,8 +919,6 @@ class CCompiler(Compiler):
dependencies, 'preprocess') dependencies, 'preprocess')
def has_header_symbol(self, hname, symbol, prefix, env, extra_args=None, dependencies=None): 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} fargs = {'prefix': prefix, 'header': hname, 'symbol': symbol}
t = '''{prefix} t = '''{prefix}
#include <{header}> #include <{header}>
@ -934,7 +930,7 @@ class CCompiler(Compiler):
}}''' }}'''
return self.compiles(t.format(**fargs), env, extra_args, dependencies) 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: if extra_args is None:
extra_args = [] extra_args = []
elif isinstance(extra_args, str): elif isinstance(extra_args, str):
@ -943,49 +939,43 @@ class CCompiler(Compiler):
dependencies = [] dependencies = []
elif not isinstance(dependencies, list): elif not isinstance(dependencies, list):
dependencies = [dependencies] dependencies = [dependencies]
# Add compile flags needed by dependencies # Collect compiler arguments
args = CompilerArgs(self) args = CompilerArgs(self)
for d in dependencies: for d in dependencies:
# Add compile flags needed by dependencies
args += d.get_compile_args() 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) # Read c_args/cpp_args/etc from the cross-info file (if needed)
args += self.get_cross_extra_flags(env, compile=True, link=False) 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 # 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 += 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() args += self.get_compiler_check_args()
# extra_args must override all other arguments, so we add them last # extra_args must override all other arguments, so we add them last
args += extra_args 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 # We only want to compile; not link
with self.compile(code, args.to_native(), mode) as p: with self.compile(code, args.to_native(), mode) as p:
return p.returncode == 0 return p.returncode == 0
def _links_wrapper(self, code, env, extra_args, dependencies): def _links_wrapper(self, code, env, extra_args, dependencies):
"Shares common code between self.links and self.run" "Shares common code between self.links and self.run"
if extra_args is None: args = self._get_compiler_check_args(env, extra_args, dependencies, mode='link')
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
return self.compile(code, args.to_native()) return self.compile(code, args.to_native())
def links(self, code, env, extra_args=None, dependencies=None): 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) raise EnvironmentException('Could not determine alignment of %s. Sorry. You might want to file a bug.' % typename)
return align 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 @staticmethod
def _no_prototype_templ(): def _no_prototype_templ():
""" """
@ -2382,10 +2390,10 @@ class GnuCompiler:
args[args.index('-Wpedantic')] = '-pedantic' args[args.index('-Wpedantic')] = '-pedantic'
return args return args
def has_define(self, define): def has_builtin_define(self, define):
return define in self.defines return define in self.defines
def get_define(self, define): def get_builtin_define(self, define):
if define in self.defines: if define in self.defines:
return self.defines[define] return self.defines[define]
@ -2896,10 +2904,10 @@ class GnuFortranCompiler(FortranCompiler):
self.defines = defines or {} self.defines = defines or {}
self.id = 'gcc' self.id = 'gcc'
def has_define(self, define): def has_builtin_define(self, define):
return define in self.defines return define in self.defines
def get_define(self, define): def get_builtin_define(self, define):
if define in self.defines: if define in self.defines:
return self.defines[define] return self.defines[define]

@ -148,10 +148,11 @@ class CoreData:
self.user_options = {} self.user_options = {}
self.compiler_options = {} self.compiler_options = {}
self.base_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. # but only when not cross-compiling.
self.external_args = {} self.external_preprocess_args = {} # CPPFLAGS only
self.external_link_args = {} self.external_args = {} # CPPFLAGS + CFLAGS
self.external_link_args = {} # CFLAGS + LDFLAGS (with MSVC: only LDFLAGS)
if options.cross_file is not None: if options.cross_file is not None:
self.cross_file = os.path.join(os.getcwd(), options.cross_file) self.cross_file = os.path.join(os.getcwd(), options.cross_file)
else: else:

@ -102,7 +102,7 @@ def detect_windows_arch(compilers):
platform = os.environ.get('Platform', 'x86').lower() platform = os.environ.get('Platform', 'x86').lower()
if platform == 'x86': if platform == 'x86':
return platform return platform
if compiler.id == 'gcc' and compiler.has_define('__i386__'): if compiler.id == 'gcc' and compiler.has_builtin_define('__i386__'):
return 'x86' return 'x86'
return os_arch return os_arch
@ -129,10 +129,10 @@ def detect_cpu_family(compilers):
# to know is to check the compiler defines. # to know is to check the compiler defines.
for c in compilers.values(): for c in compilers.values():
try: try:
if c.has_define('__i386__'): if c.has_builtin_define('__i386__'):
return 'x86' return 'x86'
except mesonlib.MesonException: except mesonlib.MesonException:
# Ignore compilers that do not support has_define. # Ignore compilers that do not support has_builtin_define.
pass pass
return 'x86_64' return 'x86_64'
# Add fixes here as bugs are reported. # Add fixes here as bugs are reported.
@ -149,7 +149,7 @@ def detect_cpu(compilers):
# Same check as above for cpu_family # Same check as above for cpu_family
for c in compilers.values(): for c in compilers.values():
try: 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. return 'i686' # All 64 bit cpus have at least this level of x86 support.
except mesonlib.MesonException: except mesonlib.MesonException:
pass pass
@ -770,7 +770,7 @@ def get_args_from_envvars(compiler):
compiler_is_linker = (compiler.get_exelist() == compiler.get_linker_exelist()) compiler_is_linker = (compiler.get_exelist() == compiler.get_linker_exelist())
if lang not in ('c', 'cpp', 'objc', 'objcpp', 'fortran', 'd'): if lang not in ('c', 'cpp', 'objc', 'objcpp', 'fortran', 'd'):
return [], [] return [], [], []
# Compile flags # Compile flags
cflags_mapping = {'c': 'CFLAGS', cflags_mapping = {'c': 'CFLAGS',
@ -781,12 +781,12 @@ def get_args_from_envvars(compiler):
'd': 'DFLAGS'} 'd': 'DFLAGS'}
compile_flags = os.environ.get(cflags_mapping[lang], '') compile_flags = os.environ.get(cflags_mapping[lang], '')
log_var(cflags_mapping[lang], compile_flags) 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 (same for all languages)
link_flags = os.environ.get('LDFLAGS', '') link_flags = os.environ.get('LDFLAGS', '')
log_var('LDFLAGS', link_flags) log_var('LDFLAGS', link_flags)
link_flags = link_flags.split() link_flags = shlex.split(link_flags)
if compiler_is_linker: if compiler_is_linker:
# When the compiler is used as a wrapper around the linker (such as # 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 # 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. # this when the linker is stand-alone such as with MSVC C/C++, etc.
link_flags = compile_flags + link_flags link_flags = compile_flags + link_flags
# Pre-processof rlags (not for fortran) # Pre-processor flags (not for fortran or D)
preproc_flags = '' preproc_flags = ''
if lang in ('c', 'cpp', 'objc', 'objcpp'): if lang in ('c', 'cpp', 'objc', 'objcpp'):
preproc_flags = os.environ.get('CPPFLAGS', '') preproc_flags = os.environ.get('CPPFLAGS', '')
log_var('CPPFLAGS', preproc_flags) 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: class CrossBuildInfo:
def __init__(self, filename): def __init__(self, filename):

@ -634,6 +634,7 @@ class CompilerHolder(InterpreterObject):
'get_id': self.get_id_method, 'get_id': self.get_id_method,
'compute_int': self.compute_int_method, 'compute_int': self.compute_int_method,
'sizeof': self.sizeof_method, 'sizeof': self.sizeof_method,
'get_define': self.get_define_method,
'has_header': self.has_header_method, 'has_header': self.has_header_method,
'has_header_symbol': self.has_header_symbol_method, 'has_header_symbol': self.has_header_symbol_method,
'run': self.run_method, 'run': self.run_method,
@ -865,6 +866,20 @@ class CompilerHolder(InterpreterObject):
mlog.log('Checking for size of "%s": %d' % (element, esize)) mlog.log('Checking for size of "%s": %d' % (element, esize))
return 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): def compiles_method(self, args, kwargs):
if len(args) != 1: if len(args) != 1:
raise InterpreterException('compiles method takes exactly one argument.') raise InterpreterException('compiles method takes exactly one argument.')
@ -1615,24 +1630,28 @@ class Interpreter(InterpreterBase):
def func_project(self, node, args, kwargs): def func_project(self, node, args, kwargs):
if len(args) < 1: if len(args) < 1:
raise InvalidArguments('Not enough arguments to project(). Needs at least the project name.') 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', []) default_options = kwargs.get('default_options', [])
if self.environment.first_invocation and (len(default_options) > 0 or if self.environment.first_invocation and (len(default_options) > 0 or
len(self.default_project_options) > 0): len(self.default_project_options) > 0):
self.parse_default_options(default_options) self.parse_default_options(default_options)
if not self.is_subproject(): if not self.is_subproject():
self.build.project_name = args[0] self.build.project_name = proj_name
if os.path.exists(self.option_file): if os.path.exists(self.option_file):
oi = optinterpreter.OptionInterpreter(self.subproject, oi = optinterpreter.OptionInterpreter(self.subproject,
self.build.environment.cmd_line_options.projectoptions, self.build.environment.cmd_line_options.projectoptions,
) )
oi.process(self.option_file) oi.process(self.option_file)
self.build.environment.merge_options(oi.options) self.build.environment.merge_options(oi.options)
self.active_projectname = args[0] self.active_projectname = proj_name
self.project_version = kwargs.get('version', 'undefined') self.project_version = kwargs.get('version', 'undefined')
if self.build.project_version is None: if self.build.project_version is None:
self.build.project_version = self.project_version self.build.project_version = self.project_version
proj_license = mesonlib.stringlistify(kwargs.get('license', 'unknown')) proj_license = mesonlib.stringlistify(kwargs.get('license', 'unknown'))
self.build.dep_manifest[args[0]] = {'version': self.project_version, self.build.dep_manifest[proj_name] = {'version': self.project_version,
'license': proj_license} 'license': proj_license}
if self.subproject in self.build.projects: if self.subproject in self.build.projects:
raise InvalidCode('Second call to project().') raise InvalidCode('Second call to project().')
@ -1644,9 +1663,9 @@ class Interpreter(InterpreterBase):
pv = kwargs['meson_version'] pv = kwargs['meson_version']
if not mesonlib.version_compare(cv, pv): if not mesonlib.version_compare(cv, pv):
raise InterpreterException('Meson version is %s but project requires %s.' % (cv, pv)) raise InterpreterException('Meson version is %s but project requires %s.' % (cv, pv))
self.build.projects[self.subproject] = args[0] self.build.projects[self.subproject] = proj_name
mlog.log('Project name: ', mlog.bold(args[0]), sep='') mlog.log('Project name: ', mlog.bold(proj_name), sep='')
self.add_languages(args[1:], True) self.add_languages(proj_langs, True)
langs = self.coredata.compilers.keys() langs = self.coredata.compilers.keys()
if 'vala' in langs: if 'vala' in langs:
if 'c' not in langs: if 'c' not in langs:
@ -1772,9 +1791,10 @@ class Interpreter(InterpreterBase):
raise raise
mlog.log('Native %s compiler: ' % lang, mlog.bold(' '.join(comp.get_exelist())), ' (%s %s)' % (comp.id, comp.version), sep='') 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: if not comp.get_language() in self.coredata.external_args:
(ext_compile_args, ext_link_args) = environment.get_args_from_envvars(comp) (preproc_args, compile_args, link_args) = environment.get_args_from_envvars(comp)
self.coredata.external_args[comp.get_language()] = ext_compile_args self.coredata.external_preprocess_args[comp.get_language()] = preproc_args
self.coredata.external_link_args[comp.get_language()] = ext_link_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) self.build.add_compiler(comp)
if need_cross_compiler: 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='') 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 = {} self.cmd_line_options = {}
for o in command_line_options: for o in command_line_options:
if self.subproject != '': # Strip the beginning. if self.subproject != '': # Strip the beginning.
# Ignore options that aren't for this subproject
if not o.startswith(self.sbprefix): if not o.startswith(self.sbprefix):
continue continue
else:
if ':' in o:
continue
try: try:
(key, value) = o.split('=', 1) (key, value) = o.split('=', 1)
except ValueError: except ValueError:
raise OptionException('Option {!r} must have a value separated by equals sign.'.format(o)) 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 self.cmd_line_options[key] = value
def process(self, option_file): def process(self, option_file):

@ -947,6 +947,21 @@ class AllPlatformTests(BasePlatformTests):
m = re.search('build c-asm.*: c_LINKER', contents) m = re.search('build c-asm.*: c_LINKER', contents)
self.assertIsNotNone(m, msg=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): 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)

Loading…
Cancel
Save