summaryrefslogtreecommitdiff
path: root/mesonbuild
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2025-10-26 08:36:16 +0100
committerPaolo Bonzini <pbonzini@redhat.com>2025-12-22 11:58:30 +0100
commitecc8032dcb7cd5fc8c3b8e468818ca235cfa9ff4 (patch)
tree6f76f599281f8016c6bd94512b83624dcf9e9a1c /mesonbuild
parent5de9723535506f625e093cbf9549b3ce0bf5940f (diff)
downloadmeson-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__.py3
-rw-r--r--mesonbuild/cargo/interpreter.py56
-rw-r--r--mesonbuild/modules/__init__.py10
-rw-r--r--mesonbuild/modules/rust.py59
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):