summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2025-10-24 10:24:45 +0200
committerPaolo Bonzini <pbonzini@redhat.com>2025-12-22 11:59:07 +0100
commit7a1aa179087a274d6f6b13c2c666043ac9760ea5 (patch)
treeb9bf5eed53e6a3ff41dee74b6429ef2916dae37a
parent9752def1c96eb32dc37c32398a0fe8802392fbc9 (diff)
downloadmeson-7a1aa179087a274d6f6b13c2c666043ac9760ea5.tar.gz
modules: rust: implement workspace.package()
Note that, as shown in the testcase, package() works in the subproject as well. This means that in the future the Cargo code generator can be changed to reduce the amount of generated code and instead rely on the package object. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
-rw-r--r--docs/markdown/Rust-module.md29
-rw-r--r--mesonbuild/cargo/interpreter.py15
-rw-r--r--mesonbuild/modules/rust.py29
-rw-r--r--test cases/rust/31 rust.workspace package/meson.build15
-rw-r--r--test cases/rust/31 rust.workspace package/subprojects/answer-2.1/meson.build4
-rw-r--r--test cases/rust/32 rust.workspace workspace/meson.build15
-rw-r--r--test cases/rust/32 rust.workspace workspace/subprojects/answer-2.1/meson.build4
7 files changed, 100 insertions, 11 deletions
diff --git a/docs/markdown/Rust-module.md b/docs/markdown/Rust-module.md
index fbe65f1b6..ba500e7ee 100644
--- a/docs/markdown/Rust-module.md
+++ b/docs/markdown/Rust-module.md
@@ -241,6 +241,19 @@ packages = ws.packages()
Returns a list of package names in the workspace.
+### workspace.package()
+
+```meson
+pkg = ws.package([package_name])
+```
+
+Returns a package object for the given package member. If empty, returns
+the object for the root package.
+
+Arguments:
+- `package_name`: (str, optional) Name of the package; not needed for the
+ root package of a workspace
+
### workspace.subproject()
```meson
@@ -258,15 +271,15 @@ Positional arguments:
The package object returned by `workspace.subproject()` provides methods
for working with individual packages in a Cargo workspace.
-### subproject.name()
+### package.name(), subproject.name()
```meson
name = pkg.name()
```
-Returns the name of the subproject.
+Returns the name of a package or subproject.
-### subproject.version()
+### package.version(), subproject.version()
```meson
version = pkg.version()
@@ -274,7 +287,7 @@ version = pkg.version()
Returns the normalized version number of the subproject.
-### subproject.api()
+### package.api(), subproject.api()
```meson
api = pkg.api()
@@ -283,21 +296,21 @@ api = pkg.api()
Returns the API version of the subproject, that is the version up to the first
nonzero element.
-### subproject.features()
+### package.features(), subproject.features()
```meson
features = pkg.features()
```
-Returns selected features for a specific subproject.
+Returns selected features for a specific package or subproject.
-### subproject.all_features()
+### package.all_features(), subproject.all_features()
```meson
all_features = pkg.all_features()
```
-Returns all defined features for a specific subproject.
+Returns all defined features for a specific package or subproject.
### subproject.dependency()
diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py
index 30639f526..dc5cdef2a 100644
--- a/mesonbuild/cargo/interpreter.py
+++ b/mesonbuild/cargo/interpreter.py
@@ -298,6 +298,21 @@ class Interpreter:
for feature in self.features:
self._enable_feature(pkg, feature)
+ def load_package(self, ws: WorkspaceState, package_name: T.Optional[str]) -> PackageState:
+ if package_name is None:
+ if not ws.workspace.root_package:
+ raise MesonException('no root package in workspace')
+ path = '.'
+ else:
+ try:
+ path = ws.packages_to_member[package_name]
+ except KeyError:
+ raise MesonException(f'workspace member "{package_name}" not found')
+
+ if is_parent_path(self.subprojects_dir, path):
+ raise MesonException('argument to package() cannot be a subproject')
+ return ws.packages[path]
+
def interpret(self, subdir: str, project_root: T.Optional[str] = None) -> mparser.CodeBlockNode:
filename = os.path.join(self.environment.source_dir, subdir, 'Cargo.toml')
build = builder.Builder(filename)
diff --git a/mesonbuild/modules/rust.py b/mesonbuild/modules/rust.py
index 29d1b1fbb..186774413 100644
--- a/mesonbuild/modules/rust.py
+++ b/mesonbuild/modules/rust.py
@@ -101,6 +101,7 @@ class RustWorkspace(ModuleObject):
self.ws = ws
self.methods.update({
'packages': self.packages_method,
+ 'package': self.package_method,
'subproject': self.subproject_method,
})
@@ -111,6 +112,12 @@ class RustWorkspace(ModuleObject):
package_names = [pkg.manifest.package.name for pkg in self.ws.packages.values()]
return sorted(package_names)
+ @typed_pos_args('workspace.package', optargs=[str])
+ def package_method(self, state: 'ModuleState', args: T.List, kwargs: TYPE_kwargs) -> RustPackage:
+ """Returns a package object."""
+ package_name = args[0] if args else None
+ return RustPackage(self, self.interpreter.cargo.load_package(self.ws, package_name))
+
def _do_subproject(self, pkg: cargo.PackageState) -> None:
kw: _kwargs.DoSubproject = {
'required': True,
@@ -138,8 +145,8 @@ class RustWorkspace(ModuleObject):
return RustSubproject(self, pkg)
-class RustSubproject(ModuleObject):
- """Represents a Rust package within a workspace."""
+class RustCrate(ModuleObject):
+ """Abstract base class for Rust crate representations."""
def __init__(self, rust_ws: RustWorkspace, package: cargo.PackageState) -> None:
super().__init__()
@@ -148,7 +155,6 @@ class RustSubproject(ModuleObject):
self.methods.update({
'all_features': self.all_features_method,
'api': self.api_method,
- 'dependency': self.dependency_method,
'features': self.features_method,
'name': self.name_method,
'version': self.version_method,
@@ -184,6 +190,23 @@ class RustSubproject(ModuleObject):
"""Returns chosen features for specific package."""
return sorted(list(self.package.cfg.features))
+
+class RustPackage(RustCrate):
+ """Represents a Rust package within a workspace."""
+
+ def __init__(self, rust_ws: RustWorkspace, package: cargo.PackageState) -> None:
+ super().__init__(rust_ws, package)
+
+
+class RustSubproject(RustCrate):
+ """Represents a Cargo subproject."""
+
+ def __init__(self, rust_ws: RustWorkspace, package: cargo.PackageState) -> None:
+ super().__init__(rust_ws, 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'})))
diff --git a/test cases/rust/31 rust.workspace package/meson.build b/test cases/rust/31 rust.workspace package/meson.build
index d6e9aa42d..ec37a33d7 100644
--- a/test cases/rust/31 rust.workspace package/meson.build
+++ b/test cases/rust/31 rust.workspace package/meson.build
@@ -6,6 +6,13 @@ cargo_ws = rust.workspace()
# Test workspace.packages() method
assert(cargo_ws.packages() == ['answer', 'hello', 'package_test'])
+main_pkg = cargo_ws.package()
+assert(main_pkg.name() == 'package_test')
+assert(main_pkg.version() == '0.1.0')
+assert(main_pkg.api() == '0.1')
+assert(main_pkg.all_features() == ['answer', 'default', 'feature1', 'feature2'])
+assert(main_pkg.features() == ['default', 'feature1'])
+
hello_rs = cargo_ws.subproject('hello')
assert(hello_rs.name() == 'hello')
assert(hello_rs.version() == '1.0.0')
@@ -25,6 +32,14 @@ e = executable('package-test', 'src/main.rs',
)
test('package-test', e)
+# failure test cases for package()
+testcase expect_error('argument to package() cannot be a subproject')
+ cargo_ws.package('hello')
+endtestcase
+testcase expect_error('workspace member "nonexistent" not found')
+ cargo_ws.package('nonexistent')
+endtestcase
+
# 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')
diff --git a/test cases/rust/31 rust.workspace package/subprojects/answer-2.1/meson.build b/test cases/rust/31 rust.workspace package/subprojects/answer-2.1/meson.build
index e8d10117f..ece234f27 100644
--- a/test cases/rust/31 rust.workspace package/subprojects/answer-2.1/meson.build
+++ b/test cases/rust/31 rust.workspace package/subprojects/answer-2.1/meson.build
@@ -4,6 +4,10 @@ rust = import('rust')
cargo_ws = rust.workspace()
assert(cargo_ws.packages() == ['answer'])
+answer_pkg = cargo_ws.package()
+assert(answer_pkg.all_features() == ['default', 'large'])
+assert(answer_pkg.features() == ['default', 'large'])
+
l = static_library('answer', 'src/lib.rs', rust_args: ['--cfg', 'feature="large"'])
dep = declare_dependency(link_with: l)
meson.override_dependency('answer-2-rs', dep)
diff --git a/test cases/rust/32 rust.workspace workspace/meson.build b/test cases/rust/32 rust.workspace workspace/meson.build
index 2626d3d50..185759413 100644
--- a/test cases/rust/32 rust.workspace workspace/meson.build
+++ b/test cases/rust/32 rust.workspace workspace/meson.build
@@ -6,6 +6,13 @@ cargo_ws = rust.workspace()
# Test workspace.packages() method
assert(cargo_ws.packages() == ['answer', 'hello', 'workspace_test'])
+main_pkg = cargo_ws.package()
+assert(main_pkg.name() == 'workspace_test')
+assert(main_pkg.version() == '0.1.0')
+assert(main_pkg.api() == '0.1')
+assert(main_pkg.all_features() == ['answer', 'default', 'feature1', 'feature2'])
+assert(main_pkg.features() == ['default', 'feature1'])
+
hello_rs = cargo_ws.subproject('hello')
assert(hello_rs.name() == 'hello')
assert(hello_rs.version() == '1.0.0')
@@ -25,6 +32,14 @@ e = executable('workspace-test', 'src/main.rs',
)
test('workspace-test', e)
+# failure test cases for package()
+testcase expect_error('argument to package() cannot be a subproject')
+ cargo_ws.package('hello')
+endtestcase
+testcase expect_error('workspace member "nonexistent" not found')
+ cargo_ws.package('nonexistent')
+endtestcase
+
# 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')
diff --git a/test cases/rust/32 rust.workspace workspace/subprojects/answer-2.1/meson.build b/test cases/rust/32 rust.workspace workspace/subprojects/answer-2.1/meson.build
index e8d10117f..ece234f27 100644
--- a/test cases/rust/32 rust.workspace workspace/subprojects/answer-2.1/meson.build
+++ b/test cases/rust/32 rust.workspace workspace/subprojects/answer-2.1/meson.build
@@ -4,6 +4,10 @@ rust = import('rust')
cargo_ws = rust.workspace()
assert(cargo_ws.packages() == ['answer'])
+answer_pkg = cargo_ws.package()
+assert(answer_pkg.all_features() == ['default', 'large'])
+assert(answer_pkg.features() == ['default', 'large'])
+
l = static_library('answer', 'src/lib.rs', rust_args: ['--cfg', 'feature="large"'])
dep = declare_dependency(link_with: l)
meson.override_dependency('answer-2-rs', dep)