- Feature Name:
cargo-features2
- Start Date: 2020-05-09
- RFC PR: rust-lang/rfcs#2957
- Cargo Issue: rust-lang/cargo#8088
Summary
This RFC is to gather final feedback on stabilizing the new feature resolver in Cargo. This new feature resolver introduces a new algorithm for computing
These changes have already been implemented
Note: The new feature resolver does not address all of the enhancement requests for feature resolution. Some of these are listed
リスト、列挙するbelow in the Feature resolver enhancements section.節These are explicitly明示的にdeferred for future work.
Motivation
Feature unification
Currently, when features are computed
-
Unused targets: If a dependency
依存、依存関係shows up multiple複数のtimes in the resolve解決するgraph, and one of those situations is a target-specific dependency,依存、依存関係the features of the target-specific dependency依存、依存関係are enabled on all platforms. See target dependencies依存、依存関係below for how this problem is solved. -
Dev-dependencies: If a dependency
依存、依存関係is shared as a normal dependency依存、依存関係and a dev-dependency, then any features enabled on the dev-dependency will also show up when used as a normal dependency.依存、依存関係This only applies適用するto workspace packages; dev-dependencies in packages on registries like crates.io have always been ignored.無視するcargo install
has also always ignored無視するdev-dependencies. See dev-dependencies below for how this problem is solved. -
Host-dependencies: Similarly
同様にto dev-dependencies, if a build-dependency or proc-macro has a shared dependency依存、依存関係with a normal dependency,依存、依存関係then the features are unified with the normal dependency.依存、依存関係See host dependencies依存、依存関係below for how this problem is solved.
Command-line feature selection選択
Cargo has several flags for choosing which features are enabled during a build. --features
allows--all-features
enables all features, and --no-default-features
ensures the "default" feature is not automatically
These are fairly straightforward when used with a single
cargo build -p other_member --features …
— The listedリスト、列挙するfeatures are for the package in the current directory, even if that package isn't being built! This also makes it difficult or impossible to build multiple複数のpackages at once with different features enabled.--features
and--no-default-features
flags are not allowed許可する、可能にするin the root of a virtual workspace.
See New command-line behavior
Guide-level explanation
New resolver behaviorふるまい
When the new feature resolver is enabled, features are not always unified when a dependency
For target dependencies
The following three sections
Target dependencies依存、依存関係
When a package appears
[dependency.common]
version = "1.0"
features = ["f1"]
[target.'cfg(windows)'.dependencies.common]
version = "1.0"
features = ["f2"]
When building this example for a non-Windows platform, the f2
feature will not be enabled.
dev-dependencies
When a package is shared as a normal dependency
[dependencies]
serde = {version = "1.0", default-features = false}
[dev-dependencies]
serde = {version = "1.0", features = ["std"]}
In this situation, a normal cargo build
will build serde
without any features. When built with cargo test
, Cargo will build serde
with its default features plus the "std" feature.
Note that this is a global decision. So a command like cargo build --all-targets
will include examples and tests, and thus
Host dependencies依存、依存関係
When a package is shared as a normal dependency
[dependencies]
log = "0.4"
[build-dependencies]
log = {version = "0.4", features=['std']}
In this situation, the log
package will be built with the default features for the normal dependencies.std
feature enabled. This means that log
will be built twice,std
and once with std
.
Note that a dependency
Resolver opt-in
Testing has been performed on variousresolver
field in Cargo.toml
:
[package]
name = "my-package"
version = "1.0.0"
resolver = "2"
Setting"2"
switches Cargo to use the new feature resolver. It also enables backwards-incompatible behavior"1"
uses the previous
The value is a string (instead of an integer) to allow
The resolver
field is only honored in the top-level package or workspace, it is ignored
If using a virtual workspace, the root definition[workspace]
table like this:
[workspace]
members = ["member1", "member2"]
resolver = "2"
For packages that encounter
It is intended that resolver = "2"
will likely become the default setting
New command-line behaviorふるまい
The following changes are made to the behavior
-
Features listed
リスト、列挙するin the--features
flag no longer pay attention to the package in the current directory. Instead, it only enables the given与えられたfeatures for the selected packages. Additionally, the features are enabled only if the the package defines定義するthe given与えられたfeatures.For example:
cargo build -p member1 -p member2 --features foo,bar
In this situation, features "foo" and "bar" are enabled on the given
与えられたmembers only if the member defines定義するthat feature. It is still an error if none of the selected packages defines定義するa given与えられたfeature. -
Features for individual
個々の、それぞれのpackages can be enabled by usingmember_name/feature_name
syntax.文法For example,cargo build --workspace --feature member_name/feature_name
will build all packages in a workspace, and enable the given与えられたfeature only for the given与えられたmember. -
The
--features
and--no-default-features
flags may now be used in the root of a virtual workspace.
The ability to set
The first change is only enabled if the resolver = "2"
value is set
cargo metadata
At this time, the cargo metadata
command will not be changed to expose the new feature resolver. The "features" field will continue to display the features as computed
Properly expressing the dependencycargo metadata
that can add complexity to the interface. For example, the following flags would need to be added
- Workspace selection選択flags (
-p
,--workspace
,--exclude
). - Whether or not dev-dependencies are included (
--dep-kinds
?).
Additionally, the current graph structure does not expose the host-vs-target dependency
It is intended that this will be addressed at some point in the future. Feedback on desired use cases for feature information will help define--unit-graph
flag, which exposes Cargo's internal graph structure, which accurately
For non-parseable output, cargo tree
will show features from the new resolver.
Drawbacks
There are a number of drawbacks to this approach:
-
In some situations, dependencies
依存、依存関係will be built multiple複数のtimes where they were previously only built once. This causes two problems: increased build times, and potentially broken builds when transitioning to the new resolver. It is intended that if the user wants to build a dependency依存、依存関係once that now has non-unified features, they will need to add feature declarations宣言within their dependencies依存、依存関係so that they once again have the same features. Thecargo tree
command has been addedたすto help the user identify同定する、特定するand remedy these situations.cargo tree -d
will expose dependencies依存、依存関係that are built multiple複数のtimes, and the-e features
flag can be used to see which packages are enabling which features.Unfortunately the error message is not very clear when a feature that was previously assumed to be enabled is no longer enabled. Typically
一般的に、典型的にthese appear現れるin the form形式、形態、形作るof unresolved paths. In testing so far, this has come up occasionally, but is usually fairly easy to identify同定する、特定するwhat is wrong. Once more of the ecosystem starts using the new resolver, these errors should become less frequent. -
Feature unification with dev-dependencies being a global decision can result
結果、戻り値in some artifacts including features that may not be desired. For example, a project with a binary2進数and a shared dependency依存、依存関係that is used as a dev-dependency and a normal dependency.依存、依存関係When runningcargo test
the binary2進数will include the shared dev-dependency features. Compare this to a normalcargo build --bin name
, where the binary2進数will be built without those features. This means that if you are testing a binary2進数with an integration test, you end up not testing the same thing as what is normally built. Changing this has significant drawbacks. Cargo's dependency依存、依存関係graph construction構築will require fundamental changes to support this scenario. Additionally, it has a high risk that will cause起こすincreased build times for many projects that aren't affected or don't care that it may have slightly different features enabled. -
This adds complexity to Cargo, and adds boilerplate to
Cargo.toml
. It can also be confusing when switching between projects that use different settings. It is intended in the future that new resolver will become the default via the "edition" declaration.宣言This will remove the extra boilerplate, and hopefully most projects will eventually adopt the new edition, so that there will be consistency between projects. See "Default opt-in" below for more details -
This may not cover all of the backwards-incompatible changes that we may want to make to the feature resolver. At this time, we do not have any specific
特定のenhancements planned that are backwards-incompatible, but there is a risk that additional追加のenhancements will require a bump to version"3"
of the resolver field, causing furtherさらなる、それ以上ecosystem churn. Since there aren't any specific特定のchanges on the horizon that we know will cause起こすproblems, I am reluctant to force the new resolver to wait until some uncertain point in the future. See Future possibilities for a listリスト、列挙するof possible changes. -
The new resolver has not had widespread testing. It is unclear if it covers most of the concerns that motivated it, or if there are shortcomings or problems. It is difficult to get sufficient testing, particularly when only available as an unstable feature.
Subtle behaviorsふるまい
The following are behaviors
Optional必須でない dependency依存、依存関係 feature names
-
dep_name/feat_name
will always enable the featuredep_name
, even if it is an inactive optional必須でないdependency依存、依存関係(such as a dependency依存、依存関係for another platform). The intent here is to be consistent where features are always activated when explicitly明示的にwritten, but the dependency依存、依存関係is not activated. -
--all-features
enables features for inactive optional必須でないdependencies依存、依存関係(but does not activate the dependency依存、依存関係). This is consistent with--features foo
enablingfoo
, even if thefoo
dependency依存、依存関係is not activated.
Code that needs to have a cfg
expressioncfg
that matchescfg(windows)
) or use cfg(accessible(dep_name))
when that syntax
This is somewhat intertwined with the upcoming namespaced features. For an optional
Proc-macro unification in a workspace
If there is a proc-macro in a workspace, and the proc-macro is included as a "root" package along with other packages in a workspace (for example with cargo build --workspace
), then there can be some potentially surprising feature unification between the proc-macro and the other members of the workspace. This is because proc-macros may have normal targets such as binaries
This issue is detailed in issue #8312.
At this time, there isn't a clear solution to this problem. If this is an issue, projects are encouraged to avoid--workspace
or use --exclude
or otherwise
Rationale and alternatives代わりのもの、選択肢
-
These changes could be forced on all users without an opt-in. The amount of breakage is not expected to be widespread, though limited testing has exposed that it will happen some of the time. Generally, Cargo tries to avoid
避ける、回避するbreaking changes that affect a significant portion of users, and we feel that breakage will come up often enough that an opt-in is the best route. -
An alternative
代わりのもの、選択肢approach would be to give the user manualマニュアル、手動control制御するover which specific特定のdependencies依存、依存関係are unified and which aren't. A similar似ている、同様のoption would be feature masks.マスク、隠すThis would likely be a tedious process, whereas hopefully this RFC's approach is more automatic自動のand streamlined for the common case.
Prior art
Other tools have various
- Ivy has module configurations for conditionally selecting dependencies.依存、依存関係It also has pluggable resolvers.
- Maven has optional必須でないdependencies依存、依存関係with the ability to express exclusions.
- Gradle has feature variants, with capabilities indicating what is provided.与えるConflicts can be resolved with user-defined code.
- Bazel has configurable build attributes to change build rules on the command-line.
- Several build tools, like make, rely on user scripting to inspect variables変数、ストレージto make decisions on build settings.
- Meson has optional必須でないdependencies依存、依存関係which are skipped if not available. Build options provide与えるa way to setセットする、集合different settings, including enabled/disabled/auto features.
- go has build constraints制約which can conditionally include a file.
- NuGet dependencies依存、依存関係can use the PackageReference to specify特定する、指定する、規定するconditions条件for inclusion.
- Cabal has conditional条件付き、条件的features to control制御するconfiguration flags.
- Bundler can use arbitrary任意のRuby code to define定義するconditions.条件Optional必須でないdependency依存、依存関係groups can be toggled by the user.
- pip dependencies依存、依存関係can have constraints制約, and can have "extras" which can be enabled by dependencies.依存、依存関係Environment環境markers also provide与えるa way to furtherさらなる、それ以上restrict制限するwhen a dependency依存、依存関係is used.
- CPAN dependencies依存、依存関係use a requires/recommends/suggests/conflicts model. Optional必須でないfeatures are also available.
- npm and yarn have optional必須でないdependencies依存、依存関係that are skipped if they fail to install.
- Gentoo Linux Portage has one of the most sophisticated feature selection選択capabilities of the common system packagers. Its USE flags control制御するdependencies依存、依存関係and features. Dependencies依存、依存関係can specify特定する、指定する、規定するUSE flag requirements. REQUIRED_USE supports expressions式for USE restrictions,制限mutually exclusive flags, etc. Profiles provide与えるa way to group USE flags.
Unresolved questions
None at this time.
Motivating issues
The Cargo issue tracker contains
- #8088 Features 2.0 meta tracking issue.
- #7914 Tracking issue for -Z features=itarget
- #7915 Tracking issue for -Z features=host_dep
- #2589 Build Deps getting mixed in with dependencies依存、依存関係
- #4361 Shared build+target dependency依存、依存関係crates conflate features
- #4866 build-dependencies and dependencies依存、依存関係should not have features unified
- #5730 Features of dependencies依存、依存関係are enabled if they're enabled in build-dependencies; breaks no_std libs
- #2589 Build Deps getting mixed in with dependencies
- #7916 Tracking issue for -Z features=dev_dep
- #5364 New behaviorふるまいof
--feature
+--package
combination- #4106 Testing workspace package with features expects the root package to have those features
- #4753 Add support for --features and --no-default-features flags in workspace builds
- #5015 building workspaces can't use --features flag
- #5362 Cargo sometimes doesn't ungate crate features
- #6195 Testing whole workspace with features enabled in some crate(s)
Future possibilities
Feature resolver enhancements
The following changes are things we are thinking about, but are not in a fully-baked state. It is uncertain if they will require backwards-incompatible changes or not.
- Workspace feature unification. Currently the features enabled in a workspace depend on which workspace members are built (and those members' dependency依存、依存関係tree). Sometimes projects want to ensure保証するa dependency依存、依存関係is only built once, regardless〜に関わらずof which member included it, to avoid避ける、回避するduplicate builds, or surprising changes in behavior.ふるまいSometimes projects want to ensure保証するdependencies依存、依存関係are not unified, since they don't want unrelated workspace members to affect one another. It seems likely this may require explicit明示的なnotation記法to control制御するthe behavior,ふるまいso it may be possible to add in a backwards-compatible fashion. There are also workarounds for this behavior,ふるまいso it is not as urgent.
- Automatic自動のfeatures. This allows許可する、可能にするa dependency依存、依存関係to automatically自動的にbe enabled if it is already enabled somewhere else in the graph. rfc#1787
- Profile and target default features.
- Namespaced features. rust-lang/cargo#5565
- Mutually-exclusive features. rust-lang/cargo#2980
- Private and unstable features.
- And many other issues and enhancements in the Cargo tracker: A-features
Default opt-in
We are planning to make it so that in the next Rust Edition, Cargo will automaticallyresolver = "2"
when a workspace specifies
Default cargo new
In the short term,cargo new
(and init
) will not setresolver
field. After this feature has had some time on stable and more projects have some experience with it, the default manifest for cargo new
will be modified to setresolver = "2"
.