diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index f2d21297a..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` 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 ', + header_include_directories : include_directories('.')) +``` diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 6870fc1e6..0571eba92 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -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) @@ -1345,14 +1362,7 @@ class CompilerHolder(InterpreterObject): @FeatureNew('compiler.check_header', '0.47.0') @FeatureNewKwargs('compiler.check_header', '0.50.0', ['required']) - @permittedKwargs({ - 'required', - 'prefix', - 'no_builtin_args', - 'include_directories', - 'args', - 'dependencies', - }) + @permittedKwargs(header_permitted_kwargs) def check_header_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('check_header method takes exactly one argument.') @@ -1380,14 +1390,7 @@ class CompilerHolder(InterpreterObject): return haz @FeatureNewKwargs('compiler.has_header', '0.50.0', ['required']) - @permittedKwargs({ - 'required', - 'prefix', - 'no_builtin_args', - 'include_directories', - 'args', - 'dependencies', - }) + @permittedKwargs(header_permitted_kwargs) def has_header_method(self, args, kwargs): if len(args) != 1: raise InterpreterException('has_header method takes exactly one argument.') @@ -1414,14 +1417,7 @@ class CompilerHolder(InterpreterObject): return haz @FeatureNewKwargs('compiler.has_header_symbol', '0.50.0', ['required']) - @permittedKwargs({ - 'required', - 'prefix', - 'no_builtin_args', - 'include_directories', - 'args', - 'dependencies', - }) + @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.') @@ -1449,12 +1445,17 @@ class CompilerHolder(InterpreterObject): 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: @@ -1466,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')