diff options
author | Jussi Pakkanen <jpakkane@gmail.com> | 2018-12-29 18:57:21 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-12-29 18:57:21 +0200 |
commit | 3b495c397ec1baffe99d63031f2b03301b7f6ba5 (patch) | |
tree | 660439db681ece26c622af11eff2535c507fca1c | |
parent | 3d7c32a34e9d7a1f0e0c5c701f1afdf411b0194c (diff) | |
parent | b6cede2928e80d6bd0512c82ad6787c03faae1e6 (diff) | |
download | meson-3b495c397ec1baffe99d63031f2b03301b7f6ba5.tar.gz |
Merge pull request #4672 from xclaesse/find-library-headers
find_library: Add 'has_headers' kwarg
-rw-r--r-- | docs/markdown/Reference-manual.md | 16 | ||||
-rw-r--r-- | docs/markdown/snippets/find_library_header.md | 21 | ||||
-rw-r--r-- | mesonbuild/interpreter.py | 98 | ||||
-rw-r--r-- | test cases/common/209 find_library and headers/foo.h | 1 | ||||
-rw-r--r-- | test cases/common/209 find_library and headers/meson.build | 23 |
5 files changed, 120 insertions, 39 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 8d0d123e3..92a7aed7e 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -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 diff --git a/docs/markdown/snippets/find_library_header.md b/docs/markdown/snippets/find_library_header.md new file mode 100644 index 000000000..55597abc5 --- /dev/null +++ b/docs/markdown/snippets/find_library_header.md @@ -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('.')) +``` diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 6fef4a90a..0571eba92 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -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: diff --git a/test cases/common/209 find_library and headers/foo.h b/test cases/common/209 find_library and headers/foo.h new file mode 100644 index 000000000..014e06e82 --- /dev/null +++ b/test cases/common/209 find_library and headers/foo.h @@ -0,0 +1 @@ +#define VAL 42 diff --git a/test cases/common/209 find_library and headers/meson.build b/test cases/common/209 find_library and headers/meson.build new file mode 100644 index 000000000..bcd71f12a --- /dev/null +++ b/test cases/common/209 find_library and headers/meson.build @@ -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') |