diff options
| author | Paolo Bonzini <pbonzini@redhat.com> | 2025-10-17 18:50:15 +0200 |
|---|---|---|
| committer | Paolo Bonzini <pbonzini@redhat.com> | 2025-12-22 11:58:30 +0100 |
| commit | 5de9723535506f625e093cbf9549b3ce0bf5940f (patch) | |
| tree | 49dedb66344ec9fad644c03a21fff43990302ec2 | |
| parent | 646593856800c55f44fe2b15991570737709a36e (diff) | |
| download | meson-5de9723535506f625e093cbf9549b3ce0bf5940f.tar.gz | |
cargo: add configurable features to Interpreter
Add features property to cargo.Interpreter to make default features configurable;
customization of which features are enabled by default is triggered by
rust.workspace().
Fixes: #14290
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
| -rw-r--r-- | docs/markdown/Rust-module.md | 40 | ||||
| -rw-r--r-- | mesonbuild/cargo/interpreter.py | 22 | ||||
| -rw-r--r-- | mesonbuild/modules/rust.py | 31 |
3 files changed, 87 insertions, 6 deletions
diff --git a/docs/markdown/Rust-module.md b/docs/markdown/Rust-module.md index 64c4c2311..1e8305d07 100644 --- a/docs/markdown/Rust-module.md +++ b/docs/markdown/Rust-module.md @@ -174,15 +174,29 @@ Only a subset of [[shared_library]] keyword arguments are allowed: ### workspace() +Basic usage: + ``` cargo_ws = rustmod.workspace() ``` +With custom features: + +``` +feature_list = get_feature('f1') ? ['feature1'] : [] +feature_list += get_feature('f2') ? ['feature2'] : [] +cargo_ws = rustmod.workspace(features: feature_list) +``` + *Since 1.11.0* Create and return a `workspace` object for managing the project's Cargo workspace. +Keyword arguments: +- `default_features`: (`bool`, optional) Whether to enable default features. +- `features`: (`list[str]`, optional) List of additional features to enable globally. + A project that wishes to use Cargo subprojects should have `Cargo.lock` and `Cargo.toml` files in the root source directory, and should call this function before using Cargo subprojects. @@ -190,3 +204,29 @@ Cargo subprojects. The first invocation of `workspace()` establishes the *Cargo interpreter* that resolves dependencies and features for both the toplevel project (the one containing `Cargo.lock`) and all subprojects that are invoked with the `cargo` method, + +You can optionally customize the feature set, by providing `default_features` +and `features` when the Cargo interpreter is established. If any of these +arguments is not specified, `default_features` is taken as `true` and +`features` as the empty list. + +Once established, the Cargo interpreter's configuration is locked. Later calls to +`workspace()` must either omit all arguments (accepting the existing configuration) +or provide the same set of features as the first call. Mismatched arguments will cause +a build error. + +The recommendation is to not specify any keyword arguments in a subproject, so +that they simply inherit the parent's configuration. Be careful about the +difference between specifying arguments and not doing so: + +``` +# always works regardless of parent configuration +cargo_ws = rustmod.workspace() + +# fails if parent configured different features +cargo_ws = rustmod.workspace(default_features: true) +cargo_ws = rustmod.workspace(features: []) +``` + +The first form says "use whatever features are configured," while the latter forms +say "require this specific configuration," which may conflict with the parent project. diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index cc7e2a65b..2a14fc096 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -24,7 +24,7 @@ from . import builder, version from .cfg import eval_cfg from .toml import load_toml from .manifest import Manifest, CargoLock, CargoLockPackage, Workspace, fixup_meson_varname -from ..mesonlib import is_parent_path, MesonException, MachineChoice, version_compare +from ..mesonlib import is_parent_path, MesonException, MachineChoice, unique_list, version_compare from .. import coredata, mlog from ..wrap.wrap import PackageDefinition @@ -205,6 +205,8 @@ class WorkspaceState: class Interpreter: + _features: T.Optional[T.List[str]] = None + def __init__(self, env: Environment, subdir: str, subprojects_dir: str) -> None: self.environment = env self.subprojects_dir = subprojects_dir @@ -224,6 +226,21 @@ class Interpreter: self.environment.wrap_resolver.merge_wraps(self.cargolock.wraps) self.build_def_files.append(filename) + @property + def features(self) -> T.List[str]: + """Get the features list. Once read, it cannot be modified.""" + if self._features is None: + self._features = ['default'] + return self._features + + @features.setter + def features(self, value: T.List[str]) -> None: + """Set the features list. Can only be set before first read.""" + value_unique = sorted(unique_list(value)) + if self._features is not None and value_unique != self._features: + raise MesonException("Cannot modify features after they have been selected or used") + self._features = value_unique + def get_build_def_files(self) -> T.List[str]: return self.build_def_files @@ -240,7 +257,8 @@ class Interpreter: pkgs = [self._require_workspace_member(ws, m) for m in ws.workspace.default_members] for pkg in pkgs: self._prepare_package(pkg) - self._enable_feature(pkg, 'default') + for feature in self.features: + self._enable_feature(pkg, feature) def interpret(self, subdir: str, project_root: T.Optional[str] = None) -> mparser.CodeBlockNode: filename = os.path.join(self.environment.source_dir, subdir, 'Cargo.toml') diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 84ea70fce..39e1d9ce1 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -19,8 +19,7 @@ from ..interpreter.type_checking import ( DEPENDENCIES_KW, LINK_WITH_KW, LINK_WHOLE_KW, SHARED_LIB_KWS, TEST_KWS, TEST_KWS_NO_ARGS, OUTPUT_KW, INCLUDE_DIRECTORIES, SOURCES_VARARGS, NoneType, in_set_validator ) -from ..interpreterbase import ContainerTypeInfo, InterpreterException, KwargInfo, typed_kwargs, typed_pos_args, noKwargs, noPosargs, permittedKwargs -from ..interpreterbase.baseobjects import TYPE_kwargs +from ..interpreterbase import ContainerTypeInfo, InterpreterException, KwargInfo, typed_kwargs, typed_pos_args, noPosargs, permittedKwargs from ..interpreter.interpreterobjects import Doctest from ..mesonlib import File, MachineChoice, MesonException, PerMachine from ..programs import ExternalProgram, NonExistingExternalProgram @@ -65,6 +64,9 @@ if T.TYPE_CHECKING: language: T.Optional[Literal['c', 'cpp']] bindgen_version: T.List[str] + class FuncWorkspace(TypedDict): + default_features: T.Optional[bool] + features: T.List[str] RUST_TEST_KWS: T.List[KwargInfo] = [ KwargInfo( @@ -515,8 +517,17 @@ class RustModule(ExtensionModule): @FeatureNew('rust.workspace', '1.11.0') @noPosargs - @noKwargs - def workspace(self, state: ModuleState, args: T.List, kwargs: TYPE_kwargs) -> RustWorkspace: + @typed_kwargs( + 'rust.workspace', + KwargInfo('default_features', (bool, NoneType), default=None), + KwargInfo( + 'features', + (ContainerTypeInfo(list, str), NoneType), + default=None, + listify=True, + ), + ) + def workspace(self, state: ModuleState, args: T.List, kwargs: FuncWorkspace) -> RustWorkspace: """Creates a Rust workspace object, controlling the build of all the packages in a Cargo.lock file.""" if self.interpreter.cargo is None: @@ -525,6 +536,18 @@ class RustModule(ExtensionModule): self.interpreter.add_languages(['rust'], True, MachineChoice.HOST) self.interpreter.add_languages(['rust'], True, MachineChoice.BUILD) + default_features = kwargs['default_features'] + features = kwargs['features'] + if default_features is not None or features is not None: + # If custom features are provided, default_features = None should be treated as True + if default_features is None: + default_features = True + + cargo_features = ['default'] if default_features else [] + if features is not None: + cargo_features.extend(features) + self.interpreter.cargo.features = cargo_features + ws = self.interpreter.cargo.load_workspace(state.root_subdir) return RustWorkspace(self.interpreter, ws) |
