diff --git a/docs/markdown/Wrap-dependency-system-manual.md b/docs/markdown/Wrap-dependency-system-manual.md index bba1971c4..5f0b473e7 100644 --- a/docs/markdown/Wrap-dependency-system-manual.md +++ b/docs/markdown/Wrap-dependency-system-manual.md @@ -316,11 +316,24 @@ foo-bar-1.0 = foo_bar_dep ``` ### Cargo wraps -Cargo subprojects automatically override the `-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 `--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 `-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, }, ) ``` diff --git a/docs/markdown/snippets/cargo_dep_name.md b/docs/markdown/snippets/cargo_dep_name.md new file mode 100644 index 000000000..b769f2b90 --- /dev/null +++ b/docs/markdown/snippets/cargo_dep_name.md @@ -0,0 +1,19 @@ +## Cargo dependencies names now include the API version + +Cargo dependencies names are now in the format `--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 `-rs` format. diff --git a/mesonbuild/cargo/interpreter.py b/mesonbuild/cargo/interpreter.py index a919563e0..f1ed23953 100644 --- a/mesonbuild/cargo/interpreter.py +++ b/mesonbuild/cargo/interpreter.py @@ -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: diff --git a/test cases/rust/22 cargo subproject/meson.build b/test cases/rust/22 cargo subproject/meson.build index 420e6e3b9..1b60014c5 100644 --- a/test cases/rust/22 cargo subproject/meson.build +++ b/test cases/rust/22 cargo subproject/meson.build @@ -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, ) diff --git a/test cases/rust/22 cargo subproject/subprojects/bar-rs.wrap b/test cases/rust/22 cargo subproject/subprojects/bar-0.1-rs.wrap similarity index 100% rename from test cases/rust/22 cargo subproject/subprojects/bar-rs.wrap rename to test cases/rust/22 cargo subproject/subprojects/bar-0.1-rs.wrap diff --git a/test cases/rust/22 cargo subproject/subprojects/bar-rs/Cargo.toml b/test cases/rust/22 cargo subproject/subprojects/bar-0.1-rs/Cargo.toml similarity index 100% rename from test cases/rust/22 cargo subproject/subprojects/bar-rs/Cargo.toml rename to test cases/rust/22 cargo subproject/subprojects/bar-0.1-rs/Cargo.toml diff --git a/test cases/rust/22 cargo subproject/subprojects/bar-rs/src/lib.rs b/test cases/rust/22 cargo subproject/subprojects/bar-0.1-rs/src/lib.rs similarity index 100% rename from test cases/rust/22 cargo subproject/subprojects/bar-rs/src/lib.rs rename to test cases/rust/22 cargo subproject/subprojects/bar-0.1-rs/src/lib.rs diff --git a/test cases/rust/22 cargo subproject/subprojects/extra-dep-rs/lib.c b/test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/lib.c similarity index 100% rename from test cases/rust/22 cargo subproject/subprojects/extra-dep-rs/lib.c rename to test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/lib.c diff --git a/test cases/rust/22 cargo subproject/subprojects/extra-dep-rs/meson.build b/test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/meson.build similarity index 82% rename from test cases/rust/22 cargo subproject/subprojects/extra-dep-rs/meson.build rename to test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/meson.build index 3ba7852cf..40d109b2d 100644 --- a/test cases/rust/22 cargo subproject/subprojects/extra-dep-rs/meson.build +++ b/test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/meson.build @@ -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) diff --git a/test cases/rust/22 cargo subproject/subprojects/extra-dep-rs/meson_options.txt b/test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/meson_options.txt similarity index 100% rename from test cases/rust/22 cargo subproject/subprojects/extra-dep-rs/meson_options.txt rename to test cases/rust/22 cargo subproject/subprojects/extra-dep-1-rs/meson_options.txt diff --git a/test cases/rust/22 cargo subproject/subprojects/foo-rs.wrap b/test cases/rust/22 cargo subproject/subprojects/foo-0-rs.wrap similarity index 100% rename from test cases/rust/22 cargo subproject/subprojects/foo-rs.wrap rename to test cases/rust/22 cargo subproject/subprojects/foo-0-rs.wrap diff --git a/test cases/rust/22 cargo subproject/subprojects/foo-rs/Cargo.toml b/test cases/rust/22 cargo subproject/subprojects/foo-0-rs/Cargo.toml similarity index 100% rename from test cases/rust/22 cargo subproject/subprojects/foo-rs/Cargo.toml rename to test cases/rust/22 cargo subproject/subprojects/foo-0-rs/Cargo.toml diff --git a/test cases/rust/22 cargo subproject/subprojects/foo-rs/lib.rs b/test cases/rust/22 cargo subproject/subprojects/foo-0-rs/lib.rs similarity index 100% rename from test cases/rust/22 cargo subproject/subprojects/foo-rs/lib.rs rename to test cases/rust/22 cargo subproject/subprojects/foo-0-rs/lib.rs diff --git a/test cases/rust/22 cargo subproject/subprojects/foo-rs/meson/meson.build b/test cases/rust/22 cargo subproject/subprojects/foo-0-rs/meson/meson.build similarity index 100% rename from test cases/rust/22 cargo subproject/subprojects/foo-rs/meson/meson.build rename to test cases/rust/22 cargo subproject/subprojects/foo-0-rs/meson/meson.build