diff options
| author | Paolo Bonzini <pbonzini@redhat.com> | 2025-10-26 08:36:16 +0100 |
|---|---|---|
| committer | Paolo Bonzini <pbonzini@redhat.com> | 2025-12-22 11:58:30 +0100 |
| commit | ecc8032dcb7cd5fc8c3b8e468818ca235cfa9ff4 (patch) | |
| tree | 6f76f599281f8016c6bd94512b83624dcf9e9a1c /mesonbuild | |
| parent | 5de9723535506f625e093cbf9549b3ce0bf5940f (diff) | |
| download | meson-ecc8032dcb7cd5fc8c3b8e468818ca235cfa9ff4.tar.gz | |
modules: rust: implement workspace.subproject() and package.dependency()
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'mesonbuild')
| -rw-r--r-- | mesonbuild/cargo/__init__.py | 3 | ||||
| -rw-r--r-- | mesonbuild/cargo/interpreter.py | 56 | ||||
| -rw-r--r-- | mesonbuild/modules/__init__.py | 10 | ||||
| -rw-r--r-- | mesonbuild/modules/rust.py | 59 |
4 files changed, 119 insertions, 9 deletions
diff --git a/mesonbuild/cargo/__init__.py b/mesonbuild/cargo/__init__.py index d461d10bb..65e018a9d 100644 --- a/mesonbuild/cargo/__init__.py +++ b/mesonbuild/cargo/__init__.py @@ -1,8 +1,9 @@ __all__ = [ 'Interpreter', + 'PackageState', 'TomlImplementationMissing', 'WorkspaceState', ] -from .interpreter import Interpreter, WorkspaceState +from .interpreter import Interpreter, PackageState, WorkspaceState from .toml import TomlImplementationMissing diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index 2a14fc096..b29548080 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -31,12 +31,14 @@ from ..wrap.wrap import PackageDefinition if T.TYPE_CHECKING: from . import raw from .. import mparser + from typing_extensions import Literal + from .manifest import Dependency, SystemDependency from ..environment import Environment from ..interpreterbase import SubProject from ..compilers.rust import RustCompiler - from typing_extensions import Literal + RUST_ABI = Literal['rust', 'c', 'proc-macro'] def _dependency_name(package_name: str, api: str, suffix: str = '-rs') -> str: basename = package_name[:-len(suffix)] if suffix and package_name.endswith(suffix) else package_name @@ -184,6 +186,42 @@ class PackageState: args.extend(self.get_env_args(rustc, environment, subdir)) return args + def supported_abis(self) -> T.Set[RUST_ABI]: + """Return which ABIs are exposed by the package's crate_types.""" + crate_types = self.manifest.lib.crate_type + abis: T.Set[RUST_ABI] = set() + if any(ct in {'lib', 'rlib', 'dylib'} for ct in crate_types): + abis.add('rust') + if any(ct in {'staticlib', 'cdylib'} for ct in crate_types): + abis.add('c') + if 'proc-macro' in crate_types: + abis.add('proc-macro') + return abis + + def get_subproject_name(self) -> str: + return _dependency_name(self.manifest.package.name, self.manifest.package.api) + + def get_dependency_name(self, rust_abi: T.Optional[RUST_ABI]) -> str: + """Get the dependency name for a package with the given ABI.""" + supported_abis = self.supported_abis() + if rust_abi is None: + if len(supported_abis) > 1: + raise MesonException(f'Package {self.manifest.package.name} support more than one ABI') + rust_abi = next(iter(supported_abis)) + else: + if rust_abi not in supported_abis: + raise MesonException(f'Package {self.manifest.package.name} does not support ABI {rust_abi}') + + package_name = self.manifest.package.name + api = self.manifest.package.api + + if rust_abi in {'rust', 'proc-macro'}: + return _dependency_name(package_name, api) + elif rust_abi == 'c': + return _dependency_name(package_name, api, '') + else: + raise MesonException(f'Unknown rust_abi: {rust_abi}') + @dataclasses.dataclass(frozen=True) class PackageKey: @@ -296,13 +334,14 @@ class Interpreter: crate_type = pkg.manifest.lib.crate_type if 'dylib' in crate_type and 'cdylib' in crate_type: raise MesonException('Cannot build both dylib and cdylib due to file name conflict') - if 'proc-macro' in crate_type: + abis = pkg.supported_abis() + if 'proc-macro' in abis: ast.extend(self._create_lib(pkg, build, subdir, 'proc-macro', shared=True)) - if any(x in crate_type for x in ['lib', 'rlib', 'dylib']): + if 'rust' in abis: ast.extend(self._create_lib(pkg, build, subdir, 'rust', static=('lib' in crate_type or 'rlib' in crate_type), shared='dylib' in crate_type)) - if any(x in crate_type for x in ['staticlib', 'cdylib']): + if 'c' in abis: ast.extend(self._create_lib(pkg, build, subdir, 'c', static='staticlib' in crate_type, shared='cdylib' in crate_type)) @@ -414,6 +453,13 @@ class Interpreter: raise MesonException(f'Cannot determine version of cargo package {package_name}') return None + def resolve_package(self, package_name: str, api: str) -> T.Optional[PackageState]: + cargo_pkg = self._resolve_package(package_name, version.convert(api)) + if not cargo_pkg: + return None + api = version.api(cargo_pkg.version) + return self._fetch_package(package_name, api) + def _fetch_package_from_subproject(self, package_name: str, meson_depname: str) -> PackageState: subp_name, _ = self.environment.wrap_resolver.find_dep_provider(meson_depname) if subp_name is None: @@ -751,7 +797,7 @@ class Interpreter: ] def _create_lib(self, pkg: PackageState, build: builder.Builder, subdir: str, - lib_type: Literal['rust', 'c', 'proc-macro'], + lib_type: RUST_ABI, static: bool = False, shared: bool = False) -> T.List[mparser.BaseNode]: cfg = pkg.cfg dependencies: T.List[mparser.BaseNode] = [] diff --git a/mesonbuild/modules/__init__.py b/mesonbuild/modules/__init__.py index 3ff9368d9..ed8deff06 100644 --- a/mesonbuild/modules/__init__.py +++ b/mesonbuild/modules/__init__.py @@ -7,7 +7,7 @@ from __future__ import annotations import dataclasses import typing as T -from .. import build, mesonlib +from .. import build, dependencies, mesonlib from ..options import OptionKey from ..build import IncludeDirs from ..interpreterbase.decorators import noKwargs, noPosargs @@ -46,6 +46,7 @@ class ModuleState: # The backend object is under-used right now, but we will need it: # https://github.com/mesonbuild/meson/issues/1419 self.backend = interpreter.backend + self.dependency_overrides = interpreter.build.dependency_overrides self.targets = interpreter.build.targets self.data = interpreter.build.data self.headers = interpreter.build.get_headers() @@ -108,6 +109,13 @@ class ModuleState: # Normal program lookup return self.find_program(name, required=required, wanted=wanted) + def overridden_dependency(self, depname: str, for_machine: MachineChoice = MachineChoice.HOST) -> Dependency: + identifier = dependencies.get_dep_identifier(depname, {}) + try: + return self.dependency_overrides[for_machine][identifier].dep + except KeyError: + raise mesonlib.MesonException(f'dependency "{depname}" was not overridden for the {for_machine}') + def dependency(self, depname: str, native: bool = False, required: bool = True, wanted: T.Optional[str] = None) -> 'Dependency': kwargs: T.Dict[str, object] = {'native': native, 'required': required} diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py index 39e1d9ce1..86e3ce426 100644 --- a/mesonbuild/modules/rust.py +++ b/mesonbuild/modules/rust.py @@ -15,11 +15,12 @@ from .. import mesonlib, mlog from ..build import (BothLibraries, BuildTarget, CustomTargetIndex, Executable, ExtractedObjects, GeneratedList, CustomTarget, InvalidArguments, Jar, StructuredSources, SharedLibrary, StaticLibrary) from ..compilers.compilers import are_asserts_disabled_for_subproject, lang_suffixes +from ..dependencies import Dependency 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, noPosargs, permittedKwargs +from ..interpreterbase import ContainerTypeInfo, InterpreterException, KwargInfo, typed_kwargs, typed_pos_args, noKwargs, noPosargs, permittedKwargs from ..interpreter.interpreterobjects import Doctest from ..mesonlib import File, MachineChoice, MesonException, PerMachine from ..programs import ExternalProgram, NonExistingExternalProgram @@ -28,12 +29,14 @@ if T.TYPE_CHECKING: from . import ModuleState from ..build import BuildTargetTypes, ExecutableKeywordArguments, IncludeDirs, LibTypes from .. import cargo + from ..cargo.interpreter import RUST_ABI from ..compilers.rust import RustCompiler - from ..dependencies import Dependency, ExternalLibrary + from ..dependencies import ExternalLibrary from ..interpreter import Interpreter from ..interpreter import kwargs as _kwargs from ..interpreter.interpreter import SourceInputs, SourceOutputs from ..interpreter.interpreterobjects import Test + from ..interpreterbase import TYPE_kwargs from ..programs import OverrideProgram from ..interpreter.type_checking import SourcesVarargsType @@ -68,6 +71,9 @@ if T.TYPE_CHECKING: default_features: T.Optional[bool] features: T.List[str] + class FuncDependency(TypedDict): + rust_abi: T.Optional[RUST_ABI] + RUST_TEST_KWS: T.List[KwargInfo] = [ KwargInfo( 'rust_args', @@ -93,6 +99,55 @@ class RustWorkspace(ModuleObject): super().__init__() self.interpreter = interpreter self.ws = ws + self.methods.update({ + 'subproject': self.subproject_method, + }) + + def _do_subproject(self, pkg: cargo.PackageState) -> None: + kw: _kwargs.DoSubproject = { + 'required': True, + 'version': None, + 'options': None, + 'cmake_options': [], + 'default_options': {}, + } + subp_name = pkg.get_subproject_name() + self.interpreter.do_subproject(subp_name, kw, force_method='cargo') + + @typed_pos_args('workspace.subproject', str, optargs=[str]) + @noKwargs + def subproject_method(self, state: ModuleState, args: T.Tuple[str, T.Optional[str]], kwargs: TYPE_kwargs) -> RustSubproject: + """Returns a package object for a subproject package.""" + package_name = args[0] + pkg = self.interpreter.cargo.resolve_package(package_name, args[1] or '') + if pkg is None: + if args[1]: + raise MesonException(f'No version of cargo package "{package_name}" provides API {args[1]}') + else: + raise MesonException(f'Cargo package "{package_name}" not available') + + self._do_subproject(pkg) + return RustSubproject(self, pkg) + + +class RustSubproject(ModuleObject): + """Represents a Rust package within a workspace.""" + + def __init__(self, rust_ws: RustWorkspace, package: cargo.PackageState) -> None: + super().__init__() + self.rust_ws = rust_ws + self.package = package + self.methods.update({ + 'dependency': self.dependency_method, + }) + + @noPosargs + @typed_kwargs('package.dependency', + KwargInfo('rust_abi', (str, NoneType), default=None, validator=in_set_validator({'rust', 'c', 'proc-macro'}))) + def dependency_method(self, state: ModuleState, args: T.List, kwargs: FuncDependency) -> Dependency: + """Returns dependency for the package with the given ABI.""" + depname = self.package.get_dependency_name(kwargs['rust_abi']) + return state.overridden_dependency(depname) class RustModule(ExtensionModule): |
