aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Claessens <xavier.claessens@collabora.com>2023-06-13 12:56:19 -0400
committerXavier Claessens <xavier.claessens@collabora.com>2024-02-26 10:03:52 -0500
commit4d55645c397e2f338a09ebd1f0f564c64b98dafa (patch)
tree49900e8e73216724ec13f99707694fd3658f29a4
parent114e032e6a27d0eb9ef5de1a811ce7b0461c3efc (diff)
downloadmeson-4d55645c397e2f338a09ebd1f0f564c64b98dafa.zip
meson-4d55645c397e2f338a09ebd1f0f564c64b98dafa.tar.gz
meson-4d55645c397e2f338a09ebd1f0f564c64b98dafa.tar.bz2
cargo: Abort if features are missing
-rw-r--r--docs/markdown/Wrap-dependency-system-manual.md3
-rw-r--r--mesonbuild/cargo/interpreter.py71
2 files changed, 72 insertions, 2 deletions
diff --git a/docs/markdown/Wrap-dependency-system-manual.md b/docs/markdown/Wrap-dependency-system-manual.md
index 7a0cea6..70a7b30 100644
--- a/docs/markdown/Wrap-dependency-system-manual.md
+++ b/docs/markdown/Wrap-dependency-system-manual.md
@@ -345,7 +345,8 @@ the main project depends on `foo-rs` and `bar-rs`, and they both depend on
`common-rs`. The main project will first look up `foo-rs` which itself will
configure `common-rs` with a set of features. Later, when `bar-rs` does a lookup
for `common-rs` it has already been configured and the set of features cannot be
-changed. It is currently the responsability of the main project to resolve those
+changed. If `bar-rs` wants extra features from `common-rs`, Meson will error out.
+It is currently the responsability of the main project to resolve those
issues by enabling extra features on each subproject:
```meson
project(...,
diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py
index 570087d..6680ea1 100644
--- a/mesonbuild/cargo/interpreter.py
+++ b/mesonbuild/cargo/interpreter.py
@@ -354,9 +354,12 @@ def _dependency_varname(package_name: str) -> str:
return f'{fixup_meson_varname(package_name)}_dep'
+_OPTION_NAME_PREFIX = 'feature-'
+
+
def _option_name(feature: str) -> str:
# Add a prefix to avoid collision with Meson reserved options (e.g. "debug")
- return f'feature-{feature}'
+ return _OPTION_NAME_PREFIX + feature
def _options_varname(depname: str) -> str:
@@ -457,6 +460,11 @@ def _create_features(cargo: Manifest, build: builder.Builder) -> T.List[mparser.
ast.append(build.if_(build.function('get_option', [build.string(_option_name(feature))]), build.block(lines)))
+ ast.append(build.function('message', [
+ build.string('Enabled features:'),
+ build.method('keys', build.identifier('features'))],
+ ))
+
return ast
@@ -482,7 +490,19 @@ def _create_dependencies(cargo: Manifest, build: builder.Builder) -> T.List[mpar
build.string(name), build.bool(False)
])
+ # Lookup for this dependency with the features we want in default_options kwarg.
+ #
+ # However, this subproject could have been previously configured with a
+ # different set of features. Cargo collects the set of features globally
+ # but Meson can only use features enabled by the first call that triggered
+ # the configuration of that subproject.
+ #
+ # Verify all features that we need are actually enabled for that dependency,
+ # otherwise abort with an error message. The user has to set the corresponding
+ # option manually with -Dxxx-rs:feature-yyy=true, or the main project can do
+ # that in its project(..., default_options: ['xxx-rs:feature-yyy=true']).
ast.extend([
+ # xxx_dep = dependency('xxx', version : ..., default_options : xxx_options)
build.assign(
build.function(
'dependency',
@@ -491,6 +511,52 @@ def _create_dependencies(cargo: Manifest, build: builder.Builder) -> T.List[mpar
),
_dependency_varname(package_name),
),
+ # if xxx_dep.found()
+ build.if_(build.method('found', build.identifier(_dependency_varname(package_name))), build.block([
+ # actual_features = xxx_dep.get_variable('features', default_value : '').split(',')
+ build.assign(
+ build.method(
+ 'split',
+ build.method(
+ 'get_variable',
+ build.identifier(_dependency_varname(package_name)),
+ [build.string('features')],
+ {'default_value': build.string('')}
+ ),
+ [build.string(',')],
+ ),
+ 'actual_features'
+ ),
+ # needed_features = []
+ # foreach f, _ : xxx_options
+ # needed_features += f.substring(8)
+ # endforeach
+ build.assign(build.array([]), 'needed_features'),
+ build.foreach(['f', 'enabled'], build.identifier(_options_varname(name)), build.block([
+ build.if_(build.identifier('enabled'), build.block([
+ build.plusassign(
+ build.method('substring', build.identifier('f'), [build.number(len(_OPTION_NAME_PREFIX))]),
+ 'needed_features'),
+ ])),
+ ])),
+ # foreach f : needed_features
+ # if f not in actual_features
+ # error()
+ # endif
+ # endforeach
+ build.foreach(['f'], build.identifier('needed_features'), build.block([
+ build.if_(build.not_in(build.identifier('f'), build.identifier('actual_features')), build.block([
+ build.function('error', [
+ build.string('Dependency'),
+ build.string(_dependency_name(package_name)),
+ build.string('previously configured with features'),
+ build.identifier('actual_features'),
+ build.string('but need'),
+ build.identifier('needed_features'),
+ ])
+ ]))
+ ])),
+ ])),
])
return ast
@@ -555,6 +621,9 @@ def _create_lib(cargo: Manifest, build: builder.Builder, crate_type: manifest.CR
'declare_dependency',
kw={
'link_with': build.identifier('lib'),
+ 'variables': build.dict({
+ build.string('features'): build.method('join', build.string(','), [build.method('keys', build.identifier('features'))]),
+ })
},
),
'dep'