cargo: Tracking issue for Deduplicate Cargo workspace information RFC
This is a tracking issue for the RFC: Deduplicate Cargo workspace information (https://github.com/rust-lang/rfcs/pull/2906).
Documentation:
Steps:
- Implement the RFC (cc @rust-lang/cargo – can anyone write up mentoring
instructions?)
- Support for inheriting in root package (#10497)
- Support inheriting in any workspace member (#10517)
- Correctly inherit fields that are relative paths (#10538, #10548)
- Add
rust-version(not in RFC), see epage’s comment (https://github.com/rust-lang/cargo/pull/10563) - Switch the inheritance source from
workspacetoworkspace.package, see epage’s comment (https://github.com/rust-lang/cargo/pull/10564) - Add
includeandexcludenow thatworkspace.packageis done, see epage’s comment (https://github.com/rust-lang/cargo/pull/10565) - cargo-add support (https://github.com/rust-lang/cargo/pull/10606)
- Non-blocking: https://github.com/rust-lang/cargo/issues/10608
- Optimizations, as needed (https://github.com/rust-lang/cargo/pull/10568)
- Adjust documentation (https://github.com/rust-lang/cargo/pull/10611)
- Decide on documented style (
key = {worskspace = true}vskey.workspace = true)
- Decide on documented style (
- Stabilization PR (see instructions on cargo-dev-guide)
Changes from RFC
- Not locking workspace dependencies, see alexchrichton’s comment
- Not inserting a version into path dependencies when publishing, see epage’s comment
- Changed the table
packagefields inherit from, fromworkspacetoworkspace.package, see epage’s comment - Fields
- Explicitly not supporting
resolver, see epage’s comment - Supporting
editionwhich the RFC showed inconsistent status for - Supporting
rust-versionwhich is newer than the RFC - Supporting
includeandexcludewhich the RFC couldn’t, see epage’s comment
- Explicitly not supporting
Unresolved questions:
- Do we stablize inheriting from
workspace.packageandworkspace.dependenciesor do we switch which tables we use? see epage’s comment - Preferred documentation style: RFC uses
field = { workspace = true }but TOML allowsfield.workspace = true - Is the performance good enough?
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 93
- Comments: 47 (37 by maintainers)
Links to this issue
Commits related to this issue
- Auto merge of #10497 - Muscraft:rfc2906-part1, r=epage Part 1 of RFC2906 - Packages can inherit fields from their root workspace Tracking issue: #8415 RFC: rust-lang/rfcs#2906 All changes were heav... — committed to rust-lang/cargo by bors 2 years ago
- Auto merge of #10517 - Muscraft:rfc2906-part2, r=epage Part 2 of RFC2906 -- allow inheriting from a different `Cargo.toml` Tracking issue: #8415 RFC: rust-lang/rfcs#2906 [Part 1](https://github.com... — committed to rust-lang/cargo by bors 2 years ago
- Auto merge of #10538 - Muscraft:rfc2906-part3, r=epage Part 3 of RFC2906 - Add support for inheriting `license-path`, and `depednency.path` Tracking issue: #8415 RFC: rust-lang/rfcs#2906 [Part 1](h... — committed to rust-lang/cargo by bors 2 years ago
- Auto merge of #10548 - Muscraft:rfc2906-part4, r=epage Part 4 of RFC2906 - Add support for inheriting `readme` Tracking issue: #8415 RFC: rust-lang/rfcs#2906 [Part 1](https://github.com/rust-lang/c... — committed to rust-lang/cargo by bors 2 years ago
- Auto merge of #10563 - Muscraft:rfc2906-part5, r=epage Part 5 of RFC2906 - Add support for inheriting `rust-version` Tracking issue: #8415 RFC: rust-lang/rfcs#2906 Part 1: #10497 Part 2: #10517 Par... — committed to rust-lang/cargo by bors 2 years ago
- Auto merge of #10564 - Muscraft:rfc2906-part6, r=epage Part 6 of RFC2906 - Switch the inheritance source from `workspace` to… Tracking issue: #8415 RFC: rust-lang/rfcs#2906 Part 1: #10497 Part 2: #... — committed to rust-lang/cargo by bors 2 years ago
- Auto merge of #10565 - Muscraft:rfc2906-part7, r=epage Part 7 of RFC2906 - Add support for inheriting `exclude` and `include` Tracking issue: #8415 RFC: rust-lang/rfcs#2906 Part 1: #10497 Part 2: #... — committed to rust-lang/cargo by bors 2 years ago
- Auto merge of #10568 - Muscraft:rfc2906-part8, r=epage Part 8 of RFC2906 - Keep `InheritableFields` in a `LazyCell` inside `… Tracking issue: #8415 RFC: rust-lang/rfcs#2906 Part 1: #10497 Part 2: #... — committed to rust-lang/cargo by bors 2 years ago
- Auto merge of #10584 - Muscraft:rfc2906-part9, r=epage Prefer `key.workspace = true` to `key = { workspace = true }` Tracking issue: #8415 RFC: rust-lang/rfcs#2906 Part 1: #10497 Part 2: #10517 Par... — committed to rust-lang/cargo by bors 2 years ago
- Auto merge of #10606 - Muscraft:cargo-add-support, r=epage Cargo add support for workspace inheritance Tracking issue: #8415 RFC: rust-lang/rfcs#2906 This PR adds all the required support for works... — committed to rust-lang/cargo by bors 2 years ago
- Auto merge of #10611 - Muscraft:workspace-inheritance-documentaion, r=epage Update documentation for workspace inheritance Tracking issue: #8415 RFC: rust-lang/rfcs#2906 This updates documentation ... — committed to rust-lang/cargo by bors 2 years ago
- Auto merge of #10859 - Muscraft:stabilize-workspace-inheritance, r=epage Stabilize Workspace Inheritance What does this PR try to resolve? Stabilize Workspace Inheritance as [#8415 (comment)](https... — committed to rust-lang/cargo by bors 2 years ago
- Auto merge of #11099 - cassaundra:cargo-remove, r=epage Import `cargo remove` into cargo ## What does this PR try to resolve? This PR merges `cargo remove` from [cargo-edit](https://github.com/kill... — committed to rust-lang/cargo by bors 2 years ago
- Auto merge of #11099 - cassaundra:cargo-remove, r=epage Import `cargo remove` into cargo ## What does this PR try to resolve? This PR merges `cargo remove` from [cargo-edit](https://github.com/kill... — committed to rust-lang/cargo by bors 2 years ago
- Auto merge of #11099 - cassaundra:cargo-remove, r=epage Import `cargo remove` into cargo ## What does this PR try to resolve? This PR merges `cargo remove` from [cargo-edit](https://github.com/kill... — committed to rust-lang/cargo by bors 2 years ago
- Restore version specifier for local dependencies. It was initially removed because [RFC #2906][1] mentioned it is no longer necessary and the version is inferred. However, the version that was actual... — committed to google/fleetspeak-rs by panhania a year ago
- Move dependencies to workspace (#4650) ## Issue Addressed Synchronize dependencies and edition on the workspace `Cargo.toml` ## Proposed Changes with https://github.com/rust-lang/cargo/issue... — committed to sigp/lighthouse by jxs 9 months ago
- Move dependencies to workspace (#4650) ## Issue Addressed Synchronize dependencies and edition on the workspace `Cargo.toml` ## Proposed Changes with https://github.com/rust-lang/cargo/issue... — committed to sigp/lighthouse by jxs 9 months ago
- Move dependencies to workspace (#4650) ## Issue Addressed Synchronize dependencies and edition on the workspace `Cargo.toml` ## Proposed Changes with https://github.com/rust-lang/cargo/issue... — committed to sigp/lighthouse by jxs 9 months ago
- Move dependencies to workspace (#4650) ## Issue Addressed Synchronize dependencies and edition on the workspace `Cargo.toml` ## Proposed Changes with https://github.com/rust-lang/cargo/issue... — committed to sigp/lighthouse by jxs 9 months ago
This feature would be incredible 😄
If you then change your repository, you then have one place to update.
I see this less about reducing boiler plate and more about having one place to edit,. so long as the boilerplate isn’t too high.
In particular, I manage a good number of crates. They all follow the same pattern so whenever I improve the pattern, I update the first alphabetically. Every once in a while, I will then diff that first repo against all of the other repos, updating them. There are many times when I forget there are other crates in the repo and don’t update them. This will reduce the chance of that happening.
We can always add change to implicit in a new edition if we find that it is too much.
Team member @epage 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.
Ok I’ve got a moment to write up some instructions, I shall do so! I think this’ll be posted on TWiR soon to see if others are interested, so I’m hoping that I can help guide someone interested to implement this.
To get started, I’d probably break this down based on the reference level explanation:
Update
[workspace]parsingFirst you’ll probably want to implement
[workspace]parsing updates.That’ll be done by updating this structure with all the relevant fields. You’ll need to take care when handling the dependencies, being careful to share code with existing dependency parsing.
Once the fields are parsed you’ll want to carry them over to
WorkspaceRootConfigwhich contains the format of each field that is persisted throughout the lifetime of Cargo itself. This’ll be where dependencies are elaborated (again, sharing code from before) into aDependencystructure for example.At this point you should be able to write some tests. All our tests are in
tests/testsuite/*.rsand you’ll probably want to make a new test suite at something liketests/testsuite/deduplicate_workspace.rsand addmod deduplicate_workspace;totests/testsuite/main.rs. You can then run tests withcargo test deduplicate_workspace.Placeholder
{ workspace = true }Next you can tackle
workspace = truedirectives inCargo.toml. The best way to implement this is probably going to be making a generic thing like:If possible, the best way to handle this will be to not persist this
MaybeWorkspacetype all throughout Cargo. Ideally aManifestin Cargo would retain the actual value for each manifest. I’m not sure whether this is available at this time, but in theory you’ll want to “elaborate” a manifest with aWorkspaceRootConfigon-hand where you can resolve anyMaybeWorkspace<T>with the&WorkspaceRootConfignext to it.New dependency directives
To implement new dependency directives you’ll update this structure along with this method. Again ideally there will be a new
&WorkspaceRootConfigargument which allows resolving allworkspace = truedependencies by basicallyclone-ing theDependencyfrom the workspace.Inferring version directives
For inferring
versiononpathdependencies that will need to happen on the packaging phase of Cargo. First you’ll want to update where the error is generated today. Next you’ll want to updateto_registry_tomlwhich will update the manifest (or probably a clone of the current manifest) with version numbers.And I think that should at least be enough to get almost all the way there! I’m sure there’s things that will likely come up in review and questions a long the way. The hardest part will probably be getting
WorkspaceRootConfigpresent while parsing other manifests, that may require some refactoring in Cargo (or maybe there’s a better way I’m not seeing!)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.
This will be merged soon.
Not all fields are as trivially updatable.
I’ve started work on this.
Thank you for the super detailed writeup, @alexcrichton . You can see my progress here, but for convenience I’ll keep this list updated:
EDIT: The PR is open, so please check there.
Aiming to complete this list relatively soon - hopefully ~tomorrow~ in the coming week.
The way this is implemented in
cargo-add:workspace = true, usedep.workspace = truedep = { workspace = true, ... }To mirror thedep = "version"short-form and long-formThe main motivation for the preferred style is more all of the other keys (e.g.
version.workspace = true) since no other keys will be set, making this style shorter and easier to read (in my opinion). We then chose to keep this consistent when going into the dependencies and to be like settingdep = "version". Keep in mind that unlikegit,workspacewill have a lot fewer keys that can be set with it (since they can only be set in the workspace dependency and not in the package dependency) so its a lower probability of adding more keys (optionalbeing most likely).Looks like dotted keys were added in 2018 (no idea how much
toml-rslagged behind that) and there is momentum for adopting change.Plus, I suspect it doesn’t scale well if you have a lot of items in a table, so we should be judicious in where its used.
While some things can be trivially updated using command line tools, first-party support that is guaranteed to work is usually a better solution.
@epage is correct that this is about having one place to edit more specifically deduplicating manifest information across workspace members. Having users write
repository.workspace = truemakes this an opt-in feature where inheriting by default would be (more than likely) a breaking change and be opt-out. Solving both being opt-out and it being a breaking change would require extending the current implementation to be similar to:If that is something that should be done, I think it should be a different change set or have an RFC to discuss it. This is mostly because it is out of the scope of this RFC.
As for
[package.metadata], the RFC mentions it just above this link:Yep, dependencies are very useful, I’m currently testing using this for internal workspace references, it’s nice to be able to collapse from the below down to
stylish-core.workspace = true.One other set of fields I just noticed I have duplicated everywhere is:
Reskimming the RFC discussion I don’t see any mention of
package.metadata, and something likepackage.metadata.docs.rs.workspace = truesyntax wouldn’t be possible for it as it could already have meaning inside the inner tables.Deviations from the RFC are summarized in https://github.com/rust-lang/cargo/issues/8415#issuecomment-1112618913.
Specifically you are looking for
FWIW, I like
field = { workspace = true }better because:{}Where as I find
field.workspace = trueis little bit difficult to parse since we need to trim the.workspacewhen we are glancing at it.@kornelski brought this up in the RFC discussion:
I didn’t really see any other acknowledgement of this in the thread.
I brought up having these in a
workspace.inherit. @kornelski mentionedworkspace.package-defaults. I think to be consistent with dependencies, we should doworkspace.package.If we are ok with an extra layer of nesting, we could consider
workspace.default.packageandworkspace.default.dependencies. We’d have to bike shed on the worddefaultto convey the right semantics (“default” implies implicit while we do explicit inheritance).I’m going to propose we switch the implementation to
workspace.packageand mark it as an open issue for resolving. This will unblockworkspace.package.excludewhich will unblockworkspace.package.include.@alexcrichton Ah, I see. That’s not bad at all, and addresses my main concern about managing the version numbers centrally. Thanks for clarifying that!
The piece I was missing is the fact that specifying a non-additive, non-default feature set at the package level will no longer require that
default-features = falsebe set locally (in the package-levelCargo.tomlfile), as long as that value has been set at the workspace level.I now see that it is right there in the text of the rfc that I quoted, but I suppose the way I was reading it was too heavily shaped by current practice (where
default-features = falseandfeatures = [...]always appear as a pair in the non-additive case) 😃@salewski it’s always something we can tweak at a later date (or even now before stabilization), but in our discussions amongst the Cargo team awhile back the conclusion was that we couldn’t really think of a non-surprising way of dealing with
default-features. For projects that need to toggle it we figured that you could disable default features in[workspace.dependencies]and then enable thedefaultfeature in directives as necessary. This should still give the same benefit of a workspace-level definition and dependencies enabling thedefaultfeature would be just a little more wordy.Hello, I’ve been making steady progress on this and wanted to give an update:
The
WorkspaceRootConfigis not available when we’re parsing the manifest, so we have to locate and parse it somewhere indo_read_manifest(). Currently the logic for finding the root and parsing itsConfig.tomllives instruct Workspaceitself and makes use ofworkspace.packagesto cache parsed tomls.I see a few different ways to proceed:
Workspaceindo_read_manifestsomehow.Workspace::new()currently parses the entire workspace, so this could get complicated.ParserCacheout ofWorkspace. It would sit in front of thePackagescache, accept amanifest_pathargument, and return a deserializedTomlManifest.packagesregistry entirely. This would be easiest, but worst-case would require parsing every toml in the workspace every time you parse any manifest. I don’t think this is a good solution, but mention it for completeness’ sake.I’m not blocked - am currently leaning towards approach 2 - but please let me know if I’m missing anything or if there’s a better way to proceed.
Edit: In case anyone’s seeing this now, I went with 2 and it seems to work 😃
One place where this is very helpful is for git dependencies. Git dependencies have a lot of “noise” (much longer than version numbers) and are hard to edit automatically. If you have a mismatch you end up with duplicate versions, because cargo doesn’t know how to unify versions from different
revcommits. Having a single place to edit git deps would be extremely useful.Starting FCP for stablizing workspace inheritance where workspace package’s could share metadata and dependency definitions.
We sought community feedback and I have seen projects actively using it. Feedback has been positive though not very loud. We had questions from users in this thread that seemed to be resolved. With nothing known in the way, I felt it was time to propose to merge.
@rfcbot fcp merge
Deviations from the original RFC
packagefields inherit from, fromworkspacetoworkspace.package, see epage’s comment, this PR, and this threadcargo-add, documentation, and tests preferfield.workspace = trueoverfield = { workspace = true }cargo-addsupport see this comment and this PRresolver, see epage’s commenteditionwhich the RFC showed inconsistent status forrust-versionwhich is newer than the RFCincludeandexcludewhich the RFC couldn’t, see epage’s commentWe had questions on whether
workspace.packageis the best fit but we weren’t able to come up with anything better. It does improve upon the RFC putting inheritable package keys underworkspace. As the usage of this spreads, if we find out this is too confusing, we can always deprecate the current table and put it under a new table.One issue I often have in workspaces is that features are unified between all crates when using
cargo checkby itself, but not when usingcargo check -p whatever. So you end up recompiling half the dependency tree when you try to build anything less than “all crates at once”. https://docs.rs/cargo-hakari/0.9.13/cargo_hakari/about/ works around this by having all crates depend on aworkspace-hackcrate which unifies the features even when using-p whatever.Would that be addressed by this RFC? Would
workspace = trueunify features between all dependencies, even if they are not direct dependencies? Or does that need other changes to cargo?A lot of the existing documentation for cargo prefers
a = { b = c}overa.b = c. For instance, see this which specifies git deps asregex = { git = "whatev" }, or this part of the reference which specifies optional dependencies asgif = { version = "0.11.1", optional = true }. In fact, I don’t think I’ve seen thea.b = truesyntax used anywhere in the current cargo doc (though I may have missed it!)I don’t really mind either way, but I think having a unified style for subkeys would make it easier to understand what’s going on.
I am not sure if I am capable enough to work on this, but I would like to follow the development to see how things work. Does https://github.com/alexcrichton/toml-rs also needs to be updated?