diff options
author | Marvin Scholz <epirat07@gmail.com> | 2023-09-06 03:12:55 +0200 |
---|---|---|
committer | Jussi Pakkanen <jpakkane@gmail.com> | 2023-09-07 00:45:38 +0300 |
commit | 3fc16f05b513f26aa5da614673116074f5d60396 (patch) | |
tree | 4227d8e3c741ecefb8fc8849cd25e1399f004741 | |
parent | 346a9157436fb88fbf38e2d1284fa7373c14d4a1 (diff) | |
download | meson-3fc16f05b513f26aa5da614673116074f5d60396.zip meson-3fc16f05b513f26aa5da614673116074f5d60396.tar.gz meson-3fc16f05b513f26aa5da614673116074f5d60396.tar.bz2 |
Add compiler.has_define
Adds a new method to the compiler object, has_define.
This makes it possible to check if a preprocessor macro/define
is set or not.
This is especially helpful if the define in question is empty,
for example:
#define MESON_EMPTY_DEFINE
This would yield the same results as a missing define with
the existing get_define method, as it would return an empty
string for both cases. Therefore this additional method is
needed.
-rw-r--r-- | docs/markdown/snippets/compiler_has_define.md | 10 | ||||
-rw-r--r-- | docs/yaml/objects/compiler.yaml | 11 | ||||
-rw-r--r-- | mesonbuild/compilers/mixins/clike.py | 27 | ||||
-rw-r--r-- | mesonbuild/interpreter/compiler.py | 22 | ||||
-rw-r--r-- | test cases/common/132 get define/meson.build | 89 |
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']) |