-
Notifications
You must be signed in to change notification settings - Fork 52
Description
Cargo resolves features for a given build across all crates it knows about, and adds all features that are implied by every other one, including when a feature adds additional dependencies, etc. When we add a fixups.toml conditional target.cfg(...) block that includes a feature, it propagates that feature up the dep graph like so:
packagegets featureabcenabled, which is described in its Cargo.toml as[features] abc = ["dep1/abc"]- Therefore
dep1gets featureabcenabled, i.e.[features] abc = ["dep:some_dep"] - Cargo adds package
some_depto the build graph. - Etc
In contrast, when I add a feature via reindeer fixups, Reindeer does no propagation. It would be really useful if it could behave like enabling a cargo feature. Currently it just gets passed through to rust_library(features = ["..."]) which is really just a shorthand for --cfg=feature=abc. I want reindeer to be able to modify the build graph, not just compiler flags.
The only workaround is very painful
Many crates and projects rely heavily on Cargo's feature propagation. You will frequently see a feature being passed down through 5 or more levels of internal dependencies, and it is often named differently at each level because these are not public API really. If you cannot use Cargo's feature resolution for some reason, then you've got a problem. To directly fix this you must literally look at Cargo.tomls for 15-20 crates and manually do Cargo's feature resolution, or unconditionally add the feature, save a copy of the BUCK file and keep adding fixups until your generated BUCK file gets close to it.
Why can't we just add a dep in the top level Cargo.toml and add features to it like normal?
Yes, Cargo can do some of this, and also has conditional [target.'cfg(...)'.dependencies] which cover quite a lot of situations. But this does not support the custom cfg() settings that reindeer does. Cargo can only branch on cfgs inherent to a target tuple.
Imagine you have two different builds for a single rust target triple. You have a buck constraint_setting for it, and you tell reindeer about this so that it can configure some part of your dependency graph in two different ways. You do this via doubling up on reindeer platforms like so:
# reindeer.toml
[[platforms.abc]]
target = "x86_64-unknown-linux-gnu"
custom_config = "abc"
...
[[platforms.def]]
target = "x86_64-unknown-linux-gnu"
custom_config = "def"
...# fixups/package/fixups.toml
['cfg(custom_config = "abc")']
features = ["abc"]But this just results in a single feature with no propagation:
alias(
name = "dep1",
actual = ":dep1-0.1.0",
visibility = ["PUBLIC"],
)
cargo.rust_library(
name = "dep1-0.1.0",
srcs = ["dep1/src/lib.rs"],
crate = "dep1",
crate_root = "dep1/src/lib.rs",
edition = "2024",
visibility = [],
### no sign of features abc/def, should be conditionally enabled
### given package/Cargo.toml [features] abc=["dep1/abc"]
)
cargo.rust_library(
name = "package",
srcs = ["src/lib.rs"],
crate = "package",
crate_root = "src/lib.rs",
edition = "2024",
platform = {
"linux-abc": dict(
features = ["abc"],
),
"linux-def": dict(
features = ["def"],
),
},
visibility = ["PUBLIC"],
deps = [":dep1-0.1.0"],
)There is no way to encode this in Cargo only. A real-world use case is selecting wgpu backends to produce multiple builds for different GPU backends on a single rust target.