From a4f1caa405a378c36cf856fd6e0e4769f3bc856d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 14 Sep 2020 22:39:32 +0200 Subject: docs: improve documentation of subproject fallback Automatic fallback to subprojects is complicated and should be pointed out outside the "fallback" keyword argument. It is also surprising that fallback to a subproject will not happen if override_dependency has already been used with the request dependency. Document all this. --- docs/markdown/Reference-manual.md | 49 +++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 15 deletions(-) (limited to 'docs') diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index c1e509ecd..b6ca743bf 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -440,33 +440,52 @@ system) with the given name with `pkg-config` and [with CMake](Dependencies.md#cmake) if `pkg-config` fails. Additionally, frameworks (OSX only) and [library-specific fallback detection logic](Dependencies.md#dependencies-with-custom-lookup-functionality) -are also supported. This function supports the following keyword -arguments: +are also supported. + +Dependencies can also be resolved in two other ways: + +* if the same name was used in a `meson.override_dependency` prior to + the call to `dependency`, the overriding dependency will be returned + unconditionally; that is, the overriding dependency will be used + independent of whether an external dependency is installed in the system. + Typically, `meson.override_dependency` will have been used by a + subproject. + +* by a fallback subproject which, if needed, will be brought into the current + build specification as if [`subproject()`](#subproject) had been called. + The subproject can be specified with the `fallback` argument. Alternatively, + if the `fallback` argument is absent and `required` is `true` or + [`enabled`](Build-options.md#features), *since 0.55.0* Meson will + automatically identify a subproject as a fallback if a wrap file + [provides](Wrap-dependency-system-manual.md#provide-section) the + dependency, or if a subproject has the same name as the dependency. + In the latter case, the subproject must use `meson.override_dependency` to + specify the replacement, or Meson will report a hard error. See the + [Wrap documentation](Wrap-dependency-system-manual.md#provide-section) + for more details. + +This function supports the following keyword arguments: - `default_options` *(since 0.37.0)*: an array of default option values that override those set in the subproject's `meson_options.txt` (like `default_options` in [`project()`](#project), they only have effect when Meson is run for the first time, and command line arguments override any default options in build files) -- `fallback`: specifies a subproject fallback to use in case the - dependency is not found in the system. The value is an array - `['subproj_name', 'subproj_dep']` where the first value is the name +- `fallback`: manually specifies a subproject + fallback to use in case the dependency is not found in the system. + This is useful if the automatic search is not applicable or if you + want to support versions of Meson older than 0.55.0. If the value is an + array `['subproj_name', 'subproj_dep']`, the first value is the name of the subproject and the second is the variable name in that subproject that contains a dependency object such as the return value of [`declare_dependency`](#declare_dependency) or [`dependency()`](#dependency), etc. Note that this means the fallback dependency may be a not-found dependency, in which case the value of the `required:` kwarg will be obeyed. - *(since 0.54.0)* `'subproj_dep'` argument can be omitted in the case the - subproject used `meson.override_dependency('dependency_name', subproj_dep)`. - In that case, the `fallback` keyword argument can be a single string instead - of a list of 2 strings. *Since 0.55.0* the `fallback` keyword argument can be - omitted when there is a wrap file or a directory with the same `dependency_name`, - and subproject registered the dependency using - `meson.override_dependency('dependency_name', subproj_dep)`, or when the wrap - file has `dependency_name` in its `[provide]` section. - See [Wrap documentation](Wrap-dependency-system-manual.md#provide-section) - for more details. + *Since 0.54.0* the value can be a single string, the subproject name; + in this case the subproject must use + `meson.override_dependency('dependency_name', subproj_dep)` + to specify the dependency object used in the superproject. - `language` *(since 0.42.0)*: defines what language-specific dependency to find if it's available for multiple languages. - `method`: defines the way the dependency is detected, the default is -- cgit v1.2.3 From 726b82205492a9e1f2dfd0fba96b237b51eeb428 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 8 Oct 2020 12:05:51 +0200 Subject: dependency: support boolean argument "allow_fallback" Sometimes, distros want to configure a project so that it does not use any bundled library. In this case, meson.build might want to do something like this, where slirp is a combo option with values auto/system/internal: slirp = dependency('', required: false) if get_option('slirp') != 'internal' slirp = dependency('slirp', required: get_option('slirp') == 'system') endif if not slirp.found() slirp = subproject('libslirp', ...) .variable('...') endif and we cannot use "fallback" because the "system" value should never look for a subproject. This worked until 0.54.x, but in 0.55.x this breaks because of the automatic subproject search. Note that the desired effect here is backwards compared to the policy of doing an automatic search on "required: true"; we only want to do the search if "required" is false! It would be possible to look for the dependency with `required: false` and issue the error manually, but it's ugly and it may produce an error message that looks "different" from Meson's. Instead, with this change it is possible to achieve this effect in an even simpler way: slirp = dependency('slirp', required: get_option('slirp') != 'auto', allow_fallback: get_option('slirp') == 'system' ? false : ['slirp', 'libslirp_dep']) The patch also adds support for "allow_fallback: true", which is simple and enables automatic fallback to a wrap even for non-required dependencies. --- docs/markdown/Reference-manual.md | 17 +++++++++++++---- docs/markdown/Wrap-dependency-system-manual.md | 10 ++++++---- docs/markdown/snippets/fallback_bool.md | 8 ++++++++ mesonbuild/dependencies/base.py | 6 ++++-- mesonbuild/interpreter.py | 15 ++++++++++++--- .../common/239 dependency allow_fallback/meson.build | 12 ++++++++++++ .../subprojects/foob/meson.build | 2 ++ .../subprojects/foob3/meson.build | 2 ++ test cases/failing/110 no fallback/meson.build | 2 ++ .../110 no fallback/subprojects/foob/meson.build | 2 ++ test cases/failing/110 no fallback/test.json | 8 ++++++++ 11 files changed, 71 insertions(+), 13 deletions(-) create mode 100644 docs/markdown/snippets/fallback_bool.md create mode 100644 test cases/common/239 dependency allow_fallback/meson.build create mode 100644 test cases/common/239 dependency allow_fallback/subprojects/foob/meson.build create mode 100644 test cases/common/239 dependency allow_fallback/subprojects/foob3/meson.build create mode 100644 test cases/failing/110 no fallback/meson.build create mode 100644 test cases/failing/110 no fallback/subprojects/foob/meson.build create mode 100644 test cases/failing/110 no fallback/test.json (limited to 'docs') diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index b6ca743bf..38ae5582b 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -454,15 +454,15 @@ Dependencies can also be resolved in two other ways: * by a fallback subproject which, if needed, will be brought into the current build specification as if [`subproject()`](#subproject) had been called. The subproject can be specified with the `fallback` argument. Alternatively, - if the `fallback` argument is absent and `required` is `true` or - [`enabled`](Build-options.md#features), *since 0.55.0* Meson will + if the `fallback` argument is absent, *since 0.55.0* Meson can automatically identify a subproject as a fallback if a wrap file [provides](Wrap-dependency-system-manual.md#provide-section) the dependency, or if a subproject has the same name as the dependency. In the latter case, the subproject must use `meson.override_dependency` to specify the replacement, or Meson will report a hard error. See the [Wrap documentation](Wrap-dependency-system-manual.md#provide-section) - for more details. + for more details. This automatic search can be controlled using the + `allow_fallback` keyword argument. This function supports the following keyword arguments: @@ -471,7 +471,16 @@ This function supports the following keyword arguments: (like `default_options` in [`project()`](#project), they only have effect when Meson is run for the first time, and command line arguments override any default options in build files) -- `fallback`: manually specifies a subproject +- `allow_fallback` (boolean argument, *since 0.56.0*): specifies whether Meson + should automatically pick a fallback subproject in case the dependency + is not found in the system. If `true` and the dependency is not found + on the system, Meson will fallback to a subproject that provides this + dependency. If `false`, Meson will not fallback even if a subproject + provides this dependency. By default, Meson will do so if `required` + is `true` or [`enabled`](Build-options.md#features); see the [Wrap + documentation](Wrap-dependency-system-manual.md#provide-section) + for more details. +- `fallback` (string or array argument): manually specifies a subproject fallback to use in case the dependency is not found in the system. This is useful if the automatic search is not applicable or if you want to support versions of Meson older than 0.55.0. If the value is an diff --git a/docs/markdown/Wrap-dependency-system-manual.md b/docs/markdown/Wrap-dependency-system-manual.md index 8e6282e61..4189709a5 100644 --- a/docs/markdown/Wrap-dependency-system-manual.md +++ b/docs/markdown/Wrap-dependency-system-manual.md @@ -182,10 +182,12 @@ endif `dependency('foo-1.0', required: get_option('foo_opt'))` will only fallback when the user sets `foo_opt` to `enabled` instead of `auto`. -If it is desired to fallback for an optional dependency, the `fallback` keyword -argument must be passed explicitly. For example -`dependency('foo-1.0', required: get_option('foo_opt'), fallback: 'foo')` will -use the fallback even when `foo_opt` is set to `auto`. +If it is desired to fallback for an optional dependency, the `fallback` +or `allow_fallback` keyword arguments must be passed explicitly. *Since +0.56.0*, `dependency('foo-1.0', required: get_option('foo_opt'), +allow_fallback: true)` will use the fallback even when `foo_opt` is set +to `auto`. On version *0.55.0* the same effect could be achieved with +`dependency('foo-1.0', required: get_option('foo_opt'), fallback: 'foo')`. This mechanism assumes the subproject calls `meson.override_dependency('foo-1.0', foo_dep)` so Meson knows which dependency object should be used as fallback. Since that diff --git a/docs/markdown/snippets/fallback_bool.md b/docs/markdown/snippets/fallback_bool.md new file mode 100644 index 000000000..14bef50e5 --- /dev/null +++ b/docs/markdown/snippets/fallback_bool.md @@ -0,0 +1,8 @@ +## Controlling subproject dependencies with `dependency(allow_fallback: ...)` + +As an alternative to the `fallback` keyword argument to `dependency`, +you may use `allow_fallback`, which accepts a boolean value. If `true` +and the dependency is not found on the system, Meson will fallback +to a subproject that provides this dependency, even if the dependency +is optional. If `false`, Meson will not fallback even if a subproject +provides this dependency. diff --git a/mesonbuild/dependencies/base.py b/mesonbuild/dependencies/base.py index 7c64b9c49..95202fee2 100644 --- a/mesonbuild/dependencies/base.py +++ b/mesonbuild/dependencies/base.py @@ -2320,9 +2320,11 @@ def get_dep_identifier(name, kwargs) -> T.Tuple: # 'version' is irrelevant for caching; the caller must check version matches # 'native' is handled above with `for_machine` # 'required' is irrelevant for caching; the caller handles it separately - # 'fallback' subprojects cannot be cached -- they must be initialized + # 'fallback' and 'allow_fallback' is not part of the cache because, + # once a dependency has been found through a fallback, it should + # be used for the rest of the Meson run. # 'default_options' is only used in fallback case - if key in ('version', 'native', 'required', 'fallback', 'default_options'): + if key in ('version', 'native', 'required', 'fallback', 'allow_fallback', 'default_options'): continue # All keyword arguments are strings, ints, or lists (or lists of lists) if isinstance(value, list): diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 07cf1e2a7..ca1411e4a 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -3707,21 +3707,30 @@ external dependencies (including libraries) must go to "dependencies".''') return self.notfound_dependency() fallback = kwargs.get('fallback', None) + allow_fallback = kwargs.get('allow_fallback', None) + if allow_fallback is not None: + FeatureNew.single_use('"allow_fallback" keyword argument for dependency', '0.56.0', self.subproject) + if fallback is not None: + raise InvalidArguments('"fallback" and "allow_fallback" arguments are mutually exclusive') + if not isinstance(allow_fallback, bool): + raise InvalidArguments('"allow_fallback" argument must be boolean') # If "fallback" is absent, look for an implicit fallback. - if name and fallback is None: + if name and fallback is None and allow_fallback is not False: # Add an implicit fallback if we have a wrap file or a directory with the same name, # but only if this dependency is required. It is common to first check for a pkg-config, # then fallback to use find_library() and only afterward check again the dependency # with a fallback. If the fallback has already been configured then we have to use it # even if the dependency is not required. provider = self.environment.wrap_resolver.find_dep_provider(name) + if not provider and allow_fallback is True: + raise InvalidArguments('Fallback wrap or subproject not found for dependency \'%s\'' % name) dirname = mesonlib.listify(provider)[0] - if provider and (required or self.get_subproject(dirname)): + if provider and (allow_fallback is True or required or self.get_subproject(dirname)): fallback = provider if 'default_options' in kwargs and not fallback: - mlog.warning('The "default_options" keyworg argument does nothing without a "fallback" keyword argument.', + mlog.warning('The "default_options" keyword argument does nothing without a fallback subproject.', location=self.current_node) # writing just "dependency('')" is an error, because it can only fail diff --git a/test cases/common/239 dependency allow_fallback/meson.build b/test cases/common/239 dependency allow_fallback/meson.build new file mode 100644 index 000000000..b189faf12 --- /dev/null +++ b/test cases/common/239 dependency allow_fallback/meson.build @@ -0,0 +1,12 @@ +project('subproject fallback', 'c') + +foob_dep = dependency('foob', allow_fallback: true, required: false) +assert(foob_dep.found()) + +# Careful! Once a submodule has been triggered and it has +# overridden the dependency, it sticks. +foob_dep = dependency('foob', allow_fallback: false, required: false) +assert(foob_dep.found()) + +foob3_dep = dependency('foob3', allow_fallback: false, required: false) +assert(not foob3_dep.found()) diff --git a/test cases/common/239 dependency allow_fallback/subprojects/foob/meson.build b/test cases/common/239 dependency allow_fallback/subprojects/foob/meson.build new file mode 100644 index 000000000..b2c4814e2 --- /dev/null +++ b/test cases/common/239 dependency allow_fallback/subprojects/foob/meson.build @@ -0,0 +1,2 @@ +project('foob', 'c') +meson.override_dependency('foob', declare_dependency()) diff --git a/test cases/common/239 dependency allow_fallback/subprojects/foob3/meson.build b/test cases/common/239 dependency allow_fallback/subprojects/foob3/meson.build new file mode 100644 index 000000000..9fdb18817 --- /dev/null +++ b/test cases/common/239 dependency allow_fallback/subprojects/foob3/meson.build @@ -0,0 +1,2 @@ +project('foob3', 'c') +# Note that there is no override_dependency here diff --git a/test cases/failing/110 no fallback/meson.build b/test cases/failing/110 no fallback/meson.build new file mode 100644 index 000000000..0101bb84e --- /dev/null +++ b/test cases/failing/110 no fallback/meson.build @@ -0,0 +1,2 @@ +project('no fallback', 'c') +foob_dep = dependency('foob', allow_fallback: false, required: true) diff --git a/test cases/failing/110 no fallback/subprojects/foob/meson.build b/test cases/failing/110 no fallback/subprojects/foob/meson.build new file mode 100644 index 000000000..b2c4814e2 --- /dev/null +++ b/test cases/failing/110 no fallback/subprojects/foob/meson.build @@ -0,0 +1,2 @@ +project('foob', 'c') +meson.override_dependency('foob', declare_dependency()) diff --git a/test cases/failing/110 no fallback/test.json b/test cases/failing/110 no fallback/test.json new file mode 100644 index 000000000..e0340616c --- /dev/null +++ b/test cases/failing/110 no fallback/test.json @@ -0,0 +1,8 @@ +{ + "stdout": [ + { + "match": "re", + "line": ".*/meson\\.build:2:0: ERROR: (Pkg-config binary for machine MachineChoice\\.HOST not found\\. Giving up\\.|Dependency \"foob\" not found, tried .*)" + } + ] +} -- cgit v1.2.3