compiler: Add required keyword to has_* methods

add the "required" keyword to the functions

has_function
has_type
has_member
has_members
has_argument
has_multi_arguments
has_link_argument
has_multi_link_argument
has_function_attribute

Co-authored-by: Milan Hauth <milahu@gmail.com>
pull/12011/head
Xavier Claessens 2 years ago committed by Xavier Claessens
parent 50baf3c626
commit e01d53b816
  1. 19
      docs/markdown/snippets/required_keyword_for_has_functions.md
  2. 43
      docs/yaml/objects/compiler.yaml
  3. 140
      mesonbuild/interpreter/compiler.py
  4. 67
      test cases/common/262 required keyword in has functions/meson.build
  5. 1
      test cases/common/262 required keyword in has functions/meson_options.txt

@ -0,0 +1,19 @@
## All compiler `has_*` methods support the `required` keyword
Now instead of
```meson
assert(cc.has_function('some_function'))
assert(cc.has_type('some_type'))
assert(cc.has_member('struct some_type', 'x'))
assert(cc.has_members('struct some_type', ['x', 'y']))
```
we can use
```meson
cc.has_function('some_function', required: true)
cc.has_type('some_type', required: true)
cc.has_member('struct some_type', 'x', required: true)
cc.has_members('struct some_type', ['x', 'y'], required: true)
```

@ -143,6 +143,19 @@ methods:
When set to a [`feature`](Build-options.md#features) option, the feature
will control if it is searched and whether to fail if not found.
- name: _required
returns: void
description: You have found a bug if you can see this!
kwargs:
required:
type: bool | feature
default: false
since: 1.1.0
description:
When set to `true`, Meson will halt if the check fails.
When set to a [`feature`](Build-options.md#features) option, the feature
will control if it is searched and whether to fail if not found.
# Star of the actual functions
- name: version
@ -196,7 +209,9 @@ methods:
- name: has_member
returns: bool
description: Returns true if the type has the specified member.
kwargs_inherit: compiler._common
kwargs_inherit:
- compiler._common
- compiler._required
posargs:
typename:
type: str
@ -208,7 +223,9 @@ methods:
- name: has_members
returns: bool
description: Returns `true` if the type has *all* the specified members.
kwargs_inherit: compiler._common
kwargs_inherit:
- compiler._common
- compiler._required
posargs:
typename:
type: str
@ -225,7 +242,9 @@ methods:
Returns true if the given function is provided
by the standard library or a library passed in with the `args` keyword.
kwargs_inherit: compiler._common
kwargs_inherit:
- compiler._common
- compiler._required
posargs:
funcname:
type: str
@ -234,7 +253,9 @@ methods:
- name: has_type
returns: bool
description: Returns `true` if the specified token is a type.
kwargs_inherit: compiler._common
kwargs_inherit:
- compiler._common
- compiler._required
posargs:
typename:
type: str
@ -457,6 +478,8 @@ methods:
argument:
type: str
description: The argument to check.
kwargs_inherit:
- compiler._required
- name: has_multi_arguments
since: 0.37.0
@ -469,6 +492,8 @@ methods:
name: arg
type: str
description: The arguments to check.
kwargs_inherit:
- compiler._required
- name: get_supported_arguments
returns: list[str]
@ -515,6 +540,8 @@ methods:
argument:
type: str
description: The argument to check.
kwargs_inherit:
- compiler._required
- name: has_multi_link_arguments
since: 0.46.0
@ -527,6 +554,8 @@ methods:
name: arg
type: str
description: The link arguments to check.
kwargs_inherit:
- compiler._required
- name: get_supported_link_arguments
returns: list[str]
@ -556,10 +585,6 @@ methods:
Given a list of strings, returns the first argument that passes the
[[compiler.has_link_argument]] test or an empty array if none pass.
- name: has_function_attribute
returns: bool
since: 0.48.0
@ -573,6 +598,8 @@ methods:
name:
type: str
description: The attribute name to check.
kwargs_inherit:
- compiler._required
- name: get_supported_function_attributes
returns: list[str]

@ -31,6 +31,7 @@ if T.TYPE_CHECKING:
from ..interpreterbase import TYPE_var, TYPE_kwargs
from .kwargs import ExtractRequired, ExtractSearchDirs
from .interpreter.interpreter import SourceOutputs
from ..mlog import TV_LoggableList
from typing_extensions import TypedDict, Literal
@ -69,6 +70,12 @@ if T.TYPE_CHECKING:
class HeaderKW(CommonKW, ExtractRequired):
pass
class HasKW(CommonKW, ExtractRequired):
pass
class HasArgumentKW(ExtractRequired):
pass
class FindLibraryKW(ExtractRequired, ExtractSearchDirs):
disabler: bool
@ -165,6 +172,7 @@ _COMMON_KWS: T.List[KwargInfo] = [_ARGS_KW, _DEPENDENCIES_KW, _INCLUDE_DIRS_KW,
_COMPILES_KWS: T.List[KwargInfo] = [_NAME_KW, _ARGS_KW, _DEPENDENCIES_KW, _INCLUDE_DIRS_KW, _NO_BUILTIN_ARGS_KW]
_HEADER_KWS: T.List[KwargInfo] = [REQUIRED_KW.evolve(since='0.50.0', default=False), *_COMMON_KWS]
_HAS_REQUIRED_KW = REQUIRED_KW.evolve(since='1.1.0', default=False)
class CompilerHolder(ObjectHolder['Compiler']):
preprocess_uid: T.Dict[str, itertools.count] = collections.defaultdict(itertools.count)
@ -325,9 +333,13 @@ class CompilerHolder(ObjectHolder['Compiler']):
return self.compiler.symbols_have_underscore_prefix(self.environment)
@typed_pos_args('compiler.has_member', str, str)
@typed_kwargs('compiler.has_member', *_COMMON_KWS)
def has_member_method(self, args: T.Tuple[str, str], kwargs: 'CommonKW') -> bool:
@typed_kwargs('compiler.has_member', _HAS_REQUIRED_KW, *_COMMON_KWS)
def has_member_method(self, args: T.Tuple[str, str], kwargs: 'HasKW') -> bool:
typename, membername = args
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
if disabled:
mlog.log('Type', mlog.bold(typename, True), 'has member', mlog.bold(membername, True), 'skipped: feature', mlog.bold(feature), 'disabled')
return False
extra_args = functools.partial(self._determine_args, kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args'])
deps, msg = self._determine_dependencies(kwargs['dependencies'])
had, cached = self.compiler.has_members(typename, [membername], kwargs['prefix'],
@ -335,7 +347,9 @@ class CompilerHolder(ObjectHolder['Compiler']):
extra_args=extra_args,
dependencies=deps)
cached_msg = mlog.blue('(cached)') if cached else ''
if had:
if required and not had:
raise InterpreterException(f'{self.compiler.get_display_language()} member {membername!r} of type {typename!r} not usable')
elif had:
hadtxt = mlog.green('YES')
else:
hadtxt = mlog.red('NO')
@ -344,9 +358,14 @@ class CompilerHolder(ObjectHolder['Compiler']):
return had
@typed_pos_args('compiler.has_members', str, varargs=str, min_varargs=1)
@typed_kwargs('compiler.has_members', *_COMMON_KWS)
def has_members_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'CommonKW') -> bool:
@typed_kwargs('compiler.has_members', _HAS_REQUIRED_KW, *_COMMON_KWS)
def has_members_method(self, args: T.Tuple[str, T.List[str]], kwargs: 'HasKW') -> bool:
typename, membernames = args
members = mlog.bold(', '.join([f'"{m}"' for m in membernames]))
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
if disabled:
mlog.log('Type', mlog.bold(typename, True), 'has members', members, 'skipped: feature', mlog.bold(feature), 'disabled')
return False
extra_args = functools.partial(self._determine_args, kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args'])
deps, msg = self._determine_dependencies(kwargs['dependencies'])
had, cached = self.compiler.has_members(typename, membernames, kwargs['prefix'],
@ -354,26 +373,34 @@ class CompilerHolder(ObjectHolder['Compiler']):
extra_args=extra_args,
dependencies=deps)
cached_msg = mlog.blue('(cached)') if cached else ''
if had:
if required and not had:
# print members as array: ['member1', 'member2']
raise InterpreterException(f'{self.compiler.get_display_language()} members {membernames!r} of type {typename!r} not usable')
elif had:
hadtxt = mlog.green('YES')
else:
hadtxt = mlog.red('NO')
members = mlog.bold(', '.join([f'"{m}"' for m in membernames]))
mlog.log('Checking whether type', mlog.bold(typename, True),
'has members', members, msg, hadtxt, cached_msg)
return had
@typed_pos_args('compiler.has_function', str)
@typed_kwargs('compiler.has_function', *_COMMON_KWS)
def has_function_method(self, args: T.Tuple[str], kwargs: 'CommonKW') -> bool:
@typed_kwargs('compiler.has_function', _HAS_REQUIRED_KW, *_COMMON_KWS)
def has_function_method(self, args: T.Tuple[str], kwargs: 'HasKW') -> bool:
funcname = args[0]
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
if disabled:
mlog.log('Has function', mlog.bold(funcname, True), 'skipped: feature', mlog.bold(feature), 'disabled')
return False
extra_args = self._determine_args(kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args'])
deps, msg = self._determine_dependencies(kwargs['dependencies'], compile_only=False)
had, cached = self.compiler.has_function(funcname, kwargs['prefix'], self.environment,
extra_args=extra_args,
dependencies=deps)
cached_msg = mlog.blue('(cached)') if cached else ''
if had:
if required and not had:
raise InterpreterException(f'{self.compiler.get_display_language()} function {funcname!r} not usable')
elif had:
hadtxt = mlog.green('YES')
else:
hadtxt = mlog.red('NO')
@ -381,15 +408,21 @@ class CompilerHolder(ObjectHolder['Compiler']):
return had
@typed_pos_args('compiler.has_type', str)
@typed_kwargs('compiler.has_type', *_COMMON_KWS)
def has_type_method(self, args: T.Tuple[str], kwargs: 'CommonKW') -> bool:
@typed_kwargs('compiler.has_type', _HAS_REQUIRED_KW, *_COMMON_KWS)
def has_type_method(self, args: T.Tuple[str], kwargs: 'HasKW') -> bool:
typename = args[0]
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
if disabled:
mlog.log('Has type', mlog.bold(typename, True), 'skipped: feature', mlog.bold(feature), 'disabled')
return False
extra_args = functools.partial(self._determine_args, kwargs['no_builtin_args'], kwargs['include_directories'], kwargs['args'])
deps, msg = self._determine_dependencies(kwargs['dependencies'])
had, cached = self.compiler.has_type(typename, kwargs['prefix'], self.environment,
extra_args=extra_args, dependencies=deps)
cached_msg = mlog.blue('(cached)') if cached else ''
if had:
if required and not had:
raise InterpreterException(f'{self.compiler.get_display_language()} type {typename!r} not usable')
elif had:
hadtxt = mlog.green('YES')
else:
hadtxt = mlog.red('NO')
@ -646,33 +679,46 @@ class CompilerHolder(ObjectHolder['Compiler']):
return lib
def _has_argument_impl(self, arguments: T.Union[str, T.List[str]],
mode: _TestMode = _TestMode.COMPILER) -> bool:
mode: _TestMode = _TestMode.COMPILER,
kwargs: T.Optional['ExtractRequired'] = None) -> bool:
"""Shared implementation for methods checking compiler and linker arguments."""
# This simplifies the callers
if isinstance(arguments, str):
arguments = [arguments]
test = self.compiler.has_multi_link_arguments if mode is _TestMode.LINKER else self.compiler.has_multi_arguments
result, cached = test(arguments, self.environment)
cached_msg = mlog.blue('(cached)') if cached else ''
mlog.log(
logargs: TV_LoggableList = [
'Compiler for',
self.compiler.get_display_language(),
'supports{}'.format(' link' if mode is _TestMode.LINKER else ''),
'arguments {}:'.format(' '.join(arguments)),
]
kwargs = kwargs or {'required': False}
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
if disabled:
logargs += ['skipped: feature', mlog.bold(feature), 'disabled']
mlog.log(*logargs)
return False
test = self.compiler.has_multi_link_arguments if mode is _TestMode.LINKER else self.compiler.has_multi_arguments
result, cached = test(arguments, self.environment)
if required and not result:
logargs += ['not usable']
raise InterpreterException(*logargs)
logargs += [
mlog.green('YES') if result else mlog.red('NO'),
cached_msg)
mlog.blue('(cached)') if cached else '',
]
mlog.log(*logargs)
return result
@noKwargs
@typed_pos_args('compiler.has_argument', str)
def has_argument_method(self, args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> bool:
return self._has_argument_impl([args[0]])
@typed_kwargs('compiler.has_argument', _HAS_REQUIRED_KW)
def has_argument_method(self, args: T.Tuple[str], kwargs: 'HasArgumentKW') -> bool:
return self._has_argument_impl([args[0]], kwargs=kwargs)
@noKwargs
@typed_pos_args('compiler.has_multi_arguments', varargs=str)
@typed_kwargs('compiler.has_multi_arguments', _HAS_REQUIRED_KW)
@FeatureNew('compiler.has_multi_arguments', '0.37.0')
def has_multi_arguments_method(self, args: T.Tuple[T.List[str]], kwargs: 'TYPE_kwargs') -> bool:
return self._has_argument_impl(args[0])
def has_multi_arguments_method(self, args: T.Tuple[T.List[str]], kwargs: 'HasArgumentKW') -> bool:
return self._has_argument_impl(args[0], kwargs=kwargs)
@FeatureNew('compiler.get_supported_arguments', '0.43.0')
@typed_pos_args('compiler.get_supported_arguments', varargs=str)
@ -707,16 +753,16 @@ class CompilerHolder(ObjectHolder['Compiler']):
return []
@FeatureNew('compiler.has_link_argument', '0.46.0')
@noKwargs
@typed_pos_args('compiler.has_link_argument', str)
def has_link_argument_method(self, args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> bool:
return self._has_argument_impl([args[0]], mode=_TestMode.LINKER)
@typed_kwargs('compiler.has_link_argument', _HAS_REQUIRED_KW)
def has_link_argument_method(self, args: T.Tuple[str], kwargs: 'HasArgumentKW') -> bool:
return self._has_argument_impl([args[0]], mode=_TestMode.LINKER, kwargs=kwargs)
@FeatureNew('compiler.has_multi_link_argument', '0.46.0')
@noKwargs
@typed_pos_args('compiler.has_multi_link_argument', varargs=str)
def has_multi_link_arguments_method(self, args: T.Tuple[T.List[str]], kwargs: 'TYPE_kwargs') -> bool:
return self._has_argument_impl(args[0], mode=_TestMode.LINKER)
@typed_kwargs('compiler.has_multi_link_argument', _HAS_REQUIRED_KW)
def has_multi_link_arguments_method(self, args: T.Tuple[T.List[str]], kwargs: 'HasArgumentKW') -> bool:
return self._has_argument_impl(args[0], mode=_TestMode.LINKER, kwargs=kwargs)
@FeatureNew('compiler.get_supported_link_arguments', '0.46.0')
@noKwargs
@ -739,19 +785,33 @@ class CompilerHolder(ObjectHolder['Compiler']):
mlog.log('First supported link argument:', mlog.red('None'))
return []
def _has_function_attribute_impl(self, attr: str) -> bool:
def _has_function_attribute_impl(self, attr: str, kwargs: T.Optional['ExtractRequired'] = None) -> bool:
"""Common helper for function attribute testing."""
result, cached = self.compiler.has_func_attribute(attr, self.environment)
cached_msg = mlog.blue('(cached)') if cached else ''
h = mlog.green('YES') if result else mlog.red('NO')
mlog.log(f'Compiler for {self.compiler.get_display_language()} supports function attribute {attr}:', h, cached_msg)
return result
logargs: TV_LoggableList = [
f'Compiler for {self.compiler.get_display_language()} supports function attribute {attr}:',
]
kwargs = kwargs or {'required': False}
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
if disabled:
logargs += ['skipped: feature', mlog.bold(feature), 'disabled']
mlog.log(*logargs)
return False
had, cached = self.compiler.has_func_attribute(attr, self.environment)
if required and not had:
logargs += ['not usable']
raise InterpreterException(*logargs)
logargs += [
mlog.green('YES') if had else mlog.red('NO'),
mlog.blue('(cached)') if cached else ''
]
mlog.log(*logargs)
return had
@FeatureNew('compiler.has_function_attribute', '0.48.0')
@noKwargs
@typed_pos_args('compiler.has_function_attribute', str)
def has_func_attribute_method(self, args: T.Tuple[str], kwargs: 'TYPE_kwargs') -> bool:
return self._has_function_attribute_impl(args[0])
@typed_kwargs('compiler.has_function_attribute', _HAS_REQUIRED_KW)
def has_func_attribute_method(self, args: T.Tuple[str], kwargs: 'HasArgumentKW') -> bool:
return self._has_function_attribute_impl(args[0], kwargs)
@FeatureNew('compiler.get_supported_function_attributes', '0.48.0')
@noKwargs

@ -0,0 +1,67 @@
project('required keyword in has functions', 'c')
cc = meson.get_compiler('c')
opt = get_option('opt')
cc.has_function('printf', prefix : '#include<stdio.h>', required : true)
cc.has_type('time_t', prefix : '#include<time.h>', required : true)
cc.has_member('struct tm', 'tm_sec', prefix : '#include<time.h>', required : true)
cc.has_members('struct tm', ['tm_sec', 'tm_min'], prefix : '#include<time.h>', required : true)
cc.has_header('time.h', required : true)
cc.has_header_symbol('time.h', 'time', required : true)
assert(not cc.has_function('printf', prefix : '#include<stdio.h>', required : opt))
assert(not cc.has_type('time_t', prefix : '#include<time.h>', required : opt))
assert(not cc.has_member('struct tm', 'tm_sec', prefix : '#include<time.h>', required : opt))
assert(not cc.has_members('struct tm', ['tm_sec', 'tm_min'], prefix : '#include<time.h>', required : opt))
assert(not cc.has_header('time.h', required : opt))
assert(not cc.has_header_symbol('time.h', 'time', required : opt))
# compiler.has_argument
if cc.get_id() == 'msvc'
is_arg = '/O2'
else
is_arg = '-O2'
endif
cc.has_argument(is_arg, required: true)
assert(not cc.has_argument(is_arg, required: opt))
# compiler.has_multi_arguments
if cc.get_id() == 'gcc'
pre_arg = '-Wformat'
arg = '-Werror=format-security'
cc.has_multi_arguments([pre_arg, arg], required: true)
assert(not cc.has_multi_arguments(pre_arg, arg, required: opt))
endif
# compiler.has_link_argument
if cc.get_argument_syntax() == 'msvc'
is_arg = '/OPT:REF'
else
is_arg = '-Wl,-L/tmp'
endif
cc.has_link_argument(is_arg, required: true)
assert(not cc.has_link_argument(is_arg, required: opt))
# compiler.has_function_attribute
if not ['pgi', 'msvc', 'clang-cl', 'intel-cl'].contains(cc.get_id())
a = 'aligned'
cc.has_function_attribute(a, required: true)
assert(not cc.has_function_attribute(a, required: opt))
endif
testcase expect_error('''compiler.has_function keyword argument 'required' was of type str but should have been one of: bool, UserFeatureOption''')
cc.has_function('printf', required : 'not a bool')
endtestcase
testcase expect_error('''C function 'asdfkawlegsdiovapfjhkr' not usable''')
cc.has_function('asdfkawlegsdiovapfjhkr', required : true)
endtestcase
testcase expect_error('''C header 'asdfkawlegsdiovapfjhkr.h' not found''')
cc.has_header('asdfkawlegsdiovapfjhkr.h', required : true)
endtestcase
testcase expect_error('''C symbol time_not_found not found in header time.h''')
cc.has_header_symbol('time.h', 'time_not_found', required : true)
endtestcase

@ -0,0 +1 @@
option('opt', type: 'feature', value: 'disabled')
Loading…
Cancel
Save