cargo icon indicating copy to clipboard operation
cargo copied to clipboard

Platform target-specific features

Open alexcrichton opened this issue 10 years ago • 68 comments

It would be nice to support target-specific features for cases such as when certain functionality is only available on one platform or the default set of functionality should vary per-platform.

alexcrichton avatar Jan 20 '15 01:01 alexcrichton

cc @huonw

alexcrichton avatar Jan 20 '15 01:01 alexcrichton

Shouldn't this work?

[target.'cfg(not(any(target_os = "android", target_os = "windows")))'.dependencies]
ipc-channel = {git = "https://github.com/servo/ipc-channel"}

[target.'cfg(any(target_os = "android", target_os = "windows"))'.dependencies]
ipc-channel = {git = "https://github.com/servo/ipc-channel", features = ["inprocess"]}

nox avatar Jul 10 '16 11:07 nox

@nox in theory yeah that seems like it should work, but today due to the structure of Resolve it doesn't. The entire target-independent resolution graph is created and then only later filtered down to the relevant set of packages. As an artifact of creating Resolve at the beginning it resolves all features, basically with a map from package to list of features. That means that all instances of ipc-channel in your example would have the inprocess feature enabled, regardless of what platform it was on.

I think that's a fixable bug, however, although likely quite invasive as it would require deferring calculation of features to the compilation phase rather than the resolution phase.

alexcrichton avatar Jul 11 '16 16:07 alexcrichton

Ran into this extremely confusing bug today...

jethrogb avatar Nov 05 '16 00:11 jethrogb

Would this also allow specifying a target-specific features.default array? Something like:

[target.'cfg(not(target_os = "windows"))'.features]
default = ["general_feature"]

[target.'cfg(target_os = "windows")'.features]
default = ["winonly_replacement"]

I suppose a workaround to get this would be an intermediate crate which depends on the actual crate with target-dependent features, but it doesn't sound very convenient.

gyscos avatar Dec 20 '16 10:12 gyscos

@gyscos that seems like a nifty and natural way to encode this information! I haven't though too much about the implications of target-specific features, but I don't particularly see any immediate reason to block anything.

alexcrichton avatar Dec 20 '16 17:12 alexcrichton

This isn't enough, I don't want my crate to stop working because someone used no-default-features.

I guess one could use @retep998's trick of enabling a feature programatically from a build.rs script.

nox avatar Mar 04 '17 11:03 nox

@nox That enables features in code programmatically. It doesn't let me control dependencies, but for some people just controlling their code might be enough. The reason I even wrote that trick was due to the lack of support for cyclic features. https://github.com/retep998/winapi-rs/blob/dev/build.rs#L193

retep998 avatar Mar 04 '17 12:03 retep998

What's the status of this issue? It would be interesting to use target-specific features in gecko-media (enable pulseaudio only for linux for instance).

philn avatar Oct 18 '17 14:10 philn

@philn AFAIK no progress has been made

alexcrichton avatar Oct 18 '17 20:10 alexcrichton

Hope we can see progress on this issue...

praetp avatar May 03 '18 17:05 praetp

This will help a lot with https://github.com/rust-lang-nursery/rand/issues/442

huangjj27 avatar May 12 '18 15:05 huangjj27

So is this a bug ( the [target.'cfg(target_os = "android")'.dependencies] / target.'cfg(target_os = "android")'.features notation should work, but it does not ) or a feature that requires a RFC?

plyhun avatar Oct 03 '18 13:10 plyhun

@plyhun it's intended behavior that it's not supported, but it's a bug if Cargo doesn't warn you about unused keys. I'm not sure about whether or not adding the functionality requires an RFC, but if it becomes semi-large than I think it might!

alexcrichton avatar Oct 04 '18 06:10 alexcrichton

is there any work-around for this besides creating an intermediate crate which depends on the actual crate? - that's very ugly. Especially for code targeting wasm this could be very helpful. Thanks

stefandeml avatar Nov 19 '18 20:11 stefandeml

Making [target.'cfg(…)'.**] work for everything, rather than just [dependencies], seems to me like it should be a straightforward and sensible change to make. I can’t see any downsides to it. Am I missing anything?

chris-morgan avatar Feb 12 '19 01:02 chris-morgan

I naively expected [target.'cfg(…)'.**] to work for everything. I guess I'd be great to if this was the reality - after if I expected it other people might've too.

That said, it might make sense to enable it just for the features - it's the safer way, because we might not want to allow that for [package] ([target.'cfg(…)'.package]) for example.

My use case is to write something like this:

[dependencies.tui]
version = "..."
default-features = false
optional = true

[dependencies.termion]
version = "..."
optional = true

[dependencies.crossterm]
version = "..."
optional = true

[features]
crossterm_backend = ["crossterm", "tui", "tui/crossterm"]
termion_backend = ["termion", "tui", "tui/termion"]

[target.'cfg(windows)'.features]
default = ["crossterm_backend"]

[target.'cfg(not(windows))'.features]
default = ["termion_backend"]

I don't think this can currently be achieved, unfortunately. :( The tricky part is passing the features to dependency conditionally. Is there some way I can do it today?

MOZGIII avatar Apr 17 '19 00:04 MOZGIII

Can someone please explain how to work around this with a dummy crate? I tried, but I always end up with the feature enabled. My question on the forum has all the details of what I did, but no-one answered it yet.

Minoru avatar Jul 27 '19 21:07 Minoru

Just ran into the bug that target specific dependency features are applied also for other targets :( Is there any chance of getting this bug fixed?

aknuds1 avatar Jul 31 '19 14:07 aknuds1

Target-specific features for dependencies has been implemented and is available as a nightly-only feature on the latest nightly 2020-02-23. See the tracking issue at #7914.

If people following this issue could try it out, and leave your feedback on the tracking issue (#7914), I would appreciate it. Particularly we'd like to know if it helps your project and does it cause any breakage?

I'm going to leave this issue open, for the request of also changing the default features.

ehuss avatar Feb 24 '20 15:02 ehuss

Hello, what's the status of this issue? Is there still hope for it to see the light of day (and stabilization)?

silverweed avatar Jul 21 '21 19:07 silverweed

features for dependencies has been implemented and is available as a nightly-only feature on the latest nightly 2020-02-23. See the

From what I can tell that only implements target specific dependencies, not target specific features. So:

[target.'cfg(target_os="linux")'.features]
default-backend = ["gtk"]

[target.'cfg(target_os="macos")'.features]
default-backend = ["mac"]

[target.'cfg(target_os="windows")'.features]
default-backend = ["win"]

Still doesnt work while I would expect it to work given that this this PR is supposed to solve this (github) issue.

JAicewizard avatar Aug 12 '21 09:08 JAicewizard

I got a working draft of this, its currently one big commit over at https://github.com/JAicewizard/cargo/tree/target_specific_features.

@ehuss could you help me with getting this into cargo? There is probably an RFC process, but I think this issue shows good support for this already. I think its more important to the code into a state where it can be put into cargo. Feature flags, stabilisation process etc.

Code is definitely not clean ATM, I plan on splitting it up into different commits tomorrow.

JAicewizard avatar Aug 13 '21 20:08 JAicewizard

@JAicewizard I think it would be good to start by writing some clear motivations and use cases. Alex can't remember why he opened this issue, and we couldn't think of any use cases that aren't already solvable by existing means.

ehuss avatar Aug 18 '21 18:08 ehuss

The prime use case I would have is, to have a feature that is not-mandatory for functionality, but it only works on certain OSes, i.e. jemalloc-ctl which is only viable on unix platforms when used with jemalloc, where it should be enabled by default, but not on windows. So that'd require target dependent default features or a similar mechanism. The approach as shown in https://github.com/rust-lang/cargo/issues/1197#issuecomment-897501839 does not work with 1.54.0 stable.

drahnr avatar Aug 18 '21 18:08 drahnr

I am running into the same issue. I am currently writing a dll injector, as a library. I plan to implement it on all platforms. On windows I need some windows specific crates. On Linux I'll need other crates. The way, things are currently, I'd just end up compiling all libraries, if I don't wanna implement target-specific features for every os.

I do not wanna add target-specific features, the user has to supply, because my goal is, to make the library, that I am writing, completely platform independent, to the one using it. We have target specific modules in rust, but we do not have any way, to define dependencies, for those target specific modules.

C0D3-M4513R avatar Aug 18 '21 19:08 C0D3-M4513R

When building a cross-platform application, sometimes I want FFI bindings to download/build/statically link the foreign library automatically on Windows, but use the system package on Linux, where installing stuff doesn't suck as bad. This often maps to wanting to enable a feature to bundle the foreign library on Windows only.

Ralith avatar Aug 18 '21 19:08 Ralith

My use case is for druid which is a GUI toolkit. My design for backend selection relies on features, and we need one (or more) default backend(s) for each target OS that are compiled into the binary. This would allow someone to use a GTK backend on windows or mac for example, same for X11 (using some X11 compatibility layer). This would mostly just be for experimentation, but it doesnt have to be.

Even outside that, it would allow more clean cargo.tomls + code communication. You dont need complex cargo.tomls with lots of targets each with 5 features, instead you have 1 feature for each target each including their optional dependencies. Then the existance of these dependencies can be easily checked by locking it behind the feature that enables these dependencies. This is much cleaner and allows for easier feature management.

JAicewizard avatar Aug 18 '21 22:08 JAicewizard

I think the current syntax makes this more confusing that it needs to be. The current [features] section does two things: it declare what the features are, and specifies the dependencies between features.

I think based on what the others say there is good need for target-specific dependencies. Note that this includes target specific default features as they are target-specific dependencies of the "default" feature.

However, there is no need for the mere existence of features to vary between platforms. It would be much better to solve that with something like Cabal's "buildable: false". Not only can that nicely express not all features work everywhere, it also can be used to express that certain features are incompatible wihout violating the additivity of features! (Technically, one can express "buildable: false" today with an impossible depenency, so we know it's safe. This just takes that existing functionality and makes it more ergonomic.)

This, and weak deps as I described in https://github.com/rust-lang/rfcs/pull/3143, are both cases of things we can't get today, but a more flexible language for specifying dependencies would get is for free. I really hope we can get that more flexible language!

CC @Eh2406

Ericson2314 avatar Aug 18 '21 22:08 Ericson2314

I think the current syntax makes this more confusing that it needs to be. The current [features] section does two things: it declare what the features are, and specifies the dependencies between features.

I disagree, it is in line with the target specific dependency declaration so imho it's consistent.

I think based on what the others say there is good need for target-specific dependencies. Note that this includes target specific default features as they are target-specific dependencies of the "default" feature.

However, there is no need for the mere existence of features to vary between platforms. It would be much better to solve that with something like Cabal's "buildable: false". Not only can that nicely express not all features work everywhere, it also can be used to express that certain features are incompatible wihout violating the additivity of features! (Technically, one can express "buildable: false" today with an impossible dependency, so we know it's safe. This just takes that existing functionality and makes it more ergonomic.)

Good catch! Couldn't this be solved by an impl detail to check if a feature exists at all before checking if it exists for the current target, and show a sufficiently detailed error message? Iiuc that would be equal to an explicit buildable flag. Could you reference where this is outlined? Thanks!

drahnr avatar Aug 19 '21 06:08 drahnr