summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/markdown/Rust-module.md35
-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
-rw-r--r--test cases/rust/31 rust.workspace package/meson.build14
-rw-r--r--test cases/rust/32 rust.workspace workspace/meson.build14
7 files changed, 176 insertions, 15 deletions
diff --git a/docs/markdown/Rust-module.md b/docs/markdown/Rust-module.md
index 1e8305d07..1126723ca 100644
--- a/docs/markdown/Rust-module.md
+++ b/docs/markdown/Rust-module.md
@@ -230,3 +230,38 @@ 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.
+
+## Workspace object
+
+### workspace.subproject()
+
+```meson
+package = ws.subproject(package_name, api)
+```
+
+Returns a `package` object for managing a specific package within the workspace.
+
+Positional arguments:
+- `package_name`: (`str`) The name of the package to retrieve
+- `api`: (`str`, optional) The version constraints for the package in Cargo format
+
+## Package object
+
+The package object returned by `workspace.subproject()` provides methods
+for working with individual packages in a Cargo workspace.
+
+### subproject.dependency()
+
+```meson
+dep = subproject.dependency(...)
+```
+
+Returns a dependency object for the subproject that can be used with other Meson targets.
+
+*Note*: right now, this method is implemented on top of the normal Meson function
+[[dependency]]; this is subject to change in future releases. It is recommended
+to always retrieve a Cargo subproject's dependency object via this method.
+
+Keyword arguments:
+- `rust_abi`: (`str`, optional) The ABI to use for the dependency. Valid values are
+ `'rust'`, `'c'`, or `'proc-macro'`. The package must support the specified ABI.
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):
diff --git a/test cases/rust/31 rust.workspace package/meson.build b/test cases/rust/31 rust.workspace package/meson.build
index 483b9ae52..a7544d609 100644
--- a/test cases/rust/31 rust.workspace package/meson.build
+++ b/test cases/rust/31 rust.workspace package/meson.build
@@ -3,10 +3,18 @@ project('package test', 'rust', default_options: ['rust_std=2021'])
rust = import('rust')
cargo_ws = rust.workspace()
-hello_dep = dependency('hello-1-rs')
-answer_dep = dependency('answer-2-rs')
+hello_rs = cargo_ws.subproject('hello')
+answer_rs = cargo_ws.subproject('answer', '2')
e = executable('package-test', 'src/main.rs',
- dependencies: [hello_dep, answer_dep],
+ dependencies: [hello_rs.dependency(), answer_rs.dependency()],
)
test('package-test', e)
+
+# failure test cases for dependency()
+testcase expect_error('package.dependency.*must be one of c, proc-macro, rust.*', how: 're')
+ hello_rs.dependency(rust_abi: 'something else')
+endtestcase
+testcase expect_error('Package hello does not support ABI c')
+ hello_rs.dependency(rust_abi: 'c')
+endtestcase
diff --git a/test cases/rust/32 rust.workspace workspace/meson.build b/test cases/rust/32 rust.workspace workspace/meson.build
index 35e1c8482..49800c524 100644
--- a/test cases/rust/32 rust.workspace workspace/meson.build
+++ b/test cases/rust/32 rust.workspace workspace/meson.build
@@ -3,10 +3,18 @@ project('workspace test', 'rust', default_options: ['rust_std=2021'])
rust = import('rust')
cargo_ws = rust.workspace()
-hello_dep = dependency('hello-1-rs')
-answer_dep = dependency('answer-2-rs')
+hello_rs = cargo_ws.subproject('hello')
+answer_rs = cargo_ws.subproject('answer', '2')
e = executable('workspace-test', 'src/main.rs',
- dependencies: [hello_dep, answer_dep],
+ dependencies: [hello_rs.dependency(), answer_rs.dependency()],
)
test('workspace-test', e)
+
+# failure test cases for dependency()
+testcase expect_error('package.dependency.*must be one of c, proc-macro, rust.*', how: 're')
+ hello_rs.dependency(rust_abi: 'something else')
+endtestcase
+testcase expect_error('Package hello does not support ABI c')
+ hello_rs.dependency(rust_abi: 'c')
+endtestcase