Completion support for `cargo-add`
### What does this PR try to resolve?
cargo-add's PR was minimal to ensure we had settled on the CLI before
adding references to the CLI and have cascading churn. This updates
bash and zsh completion to include cargo-add.
### How should we test and review this PR?
I am unsure how to handle testing of completions, so this is a
best-effort from looking at other completions and the docs.
### Additional information
To keep the PRs focused, I am submitting completions separate from documentation updates so one does not get blocked on the other and its easier to see all relevant parts and sign off on them.
cargo-add's PR was minimal to ensure we had settled on the CLI before
adding references to the CLI and have cascading churn. This updates
bash and zsh completion to include cargo-add.
I am unsure how to handle testing of completions, so this is a
best-effort from looking at other completions and the docs.
feat: Import cargo-add into cargo
### Motivation
The reasons I'm aware of are:
- Large interest, see #5586
- Make it easier to add a dependency when you don't care about the version (instead of having to find it or just using the major version if thats all you remember)
- Provide a guided experience, including
- Catch or prevent errors earlier in the process
- Bring the Manifest format documentation into the terminal via `cargo add --help`
- Using `version` and `path` for `dependencies` but `path` only for `dev-dependencies` (see crate-ci/cargo-release#288 which led to killercup/cargo-edit#480)
### Drawbacks
1. This is another area of consideration for new RFCs, like rust-lang/rfcs#3143 (this PR supports it) or rust-lang/rfcs#2906 (implementing it will require updating `cargo-add`)
2. This is a high UX feature that will draw a lot of attention (ie Issue influx)
e.g.
- killercup/cargo-edit#521
- killercup/cargo-edit#126
- killercup/cargo-edit#217
We've tried to reduce the UX influx by focusing the scope to preserving semantic information (custom sort order, comments, etc) but being opinionated on syntax (style of strings, etc)
### Behavior
Help output
<details>
```console
$ cargo run -- add --help
cargo-add [4/4594]
Add dependencies to a Cargo.toml manifest file
USAGE:
cargo add [OPTIONS] <DEP>[`@<VERSION>]` ...
cargo add [OPTIONS] --path <PATH> ...
cargo add [OPTIONS] --git <URL> ...
ARGS:
<DEP_ID>... Reference to a package to add as a dependency
OPTIONS:
--no-default-features Disable the default features
--default-features Re-enable the default features
-F, --features <FEATURES> Space-separated list of features to add
--optional Mark the dependency as optional
-v, --verbose Use verbose output (-vv very verbose/build.rs output)
--no-optional Mark the dependency as required
--color <WHEN> Coloring: auto, always, never
--rename <NAME> Rename the dependency
--frozen Require Cargo.lock and cache are up to date
--manifest-path <PATH> Path to Cargo.toml
--locked Require Cargo.lock is up to date
-p, --package <SPEC> Package to modify
--offline Run without accessing the network
--config <KEY=VALUE> Override a configuration value (unstable)
-q, --quiet Do not print cargo log messages
--dry-run Don't actually write the manifest
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
details
-h, --help Print help information
SOURCE:
--path <PATH> Filesystem path to local crate to add
--git <URI> Git repository location
--branch <BRANCH> Git branch to download the crate from
--tag <TAG> Git tag to download the crate from
--rev <REV> Git reference to download the crate from
--registry <NAME> Package registry for this dependency
SECTION:
--dev Add as development dependency
--build Add as build dependency
--target <TARGET> Add as dependency to the given target platform
EXAMPLES:
$ cargo add regex --build
$ cargo add trycmd --dev
$ cargo add --path ./crate/parser/
$ cargo add serde serde_json -F serde/derive
```
</details>
Example commands
```rust
cargo add regex
cargo add regex serde
cargo add regex@1
cargo add regex@~1.0
cargo add --path ../dependency
```
For an exhaustive set of examples, see [tests](https://github.com/killercup/cargo-edit/blob/merge-add/crates/cargo-add/tests/testsuite/cargo_add.rs) and associated snapshots
Particular points
- Effectively there are two modes
- Fill in any relevant field for one package
- Add multiple packages, erroring for fields that are package-specific (`--rename`)
- Note that `--git` and `--path` only accept multiple packages from that one source
- We infer if the `dependencies` table is sorted and preserve that sorting when adding a new dependency
- Adding a workspace dependency
- dev-dependencies always use path
- all other dependencies use version + path
- Behavior is idempotent, allowing you to run `cargo add serde serde_json -F serde/derive` safely if you already had a dependency on `serde` but without `serde_json`
- When a registry dependency's version req is unspecified, we'll first reuse the version req from another dependency section in the manifest. If that doesn't exist, we'll use the latest version in the registry as the version req
### Additional decisions
Accepting the proposed `cargo-add` as-is assumes the acceptance of the following:
- Add the `-F` short-hand for `--features` to all relevant cargo commands
- Support ``@`` in pkgids in other commands where we accept `:`
- Add support for `<name>`@<version>`` in more commands, like `cargo yank` and `cargo install`
### Alternatives
- Use `:` instead of ``@`` for versions
- Flags like `--features`, `--optional`, `--no-default-features` would be position-sensitive, ie they would only apply to the crate immediate preceding them
- This removes the dual-mode nature of the command and remove the need for the `+feature` syntax (`cargo add serde -F derive serde_json`)
- There was concern over the rarity of position-sensitive flags in CLIs for adopting it here
- Support a `--sort` flag to sort the dependencies (existed previously)
- To keep the scope small, we didn't want general manifest editing capabilities
- `--upgrade <POLICY>` flag to choose constraint (existed previously)
- The flag was confusing as-is and we feel we should instead encourage people towards `^`
- `--allow-prerelease` so a `cargo add clap` can choose among pre-releases as well
- We felt the pre-release story is too weak in cargo-generally atm for making it first class in `cargo-add`
- Offer `cargo add serde +derive serde_json` as a shorthand
- Infer path from a positional argument
### Prior Art
- *(Python)* [poetry add](https://python-poetry.org/docs/cli/#add)
- `git+` is needed for inferring git dependencies, no separate `--git` flags
- git branch is specified via a URL fragment, instead of a `--branch`
- *(Javascript)* [yarn add](https://yarnpkg.com/cli/add)
- `name@data` where data can be version, git (with fragment for branch), etc
- `-E` / `--exact`, `-T` / `--tilde`, `-C` / `--caret` to control version requirement operator instead of `--upgrade <policy>` (also controlled through `defaultSemverRangePrefix` in config)
- `--cached` for using the lock file (killercup/cargo-edit#41)
- In addition to `--dev`, it has `--prefer-dev` which will only add the dependency if it doesn't already exist in `dependencies` as well as `dev-dependencies`
- `--mode update-lockfile` will ensure the lock file gets updated as well
- *(Javascript)* [pnpm-add](https://pnpm.io/cli/add)
- *(Javascript)* npm doesn't have a native solution
- Specify version with ``@<version>``
- Also overloads `<name>[`@<version>]`` with path and repo
- Supports a git host-specific protocol for shorthand, like `github:user/repo`
- Uses fragment for git ref, seems to have some kind of special semver syntax for tags?
- Only supports `--save-exact` / `-E` for operators outside of the default
- *(Go)* [go get](https://go.dev/ref/mod#go-get)
- Specify version with ``@<version>``
- Remove dependency with ``@none``
- *(Haskell)* stack doesn't seem to have a native solution
- *(Julia)* [pkg Add](https://docs.julialang.org/en/v1/stdlib/Pkg/)
- *(Ruby)* [bundle add](https://bundler.io/v2.2/man/bundle-add.1.html)
- Uses `--version` / `-v` instead of `--vers` (we use `--vers` because of `--version` / `-V`)
- `--source` instead of `path` (`path` correlates to manifest field)
- Uses `--git` / `--branch` like `cargo-add`
- *(Dart)* [pub add](https://dart.dev/tools/pub/cmd/pub-add)
- Uses `--git-url` instead of `--git`
- Uses `--git-ref` instead of `--branch`, `--tag`, `--rev`
### Future Possibilities
- Update lock file accordingly
- Exploring the idea of a [`--local` flag](https://github.com/killercup/cargo-edit/issues/590)
- Take the MSRV into account when automatically creating version req (https://github.com/killercup/cargo-edit/issues/587)
- Integrate rustsec to report advisories on new dependencies (https://github.com/killercup/cargo-edit/issues/512)
- Integrate with licensing to report license, block add, etc (e.g. https://github.com/killercup/cargo-edit/issues/386)
- Pull version from lock file (https://github.com/killercup/cargo-edit/issues/41)
- Exploring if any vendoring integration would be beneficial (currently errors)
- Upstream `cargo-rm` (#10520), `cargo-upgrade` (#10498), and `cargo-set-version` (in that order of priority)
- Update crates.io with `cargo add` snippets in addition to or replacing the manifest snippets
For more, see https://github.com/killercup/cargo-edit/issues?q=is%3Aissue+is%3Aopen+label%3Acargo-add
### How should we test and review this PR?
This is intentionally broken up into several commits to help reviewing
1. Import of production code from cargo-edit's `merge-add` branch, with only changes made to let it compile (e.g. fixing up of `use` statements).
2. Import of test code / snapshots. The only changes outside of the import were to add the `snapbox` dev-dependency and to `mod cargo_add` into the testsuite
3. This extends the work in #10425 so I could add back in the color highlighting I had to remove as part of switching `cargo-add` from direct termcolor calls to calling into `Shell`
Structure-wise, this is similar to other commands
- `bin` only defines a CLI and adapts it to an `AddOptions`
- `ops` contains a focused API with everything buried under it
The "op" contains a directory, instead of just a file, because of the amount of content. Currently, all editing code is contained in there. Most of this will be broken out and reused when other `cargo-edit` commands are added but holding off on that for now to separate out the editing API discussions from just getting the command in.
Within the github UI, I'd recommend looking at individual commits (and the `merge-add` branch if interested), skipping commit 2. Commit 2 would be easier to browse locally.
`cargo-add` is mostly covered by end-to-end tests written using `snapbox`, including error cases.
There is additional cleanup that would ideally happen that was excluded intentionally from this PR to keep it better scoped, including
- Consolidating environment variables for end-to-end tests of `cargo`
- Pulling out the editing API, as previously mentioned
- Where the editing API should live (`cargo::edit`?)
- Any more specific naming of types to reduce clashes (e.g. `Dependency` or `Manifest` being fairly generic).
- Possibly sharing `SourceId` creation between `cargo install` and `cargo edit`
- Explore using `snapbox` in more of cargo's tests
Implementation justifications:
- `dunce` and `pathdiff` dependencies: needed for taking paths relative to the user and make them relative to the manifest being edited
- `indexmap` dependency (already a transitive dependency): Useful for preserving uniqueness while preserving order, like with feature values
- `snapbox` dev-dependency: Originally it was used to make it easy to update tests as the UX changed in prep for merging but it had the added benefit of making some UX bugs easier to notice so they got fixed. Overall, I'd like to see it become the cargo-agnostic version of `cargo-test-support` so there is a larger impact when improvements are made
- `parse_feature` function: `CliFeatures` forces items through a `BTreeSet`, losing the users specified order which we wanted to preserve.
### Additional Information
See also [the internals thread](https://internals.rust-lang.org/t/feedback-on-cargo-add-before-its-merged/16024).
Fixes#5586
Part 8 of RFC2906 - Keep `InheritableFields` in a `LazyCell` inside `…
Tracking issue: #8415
RFC: rust-lang/rfcs#2906
Part 1: #10497
Part 2: #10517
Part 3: #10538
Part 4: #10548
Part 5: #10563
Part 6: #10564
Part 7: #10565
This PR focuses on optimizations surrounding `InheritabeFields` and searching for a `WorkspaceRootConfig`:
- Keep `InheritableFields` in a `LazyCell` inside `to_real_manifest` so we only search for a workspace root once per `to_real_manifest`
- Changed calls for getting a workspace root to use `inherit_cell.try_borrow_with()`
[Testing Framework](https://gist.github.com/Muscraft/14f6879af27500e34584296edb468d15)
Test Results:
nest=1, runs=7, members=75, step=25
- 25 Members:
- Optimized: 0.145s
- Not Optimized: 0.181s
- Normal: 0.141s
- Percent change from Normal: 2.837%
- 50 Members
- Optimized: 0.152s
- Not Optimized: 0.267s
- Normal: 0.141s
- Percent change from Normal: 7.801%
nest=2, runs=7, members=75, step=25
- 25 Members
- Optimized: 0.147s,
- Not Optimized: 0.437s
- Normal: 0.14s
- Percent change from Normal: 5.0%
- 50 Members
- Optimized: 0.159s
- Not Optimized: 1.023s
- Normal: 0.141s
- Percent change from Normal: 12.766%
Using a `LazyCell` for `InheritableFields` brought down runtimes to more acceptable levels. It is worth noting that this is a very harsh test case and not one that represents real-world scenarios. That being said there are still some optimizations that could be done if we need to. The biggest one is making sure we only parse a `Cargo.toml` once.
Remaining implementation work for the RFC
- `cargo-add` support, see [epage's comment](https://github.com/rust-lang/cargo/issues/8415#issuecomment-1075544790)
- More optimizations, as needed
Part 7 of RFC2906 - Add support for inheriting `exclude` and `include`
Tracking issue: #8415
RFC: rust-lang/rfcs#2906
Part 1: #10497
Part 2: #10517
Part 3: #10538
Part 4: #10548
Part 5: #10563
Part 6: #10564
This PR focuses on adding support for inheriting `include` and `exclude`. While they were not in the original RFC it was decided that they should be added per [epage's comment](https://github.com/rust-lang/cargo/issues/8415#issuecomment-1097422198).
- Changed `include` and `exclude` into `Option<MaybeWorkspace<Vec<String>>>` inside `TomlProject`
- Added `include` and `exclude` to `InheritbaleFields`
- Updated tests to verify `include` and `exclude` are inheriting correctly
Remaining implementation work for the RFC
- `cargo-add` support, see [epage's comment](https://github.com/rust-lang/cargo/issues/8415#issuecomment-1075544790)
- Optimizations, as needed
Part 6 of RFC2906 - Switch the inheritance source from `workspace` to…
Tracking issue: #8415
RFC: rust-lang/rfcs#2906
Part 1: #10497
Part 2: #10517
Part 3: #10538
Part 4: #10548
Part 5: #10563
This PR focuses on switching the inheritance source from `workspace` to `workspace.package`. The RFC specified `workspace` but it was decided that it should be changed to `workspace.package` per [epage's comment](https://github.com/rust-lang/cargo/issues/8415#issuecomment-1097422198).
- Moved fields that can be inherited to ` package: Option<InheritableFields>` in `TomlWorkspace`
- Making `package` `Option<InheritableFields>` was done to remove duplication of types and better signify what fields you can inherit from in one place
- Added `#[serde(skip)]` to `ws_root` and `dependencies` in `InheritableFields` since they will never be present when deserializing and we don't want them present when serializing
- Added a method to update `ws_root` and `dependencies` in `InheritableFields`
- Added for `ws_root` since it will never be present in a `Cargo.toml` and is needed to resolve relative paths
- Added for `dependencies` since they are under `workspace.dependencies` not `workspace.package.dependencies`
- `workspace.dependencies` was left out of `workspace.package` to better match `package` and `package.dependencies`
- Updated error messages from `workspace._` to `workspace.package._`
- Updated `unstable.md` to reflect change to `workspace.package`
- Updated tests to use `workspace.package`
Remaining implementation work for the RFC
- Add `include` and `exclude` now that `workspace.package` is done, see [epage's comment](https://github.com/rust-lang/cargo/issues/8415#issuecomment-1097422198)
- `cargo-add` support, see [epage's comment](https://github.com/rust-lang/cargo/issues/8415#issuecomment-1075544790)
- Optimizations, as needed
Add support for rustc --check-cfg well known names and values
This pull-request add support for `rustc` `--check-cfg` well known names and values.
### What does this PR try to resolve?
This pull-request add support for `rustc` `--check-cfg` well known names and values:
- `-Z check-cfg-well-known-names`: `--check-cfg names()` well known names
- `-Z check-cfg-well-known-values`: `--check-cfg values()` well known values
### How should we test and review this PR?
#### Testing
`cargo check -Z check-cfg-well-known-names` and
`cargo check -Z check-cfg-well-known-values`
#### Review
This PR contains one commit that split `features_args` into `check_cfg_args`, add the well known support in it, adds call to that new function and add documentation and test for those new flags.
### Additional information
This was implemented as two new additional flags because it's most likely that these flags will not be stabilize at the same time and also because some of these flags may become the default.
RFC: https://github.com/rust-lang/rfcs/pull/3013
`-Z check-cfg-features`: https://github.com/rust-lang/cargo/pull/10408
`cargo doc` support: https://github.com/rust-lang/cargo/pull/10428
Reserve filename `Cargo.toml.orig` in `cargo package`
### What does this PR try to resolve?
_([previously discussed on Zulip](https://rust-lang.zulipchat.com/#narrow/stream/246057-t-cargo/topic/Reserving.20.2FCargo.2Etoml.2Eorig))_
Currently, `cargo package` will detect if there is an existing file named `.cargo_vcs_info.json` in the project folder and raise an error. This is to avoid collisions with the file it generates of the same name.
However, there is no such logic for the other file it generates, `Cargo.toml.orig`. If such a file exists in the project folder, instead of an error, or one file taking precedence, cargo will generate a tarball containing two files with the same name. For example, from a package I uploaded as a test:
```sh
curl https://crates.io/api/v1/crates/a-/0.0.0--a-/download -L | gunzip | tar -tv
```
```text
-rw-r--r-- 0 0 0 8 29 Nov 1973 a--0.0.0--a-/.gitignore
-rw-r--r-- 0 0 0 150 31 Dec 1969 a--0.0.0--a-/Cargo.lock
-rw-r--r-- 0 0 0 641 31 Dec 1969 a--0.0.0--a-/Cargo.toml
-rw-r--r-- 0 0 0 160 29 Nov 1973 a--0.0.0--a-/Cargo.toml.orig <-- generated
-rw-r--r-- 0 0 0 14 29 Nov 1973 a--0.0.0--a-/Cargo.toml.orig <-- existing
-rw-r--r-- 0 0 0 45 29 Nov 1973 a--0.0.0--a-/src/main.rs
```
This PR is a two-line change to add this filename to the existing check for `.cargo_vcs_info.json`.
### How should we test and review this PR?
I have added a unit test to verify that the expected error is produced, copying the existing unit test for `.cargo_vcs_info.json`. I have manually tested the change locally and confirmed that it raises an error if the file exists, and has no effect if it does not. Given the small size of this change, I think this is sufficient, but please let me know if anything else is expected.
### Additional information
This raises a separate question of whether Cargo or crates.io should prohibit tarballs with duplicate filenames. It seems like most (all?) `tar` implementations will give precedence to the last file (which will be the existing copy here, not the generated one), but I don't believe this is specified behaviour, and it's possible that scripts scanning through tarballs directly (without first extracting to the filesystem) may not handle this case correctly. For example, I was working on a rough script to compare packaged libraries to their corresponding Git commits where possible, and this wasn't a case I had anticipated.
In any case, it seems like preventing such tarballs from being created by accident (this PR) is a good place to start.
Retry command invocation with argfile
### What does this PR try to resolve?
Fixes#10381
The goal is to invoke `rustc` and `rustdoc` with ``@path`` arg file as a fallback.
Since the `-Zcheck-cfg-features`[^1] has already been implemented, in the foreseeable future cargo will probably hit the “**command line too big**” more often on Windows. `rustdoc` and `rustc` both support ``@path`` argfile [^2][^3], so we can leverage the feature for the fallback logic.
The idea behind this PR is that we put the **retry logic** in `ProcessBuilder::exec*` functions and reuse them as much as possible.
### How should we test and review this PR?
Review it commit by commit is recommended.
Argfile fallback is hard to tested with integration tests, so I added some unit tests for `cargo_util::ProcessBuilder` and `cargo::ops::fix::FixArgs`.
If you do want to test the support of argfile manually, a new environment variable `__CARGO_TEST_FORCE_ARGFILE` is added for the sake of forcing cargo to use argfile for rustc invocations. For example, `__CARGO_TEST_FORCE_ARGFILE cargo test --test testsuite -- fix::` is usually a good start to test this feature.
[^1]: https://doc.rust-lang.org/beta/cargo/reference/unstable.html?highlight=check#check-cfg-features
[^2]: https://doc.rust-lang.org/rustc/command-line-arguments.html#path-load-command-line-flags-from-a-path
[^3]: https://doc.rust-lang.org/rustdoc/command-line-arguments.html#path-load-command-line-flags-from-a-path
Ensure host units don't depend on Docscrape units, fixes#10545
### What does this PR try to resolve?
This PR fixes issue #10545. The root cause was:
* Cargo workspace contains a host-type crate (e.g. a proc macro).
* `unit_dependencies` attempts to add `Docscrape` units as dependencies of the host-type crate.
* `activated_features` attempts to lookup `(dep, Host)` in the feature map, but only `(dep, NotHost)` exists.
The fix is to ensure that host-type crates do not have Docscrape units added as dependencies.
### How should we test and review this PR?
The added test `scrape_examples_issue_10545` provides a minimal example that fails before this fix, and succeeds after.
Fix docs: Bindeps env vars are passed to build script at runtime
The environment variables are not available through the `env!` macro. Instead, they are passed by cargo when the build script is invoked.
The current test suite confirms this:
fc5035d698/tests/testsuite/artifact_dep.rs (L524-L548)
Part 4 of RFC2906 - Add support for inheriting `readme`
Tracking issue: #8415
RFC: rust-lang/rfcs#2906
[Part 1](https://github.com/rust-lang/cargo/pull/10497)
[Part 2](https://github.com/rust-lang/cargo/pull/10517)
[Part 3](https://github.com/rust-lang/cargo/pull/10538)
This PR focuses on adding support for inheriting `readme`:
- Added adjusting of a `readme` path when it is outside of the `package_root`
- Copied from `license-file`'s implementation in `toml::prepare_for_publish()`
- Added copying of a `readme` file when it is outside of the `package_root` for publishing
- Copied from `license-file`'s implementation in `cargo_package::build_ar_list()`
- Merged copying of a file outside of a `package_root` to reduce duplicated code and allow for other files in the future to be copied in a similar way
Remaining implementation work for the RFC
- Path dependencies infer version directive
- Lock workspace dependencies and warn when unused
- Optimizations, as needed
- Evaluate any new fields for being inheritable (e.g. `rust-version`)
Part 3 of RFC2906 - Add support for inheriting `license-path`, and `depednency.path`
Tracking issue: #8415
RFC: rust-lang/rfcs#2906
[Part 1](https://github.com/rust-lang/cargo/pull/10497)
[Part 2](https://github.com/rust-lang/cargo/pull/10517)
This PR focuses on adding support for inheriting `license-path`, and `depednency.path`:
- To adjust the relative paths from being workspace-relative to package-relative, we use `pathdiff` which `cargo-add` is also going to be using for a similar purpose
- `ws_path` was added to `InheritableFields` so we can resolve relative paths from workspace-relative to package-relative
- Moved `resolve` for toml dependencies from `TomlDependency::<P>` to `TomlDependency`
- This was done since resolving a relative path should be a string
- You should never inherit from a `.cargo/config.toml` which is the reason `P` was added
Remaining implementation work for the RFC
- Relative paths for `readme`
- Path dependencies infer version directive
- Lock workspace dependencies and warn when unused
- Optimizations, as needed
- Evaluate any new fields for being inheritable (e.g. `rust-version`)
- Add `ProcessBuilder::output` and ProcessBuilder::status`, which are
unopinionated version of `exec_*` (won't error out when exitcode > 0)
- Add `ProcessBuilder::retry_with_argfile` to enable trying with argfile
when hitting the "command line too big" error.