Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Platform target-specific features #1197

Open
alexcrichton opened this issue Jan 20, 2015 · 68 comments
Open

Platform target-specific features #1197

alexcrichton opened this issue Jan 20, 2015 · 68 comments
Assignees
Labels
A-features Area: features — conditional compilation E-hard Experience: Hard S-needs-team-input Status: Needs input from team on whether/how to proceed.

Comments

@alexcrichton
Copy link
Member

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 alexcrichton added the A-features Area: features — conditional compilation label Jan 20, 2015
@alexcrichton
Copy link
Member Author

cc @huonw

@nox
Copy link
Contributor

nox commented Jul 10, 2016

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"]}

@alexcrichton
Copy link
Member Author

@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.

@jethrogb
Copy link
Contributor

jethrogb commented Nov 5, 2016

Ran into this extremely confusing bug today...

@gyscos
Copy link

gyscos commented Dec 20, 2016

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.

@alexcrichton
Copy link
Member Author

@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.

@nox
Copy link
Contributor

nox commented Mar 4, 2017

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.

@retep998
Copy link
Member

retep998 commented Mar 4, 2017

@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

@philn
Copy link

philn commented Oct 18, 2017

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).

@alexcrichton
Copy link
Member Author

@philn AFAIK no progress has been made

@praetp
Copy link

praetp commented May 3, 2018

Hope we can see progress on this issue...

@daxpedda
Copy link
Contributor

daxpedda commented Dec 2, 2023

Just leaving a user-story here:

wgpu (a general purpose GPU library) supports multiple backends: GLES, DX11/12, Vulkan and Metal. Unfortunately they are not all equal, e.g. Vulkan is available on Linux and Windows, but on MacOS it requires MoltenVK (a compatibility layer).

Currently wgpu enables the Vulkan backend by default on Linux and Windows but not on MacOS. To let users disable those, we need features that can be disabled by using default-features = false but are enabled by default.
So the solution here is to create two features, e.g. vulkan-native and vulkan-moltenvk.
But both would use the same dependencies, so when using default = ["vulkan-native"] it would pull in all dependencies on MacOS as well, even though the desire here is to not enable Vulkan by default on MacOS.

Unfortunately the only workaround I'm aware of, is to create a crate, e.g. wgpu-default-features, that enables the desired crate features by default on each platform by using target.'cfg(...)'.dependencies and then using default = ["dep:wgpu-default-features"].
Not only is it pretty annoying to have to maintain a workaround crate, but in wgpu cfg guards now have to test for vulkan-native, vulkan-moltenvk and default.

@aawsome
Copy link

aawsome commented Feb 12, 2024

I'm also requesting this feature, but I also have a question about possible workarounds.

In my example, I want to add a mount subcommand which depends on fuse_mt. This dependency, however, only builds on certain targets, but we do want to have successful builds on all targets for all features (we test using --all-features) .
Is there a way to achieve this without target-specific features?

@epage
Copy link
Contributor

epage commented Feb 12, 2024

What would most help this conversation is for someone to go through and summarize

  • Use cases
  • Workarounds
  • Proposed solutions

For example, from my quick skim, there are two workarounds (depending on the use case)

  • Making a target dependency optional and referring to it in a feature (example)
  • Declaring a regular dependency with the base-level features and then duplicating that in a target dependency with additional features (example)
    • This duplicates the source but that can be reduced with workspace inheritance

Neither helps with the "default backend" use case. I suspect in most cases that would be better served by global, mutually exclusive features and people should raise that there (if it isn't already addressed). If there is a use case where features is needed, rather than "global, mutually exclusive" features, then that should be called out here.

Based on my skimming and my above notes, I don't see there being anything left here which is why summarizing this is important so we can highlight what exists and help people know to call out what gaps remain that are affecting people.

@daxpedda
Copy link
Contributor

Neither helps with the "default backend" use case. I suspect in most cases that would be better served by global, mutually exclusive features and people should raise that there (if it isn't already addressed). If there is a use case where features is needed, rather than "global, mutually exclusive" features, then that should be called out here.

Unfortunately I'm not very familiar with all the different "defalt backend" use cases out there, but for the Wgpu use-case I described in #1197 (comment) "global, mutually exclusive" features wouldn't solve the problem (AFAIU).

@epage
Copy link
Contributor

epage commented Feb 13, 2024

Why is that? Backends are a top-level concern so it seems like something that should be set using globals, rather than features which are for direct dependents and unified.

@daxpedda
Copy link
Contributor

daxpedda commented Feb 14, 2024

Why is that? Backends are a top-level concern so it seems like something that should be set using globals, rather than features which are for direct dependents and unified.

This is true and would also be a good fit for Wgpu but addresses a different problem.

Taking from #1197 (comment) Wgpu needs:

  • To enable some dependencies (and features) by default depending on the target.
  • Expose crate features that do nothing depending on the target.

Specifically the proposal mentions but leaves out:

  • Target-specific set-globals, which would have to play well with default as well.
  • Multi-valued globals, which Wgpu could probably live without, but would still introduce some complexity, having to play around with multiple globals that do the same thing depending on the target or if using the default.

Maybe I didn't understand the proposal well enough or missed something?


On second thought: maybe I misunderstood your earlier comment and you meant to say that this problem should rather be solved by "global, mutually exclusive features" because these use-cases should move away from crate features anyway?

@epage
Copy link
Contributor

epage commented Feb 14, 2024

Taking from #1197 (comment) Wgpu needs:

Are those for cases that should be controlled by the direct dependent or the final artifact?

  • If its for the direct dependent, this is the appropriate issue but an explanation why this is for direct dependents would be helpful.
  • If its for the final artifact, that would be feedback on the global, mutually exclusive features proposal.

@daxpedda
Copy link
Contributor

Taking from #1197 (comment) Wgpu needs:

Are those for cases that should be controlled by the direct dependent or the final artifact?

  • If its for the direct dependent, this is the appropriate issue but an explanation why this is for direct dependents would be helpful.
  • If its for the final artifact, that would be feedback on the global, mutually exclusive features proposal.

They should be controlled by the final artifact.
I will make a post there, thanks!

@fwcd
Copy link

fwcd commented Apr 23, 2024

Haven't read the whole thread, but this is my use case:

I am building a cross platform GUI library (nuit) and would like to provide both a smooth experience for library consumers that "just want sensible defaults" (e.g. SwiftUI on macOS, GTK+ on Linux, ...), while simultaneously offering the flexibility to select a custom backend. Since backends may be supported on multiple platforms (e.g. GTK+ runs on macOS too), I don't think this can cleanly be modeled with the winit workaround of enabling all backends by default and making them a no-op on unsupported platforms, since that would e.g. always result in GTK+ libraries being pulled in on macOS.

What I would really like to write would be something along the lines of

[target.'cfg(target_os = "macos")'.features]
default = ["swiftui"]

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

which is very close to the existing suggestions from this thread. The other workaround would be introducing wrapper crates for each backend and using the existing target-specific dependency mechanism, but that still feels like an artificial restriction over something that could be expressed a lot more cleanly and concisely.

benesch added a commit to benesch/materialize that referenced this issue May 13, 2024
Twiddle our crates and Cargo configuration for activating jemalloc to
achieve the following ideal developer experience:

  * --no-default-features unequivocally disables jemalloc, regardless of
    platform.
  * --features=jemalloc unequivocally enables jemalloc, regardless of
    platform.
  * --default-features chooses the best allocator for the platform: the
    system allocator on macOS and jemalloc on Linux.

Since Cargo doesn't explicitly support target-specific features, the
trick is to introduce a crate of indirection, as described in [0].
Here's how it works:

  * The `mz-prof`, `mz-prof-http`, and `mz-alloc` crates have a
    straightforward setup: they expose a `jemalloc` feature and
    conditionally enable or disable jemalloc features based on whether
    the `jemalloc` feature is provided or not.

  * The `mz-alloc-default` crate depends on the `mz-alloc` crate and
    enables the best features for the platform: `jemalloc` on Linux, and
    no additional features on macOS. You can think of this crate as
    unconditionally enabling the best allocator for the platform.

  * The `mz-environmentd` and `mz-clusterd` crates unlock the
    conditional behavior. Bothc crates:
      1. depend on `mz-alloc-default` by default, and
      2. expose a `jemalloc` feature that force enables the
         `mz-alloc/jemalloc` feature.

The motivation for all of this is to allow macOS developers to enable
jemalloc upon request, which is useful for certain kinds of testing.

Alternative to MaterializeInc#27041.
Closes MaterializeInc#27041.

[0]: rust-lang/cargo#1197 (comment)
benesch added a commit to benesch/materialize that referenced this issue May 13, 2024
Twiddle our crates and Cargo configuration for activating jemalloc to
achieve the following ideal developer experience:

  * --no-default-features unequivocally disables jemalloc, regardless of
    platform.
  * --features=jemalloc unequivocally enables jemalloc, regardless of
    platform.
  * --default-features chooses the best allocator for the platform: the
    system allocator on macOS and jemalloc on Linux.

Since Cargo doesn't explicitly support target-specific features, the
trick is to introduce a crate of indirection, as described in [0].
Here's how it works:

  * The `mz-prof`, `mz-prof-http`, and `mz-alloc` crates have a
    straightforward setup: they expose a `jemalloc` feature and
    conditionally enable or disable jemalloc features based on whether
    the `jemalloc` feature is provided or not.

  * The `mz-alloc-default` crate depends on the `mz-alloc` crate and
    enables the best features for the platform: `jemalloc` on Linux, and
    no additional features on macOS. You can think of this crate as
    unconditionally enabling the best allocator for the platform.

  * The `mz-environmentd` and `mz-clusterd` crates unlock the
    conditional behavior. Bothc crates:
      1. depend on `mz-alloc-default` by default, and
      2. expose a `jemalloc` feature that force enables the
         `mz-alloc/jemalloc` feature.

The motivation for all of this is to allow macOS developers to enable
jemalloc upon request, which is useful for certain kinds of testing.

Alternative to MaterializeInc#27041.
Closes MaterializeInc#27041.

[0]: rust-lang/cargo#1197 (comment)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-features Area: features — conditional compilation E-hard Experience: Hard S-needs-team-input Status: Needs input from team on whether/how to proceed.
Projects
None yet
Development

No branches or pull requests