cargo: Add API version into dependency name

pull/12546/head
Xavier Claessens 1 year ago committed by Xavier Claessens
parent 3f73aaed2d
commit 4d3fb88753
  1. 41
      docs/markdown/Wrap-dependency-system-manual.md
  2. 19
      docs/markdown/snippets/cargo_dep_name.md
  3. 40
      mesonbuild/cargo/interpreter.py
  4. 2
      test cases/rust/22 cargo subproject/meson.build
  5. 0
      test cases/rust/22 cargo subproject/subprojects/bar-0.1-rs.wrap
  6. 0
      test cases/rust/22 cargo subproject/subprojects/bar-0.1-rs/Cargo.toml
  7. 0
      test cases/rust/22 cargo subproject/subprojects/bar-0.1-rs/src/lib.rs
  8. 0
      test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/lib.c
  9. 2
      test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/meson.build
  10. 0
      test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/meson_options.txt
  11. 0
      test cases/rust/22 cargo subproject/subprojects/foo-0-rs.wrap
  12. 0
      test cases/rust/22 cargo subproject/subprojects/foo-0-rs/Cargo.toml
  13. 0
      test cases/rust/22 cargo subproject/subprojects/foo-0-rs/lib.rs
  14. 0
      test cases/rust/22 cargo subproject/subprojects/foo-0-rs/meson/meson.build

@ -316,11 +316,24 @@ foo-bar-1.0 = foo_bar_dep
```
### Cargo wraps
Cargo subprojects automatically override the `<package_name>-rs` dependency name.
`package_name` is defined in `[package] name = ...` section of the `Cargo.toml`
and `-rs` suffix is added. That means the `.wrap` file should have
`dependency_names = foo-rs` in their `[provide]` section when `Cargo.toml` has
package name `foo`.
Cargo subprojects automatically override the `<package_name>-<version>-rs` dependency
name:
- `package_name` is defined in `[package] name = ...` section of the `Cargo.toml`.
- `version` is the API version deduced from `[package] version = ...` as follow:
* `x.y.z` -> 'x'
* `0.x.y` -> '0.x'
* `0.0.x` -> '0'
It allows to make different dependencies for uncompatible versions of the same
crate.
- `-rs` suffix is added to distinguish from regular system dependencies, for
example `gstreamer-1.0` is a system pkg-config dependency and `gstreamer-0.22-rs`
is a Cargo dependency.
That means the `.wrap` file should have `dependency_names = foo-1-rs` in their
`[provide]` section when `Cargo.toml` has package name `foo` and version `1.2`.
Note that the version component was added in Meson 1.4, previous versions were
using `<package_name>-rs` format.
Cargo subprojects require a toml parser. Python >= 3.11 have one built-in, older
Python versions require either the external `tomli` module or `toml2json` program.
@ -332,26 +345,26 @@ file like that:
...
method = cargo
[provide]
dependency_names = foo-bar-rs
dependency_names = foo-bar-0.1-rs
```
Cargo features are exposed as Meson boolean options, with the `feature-` prefix.
For example the `default` feature is named `feature-default` and can be set from
the command line with `-Dfoo-rs:feature-default=false`. When a cargo subproject
the command line with `-Dfoo-1-rs:feature-default=false`. When a cargo subproject
depends on another cargo subproject, it will automatically enable features it
needs using the `dependency('foo-rs', default_options: ...)` mechanism. However,
needs using the `dependency('foo-1-rs', default_options: ...)` mechanism. However,
unlike Cargo, the set of enabled features is not managed globally. Let's assume
the main project depends on `foo-rs` and `bar-rs`, and they both depend on
`common-rs`. The main project will first look up `foo-rs` which itself will
configure `common-rs` with a set of features. Later, when `bar-rs` does a lookup
for `common-rs` it has already been configured and the set of features cannot be
changed. If `bar-rs` wants extra features from `common-rs`, Meson will error out.
the main project depends on `foo-1-rs` and `bar-1-rs`, and they both depend on
`common-1-rs`. The main project will first look up `foo-1-rs` which itself will
configure `common-rs` with a set of features. Later, when `bar-1-rs` does a lookup
for `common-1-rs` it has already been configured and the set of features cannot be
changed. If `bar-1-rs` wants extra features from `common-1-rs`, Meson will error out.
It is currently the responsability of the main project to resolve those
issues by enabling extra features on each subproject:
```meson
project(...,
default_options: {
'common-rs:feature-something': true,
'common-1-rs:feature-something': true,
},
)
```

@ -0,0 +1,19 @@
## Cargo dependencies names now include the API version
Cargo dependencies names are now in the format `<package_name>-<version>-rs`:
- `package_name` is defined in `[package] name = ...` section of the `Cargo.toml`.
- `version` is the API version deduced from `[package] version = ...` as follow:
* `x.y.z` -> 'x'
* `0.x.y` -> '0.x'
* `0.0.x` -> '0'
It allows to make different dependencies for uncompatible versions of the same
crate.
- `-rs` suffix is added to distinguish from regular system dependencies, for
example `gstreamer-1.0` is a system pkg-config dependency and `gstreamer-0.22-rs`
is a Cargo dependency.
That means the `.wrap` file should have `dependency_names = foo-1-rs` in their
`[provide]` section when `Cargo.toml` has package name `foo` and version `1.2`.
This is a breaking change (Cargo subprojects are still experimental), previous
versions were using `<package_name>-rs` format.

@ -162,9 +162,23 @@ class Dependency:
package: str = ''
default_features: bool = True
features: T.List[str] = dataclasses.field(default_factory=list)
api: str = dataclasses.field(init=False)
def __post_init__(self, name: str) -> None:
self.package = self.package or name
# Extract wanted API version from version constraints.
api = set()
for v in self.version:
if v.startswith(('>=', '==')):
api.add(_version_to_api(v[2:].strip()))
elif v.startswith('='):
api.add(_version_to_api(v[1:].strip()))
if not api:
self.api = '0'
elif len(api) == 1:
self.api = api.pop()
else:
raise MesonException(f'Cannot determine minimum API version from {self.version}.')
@classmethod
def from_raw(cls, name: str, raw: manifest.DependencyV) -> Dependency:
@ -351,8 +365,21 @@ def _load_manifests(subdir: str) -> T.Dict[str, Manifest]:
return manifests
def _dependency_name(package_name: str) -> str:
return package_name if package_name.endswith('-rs') else f'{package_name}-rs'
def _version_to_api(version: str) -> str:
# x.y.z -> x
# 0.x.y -> 0.x
# 0.0.x -> 0
vers = version.split('.')
if int(vers[0]) != 0:
return vers[0]
elif len(vers) >= 2 and int(vers[1]) != 0:
return f'0.{vers[1]}'
return '0'
def _dependency_name(package_name: str, api: str) -> str:
basename = package_name[:-3] if package_name.endswith('-rs') else package_name
return f'{basename}-{api}-rs'
def _dependency_varname(package_name: str) -> str:
@ -517,7 +544,7 @@ def _create_dependencies(cargo: Manifest, build: builder.Builder) -> T.List[mpar
build.assign(
build.function(
'dependency',
[build.string(_dependency_name(dep.package))],
[build.string(_dependency_name(dep.package, dep.api))],
kw,
),
_dependency_varname(dep.package),
@ -559,7 +586,7 @@ def _create_dependencies(cargo: Manifest, build: builder.Builder) -> T.List[mpar
build.if_(build.not_in(build.identifier('f'), build.identifier('actual_features')), build.block([
build.function('error', [
build.string('Dependency'),
build.string(_dependency_name(dep.package)),
build.string(_dependency_name(dep.package, dep.api)),
build.string('previously configured with features'),
build.identifier('actual_features'),
build.string('but need'),
@ -666,7 +693,7 @@ def _create_lib(cargo: Manifest, build: builder.Builder, crate_type: manifest.CR
'override_dependency',
build.identifier('meson'),
[
build.string(_dependency_name(cargo.package.name)),
build.string(_dependency_name(cargo.package.name, _version_to_api(cargo.package.version))),
build.identifier('dep'),
],
),
@ -674,7 +701,8 @@ def _create_lib(cargo: Manifest, build: builder.Builder, crate_type: manifest.CR
def interpret(subp_name: str, subdir: str, env: Environment) -> T.Tuple[mparser.CodeBlockNode, KeyedOptionDictType]:
package_name = subp_name[:-3] if subp_name.endswith('-rs') else subp_name
# subp_name should be in the form "foo-0.1-rs"
package_name = subp_name.rsplit('-', 2)[0]
manifests = _load_manifests(os.path.join(env.source_dir, subdir))
cargo = manifests.get(package_name)
if not cargo:

@ -1,6 +1,6 @@
project('cargo subproject', 'c')
foo_dep = dependency('foo-rs')
foo_dep = dependency('foo-0-rs')
exe = executable('app', 'main.c',
dependencies: foo_dep,
)

@ -7,4 +7,4 @@ d = declare_dependency(link_with: l,
variables: {
'features': 'default',
})
meson.override_dependency('extra-dep-rs', d)
meson.override_dependency('extra-dep-1-rs', d)
Loading…
Cancel
Save