Merge pull request #4672 from xclaesse/find-library-headers

find_library: Add 'has_headers' kwarg
pull/4691/head
Jussi Pakkanen 6 years ago committed by GitHub
commit 3b495c397e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 16
      docs/markdown/Reference-manual.md
  2. 21
      docs/markdown/snippets/find_library_header.md
  3. 98
      mesonbuild/interpreter.py
  4. 1
      test cases/common/209 find_library and headers/foo.h
  5. 23
      test cases/common/209 find_library and headers/meson.build

@ -1704,7 +1704,10 @@ the following methods:
option can also be passed to the `required` keyword argument.
*Since 0.49.0* if the keyword argument `disabler` is `true` and the
dependency couldn't be found, return a [disabler object](#disabler-object)
instead of a not-found dependency.
instead of a not-found dependency. *Since 0.50.0* the `has_headers` keyword
argument can be a list of header files that must be found as well, using
`has_header()` method. All keyword arguments prefixed with `header_` will be
passed down to `has_header()` method with the prefix removed.
- `first_supported_argument(list_of_strings)`, given a list of
strings, returns the first argument that passes the `has_argument`
@ -1760,7 +1763,9 @@ the following methods:
the `prefix` keyword. In order to look for headers in a specific
directory you can use `args : '-I/extra/include/dir`, but this
should only be used in exceptional cases for includes that can't be
detected via pkg-config and passed via `dependencies`.
detected via pkg-config and passed via `dependencies`. Since *0.50.0* the
`required` keyword argument can be used to abort if the header cannot be
found.
- `has_header` returns true if the specified header *exists*, and is
faster than `check_header()` since it only does a pre-processor check.
@ -1769,13 +1774,16 @@ the following methods:
the `prefix` keyword. In order to look for headers in a specific
directory you can use `args : '-I/extra/include/dir`, but this
should only be used in exceptional cases for includes that can't be
detected via pkg-config and passed via `dependencies`.
detected via pkg-config and passed via `dependencies`. Since *0.50.0* the
`required` keyword argument can be used to abort if the header cannot be
found.
- `has_header_symbol(headername, symbolname)` allows one to detect
whether a particular symbol (function, variable, #define, type
definition, etc) is declared in the specified header, you can
specify external dependencies to use with `dependencies` keyword
argument.
argument. Since *0.50.0* the `required` keyword argument can be used to abort
if the symbol cannot be found.
- `has_member(typename, membername)` takes two arguments, type name
and member name and returns true if the type has the specified

@ -0,0 +1,21 @@
## Find library with its headers
The `find_library()` method can now also verify if the library's headers are
found in a single call, using the `has_header()` method internally.
```meson
# Aborts if the 'z' library is found but not its header file
zlib = find_library('z', has_headers : 'zlib.h')
# Returns not-found if the 'z' library is found but not its header file
zlib = find_library('z', has_headers : 'zlib.h', required : false)
```
Any keyword argument with the `header_` prefix passed to `find_library()` will
be passed to the `has_header()` method with the prefix removed.
```meson
libfoo = find_library('foo',
has_headers : ['foo.h', 'bar.h'],
header_prefix : '#include <baz.h>',
header_include_directories : include_directories('.'))
```

@ -86,8 +86,8 @@ class FeatureOptionHolder(InterpreterObject, ObjectHolder):
def auto_method(self, args, kwargs):
return self.held_object.is_auto()
def extract_required_kwarg(kwargs, subproject, feature_check=None):
val = kwargs.get('required', True)
def extract_required_kwarg(kwargs, subproject, feature_check=None, default=True):
val = kwargs.get('required', default)
disabled = False
required = False
feature = None
@ -900,6 +900,23 @@ class SubprojectHolder(InterpreterObject, ObjectHolder):
raise InvalidArguments('Requested variable "{0}" not found.'.format(varname))
return self.held_object.variables[varname]
header_permitted_kwargs = set([
'required',
'prefix',
'no_builtin_args',
'include_directories',
'args',
'dependencies',
])
find_library_permitted_kwargs = set([
'has_headers',
'required',
'dirs',
])
find_library_permitted_kwargs |= set(['header_' + k for k in header_permitted_kwargs])
class CompilerHolder(InterpreterObject):
def __init__(self, compiler, env, subproject):
InterpreterObject.__init__(self)
@ -1344,13 +1361,8 @@ class CompilerHolder(InterpreterObject):
return result
@FeatureNew('compiler.check_header', '0.47.0')
@permittedKwargs({
'prefix',
'no_builtin_args',
'include_directories',
'args',
'dependencies',
})
@FeatureNewKwargs('compiler.check_header', '0.50.0', ['required'])
@permittedKwargs(header_permitted_kwargs)
def check_header_method(self, args, kwargs):
if len(args) != 1:
raise InterpreterException('check_header method takes exactly one argument.')
@ -1359,25 +1371,26 @@ class CompilerHolder(InterpreterObject):
prefix = kwargs.get('prefix', '')
if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of has_header must be a string.')
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
if disabled:
mlog.log('Check usable header', mlog.bold(hname, True), 'skipped: feature', mlog.bold(feature), 'disabled')
return False
extra_args = functools.partial(self.determine_args, kwargs)
deps, msg = self.determine_dependencies(kwargs)
haz = self.compiler.check_header(hname, prefix, self.environment,
extra_args=extra_args,
dependencies=deps)
if haz:
if required and not haz:
raise InterpreterException('{} header {!r} not usable'.format(self.compiler.get_display_language(), hname))
elif haz:
h = mlog.green('YES')
else:
h = mlog.red('NO')
mlog.log('Check usable header', mlog.bold(hname, True), msg, h)
return haz
@permittedKwargs({
'prefix',
'no_builtin_args',
'include_directories',
'args',
'dependencies',
})
@FeatureNewKwargs('compiler.has_header', '0.50.0', ['required'])
@permittedKwargs(header_permitted_kwargs)
def has_header_method(self, args, kwargs):
if len(args) != 1:
raise InterpreterException('has_header method takes exactly one argument.')
@ -1386,24 +1399,25 @@ class CompilerHolder(InterpreterObject):
prefix = kwargs.get('prefix', '')
if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of has_header must be a string.')
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
if disabled:
mlog.log('Has header', mlog.bold(hname, True), 'skipped: feature', mlog.bold(feature), 'disabled')
return False
extra_args = functools.partial(self.determine_args, kwargs)
deps, msg = self.determine_dependencies(kwargs)
haz = self.compiler.has_header(hname, prefix, self.environment,
extra_args=extra_args, dependencies=deps)
if haz:
if required and not haz:
raise InterpreterException('{} header {!r} not found'.format(self.compiler.get_display_language(), hname))
elif haz:
h = mlog.green('YES')
else:
h = mlog.red('NO')
mlog.log('Has header', mlog.bold(hname, True), msg, h)
return haz
@permittedKwargs({
'prefix',
'no_builtin_args',
'include_directories',
'args',
'dependencies',
})
@FeatureNewKwargs('compiler.has_header_symbol', '0.50.0', ['required'])
@permittedKwargs(header_permitted_kwargs)
def has_header_symbol_method(self, args, kwargs):
if len(args) != 2:
raise InterpreterException('has_header_symbol method takes exactly two arguments.')
@ -1413,24 +1427,35 @@ class CompilerHolder(InterpreterObject):
prefix = kwargs.get('prefix', '')
if not isinstance(prefix, str):
raise InterpreterException('Prefix argument of has_header_symbol must be a string.')
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject, default=False)
if disabled:
mlog.log('Header <{0}> has symbol'.format(hname), mlog.bold(symbol, True), 'skipped: feature', mlog.bold(feature), 'disabled')
return False
extra_args = functools.partial(self.determine_args, kwargs)
deps, msg = self.determine_dependencies(kwargs)
haz = self.compiler.has_header_symbol(hname, symbol, prefix, self.environment,
extra_args=extra_args,
dependencies=deps)
if haz:
if required and not haz:
raise InterpreterException('{} symbol {} not found in header {}'.format(self.compiler.get_display_language(), symbol, hname))
elif haz:
h = mlog.green('YES')
else:
h = mlog.red('NO')
mlog.log('Header <{0}> has symbol'.format(hname), mlog.bold(symbol, True), msg, h)
return haz
def notfound_library(self, libname):
lib = dependencies.ExternalLibrary(libname, None,
self.environment,
self.compiler.language,
silent=True)
return ExternalLibraryHolder(lib, self.subproject)
@FeatureNewKwargs('compiler.find_library', '0.50.0', ['has_headers'])
@FeatureNewKwargs('compiler.find_library', '0.49.0', ['disabler'])
@disablerIfNotFound
@permittedKwargs({
'required',
'dirs',
})
@permittedKwargs(find_library_permitted_kwargs)
def find_library_method(self, args, kwargs):
# TODO add dependencies support?
if len(args) != 1:
@ -1442,11 +1467,14 @@ class CompilerHolder(InterpreterObject):
disabled, required, feature = extract_required_kwarg(kwargs, self.subproject)
if disabled:
mlog.log('Library', mlog.bold(libname), 'skipped: feature', mlog.bold(feature), 'disabled')
lib = dependencies.ExternalLibrary(libname, None,
self.environment,
self.compiler.language,
silent=True)
return ExternalLibraryHolder(lib, self.subproject)
return self.notfound_library(libname)
has_header_kwargs = {k[7:]: v for k, v in kwargs.items() if k.startswith('header_')}
has_header_kwargs['required'] = required
headers = mesonlib.stringlistify(kwargs.get('has_headers', []))
for h in headers:
if not self.has_header_method([h], has_header_kwargs):
return self.notfound_library(libname)
search_dirs = mesonlib.stringlistify(kwargs.get('dirs', []))
for i in search_dirs:

@ -0,0 +1,23 @@
project('find library and headers', 'c')
cc = meson.get_compiler('c')
if not cc.find_library('z', required : false).found()
error('MESON_SKIP_TEST: zlib not found.')
endif
lib = cc.find_library('z',
has_headers : 'foo.h',
required : false)
assert(not lib.found(), 'Header should be missing')
lib = cc.find_library('z',
has_headers : 'foo.h',
header_include_directories : include_directories('.'))
assert(lib.found(), 'Header should be found')
lib = cc.find_library('z',
has_headers : ['foo.h', 'bar.h'],
header_include_directories : include_directories('.'),
required : false)
assert(not lib.found(), 'One header should be missing')
Loading…
Cancel
Save