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

Stabilize Cargo's new feature resolver #2957

Merged
merged 8 commits into from
Dec 18, 2020
Merged

Conversation

ehuss
Copy link
Contributor

@ehuss ehuss commented Jul 16, 2020

This RFC proposes to stabilize Cargo's new feature resolver, and the new command-line behavior for feature flags.

Rendered

@ehuss
Copy link
Contributor Author

ehuss commented Jul 16, 2020

cc @rust-lang/cargo

@CryZe
Copy link

CryZe commented Jul 17, 2020

At the moment

my-feature = ["my-optional-dependency/their-feature"]

also enables an optional dependency when my-feature is activated. I've encountered this for a std feature that I have, where I delegate std to all my dependencies, even optional ones, but that now forces them to be built, even if the user of my crate never wanted them. The only alternative is to add a feature for something like i-want-the-dependency-but-also-std, but that's annoying to use and starts a combinatorial explosion.

So I feel like the new resolver should also not automatically enable optional dependencies just because a feature got delegated. Otherwise I feel like this may be something that would go into an eventual resolver version 3 in some way.

@ehuss
Copy link
Contributor Author

ehuss commented Jul 17, 2020

@CryZe I recognize that the inability to conditionally enable features on optional dependencies makes it difficult or impossible to express certain things. That request is tracked in rust-lang/cargo#3494.

There are likely several ways that can be added without breaking current behavior. Possibly through new syntax, something like namespaced features, or automatic features.

Part of the goal is to avoid highly disruptive changes, and I suspect changing the meaning of pkg/feat would break a large number of packages. Also, the resolver field is a global change, whereas a change to pkg/feat would be package-specific (that is, each package would need to decide which meaning it is using).

Finally, we recognize that there are a large number of items on the wish list for changes to features. However, I would prefer to avoid blocking progress for changes that are not on the horizon.

Copy link
Contributor

@Eh2406 Eh2406 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks to @ehuss for all the work to make this happen! Overall I love the improvements!

text/0000-cargo-features2.md Outdated Show resolved Hide resolved

Testing has been performed on various projects that shows sometimes
dependencies are written to assume that features from another part of the
graph are enabled, and fail to compile with the new resolver. Because the new
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It took me a few tries to parse this sentence. Maybe it can be broken up something like "Testing has been performed on various projects. Some whur found to fail to compile with the new resolver. Witch shows that sometimes dependencies are written to assume that features from another part of the graph are enabled."

@jonas-schievink jonas-schievink added the T-cargo Relevant to the Cargo team, which will review and decide on the RFC. label Jul 17, 2020
Setting the resolver to `"2"` switches Cargo to use the new feature resolver.
It also enables backwards-incompatible behavior detailed in [New command-line
behavior](#new-command-line-behavior). `"2"` is the only valid option, if
`resolver` is not specified then the old behavior is used.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason why resolver = "1" is not something we are going to accept, even if just for reduced surprise factor? (As a prior art --edition accepts both 2018 and 2015 as a value)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There isn't a strong reason, just a personal preference of not wanting to see resolver = "1" being used, but it can be easily added.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add this, so that if we want to change the default for resolver at a later date (maybe a new rust edition etc..)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pksunkara That assumes we want to support using a new edition but opting for the old resolver, which I don't know that we want to support. We could minimize the matrix of simultaneously supported features by not supporting the old resolver on a new edition that implies the new resolver.

Copy link
Contributor

@jonas-schievink jonas-schievink left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work @ehuss! Can't wait to see this happen, this will help immensely in the embedded ecosystem ❤️

text/0000-cargo-features2.md Outdated Show resolved Hide resolved

Note that this is a global decision. So a command like `cargo build
--all-targets` will include examples and tests, and thus features from
dev-dependencies will be enabled.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this mean that doing cargo build --lib followed by cargo build --all-targets will potentially recompile a lot of the dependency graph if a new feature is turned on?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that is possible.

[package]
name = "my-package"
version = "1.0.0"
resolver = "2"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incredibly nitpicky, but why the string "2" instead of the number 2?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intent is to keep it a free-form string for future additions, like enabling different options. Hopefully that won't be necessary, but I wanted to leave it flexible.

* 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
Copy link
Member

@nagisa nagisa Jul 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

edition is a non-global choice and therefore workspaces generally do not have the edition annotation. Does this mean that workspaces are doomed to forever using resolver annotation and/or using the old resolver?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are planning to add the edition field to the [workspace] table. That is still a bit up in the air, since we haven't really defined what that will do (would it be the default for all workspace members?). Issue rust-lang/cargo#5784 is tracking that. One of my concerns is that with RFC 2906, that inheriting package metadata will require specifying { workspace = true } and that ends up creating more boilerplate in each package. But making it implicitly inherit the edition would be inconsistent with the other fields.

Copy link
Member

@matklad matklad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent work!

I think scoping this to dev/target/host deps, and deferring workspace unification is a good call.

[dev-dependencies](#dev-dependencies), the general rule is, if a dependency is
not built, it does not affect feature resolution. For [host
dependencies](#host-dependencies), the general rule is that packages used for
building (like proc-macros) do not affect the packages being built.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be a good idea again to explicitly mention that workspace feature unification is deffered to future work, it took me a couple of reads to find out if this is in scope for the RFC.

@djc
Copy link
Contributor

djc commented Jul 22, 2020

This looks great! Minor nit: I wonder about the choice to use a string for the resolver value in the manifest. Couldn't that be an integer instead? What ideas went into deciding it should be a string?

@ehuss
Copy link
Contributor Author

ehuss commented Jul 22, 2020

@djc That is answered above here: #2957 (comment)

@Lonami
Copy link

Lonami commented Jul 22, 2020

@ehuss because multiple people have asked the same, maybe it should be written explicitly in the RFC under "Future possibilities":

By making the value a free-form string, future additions would be able to enable different options, even if this is not necessary at first.


* Features for individual packages can be enabled by using
`member_name/feature_name` syntax. For example, `cargo build --workspace
--feature member_name/feature_name` will build all packages in a workspace,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am assuming that we can do cargo build -p member1 -p member2 --features member1/foo. Would be a good idea expanding how usage in this item interacts with the usage in the above item.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what kind of expansion you are looking for. It enables the feature foo on the package member1. You can also experiment with -Zpackage-features on nightly to see how it behaves.

Feedback on desired use cases for feature information will help define the
solution. A possible alternative is to stabilize the [`--unit-graph`] flag,
which exposes Cargo's internal graph structure, which accurately indicates the
actual dependency relationships and uses the new feature resolver.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure how unit-graph's output looks like, but I want to say that cargo metadata and https://lib.rs/crates/cargo_metadata is used by a lot of projects in the ecosystem. I reckon we need to look into fixing this when this new resolver is implemented.

IIRC even rust-analyzer depends on it to understand the dependency graph. /cc @matklad

@pksunkara
Copy link

pksunkara commented Sep 5, 2020

This looks awesome! Great work @ehuss.

I have a major concern that is not addressed in the RFC though. How will this interact with #2887 (artifact dependencies) or other future plans like that? I can kinda understand how the resolver works and can extrapolate the behaviour, but I would still like to see this specifically talked about.

@ehuss
Copy link
Contributor Author

ehuss commented Nov 29, 2020

How will this interact with #2887 (artifact dependencies) or other future plans like that?

The RFC explicitly calls out the future concerns in the Future possibilities section, and in Drawbackes section, which mentions that future changes may require opting in to new behavior with the resolver setting.

I don't think it is feasible to design every potential enhancement that may come in the future and understand how they might interact. Some work along those lines has been done (see rust-lang/cargo#8799 and rust-lang/cargo#8818), but if this RFC is delayed until everything is considered, it will never be stabilized. The onus is on those future enhancements to consider how they integrate into the existing behaviors and environment.

@ehuss
Copy link
Contributor Author

ehuss commented Nov 29, 2020

I've pushed some small updates to clarify some things. I'd like to nudge this along, so:

@rfcbot fcp merge

@rust-lang/cargo I believe this is in a state to be stabilized. The only open issue I am concerned about is rust-lang/cargo#8312 (I added a summary in the RFC), but there is no clear solution to me. I am uncertain how likely it is to cause problems (there haven't been any real-world reports so far). It may be possible that we can tweak the behavior later if it becomes evident that it is a real problem.

@rfcbot
Copy link
Collaborator

rfcbot commented Nov 29, 2020

Team member @ehuss has proposed to merge this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@rfcbot rfcbot added proposed-final-comment-period Currently awaiting signoff of all team members in order to enter the final comment period. disposition-merge This RFC is in PFCP or FCP with a disposition to merge it. labels Nov 29, 2020
@Eh2406
Copy link
Contributor

Eh2406 commented Nov 29, 2020

This is a clear improvement and the nightly implementation has been widely appreciated. So:
@rfcbot reviewed

At some point we should think if a future rewrite of the dependency resolver (pubgrub-rs) can become aware of and incorporate some of this logic. But the current feature resolver is working and will work with a rewrite, so there is no reason to hold up this on that.

@rfcbot rfcbot added the final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. label Nov 29, 2020
@rfcbot
Copy link
Collaborator

rfcbot commented Nov 29, 2020

🔔 This is now entering its final comment period, as per the review above. 🔔

@rfcbot rfcbot removed the proposed-final-comment-period Currently awaiting signoff of all team members in order to enter the final comment period. label Nov 29, 2020
@rfcbot rfcbot added the finished-final-comment-period The final comment period is finished for this RFC. label Dec 9, 2020
@rfcbot rfcbot removed the final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. label Dec 9, 2020
@rfcbot
Copy link
Collaborator

rfcbot commented Dec 9, 2020

The final comment period, with a disposition to merge, as per the review above, is now complete.

As the automated representative of the governance process, I would like to thank the author for their work and everyone else who contributed.

The RFC will be merged soon.

@ehuss ehuss merged commit 301da04 into rust-lang:master Dec 18, 2020
@ehuss
Copy link
Contributor Author

ehuss commented Dec 18, 2020

Thanks everyone for the comments! I have posted rust-lang/cargo#8997 to follow through with the stabilization.

bors added a commit to rust-lang/cargo that referenced this pull request Jan 5, 2021
Stabilize -Zfeatures and -Zpackage-features.

This follows through with [RFC 2957](rust-lang/rfcs#2957) to stabilize the new feature resolver, and `-Zpackage-features` command-line changes.

This also rewrites the "Features" chapter to try to expand it a little.

I decided to leave the `-Zfeatures` flag in for now for testing, but it can be removed at a later date.

There is a code change related to the `package-name/feature-name` syntax for the `--features` flag. I wanted to stabilize that for `resolver = "1"`, but I previously neglected to separate that behavior out, so it required change to `Workspace::members_with_features` to make that work (see the `resolver1_member_features` test).

Closes #4328
Closes #5364
Closes #7914
Closes #7915
Closes #7916
Closes #8088
Closes #8431
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
disposition-merge This RFC is in PFCP or FCP with a disposition to merge it. finished-final-comment-period The final comment period is finished for this RFC. T-cargo Relevant to the Cargo team, which will review and decide on the RFC. to-announce
Projects
None yet
Development

Successfully merging this pull request may close these issues.