aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2017-04-05 00:02:41 +0300
committerGitHub <noreply@github.com>2017-04-05 00:02:41 +0300
commitb42adc8a5460c44226dc42291df790c6ec954922 (patch)
tree41a8e726bc3afdd303f46880da68fea99261e669
parent655757bc00d8588a64b4fcac5146c0dd8516b93f (diff)
parent41769d0c105f071cd7dad9eafaa4092683c583c4 (diff)
downloadmeson-b42adc8a5460c44226dc42291df790c6ec954922.zip
meson-b42adc8a5460c44226dc42291df790c6ec954922.tar.gz
meson-b42adc8a5460c44226dc42291df790c6ec954922.tar.bz2
Merge pull request #1511 from centricular/get-define
New compiler function: cc.get_define()
-rw-r--r--mesonbuild/compilers.py94
-rw-r--r--mesonbuild/coredata.py7
-rw-r--r--mesonbuild/environment.py21
-rw-r--r--mesonbuild/interpreter.py40
-rw-r--r--mesonbuild/optinterpreter.py7
-rwxr-xr-xrun_unittests.py15
-rw-r--r--test cases/common/140 get define/meson.build28
-rw-r--r--test cases/common/140 get define/meson_options.txt1
-rw-r--r--test cases/failing/14 invalid option name/meson_options.txt2
-rw-r--r--test cases/failing/43 project name colon/meson.build1
10 files changed, 146 insertions, 70 deletions
diff --git a/mesonbuild/compilers.py b/mesonbuild/compilers.py
index 5e7db24..6f08d98 100644
--- a/mesonbuild/compilers.py
+++ b/mesonbuild/compilers.py
@@ -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]
diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py
index 67516e7..27f1dd7 100644
--- a/mesonbuild/coredata.py
+++ b/mesonbuild/coredata.py
@@ -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:
diff --git a/mesonbuild/environment.py b/mesonbuild/environment.py
index 92040c4..cb62506 100644
--- a/mesonbuild/environment.py
+++ b/mesonbuild/environment.py
@@ -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):
diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py
index af2c17d..bf3dffe 100644
--- a/mesonbuild/interpreter.py
+++ b/mesonbuild/interpreter.py
@@ -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='')
diff --git a/mesonbuild/optinterpreter.py b/mesonbuild/optinterpreter.py
index 10b8fab..f9e7f26 100644
--- a/mesonbuild/optinterpreter.py
+++ b/mesonbuild/optinterpreter.py
@@ -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):
diff --git a/run_unittests.py b/run_unittests.py
index 0656f88..53abef7 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -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):
'''
diff --git a/test cases/common/140 get define/meson.build b/test cases/common/140 get define/meson.build
new file mode 100644
index 0000000..7a0969f
--- /dev/null
+++ b/test cases/common/140 get define/meson.build
@@ -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
diff --git a/test cases/common/140 get define/meson_options.txt b/test cases/common/140 get define/meson_options.txt
new file mode 100644
index 0000000..a88cecd
--- /dev/null
+++ b/test cases/common/140 get define/meson_options.txt
@@ -0,0 +1 @@
+option('MESON_TEST_DEFINE_VALUE', type : 'string', default : '')
diff --git a/test cases/failing/14 invalid option name/meson_options.txt b/test cases/failing/14 invalid option name/meson_options.txt
index c656402..aab6ae8 100644
--- a/test cases/failing/14 invalid option name/meson_options.txt
+++ b/test cases/failing/14 invalid option name/meson_options.txt
@@ -1 +1 @@
-option('invalid/name', type : 'boolean', value : false) \ No newline at end of file
+option('invalid:name', type : 'boolean', value : false)
diff --git a/test cases/failing/43 project name colon/meson.build b/test cases/failing/43 project name colon/meson.build
new file mode 100644
index 0000000..53e947e
--- /dev/null
+++ b/test cases/failing/43 project name colon/meson.build
@@ -0,0 +1 @@
+project('name with :')