cargo
cargo copied to clipboard
Platform target-specific features
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.
cc @huonw
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 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.
Ran into this extremely confusing bug today...
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 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.
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 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
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 AFAIK no progress has been made
Hope we can see progress on this issue...
This will help a lot with https://github.com/rust-lang-nursery/rand/issues/442
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 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!
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
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?
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?
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.
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?
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.
Hello, what's the status of this issue? Is there still hope for it to see the light of day (and stabilization)?
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.
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 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.
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.
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.
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.
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.
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
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!