diff --git a/docs/markdown/Wrap-dependency-system-manual.md b/docs/markdown/Wrap-dependency-system-manual.md index ff092e8a8..eb5de1b13 100644 --- a/docs/markdown/Wrap-dependency-system-manual.md +++ b/docs/markdown/Wrap-dependency-system-manual.md @@ -189,6 +189,9 @@ endif `dependency('foo-1.0', required: get_option('foo_opt'))` will only fallback when the user sets `foo_opt` to `enabled` instead of `auto`. +*Since 0.58.0* optional dependency like above will fallback to the subproject +defined in the wrap file in the case `wrap_mode` is set to `forcefallback` +or `force_fallback_for` contains the subproject. If it is desired to fallback for an optional dependency, the `fallback` or `allow_fallback` keyword arguments must be passed diff --git a/docs/markdown/snippets/forcefallback.md b/docs/markdown/snippets/forcefallback.md new file mode 100644 index 000000000..7af5d39ed --- /dev/null +++ b/docs/markdown/snippets/forcefallback.md @@ -0,0 +1,21 @@ +## Use fallback from wrap file when force fallback + +Optional dependency like below will now fallback to the subproject +defined in the wrap file in the case `wrap_mode` is set to `forcefallback` +or `force_fallback_for` contains the subproject. + +```meson +# required is false because we could fallback to cc.find_library(), but in the +# forcefallback case this now configure the subproject. +dep = dependency('foo-1.0', required: false) +if not dep.found() + dep = cc.find_library('foo', has_headers: 'foo.h') +endif +``` + +```ini +[wrap-file] +... +[provide] +dependency_names = foo-1.0 +``` diff --git a/mesonbuild/interpreter/interpreter.py b/mesonbuild/interpreter/interpreter.py index d17d7f09a..143147dde 100644 --- a/mesonbuild/interpreter/interpreter.py +++ b/mesonbuild/interpreter/interpreter.py @@ -1598,6 +1598,11 @@ external dependencies (including libraries) must go to "dependencies".''') if not isinstance(allow_fallback, bool): raise InvalidArguments('"allow_fallback" argument must be boolean') + wrap_mode = self.coredata.get_option(OptionKey('wrap_mode')) + force_fallback_for = self.coredata.get_option(OptionKey('force_fallback_for')) + force_fallback |= (wrap_mode == WrapMode.forcefallback or + name in force_fallback_for) + # If "fallback" is absent, look for an implicit fallback. 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, @@ -1609,7 +1614,8 @@ external dependencies (including libraries) must go to "dependencies".''') if not provider and allow_fallback is True: raise InvalidArguments('Fallback wrap or subproject not found for dependency \'%s\'' % name) subp_name = mesonlib.listify(provider)[0] - if provider and (allow_fallback is True or required or self.get_subproject(subp_name)): + force_fallback |= subp_name in force_fallback_for + if provider and (allow_fallback is True or required or self.get_subproject(subp_name) or force_fallback): fallback = provider if 'default_options' in kwargs and not fallback: @@ -1640,13 +1646,7 @@ external dependencies (including libraries) must go to "dependencies".''') subp_name, varname = self.get_subproject_infos(fallback) if self.get_subproject(subp_name): return self.get_subproject_dep(name, display_name, subp_name, varname, kwargs) - - wrap_mode = self.coredata.get_option(OptionKey('wrap_mode')) - force_fallback_for = self.coredata.get_option(OptionKey('force_fallback_for')) - force_fallback = (force_fallback or - wrap_mode == WrapMode.forcefallback or - name in force_fallback_for or - subp_name in force_fallback_for) + force_fallback |= subp_name in force_fallback_for if name != '' and (not fallback or not force_fallback): self._handle_featurenew_dependencies(name) diff --git a/run_unittests.py b/run_unittests.py index 6756c80ee..905cb8f72 100755 --- a/run_unittests.py +++ b/run_unittests.py @@ -2513,6 +2513,14 @@ class AllPlatformTests(BasePlatformTests): self.build() self.run_tests() + def test_implicit_forcefallback(self): + testdir = os.path.join(self.unit_test_dir, '95 implicit force fallback') + with self.assertRaises(subprocess.CalledProcessError) as cm: + self.init(testdir) + self.init(testdir, extra_args=['--wrap-mode=forcefallback']) + self.new_builddir() + self.init(testdir, extra_args=['--force-fallback-for=something']) + def test_nopromote(self): testdir = os.path.join(self.common_test_dir, '99 subproject subdir') with self.assertRaises(subprocess.CalledProcessError) as cm: diff --git a/test cases/unit/95 implicit force fallback/meson.build b/test cases/unit/95 implicit force fallback/meson.build new file mode 100644 index 000000000..623a338db --- /dev/null +++ b/test cases/unit/95 implicit force fallback/meson.build @@ -0,0 +1,8 @@ +project('implicit force fallback') + +# The dependency 'something' is provided by a subproject. Normally this won't +# use the fallback subproject because required is false. However this unit test +# is configured with wrap_mode=forcefallback and force_fallback_for=something +# in which case we are expecting the fallback to be done. +d = dependency('something', required: false) +assert(d.found()) diff --git a/test cases/unit/95 implicit force fallback/subprojects/something/meson.build b/test cases/unit/95 implicit force fallback/subprojects/something/meson.build new file mode 100644 index 000000000..89a4727dc --- /dev/null +++ b/test cases/unit/95 implicit force fallback/subprojects/something/meson.build @@ -0,0 +1,3 @@ +project('something') + +meson.override_dependency('something', declare_dependency())