rust-lang/cargo

Unable to use a feature of an optional dependency from a feature

EpicatSupercell opened this issue ยท 12 comments

For example lets say we use

[dependencies.serde]
version = "0.8.*"
optional = true
default-features = false

and we want to use the std feature of serde if it is used.

[features]
std = ["serde/std"]

But now if someone wants to use our std feature serde WILL be imported. If I would want to represent this I would write

[features]
std = ["serde", "serde/std"]

Right now it's impossible to use a feature of an optional dependency from an own feature without forcing it to be used.

Yes it's true that relying on a/b will implicitly activate the dependency on a, rather than just activating the b feature in a if a is already activated. I don't believe it's possible to express what you're thinking here today with Cargo features.

To chime in.

There is a push in slog to move to opaque key value pairs, so that keys can be variable when the same logging function is invoked.

Anyways. Since the new type definition is feature gated by slog, any project that imports slog, and feature gates this new feature, can't optionally trigger this feature gate in slog.

kvark commented

Just a friendly heads up from #7259 - this problem is hitting gfx-rs ecosystem quite badly. We want things like serde and winit to be transparently enabled for the backends that the user selects, not all the things in the universe.

Over in #7259, @est31 proposed the following backwards-compatible syntax for "enable this feature of that dependency only if the dependency is otherwise enabled": "foo" = ["?bar/foo"]. The syntax bikeshed aside, I think it would be great to have this feature available somehow; this is bound to come up whenever features get exposed recursively (such as whether backtrace depends on backtrace-sys, or the serde/winit feature in gfx-rs).

Just my 2 cents. Alternatively, this could be solved if the following syntax is supported. The same syntax will also address #1839.

[features]
default = ["std"]
std = []

[target.'cfg(feature = "std")'.dependencies]
serde = { version = "*", optional = true }

[target.'cfg(not(feature = "std"))'.dependencies]
serde = { version = "*", optional = true, default-features = false }

I just ran into this myself, and would like to see a solution as well.

@xu-cheng I would love to see that syntax work.

That syntax feels rather verbose for what I think is the common case ("crate/feature but only if crate is enabled as a dependency). In particular that syntax will lead to combinatorial explosion when there are several feature being forwarded:

[features]
default = ["std"]
std = ["?create/std"]
serde = ["?create/serde"]

Now we need to specify the crate dependency 4 times, for the 4 combinations of whether std and serde are enabled or not.

Just ran into this issue as well. FWIW, I think the ["?crate/feature"] syntax looks good.

Seems I opened a "Pre-Pre-RFC" thread about this same problem:

https://internals.rust-lang.org/t/pre-pre-rfc-weak-cargo-feature-activation/13141/1

I also like the ["?crate/feature"] syntax.

thanks @ehuss for getting the ball rolling on this!

est31 commented

Tracking issue: #8832