#186 support new Rust 1.60+ syntax for "optional dependencies" and "dependency features"
Opened 4 months ago by decathorpe. Modified 17 days ago


I think our dependency generators will just choke if they encounter a crate that uses those new features, so there will probably need to be adaptations:

  • features can now have the same name as an optional dependency
  • optional dependency foo that have the same name as a feature foo is now referenced as dep:foo in feature dependencies
  • conditional dependencies are not supported by dependency generators yet at all: foo?/bar would mean "require feature bar of crate foo if optional dependency foo is pulled in explicitly in some other way"

What worries me most is that our mapping of "optional dependency" / "cargo feature" to subpackages might no longer work with this scheme (because optional dependencies and a cargo features can now have the same name, but different dependencies ...).

This example from the docs looks like it should be solvable with our current approach:

ravif = { version = "0.6.3", optional = true }
rgb = { version = "0.8.25", optional = true }

avif = ["dep:ravif", "dep:rgb"]

Here, there's no overlap between feature names and optional dependencies.

This example looks like it might break slightly:

serde = { version = "1.0.133", optional = true }
rgb = { version = "0.8.25", optional = true }

serde = ["dep:serde", "rgb?/serde"]

We would probably need to collapse both the serde feature and serde optional dependency into the same subpackage for dependency resolution (but in this case, that should not be a problem, because they're always both enabled at the same time).

And the generated dependencies for this package rust-foo+serde-devel might need to be something like:

Requires: crate(serde)
Requires: (crate(rgb/serde) if crate(rgb))

I'm not fond of if rich dependencies, but I am not sure if this can be solved any other way in RPM metadata.

I'm only worried that some developers might get "creative" with these new features and do something weird. According to the documentation,

In some cases, you may not want to expose a feature that has the same name as the optional dependency. For example, perhaps the optional dependency is an internal detail, or you want to group multiple optional dependencies together, or you just want to use a better name. If you specify the optional dependency with the dep: prefix anywhere in the [features] table, that disables the implicit feature.

the "implicit feature" for optional dependencies is only disabled if the explicit, new dep: form is used anywhere in Cargo.toml.

Sounds like a nightmare to me ... but at least we have a test suite for this kind of stuff in rust2rpm :(

When an explicit feature mentions an optional dependency as "dep:optional", then that dependency is no longer a feature of its own at all -- regardless of whether the explicit feature is called the same thing or not. So we don't need a rust-foo+optional-devel subpackage for that dependency at all, just rust-foo+feature-devel depending on crate(optional).

For the conditional dependency, I don't think it's completely global, more like

%package -n rust-foo+serde-devel
Requires: crate(serde)
Requires: (crate(rgb/serde) if crate(foo/rgb))

i.e. looking for its sibling subpackage as the condition, especially because "rgb?" could be yet another feature name rather than a dependency.

especially because "rgb?" could be yet another feature name rather than a dependency.

Hmm, actually this point doesn't make sense, because a regular feature wouldn't have some "foo?/bar" sub-feature to enable this way.

But still, I think if crate(foo/rgb) is more correct than if crate(rgb).

One thing that should help is that cargo read-manifest is also now explicit about optional dependencies that create an implicit feature, so this manifest:

serde = { version = "1.0.133", optional = true }
rgb = { version = "0.8.25", optional = true }

serde = ["dep:serde", "rgb?/serde"]

will now list these features:

  "features": {
    "rgb": [
    "serde": [

So I think rust2rpm doesn't need to figure out optional dependencies as implicit features at all anymore -- just take what's spelled out here.

:frowning: looks like crates are starting to use this feature now ...

I think we need to do the following:

  • specify how to map those new dependency types to RPM equivalents
  • implement mapping in the dependency generator
  • add unit tests :rocket:
  • release a new rust2rpm version :tada:
serde = { version = "1.0.133", optional = true }
rgb = { version = "0.8.25", optional = true }

serde = ["dep:serde", "rgb?/serde"]

Ok, to start things off, I think these examples would translate into:

  • an rgb subpackage for the optional rgb dependency, with a generated dependency on:

(crate(rgb) >= 0.8.25 with crate(rgb) < 0.9~)

(same as "old" style, since the rgb feature is not named as dep:rgb in the features table)

  • a serde subpackage for the serde feature, with generated dependencies on both:

(crate(serde) >= 1.0.133 with crate(serde) < 2.0~)


((crate(rgb/serde) >= 0.8.25 with crate(rgb/serde) < 0.9~) if (crate(rgb) >= 0.8.25 with crate(rgb) < 0.9~)

Do we need to specify the version restrictions in all the places here? I think we do ...

  • no subpackage for the serde optional dependency, since it is named as dep:serde in the features table

This seems to correspond to what cargo read-manifest prints, so we should only need to map the new foo?/bar style dependencies to (crate(foo/bar) if crate(foo)) (with version restrictions, as shown above?)?

Metadata Update from @decathorpe:
- Issue set to the milestone: 23

17 days ago

Login to comment on this ticket.