From 9627722da402588f5703075ba85908c9c6fe3b3f Mon Sep 17 00:00:00 2001 From: Without Boats Date: Wed, 22 Mar 2017 02:27:31 -0700 Subject: [PATCH 1/2] Profile Features & Dependencies. --- text/0000-profile-features.md | 145 ++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 text/0000-profile-features.md diff --git a/text/0000-profile-features.md b/text/0000-profile-features.md new file mode 100644 index 00000000000..073ca75561b --- /dev/null +++ b/text/0000-profile-features.md @@ -0,0 +1,145 @@ +- Feature Name: profile_features +- Start Date: 22-03-2017 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +Support profile-specific overrides for cargo features and dependencies. + +# Motivation +[motivation]: #motivation + +Sometimes, users wish to control the build of their crates in a manner which +is dependent on which profile it is being built for, because of the differences +in use cases between the profiles. Today, this is not easy to achieve directly, +and instead requires hacking with the existing `cfg` options that roughly +correspond to some of the profiles (such as `test` and `debug_assertions`). + +This system is ad hoc and doesn't allow downstream users to effectively control +the way these features are used by their dependencies. To solve this problem, +we allow profiles to play a first class role in feature and dependency +resolution. + +# Detailed design +[design]: #detailed-design + +Each profile gains these additional members: + +* `features` +* `dependencies` +* `dev-dependencies` +* `build-dependencies +* `target` + +Each of these corresponds to the same top-level object. When compiling in +a profile, its members under these objects are merged with the top level +equivalent, overriding any overlapping members. This enables users to configure +thier builds differently in each profile if necessary. + +Specifically: + +* The `features` table for a profile is *merged* with the base `features` +table; each member of the `features` table defined in the profile-specific +table *replaces* the member in the base table (they are not merged together). +* An individual object representing a specific dependency is *merged* with the +base object for that dependency; each member of that object *replaces* the +member in the base table (they are not merged together). + +So from this TOML: + +```toml +[dependencies.foo] +version = "1.0.0" +features = ["foo"] + +[profile.dev.dependencies.foo] +version = ["bar"] +``` + +In the `dev` profile, the foo dependency looks like: + +```toml +[dependencies.foo] +version = "1.0.0" +features = ["bar"] # Note that the foo feature has been dropped +``` + +## Use cases + +### Profile-specific default features + +Some features are intended to be used in different profiles - for example, a +feature for testing or for optimizations which is intended for bench and +release. Today, these sorts of things are managed in an ad hoc and imprecise +way through the `test` and `debug_assertions` cfg flags. + +Instead, authors can turn features on by default in only some profiles. For +example: + +```toml +[features] +default = [] +go-fast = [] + +[profile.release.features] +default = ["go-fast"] + +[profile.bench.features] +default = ["go-fast"] +``` + +### Turning on dependency features in specific profiles + +Possibly, the profile-specific feature is not a default feature for this crate, +but a feature the user wants in a dependency, but only in some profiles. This +can be done using by overriding that crate's dependency member in those +profiles. + +```toml +[profile.release.dependencies.another-crate] +features = ["go-fast"] + +[profile.bench.dependencies.another-crate] +features = ["go-fast"] +``` + +# How We Teach This +[how-we-teach-this]: #how-we-teach-this + +This should be documented in the crates.io documentation for the Cargo.toml +manifest format. + +# Drawbacks +[drawbacks]: #drawbacks + +The primary drawback is that crates which perform conditional compilation on +profiles may not build correctly in one profile. Since most crates are tested +primarily in the `test` profile, this could be a problem. + +However, this is already an issue today, because of the way the `test` and +`debug_assertion` cfgs can already be used to conditionally compile code only +in certain profiles. This RFC gives users greater control over this by tying +the conditional compilation to explicit features which can be turned on or off. + +# Alternatives +[alternatives]: #alternatives + +One alternative would be to add profiles as a unique cfg marker: + +```rust +#[cfg(profile = "release")] +``` + +This would not be as flexible as this RFC - by tying any conditional +compilation to a _feature_, we allow dowstream users to have ultimate control +over which code is compiled in the dependency. In addition to being flexible, +this gives users a way out if a library is broken because of a broken feature +turned on by default in one profile. + +# Unresolved questions +[unresolved]: #unresolved-questions + +Does this feature overlap in purpose with `dev-dependencies` at all, and if so +is there a way to subsume `dev-dependencies` into it & deprecate that feature? From f00a583f7f6f922087cfa2eab560ae5f8e7941ee Mon Sep 17 00:00:00 2001 From: Without Boats Date: Mon, 24 Apr 2017 16:30:52 -0700 Subject: [PATCH 2/2] Scope to specifically controlling feature selection. --- text/0000-profile-features.md | 131 +++++++++++++++------------------- 1 file changed, 56 insertions(+), 75 deletions(-) diff --git a/text/0000-profile-features.md b/text/0000-profile-features.md index 073ca75561b..159f41c4f13 100644 --- a/text/0000-profile-features.md +++ b/text/0000-profile-features.md @@ -6,103 +6,85 @@ # Summary [summary]: #summary -Support profile-specific overrides for cargo features and dependencies. +Support profile-specific overrides for the `default-features` key. # Motivation [motivation]: #motivation Sometimes, users wish to control the build of their crates in a manner which -is dependent on which profile it is being built for, because of the differences -in use cases between the profiles. Today, this is not easy to achieve directly, -and instead requires hacking with the existing `cfg` options that roughly -correspond to some of the profiles (such as `test` and `debug_assertions`). - -This system is ad hoc and doesn't allow downstream users to effectively control -the way these features are used by their dependencies. To solve this problem, -we allow profiles to play a first class role in feature and dependency -resolution. +is dependent on which profile it is being built for. Today, this is not easy to +achieve directly; what users tend to do is use a `cfg` option that *usually* +corresponds to the profile being run in, suhc as using `debug_assertions` to +get a certain behavior in dev that is not desired in release. + +For example, a user writing a web service may wish to load their HTML templates +dynamically during `dev`, so that they can edit the templates without +recompiling their application. But for both performance and ease of deployment, +they would want their HTML templates to be compiled into the data section of +the binary when they perform a `release` build. + +The system today has several problems. First, it is very ad hoc, and couples +what should be independent "feature" based behavior to an unrelated feature +(debug_assertions). Second, if a library chooses to use a flag like this to +control which features they provide, it is difficult for downstream users to +opt out of that behavior (e.g. because they want dev and release to compile the +same code). + +Instead we propose that users should adopt standard `features` for behaviors +like this. In order to make this easier to do, users will be able to specify +the `default-features` separately in each profile, causing certain features to +be turned on in one profile and not in others. # Detailed design [design]: #detailed-design -Each profile gains these additional members: - -* `features` -* `dependencies` -* `dev-dependencies` -* `build-dependencies -* `target` - -Each of these corresponds to the same top-level object. When compiling in -a profile, its members under these objects are merged with the top level -equivalent, overriding any overlapping members. This enables users to configure -thier builds differently in each profile if necessary. - -Specifically: +## Specifying profile features for your package -* The `features` table for a profile is *merged* with the base `features` -table; each member of the `features` table defined in the profile-specific -table *replaces* the member in the base table (they are not merged together). -* An individual object representing a specific dependency is *merged* with the -base object for that dependency; each member of that object *replaces* the -member in the base table (they are not merged together). +Each profile gains the member `default-features`, which has the same structure +as the `features.default` member (that is, it is an array of strings, which +must be feature or dependency names). When preparing a build, if the active +profile has a `default-features` key present, cargo will use that set of +features instead of the `features.default` key. -So from this TOML: +For example, you might write: ```toml -[dependencies.foo] -version = "1.0.0" -features = ["foo"] +[features] +dynamic-templates = [] -[profile.dev.dependencies.foo] -version = ["bar"] +[profile.dev] +default-features = ["dynamic-templates"] ``` -In the `dev` profile, the foo dependency looks like: - -```toml -[dependencies.foo] -version = "1.0.0" -features = ["bar"] # Note that the foo feature has been dropped -``` +The dynamic-templates feature would be on by default, but only in the dev +profile. -## Use cases +## Controlling profile features from dependencies -### Profile-specific default features +Both the `features` and `default-features` members of a dependency object can +be TOML objects as alternative to their current form. As TOML objects, their +members are the names of profiles as well as the key `other`; the value at +each of those keys is the same as what the `features` or `default-features` +keys would otherwise contain (an array of features or a boolean respectively). -Some features are intended to be used in different profiles - for example, a -feature for testing or for optimizations which is intended for bench and -release. Today, these sorts of things are managed in an ad hoc and imprecise -way through the `test` and `debug_assertions` cfg flags. +In a particular profile, if these items are objects, that profile's key is used +to specify this value. If no key is present for this profile, the `other` key +is used. -Instead, authors can turn features on by default in only some profiles. For -example: +For example: ```toml -[features] -default = [] -go-fast = [] - -[profile.release.features] -default = ["go-fast"] - -[profile.bench.features] -default = ["go-fast"] -``` +[dependencies.foobar] +version = "1.2.0" -### Turning on dependency features in specific profiles - -Possibly, the profile-specific feature is not a default feature for this crate, -but a feature the user wants in a dependency, but only in some profiles. This -can be done using by overriding that crate's dependency member in those -profiles. - -```toml -[profile.release.dependencies.another-crate] -features = ["go-fast"] +[dependencies.foobar.default-features] +dev = false +test = false +other = true -[profile.bench.dependencies.another-crate] -features = ["go-fast"] +[dependencies.foobar.features] +dev = ["alpha", "beta"] +test = ["alpha"] ``` # How We Teach This @@ -141,5 +123,4 @@ turned on by default in one profile. # Unresolved questions [unresolved]: #unresolved-questions -Does this feature overlap in purpose with `dev-dependencies` at all, and if so -is there a way to subsume `dev-dependencies` into it & deprecate that feature? +None known.