Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions docs/markdown/Rust-module.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ authors:
- name: Dylan Baker
email: [email protected]
years: [2020, 2021, 2022, 2024]
- name: Paolo Bonzini
email: [email protected]
years: [2025]
...

# Rust module
Expand Down Expand Up @@ -168,3 +171,146 @@ Only a subset of [[shared_library]] keyword arguments are allowed:
- link_depends
- link_with
- override_options

### workspace()

Basic usage:

```
cargo = rustmod.workspace()
```

With custom features:

```
feature_list = get_feature('f1') ? ['feature1'] : []
feature_list += get_feature('f2') ? ['feature2'] : []
cargo = rust.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.

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 = rustmod.workspace()

# fails if parent configured different features
cargo = rustmod.workspace(default_features: true)
cargo = 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.packages()

```meson
packages = ws.packages()
```

Returns a list of package names in the workspace.

### 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.name()

```meson
name = pkg.name()
```

Returns the name of the subproject.

### subproject.version()

```meson
version = pkg.version()
```

Returns the normalized version number of the subproject.

### subproject.api()

```meson
api = pkg.api()
```

Returns the API version of the subproject, that is the version up to the first
nonzero element.

### subproject.features()

```meson
features = pkg.features()
```

Returns selected features for a specific subproject.

### subproject.all_features()

```meson
all_features = pkg.all_features()
```

Returns all defined features for a specific subproject.

### 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.
37 changes: 37 additions & 0 deletions docs/markdown/Rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,40 @@ target name. First, dashes, spaces and dots are replaced with underscores. Sec
*since 1.10.0* anything after the first `+` is dropped. This allows creating multiple
targets for the same crate name, for example when the same crate is built multiple
times with different features, or for both the build and the host machine.

## Cargo interaction

*Since 1.10.0*

In most cases, a Rust program will use Cargo to download crates. Meson is able
to build Rust library crates based on a `Cargo.toml` file; each external crate
corresponds to a subproject. Rust module's ` that do not need a `build.rs` file
need no intervention, whereas if a `build.rs` file is present it needs to be
converted manually to Meson code.

To enable automatic configuration of Cargo dependencies, your project must
have `Cargo.toml` and `Cargo.lock` files in the root source directory;
this enables proper feature resolution across crates. You can then
create a workspace object using the Rust module, and retrieve specific
packages from the workspace:

```meson
rust = import('rust')
cargo = rust.workspace()
anyhow_dep = ws.subproject('anyhow').dependency()
```

The workspace object also enables configuration of Cargo features, for example
from Meson options:

```meson
ws = rust.workspace(
features: ['feature1', 'feature2'])
```

### Limitations

All your own crates must be built using the usual Meson functions such as
[[static_library]] or [[executable]]. In the future, workspace object
functionality will be extended to help building rustc command lines
based on features, dependency names, and so on.
5 changes: 1 addition & 4 deletions docs/markdown/Wrap-dependency-system-manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -368,10 +368,7 @@ Since *1.5.0* Cargo wraps can also be provided with `Cargo.lock` file at the roo
of (sub)project source tree. Meson will automatically load that file and convert
it into a series of wraps definitions.

Since *1.10.0* Workspace Cargo.toml are supported. For the time being it is
recommended to regroup all Cargo dependencies inside a single workspace invoked
from the main Meson project. When invoking multiple different Cargo subprojects
from Meson, feature resolution of common dependencies might be wrong.
Since *1.10.0* Workspace Cargo.toml are supported.

## Using wrapped projects

Expand Down
13 changes: 13 additions & 0 deletions docs/markdown/snippets/cargo-workspace-object.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## Cargo workspace object

Meson now is able to parse the toplevel `Cargo.toml` file of the
project when the `workspace()` method of the Rust module is called.
This guarantees that features are resolved according to what is
in the `Cargo.toml` file, and in fact enables configuration of
features for the build.

The returned object also allows retrieving features and dependencies
for Cargo subprojects.

While Cargo subprojects remain experimental, the Meson project will
try to keep the workspace object reasonably backwards-compatible.
2 changes: 1 addition & 1 deletion mesonbuild/ast/printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def precedence_level(node: mparser.BaseNode) -> int:
return 6
elif isinstance(node, (mparser.NotNode, mparser.UMinusNode)):
return 7
elif isinstance(node, mparser.FunctionNode):
elif isinstance(node, (mparser.FunctionNode, mparser.IndexNode, mparser.MethodNode)):
return 8
elif isinstance(node, (mparser.ArrayNode, mparser.DictNode)):
return 9
Expand Down
4 changes: 3 additions & 1 deletion mesonbuild/cargo/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
__all__ = [
'Interpreter',
'PackageState',
'TomlImplementationMissing',
'WorkspaceState',
]

from .interpreter import Interpreter
from .interpreter import Interpreter, PackageState, WorkspaceState
from .toml import TomlImplementationMissing
Loading
Loading