aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/snippets/compiler_has_define.md10
-rw-r--r--docs/yaml/objects/compiler.yaml11
-rw-r--r--mesonbuild/compilers/mixins/clike.py27
-rw-r--r--mesonbuild/interpreter/compiler.py22
-rw-r--r--test cases/common/132 get define/meson.build89
5 files changed, 110 insertions, 49 deletions
diff --git a/docs/markdown/snippets/compiler_has_define.md b/docs/markdown/snippets/compiler_has_define.md
new file mode 100644
index 0000000..64de26b
--- /dev/null
+++ b/docs/markdown/snippets/compiler_has_define.md
@@ -0,0 +1,10 @@
+## Compilers now have a `has_define` method
+
+This method returns true if the given preprocessor symbol is
+defined, else false is returned. This is useful is cases where
+an empty define has to be distinguished from a non-set one, which
+is not possible using `get_define`.
+
+Additionally it makes intent clearer for code that only needs
+to check if a specific define is set at all and does not care
+about its value. \ No newline at end of file
diff --git a/docs/yaml/objects/compiler.yaml b/docs/yaml/objects/compiler.yaml
index d5d7df5..977cbdf 100644
--- a/docs/yaml/objects/compiler.yaml
+++ b/docs/yaml/objects/compiler.yaml
@@ -327,6 +327,17 @@ methods:
type: str
description: The define to check.
+- name: has_define
+ returns: bool
+ since: 1.3.0
+ description: |
+ Returns true if the given preprocessor symbol is *defined*.
+ kwargs_inherit: compiler._common
+ posargs:
+ definename:
+ type: str
+ description: The define to check.
+
- name: compiles
returns: bool
description: Returns true if the code compiles.
diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py
index 4999d60..f37bcf4 100644
--- a/mesonbuild/compilers/mixins/clike.py
+++ b/mesonbuild/compilers/mixins/clike.py
@@ -672,13 +672,15 @@ class CLikeCompiler(Compiler):
extra_args: T.Union[T.List[str], T.Callable[[CompileCheckMode], T.List[str]]],
dependencies: T.Optional[T.List['Dependency']],
disable_cache: bool = False) -> T.Tuple[str, bool]:
- delim = '"MESON_GET_DEFINE_DELIMITER"'
+ delim_start = '"MESON_GET_DEFINE_DELIMITER_START"\n'
+ delim_end = '\n"MESON_GET_DEFINE_DELIMITER_END"'
+ sentinel_undef = '"MESON_GET_DEFINE_UNDEFINED_SENTINEL"'
code = f'''
{prefix}
#ifndef {dname}
- # define {dname}
+ # define {dname} {sentinel_undef}
#endif
- {delim}\n{dname}'''
+ {delim_start}{dname}{delim_end}'''
args = self.build_wrapper_args(env, extra_args, dependencies,
mode=CompileCheckMode.PREPROCESS).to_native()
func = functools.partial(self.cached_compile, code, env.coredata, extra_args=args, mode=CompileCheckMode.PREPROCESS)
@@ -688,10 +690,21 @@ class CLikeCompiler(Compiler):
cached = p.cached
if p.returncode != 0:
raise mesonlib.EnvironmentException(f'Could not get define {dname!r}')
- # Get the preprocessed value after the delimiter,
- # minus the extra newline at the end and
- # merge string literals.
- return self._concatenate_string_literals(p.stdout.split(delim + '\n')[-1][:-1]).strip(), cached
+
+ # Get the preprocessed value between the delimiters
+ star_idx = p.stdout.find(delim_start)
+ end_idx = p.stdout.rfind(delim_end)
+ if (star_idx == -1) or (end_idx == -1) or (star_idx == end_idx):
+ raise AssertionError('BUG: Delimiters not found in preprocessor output!')
+ define_value = p.stdout[star_idx + len(delim_start):end_idx]
+
+ if define_value == sentinel_undef:
+ define_value = None
+ else:
+ # Merge string literals
+ define_value = self._concatenate_string_literals(define_value).strip()
+
+ return define_value, cached
def get_return_value(self, fname: str, rtype: str, prefix: str,
env: 'Environment', extra_args: T.Optional[T.List[str]],
diff --git a/mesonbuild/interpreter/compiler.py b/mesonbuild/interpreter/compiler.py
index b85aa37..b6a1a85 100644
--- a/mesonbuild/interpreter/compiler.py
+++ b/mesonbuild/interpreter/compiler.py
@@ -189,6 +189,7 @@ class CompilerHolder(ObjectHolder['Compiler']):
'compute_int': self.compute_int_method,
'sizeof': self.sizeof_method,
'get_define': self.get_define_method,
+ 'has_define': self.has_define_method,
'check_header': self.check_header_method,
'has_header': self.has_header_method,
'has_header_symbol': self.has_header_symbol_method,
@@ -475,8 +476,25 @@ class CompilerHolder(ObjectHolder['Compiler']):
extra_args=extra_args,
dependencies=deps)
cached_msg = mlog.blue('(cached)') if cached else ''
- mlog.log('Fetching value of define', mlog.bold(element, True), msg, value, cached_msg)
- return value
+ value_msg = '(undefined)' if value is None else value
+ mlog.log('Fetching value of define', mlog.bold(element, True), msg, value_msg, cached_msg)
+ return value if value is not None else ''
+
+ @FeatureNew('compiler.has_define', '1.3.0')
+ @typed_pos_args('compiler.has_define', str)
+ @typed_kwargs('compiler.has_define', *_COMMON_KWS)
+ def has_define_method(self, args: T.Tuple[str], kwargs: 'CommonKW') -> bool:
+ define_name = args[0]
+ extra_args = functools.partial(self._determine_args, kwargs)
+ deps, msg = self._determine_dependencies(kwargs['dependencies'], endl=None)
+ value, cached = self.compiler.get_define(define_name, kwargs['prefix'], self.environment,
+ extra_args=extra_args,
+ dependencies=deps)
+ cached_msg = mlog.blue('(cached)') if cached else ''
+ h = mlog.green('YES') if value is not None else mlog.red('NO')
+ mlog.log('Checking if define', mlog.bold(define_name, True), msg, 'exists:', h, cached_msg)
+
+ return value is not None
@typed_pos_args('compiler.compiles', (str, mesonlib.File))
@typed_kwargs('compiler.compiles', *_COMPILES_KWS)
diff --git a/test cases/common/132 get define/meson.build b/test cases/common/132 get define/meson.build
index 02e5a0c..019b17a 100644
--- a/test cases/common/132 get define/meson.build
+++ b/test cases/common/132 get define/meson.build
@@ -2,49 +2,50 @@ project('get define', 'c', 'cpp')
host_system = host_machine.system()
+system_define_map = {
+ 'linux' : ['__linux__', '1'],
+ 'darwin' : ['__APPLE__', '1'],
+ 'windows' : ['_WIN32', '1'],
+ 'cygwin' : ['__CYGWIN__', '1'],
+ 'haiku' : ['__HAIKU__', '1'],
+ 'dragonfly' : ['__DragonFly__', '1'],
+ 'netbsd' : ['__NetBSD__', '1'],
+ 'openbsd' : ['__OpenBSD__', '1'],
+ 'gnu' : ['__GNU__', '1'],
+ 'sunos' : ['__sun__', '1'],
+
+ # The __FreeBSD__ define will be equal to the major version of the release
+ # (ex, in FreeBSD 11.x, __FreeBSD__ == 11). To make the test robust when
+ # being run on various versions of FreeBSD, just test that the define is
+ # set.
+ 'freebsd' : ['__FreeBSD__'],
+}
+
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))
- elif host_system == 'cygwin'
- d = cc.get_define('__CYGWIN__')
- assert(d == '1', '__CYGWIN__ value is @0@ instead of 1'.format(d))
- elif host_system == 'haiku'
- d = cc.get_define('__HAIKU__')
- assert(d == '1', '__HAIKU__ value is @0@ instead of 1'.format(d))
- elif host_system == 'freebsd'
- # the __FreeBSD__ define will be equal to the major version of the release
- # (ex, in FreeBSD 11.x, __FreeBSD__ == 11). To make the test robust when
- # being run on various versions of FreeBSD, just test that the define is
- # set.
- d = cc.get_define('__FreeBSD__')
- assert(d != '', '__FreeBSD__ value is unset')
- elif host_system == 'dragonfly'
- d = cc.get_define('__DragonFly__')
- assert(d == '1', '__DragonFly__ value is @0@ instead of 1'.format(d))
- elif host_system == 'netbsd'
- d = cc.get_define('__NetBSD__')
- assert(d == '1', '__NetBSD__ value is @0@ instead of 1'.format(d))
- elif host_system == 'openbsd'
- d = cc.get_define('__OpenBSD__')
- assert(d == '1', '__OpenBSD__ value is @0@ instead of 1'.format(d))
- elif host_system == 'gnu'
- d = cc.get_define('__GNU__')
- assert(d == '1', '__GNU__ value is @0@ instead of 1'.format(d))
- elif host_system == 'sunos'
- d = cc.get_define('__sun__')
- assert(d == '1', '__sun__ value is @0@ instead of 1'.format(d))
- else
+
+ if not system_define_map.has_key(host_system)
error('Please report a bug and help us improve support for this platform')
endif
+ system_define = system_define_map.get(host_system)
+
+ def_name = system_define[0]
+ def_val = cc.get_define(system_define[0])
+ def_exist = cc.has_define(system_define[0])
+
+ assert((def_val != '') == def_exist,
+ 'The has_define and get_define results for @0@ disagree with each other'.format(def_name))
+
+ if system_define.length() == 2
+ assert(def_val == system_define[1],
+ '@0@ value is @1@ instead of @2@'.format(def_name, def_val, system_define[1]))
+ elif system_define.length() == 1
+ assert(def_val != '', '@0@ value is unset'.format(def_name))
+ else
+ assert('Invalid number of items in system_define array, this is a bug in the test!')
+ endif
+
if cc.find_library('z', required : false).found()
# When a C file containing #include <foo.h> is pre-processed and foo.h is
# found in the compiler's default search path, GCC inserts an extra comment
@@ -63,8 +64,16 @@ foreach lang : ['c', 'cpp']
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))
+ have_val = cc.get_define('MESON_FAIL_VALUE')
+ have = cc.has_define('MESON_FAIL_VALUE')
+ assert(have_val == '', 'MESON_FAIL_VALUE value is "@0@" instead of ""'.format(have_val))
+ assert(not have, 'MESON_FAIL_VALUE was found even though it should not have been')
+
+ # Check that an empty define is reported as existing.
+ have_val = cc.get_define('MESON_EMPTY_VALUE', prefix: ['#define MESON_EMPTY_VALUE'])
+ have = cc.has_define('MESON_EMPTY_VALUE', prefix: ['#define MESON_EMPTY_VALUE'])
+ assert(have_val == '', 'MESON_EMPTY_VALUE value is "@0@" instead of ""'.format(have_val))
+ assert(have, 'MESON_EMPTY_VALUE was not found even though it should have been')
# Check if prefix array works properly and has the expected order
have = cc.get_define('MESON_FAIL_VALUE', prefix: ['#define MESON_FAIL_VALUE 1', '#undef MESON_FAIL_VALUE'])