summaryrefslogtreecommitdiffstats
path: root/src/tools/cargo
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:59:35 +0000
commitd1b2d29528b7794b41e66fc2136e395a02f8529b (patch)
treea4a17504b260206dec3cf55b2dca82929a348ac2 /src/tools/cargo
parentReleasing progress-linux version 1.72.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.tar.xz
rustc-d1b2d29528b7794b41e66fc2136e395a02f8529b.zip
Merging upstream version 1.73.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/cargo')
-rw-r--r--src/tools/cargo/.cargo/config.toml2
-rw-r--r--src/tools/cargo/.github/renovate.json538
-rw-r--r--src/tools/cargo/.github/workflows/main.yml96
-rw-r--r--src/tools/cargo/CHANGELOG.md108
-rw-r--r--src/tools/cargo/Cargo.lock701
-rw-r--r--src/tools/cargo/Cargo.toml151
-rw-r--r--src/tools/cargo/benches/benchsuite/Cargo.toml4
-rw-r--r--src/tools/cargo/benches/capture/Cargo.toml4
-rwxr-xr-xsrc/tools/cargo/ci/validate-version-bump.sh45
-rw-r--r--src/tools/cargo/crates/cargo-platform/Cargo.toml6
-rw-r--r--src/tools/cargo/crates/cargo-test-macro/Cargo.toml4
-rw-r--r--src/tools/cargo/crates/cargo-test-support/Cargo.toml4
-rw-r--r--src/tools/cargo/crates/cargo-test-support/containers/sshd/Dockerfile2
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/compare.rs1
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/registry.rs36
-rw-r--r--src/tools/cargo/crates/cargo-util/Cargo.toml8
-rw-r--r--src/tools/cargo/crates/cargo-util/src/paths.rs20
-rw-r--r--src/tools/cargo/crates/cargo-util/src/process_builder.rs4
-rw-r--r--src/tools/cargo/crates/crates-io/Cargo.toml8
-rw-r--r--src/tools/cargo/crates/crates-io/lib.rs167
-rw-r--r--src/tools/cargo/crates/home/Cargo.toml7
-rw-r--r--src/tools/cargo/crates/mdman/Cargo.toml4
-rw-r--r--src/tools/cargo/crates/resolver-tests/Cargo.toml2
-rw-r--r--src/tools/cargo/crates/semver-check/Cargo.toml2
-rw-r--r--src/tools/cargo/crates/semver-check/src/main.rs13
-rw-r--r--src/tools/cargo/crates/xtask-build-man/Cargo.toml2
-rw-r--r--src/tools/cargo/crates/xtask-bump-check/Cargo.toml14
-rw-r--r--src/tools/cargo/crates/xtask-bump-check/src/main.rs (renamed from src/tools/cargo/crates/xtask-unpublished/src/main.rs)14
-rw-r--r--src/tools/cargo/crates/xtask-bump-check/src/xtask.rs423
-rw-r--r--src/tools/cargo/crates/xtask-stale-label/Cargo.toml2
-rw-r--r--src/tools/cargo/crates/xtask-stale-label/src/main.rs2
-rw-r--r--src/tools/cargo/crates/xtask-unpublished/Cargo.toml12
-rw-r--r--src/tools/cargo/crates/xtask-unpublished/src/xtask.rs200
-rw-r--r--src/tools/cargo/credential/cargo-credential-1password/Cargo.toml6
-rw-r--r--src/tools/cargo/credential/cargo-credential-1password/src/main.rs141
-rw-r--r--src/tools/cargo/credential/cargo-credential-gnome-secret/build.rs8
-rw-r--r--src/tools/cargo/credential/cargo-credential-gnome-secret/src/libsecret.rs190
-rw-r--r--src/tools/cargo/credential/cargo-credential-gnome-secret/src/main.rs12
-rw-r--r--src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml (renamed from src/tools/cargo/credential/cargo-credential-gnome-secret/Cargo.toml)13
-rw-r--r--src/tools/cargo/credential/cargo-credential-libsecret/README.md (renamed from src/tools/cargo/credential/cargo-credential-gnome-secret/README.md)2
-rw-r--r--src/tools/cargo/credential/cargo-credential-libsecret/src/lib.rs235
-rw-r--r--src/tools/cargo/credential/cargo-credential-macos-keychain/Cargo.toml6
-rw-r--r--src/tools/cargo/credential/cargo-credential-macos-keychain/src/lib.rs81
-rw-r--r--src/tools/cargo/credential/cargo-credential-macos-keychain/src/main.rs58
-rw-r--r--src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml6
-rw-r--r--src/tools/cargo/credential/cargo-credential-wincred/src/lib.rs125
-rw-r--r--src/tools/cargo/credential/cargo-credential-wincred/src/main.rs122
-rw-r--r--src/tools/cargo/credential/cargo-credential/Cargo.toml18
-rw-r--r--src/tools/cargo/credential/cargo-credential/README.md2
-rw-r--r--src/tools/cargo/credential/cargo-credential/examples/file-provider.rs90
-rw-r--r--src/tools/cargo/credential/cargo-credential/examples/stdout-redirected.rs25
-rw-r--r--src/tools/cargo/credential/cargo-credential/src/error.rs206
-rw-r--r--src/tools/cargo/credential/cargo-credential/src/lib.rs312
-rw-r--r--src/tools/cargo/credential/cargo-credential/src/secret.rs101
-rw-r--r--src/tools/cargo/credential/cargo-credential/src/stdio.rs163
-rw-r--r--src/tools/cargo/credential/cargo-credential/tests/examples.rs45
-rw-r--r--src/tools/cargo/deny.toml1
-rwxr-xr-xsrc/tools/cargo/publish.py6
-rw-r--r--src/tools/cargo/src/bin/cargo/cli.rs24
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/add.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/bench.rs45
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/build.rs15
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/check.rs12
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/clean.rs8
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/doc.rs22
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/fetch.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/fix.rs52
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/init.rs4
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/install.rs50
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/locate_project.rs6
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/login.rs37
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/logout.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/metadata.rs6
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/new.rs4
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/owner.rs4
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/package.rs17
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/pkgid.rs9
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/publish.rs19
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/remove.rs108
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/run.rs10
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/rustc.rs38
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/rustdoc.rs12
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/search.rs4
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/test.rs41
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/tree.rs26
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/uninstall.rs9
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/update.rs21
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/vendor.rs4
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/yank.rs6
-rw-r--r--src/tools/cargo/src/bin/cargo/main.rs15
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/build_context/target_info.rs12
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/context/compilation_files.rs4
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/custom_build.rs12
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/fingerprint/mod.rs27
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/future_incompat.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/job_queue/mod.rs4
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/mod.rs4
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/output_depinfo.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/rustdoc.rs6
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/timings.js10
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/timings.rs4
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/unit_dependencies.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/dependency.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/package.rs8
-rw-r--r--src/tools/cargo/src/cargo/core/registry.rs6
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/conflict_cache.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/context.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/dep_cache.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/encode.rs106
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/features.rs14
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/mod.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/resolve.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/source/source_id.rs74
-rw-r--r--src/tools/cargo/src/cargo/core/workspace.rs2
-rw-r--r--src/tools/cargo/src/cargo/lib.rs2
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs4
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_generate_lockfile.rs2
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_new.rs2
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_package.rs105
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_read_manifest.rs2
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_run.rs4
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_test.rs33
-rw-r--r--src/tools/cargo/src/cargo/ops/fix.rs2
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/login.rs144
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/logout.rs26
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/mod.rs18
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/owner.rs8
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/publish.rs14
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/yank.rs8
-rw-r--r--src/tools/cargo/src/cargo/ops/resolve.rs2
-rw-r--r--src/tools/cargo/src/cargo/ops/tree/graph.rs2
-rw-r--r--src/tools/cargo/src/cargo/sources/config.rs2
-rw-r--r--src/tools/cargo/src/cargo/sources/git/known_hosts.rs20
-rw-r--r--src/tools/cargo/src/cargo/sources/git/oxide.rs2
-rw-r--r--src/tools/cargo/src/cargo/sources/git/source.rs7
-rw-r--r--src/tools/cargo/src/cargo/sources/git/utils.rs10
-rw-r--r--src/tools/cargo/src/cargo/sources/path.rs6
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/download.rs9
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/http_remote.rs44
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/index.rs36
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/mod.rs9
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/remote.rs2
-rw-r--r--src/tools/cargo/src/cargo/util/auth/asymmetric.rs155
-rw-r--r--src/tools/cargo/src/cargo/util/auth/mod.rs872
-rw-r--r--src/tools/cargo/src/cargo/util/command_prelude.rs146
-rw-r--r--src/tools/cargo/src/cargo/util/config/mod.rs35
-rw-r--r--src/tools/cargo/src/cargo/util/config/path.rs25
-rw-r--r--src/tools/cargo/src/cargo/util/config/target.rs2
-rw-r--r--src/tools/cargo/src/cargo/util/credential/adaptor.rs68
-rw-r--r--src/tools/cargo/src/cargo/util/credential/mod.rs8
-rw-r--r--src/tools/cargo/src/cargo/util/credential/paseto.rs224
-rw-r--r--src/tools/cargo/src/cargo/util/credential/process.rs80
-rw-r--r--src/tools/cargo/src/cargo/util/credential/token.rs96
-rw-r--r--src/tools/cargo/src/cargo/util/diagnostic_server.rs2
-rw-r--r--src/tools/cargo/src/cargo/util/errors.rs17
-rw-r--r--src/tools/cargo/src/cargo/util/job.rs2
-rw-r--r--src/tools/cargo/src/cargo/util/mod.rs1
-rw-r--r--src/tools/cargo/src/cargo/util/network/http.rs39
-rw-r--r--src/tools/cargo/src/cargo/util/network/mod.rs2
-rw-r--r--src/tools/cargo/src/cargo/util/network/sleep.rs2
-rw-r--r--src/tools/cargo/src/cargo/util/restricted_names.rs15
-rw-r--r--src/tools/cargo/src/cargo/util/rustc.rs2
-rw-r--r--src/tools/cargo/src/cargo/util/toml/embedded.rs37
-rw-r--r--src/tools/cargo/src/cargo/util/toml/mod.rs10
-rw-r--r--src/tools/cargo/src/cargo/util/toml_mut/dependency.rs6
-rw-r--r--src/tools/cargo/src/doc/contrib/src/implementation/debugging.md15
-rw-r--r--src/tools/cargo/src/doc/contrib/src/process/release.md37
-rw-r--r--src/tools/cargo/src/doc/man/cargo-metadata.md44
-rw-r--r--src/tools/cargo/src/doc/man/cargo-test.md1
-rw-r--r--src/tools/cargo/src/doc/man/cargo-yank.md2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-metadata.txt45
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-test.txt5
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-yank.txt2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-metadata.md44
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-test.md6
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-yank.md2
-rw-r--r--src/tools/cargo/src/doc/src/guide/cargo-toml-vs-cargo-lock.md2
-rw-r--r--src/tools/cargo/src/doc/src/reference/build-scripts.md53
-rw-r--r--src/tools/cargo/src/doc/src/reference/config.md14
-rw-r--r--src/tools/cargo/src/doc/src/reference/environment-variables.md6
-rw-r--r--src/tools/cargo/src/doc/src/reference/external-tools.md3
-rw-r--r--src/tools/cargo/src/doc/src/reference/manifest.md22
-rw-r--r--src/tools/cargo/src/doc/src/reference/overriding-dependencies.md6
-rw-r--r--src/tools/cargo/src/doc/src/reference/profiles.md15
-rw-r--r--src/tools/cargo/src/doc/src/reference/resolver.md7
-rw-r--r--src/tools/cargo/src/doc/src/reference/semver.md858
-rw-r--r--src/tools/cargo/src/doc/src/reference/specifying-dependencies.md2
-rw-r--r--src/tools/cargo/src/doc/src/reference/unstable.md328
-rw-r--r--src/tools/cargo/src/doc/src/reference/workspaces.md23
-rw-r--r--src/tools/cargo/src/etc/_cargo12
-rw-r--r--src/tools/cargo/src/etc/cargo.bashcomp.sh7
-rw-r--r--src/tools/cargo/src/etc/man/cargo-metadata.149
-rw-r--r--src/tools/cargo/src/etc/man/cargo-test.17
-rw-r--r--src/tools/cargo/src/etc/man/cargo-yank.12
-rw-r--r--src/tools/cargo/tests/testsuite/alt_registry.rs41
-rw-r--r--src/tools/cargo/tests/testsuite/bench.rs61
-rw-r--r--src/tools/cargo/tests/testsuite/build_script.rs29
-rw-r--r--src/tools/cargo/tests/testsuite/build_script_env.rs10
-rw-r--r--src/tools/cargo/tests/testsuite/cargo/help/mod.rs12
-rw-r--r--src/tools/cargo/tests/testsuite/cargo/help/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/auto_git/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo/help/stdout.log39
-rw-r--r--src/tools/cargo/tests/testsuite/cargo/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/help/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/help/stdout.log124
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_bench/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_bench/help/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit_nosrc/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_bench/help/stdout.log59
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_bench/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_build/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_build/help/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_build/help/stdout.log58
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_build/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_check/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_check/help/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namenosrc/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_check/help/stdout.log56
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_check/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_clean/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_clean/help/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namesrc/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_clean/help/stdout.log29
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_clean/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_config/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_config/help/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_nosrc/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_config/help/stdout.log18
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_config/mod.rs (renamed from src/tools/cargo/tests/testsuite/cargo_config.rs)2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_doc/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_doc/help/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/both_lib_and_bin/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_doc/help/stdout.log53
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_doc/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_fetch/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_fetch/help/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/cant_create_library_when_both_binlib_present/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_fetch/help/stdout.log22
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_fetch/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_fix/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_fix/help/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_fix/help/stdout.log60
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_fix/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_generate_lockfile/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_generate_lockfile/help/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_generate_lockfile/help/stdout.log19
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_generate_lockfile/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_git_checkout/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_git_checkout/help/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_git_checkout/help/stdout.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_git_checkout/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_help/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_help/help/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_help/help/stdout.log18
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_help/mod.rs1
l---------src/tools/cargo/tests/testsuite/cargo_init/auto_git/in (renamed from src/tools/cargo/tests/testsuite/init/auto_git/in)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/auto_git/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/auto_git/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/auto_git/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/auto_git/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/auto_git/out/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/auto_git/out/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/auto_git/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/auto_git/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/auto_git/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/explicit_bin_with_git/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit/in/src/main.rs (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit/in/src/main.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit/out/src/main.rs (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit/out/src/main.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/formats_source/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/in/main.rs (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit_nosrc/in/main.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit_nosrc/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit_nosrc/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/out/main.rs (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit_nosrc/out/main.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit_nosrc/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/fossil_autodetect/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit/in/src/main.rs (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit/in/src/main.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit/out/src/main.rs (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit/out/src/main.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/git_autodetect/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/in/case.rs (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namenosrc/in/case.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namenosrc/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namenosrc/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/out/case.rs (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namenosrc/out/case.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namenosrc/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/in/src/case.rs (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namesrc/in/src/case.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namesrc/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namesrc/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/out/src/case.rs (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namesrc/out/src/case.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namesrc/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/in/main.rs (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_nosrc/in/main.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_nosrc/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_nosrc/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/out/main.rs (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_nosrc/out/main.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_nosrc/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/inferred_bin_with_git/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/both_lib_and_bin/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/both_lib_and_bin/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/both_lib_and_bin/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/both_lib_and_bin/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/both_lib_and_bin/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/inferred_lib_with_git/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/in/case.rs (renamed from src/tools/cargo/tests/testsuite/init/cant_create_library_when_both_binlib_present/in/case.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/in/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/cant_create_library_when_both_binlib_present/in/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/cant_create_library_when_both_binlib_present/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/cant_create_library_when_both_binlib_present/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/in/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/in/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/in/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/in/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/out/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/out/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/out/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/out/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/invalid_dir_name/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/in/case.rs (renamed from src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/in/case.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/in/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/in/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/out/case.rs (renamed from src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/out/case.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/out/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/out/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/lib_already_exists_nosrc/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/in/case.rs (renamed from src/tools/cargo/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/in/case.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/out/case.rs (renamed from src/tools/cargo/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/out/case.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/lib_already_exists_src/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/in/case.rs (renamed from src/tools/cargo/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/in/case.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/out/case.rs (renamed from src/tools/cargo/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/out/case.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/mercurial_autodetect/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/empty_dir/.keep (renamed from src/tools/cargo/tests/testsuite/init/empty_dir/.keep)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/empty_dir/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/empty_dir/mod.rs)0
l---------src/tools/cargo/tests/testsuite/cargo_init/explicit_bin_with_git/in (renamed from src/tools/cargo/tests/testsuite/init/explicit_bin_with_git/in)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/explicit_bin_with_git/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/explicit_bin_with_git/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/explicit_bin_with_git/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/explicit_bin_with_git/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/explicit_bin_with_git/out/src/main.rs (renamed from src/tools/cargo/tests/testsuite/init/explicit_bin_with_git/out/src/main.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/explicit_bin_with_git/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/explicit_bin_with_git/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/explicit_bin_with_git/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/formats_source/in/rustfmt.toml (renamed from src/tools/cargo/tests/testsuite/init/formats_source/in/rustfmt.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/formats_source/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/formats_source/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/formats_source/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/formats_source/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/formats_source/out/rustfmt.toml (renamed from src/tools/cargo/tests/testsuite/init/formats_source/out/rustfmt.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/formats_source/out/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/formats_source/out/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/formats_source/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/formats_source/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/formats_source/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/no_filename/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/in/.fossil/.keep (renamed from src/tools/cargo/tests/testsuite/init/fossil_autodetect/in/.fossil/.keep)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/fossil_autodetect/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/out/.fossil-settings/clean-glob (renamed from src/tools/cargo/tests/testsuite/init/fossil_autodetect/out/.fossil-settings/clean-glob)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/out/.fossil-settings/ignore-glob (renamed from src/tools/cargo/tests/testsuite/init/fossil_autodetect/out/.fossil-settings/ignore-glob)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/fossil_autodetect/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/out/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/fossil_autodetect/out/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/fossil_autodetect/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/path_contains_separator/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/git_autodetect/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/git_autodetect/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/git_autodetect/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/git_autodetect/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/git_autodetect/out/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/git_autodetect/out/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/git_autodetect/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/git_autodetect/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/git_autodetect/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/pijul_autodetect/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/out/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/out/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/reserved_name/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/help/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/in/README.md)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/help/stdout.log31
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/in/rustfmt.toml (renamed from src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/in/rustfmt.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/out/rustfmt.toml (renamed from src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/out/rustfmt.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/out/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/out/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/simple_bin/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inferred_bin_with_git/in/main.rs (renamed from src/tools/cargo/tests/testsuite/init/inferred_bin_with_git/in/main.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inferred_bin_with_git/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/inferred_bin_with_git/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inferred_bin_with_git/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/inferred_bin_with_git/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inferred_bin_with_git/out/main.rs (renamed from src/tools/cargo/tests/testsuite/init/inferred_bin_with_git/out/main.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inferred_bin_with_git/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/inferred_bin_with_git/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inferred_bin_with_git/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/simple_git/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inferred_lib_with_git/in/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/inferred_lib_with_git/in/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inferred_lib_with_git/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/inferred_lib_with_git/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inferred_lib_with_git/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/inferred_lib_with_git/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inferred_lib_with_git/out/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/inferred_lib_with_git/out/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inferred_lib_with_git/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/inferred_lib_with_git/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inferred_lib_with_git/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/simple_git_ignore_exists/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/in/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/in/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/in/README.md (renamed from src/tools/cargo/tests/testsuite/init/lib_already_exists_nosrc/in/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/in/crates/foo/src/main.rs (renamed from src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/in/crates/foo/src/main.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/in/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/in/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/out/crates/foo/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/out/crates/foo/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/out/crates/foo/src/main.rs (renamed from src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/out/crates/foo/src/main.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/out/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/out/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/simple_hg/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/invalid_dir_name/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/invalid_dir_name/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/invalid_dir_name/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/invalid_dir_name/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/invalid_dir_name/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/simple_hg_ignore_exists/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_nosrc/in/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/lib_already_exists_nosrc/out/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_nosrc/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/lib_already_exists_nosrc/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_nosrc/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/lib_already_exists_nosrc/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_nosrc/out/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/path_contains_separator/in/.keep)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_nosrc/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/lib_already_exists_nosrc/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_nosrc/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/simple_lib/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_src/in/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/lib_already_exists_src/in/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_src/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/lib_already_exists_src/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_src/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/lib_already_exists_src/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_src/out/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/lib_already_exists_src/out/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_src/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/lib_already_exists_src/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_src/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/unknown_flags/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/mercurial_autodetect/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/mercurial_autodetect/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/mercurial_autodetect/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/mercurial_autodetect/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/mercurial_autodetect/out/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/mercurial_autodetect/out/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/mercurial_autodetect/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/mercurial_autodetect/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/mercurial_autodetect/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/with_argument/stdout.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/mod.rs)1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/in/case.rs (renamed from src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/in/case.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/in/main.rs (renamed from src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/in/main.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/out/case.rs (renamed from src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/out/case.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/out/main.rs (renamed from src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/out/main.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/pijul_autodetect/in/.pijul/.keep)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/no_filename/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/no_filename/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/no_filename/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/no_filename/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/no_filename/stdout.log (renamed from src/tools/cargo/tests/testsuite/init/with_argument/in/foo/.keep)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/in/.keep0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/path_contains_separator/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/path_contains_separator/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/out/src/main.rs (renamed from src/tools/cargo/tests/testsuite/init/path_contains_separator/out/src/main.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/path_contains_separator/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/in/.pijul/.keep0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/pijul_autodetect/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/out/.ignore (renamed from src/tools/cargo/tests/testsuite/init/pijul_autodetect/out/.ignore)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/pijul_autodetect/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/out/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/pijul_autodetect/out/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/pijul_autodetect/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/reserved_name/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/reserved_name/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/reserved_name/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/reserved_name/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/reserved_name/stdout.log0
l---------src/tools/cargo/tests/testsuite/cargo_init/simple_bin/in (renamed from src/tools/cargo/tests/testsuite/init/simple_bin/in)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_bin/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/simple_bin/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_bin/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/simple_bin/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_bin/out/src/main.rs (renamed from src/tools/cargo/tests/testsuite/init/simple_bin/out/src/main.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_bin/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/simple_bin/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_bin/stdout.log0
l---------src/tools/cargo/tests/testsuite/cargo_init/simple_git/in (renamed from src/tools/cargo/tests/testsuite/init/simple_git/in)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_git/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/simple_git/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_git/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/simple_git/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_git/out/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/simple_git/out/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_git/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/simple_git/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_git/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_git_ignore_exists/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/simple_git_ignore_exists/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_git_ignore_exists/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/simple_git_ignore_exists/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_git_ignore_exists/out/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/simple_git_ignore_exists/out/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_git_ignore_exists/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/simple_git_ignore_exists/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_git_ignore_exists/stdout.log0
l---------src/tools/cargo/tests/testsuite/cargo_init/simple_hg/in (renamed from src/tools/cargo/tests/testsuite/init/simple_hg/in)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_hg/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/simple_hg/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_hg/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/simple_hg/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_hg/out/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/simple_hg/out/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_hg/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/simple_hg/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_hg/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_hg_ignore_exists/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/simple_hg_ignore_exists/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_hg_ignore_exists/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/simple_hg_ignore_exists/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_hg_ignore_exists/out/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/simple_hg_ignore_exists/out/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_hg_ignore_exists/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/simple_hg_ignore_exists/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_hg_ignore_exists/stdout.log0
l---------src/tools/cargo/tests/testsuite/cargo_init/simple_lib/in (renamed from src/tools/cargo/tests/testsuite/init/simple_lib/in)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_lib/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/simple_lib/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_lib/out/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/simple_lib/out/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_lib/out/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/init/simple_lib/out/src/lib.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_lib/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/simple_lib/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/simple_lib/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/unknown_flags/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/unknown_flags/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/with_argument/in/foo/.keep0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/with_argument/mod.rs (renamed from src/tools/cargo/tests/testsuite/init/with_argument/mod.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/with_argument/out/foo/Cargo.toml (renamed from src/tools/cargo/tests/testsuite/init/with_argument/out/foo/Cargo.toml)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/with_argument/out/foo/src/main.rs (renamed from src/tools/cargo/tests/testsuite/init/with_argument/out/foo/src/main.rs)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/with_argument/stderr.log (renamed from src/tools/cargo/tests/testsuite/init/with_argument/stderr.log)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/with_argument/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_install/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_install/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_install/help/stdout.log56
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_install/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_locate_project/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_locate_project/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_locate_project/help/stdout.log22
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_locate_project/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_login/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_login/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_login/help/stdout.log23
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_login/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_logout/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_logout/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_logout/help/stdout.log19
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_logout/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_metadata/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_metadata/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_metadata/help/stdout.log30
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_metadata/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/help/stdout.log31
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_owner/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_owner/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_owner/help/stdout.log27
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_owner/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_package/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_package/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_package/help/stdout.log39
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_package/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_pkgid/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_pkgid/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_pkgid/help/stdout.log25
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_pkgid/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_publish/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_publish/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_publish/help/stdout.log39
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_publish/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_read_manifest/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_read_manifest/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_read_manifest/help/stdout.log19
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_read_manifest/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/avoid_empty_tables/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/build/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/dev/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/serde/Cargo.toml9
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/serde/src/lib.rs0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/serde_derive/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/serde_derive/src/lib.rs0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/mod.rs27
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/serde/Cargo.toml6
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/serde/src/lib.rs0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/serde_derive/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/serde_derive/src/lib.rs0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/mod.rs9
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/out/Cargo.lock19
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_profile/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/gc_replace/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/help/stdout.log29
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/mod.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/multiple_deps/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/optional_feature/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/package/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/remove_basic/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/target/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/target_build/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/target_dev/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/workspace/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/workspace_non_virtual/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/workspace_preserved/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_report/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_report/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_report/help/stdout.log20
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_report/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_run/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_run/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_run/help/stdout.log47
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_run/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_rustc/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_rustc/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_rustc/help/stdout.log58
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_rustc/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_rustdoc/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stdout.log56
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_rustdoc/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_search/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_search/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_search/help/stdout.log24
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_search/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_test/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_test/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_test/help/stdout.log63
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_test/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_tree/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_tree/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_tree/help/stdout.log45
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_tree/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_uninstall/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_uninstall/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_uninstall/help/stdout.log28
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_uninstall/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_update/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_update/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_update/help/stdout.log26
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_update/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_vendor/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_vendor/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_vendor/help/stdout.log27
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_vendor/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_verify_project/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_verify_project/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_verify_project/help/stdout.log19
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_verify_project/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_version/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_version/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_version/help/stdout.log18
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_version/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_yank/help/mod.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_yank/help/stderr.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_yank/help/stdout.log26
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_yank/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/credential_process.rs640
-rw-r--r--src/tools/cargo/tests/testsuite/freshness.rs62
-rw-r--r--src/tools/cargo/tests/testsuite/lints.rs55
-rw-r--r--src/tools/cargo/tests/testsuite/lockfile_compat.rs195
-rw-r--r--src/tools/cargo/tests/testsuite/login.rs188
-rw-r--r--src/tools/cargo/tests/testsuite/main.rs36
-rw-r--r--src/tools/cargo/tests/testsuite/owner.rs8
-rw-r--r--src/tools/cargo/tests/testsuite/package.rs112
-rw-r--r--src/tools/cargo/tests/testsuite/profile_targets.rs8
-rw-r--r--src/tools/cargo/tests/testsuite/publish.rs10
-rw-r--r--src/tools/cargo/tests/testsuite/registry.rs35
-rw-r--r--src/tools/cargo/tests/testsuite/registry_auth.rs96
-rw-r--r--src/tools/cargo/tests/testsuite/run.rs22
-rw-r--r--src/tools/cargo/tests/testsuite/script.rs158
-rw-r--r--src/tools/cargo/tests/testsuite/test.rs71
-rw-r--r--src/tools/cargo/tests/testsuite/update.rs40
-rw-r--r--src/tools/cargo/tests/testsuite/yank.rs8
634 files changed, 9341 insertions, 3632 deletions
diff --git a/src/tools/cargo/.cargo/config.toml b/src/tools/cargo/.cargo/config.toml
index c4a595b61..17d424a5d 100644
--- a/src/tools/cargo/.cargo/config.toml
+++ b/src/tools/cargo/.cargo/config.toml
@@ -1,4 +1,4 @@
[alias]
build-man = "run --package xtask-build-man --"
stale-label = "run --package xtask-stale-label --"
-unpublished = "run --package xtask-unpublished --"
+bump-check = "run --package xtask-bump-check --"
diff --git a/src/tools/cargo/.github/renovate.json5 b/src/tools/cargo/.github/renovate.json5
new file mode 100644
index 000000000..8ad9952d2
--- /dev/null
+++ b/src/tools/cargo/.github/renovate.json5
@@ -0,0 +1,38 @@
+{
+ schedule: [
+ 'before 5am on the first day of the month',
+ ],
+ semanticCommits: 'enabled',
+ configMigration: true,
+ dependencyDashboard: false,
+ ignorePaths: [
+ "**/tests/**",
+ ],
+ packageRules: [
+ // Goals:
+ // - Rollup safe upgrades to reduce CI runner load
+ // - Have lockfile and manifest in-sync (implicit rules)
+ {
+ matchManagers: [
+ 'cargo',
+ ],
+ matchCurrentVersion: '>=0.1.0',
+ matchUpdateTypes: [
+ 'patch',
+ ],
+ automerge: false,
+ groupName: 'compatible',
+ },
+ {
+ matchManagers: [
+ 'cargo',
+ ],
+ matchCurrentVersion: '>=1.0.0',
+ matchUpdateTypes: [
+ 'minor',
+ ],
+ automerge: false,
+ groupName: 'compatible',
+ },
+ ],
+}
diff --git a/src/tools/cargo/.github/workflows/main.yml b/src/tools/cargo/.github/workflows/main.yml
index 3deae6355..2e71f14b8 100644
--- a/src/tools/cargo/.github/workflows/main.yml
+++ b/src/tools/cargo/.github/workflows/main.yml
@@ -13,6 +13,41 @@ permissions:
contents: read
jobs:
+ success:
+ permissions:
+ contents: none
+ name: bors build finished
+ needs:
+ - build_std
+ - clippy
+ - docs
+ - lockfile
+ - resolver
+ - rustfmt
+ - test
+ - test_gitoxide
+ runs-on: ubuntu-latest
+ if: "success() && github.event_name == 'push' && github.ref == 'refs/heads/auto-cargo'"
+ steps:
+ - run: echo ok
+ failure:
+ permissions:
+ contents: none
+ name: bors build finished
+ needs:
+ - build_std
+ - clippy
+ - docs
+ - lockfile
+ - resolver
+ - rustfmt
+ - test
+ - test_gitoxide
+ runs-on: ubuntu-latest
+ if: "!success() && github.event_name == 'push' && github.ref == 'refs/heads/auto-cargo'"
+ steps:
+ - run: exit 1
+
# Check Code style quickly by running `rustfmt` over all code
rustfmt:
runs-on: ubuntu-latest
@@ -52,12 +87,18 @@ jobs:
runs-on: ubuntu-latest
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
- HEAD_SHA: ${{ github.sha }}
+ HEAD_SHA: ${{ github.event.pull_request.head.sha != '' && github.event.pull_request.head.sha || github.sha }}
steps:
- uses: actions/checkout@v3
with:
- fetch-depth: 0 # make `git diff` work
+ fetch-depth: 0
- run: rustup update stable && rustup default stable
+ - name: Install cargo-semver-checks
+ run: |
+ mkdir installed-bins
+ curl -Lf https://github.com/obi1kenobi/cargo-semver-checks/releases/download/v0.22.1/cargo-semver-checks-x86_64-unknown-linux-gnu.tar.gz \
+ | tar -xz --directory=./installed-bins
+ echo `pwd`/installed-bins >> $GITHUB_PATH
- run: ci/validate-version-bump.sh
test:
@@ -119,6 +160,7 @@ jobs:
- run: cargo test -p cargo
- name: Clear intermediate test output
run: ci/clean-test-output.sh
+
- name: gitoxide tests (all git-related tests)
run: cargo test -p cargo git
env:
@@ -127,22 +169,14 @@ jobs:
# running out of disk space.
- name: Clear test output
run: ci/clean-test-output.sh
+
# This only tests `cargo fix` because fix-proxy-mode is one of the most
# complicated subprocess management in Cargo.
- name: Check operability of rustc invocation with argfile
run: 'cargo test -p cargo --test testsuite -- fix::'
env:
__CARGO_TEST_FORCE_ARGFILE: 1
- - run: cargo test -p cargo-test-support
- - run: cargo test -p cargo-platform
- - run: cargo test -p cargo-util
- - run: cargo test -p home
- - run: cargo test -p mdman
- - run: cargo build -p cargo-credential-1password
- - run: cargo build -p cargo-credential-macos-keychain
- - run: cargo build -p cargo-credential-wincred
- - run: cargo build -p cargo-credential-gnome-secret
- if: matrix.os == 'ubuntu-latest'
+ - run: cargo test --workspace --exclude cargo --exclude benchsuite
- name: Check benchmarks
run: |
# This only tests one benchmark since it can take over 10 minutes to
@@ -153,6 +187,7 @@ jobs:
# running out of disk space.
- name: Clear benchmark output
run: ci/clean-test-output.sh
+
- name: Fetch smoke test
run: ci/fetch-smoke-test.sh
@@ -203,7 +238,7 @@ jobs:
- name: Install mdbook
run: |
mkdir mdbook
- curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.27/mdbook-v0.4.27-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook
+ curl -Lf https://github.com/rust-lang/mdBook/releases/download/v0.4.31/mdbook-v0.4.31-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=./mdbook
echo `pwd`/mdbook >> $GITHUB_PATH
- run: cd src/doc && mdbook build --dest-dir ../../target/doc
- name: Run linkchecker.sh
@@ -211,38 +246,3 @@ jobs:
cd target
curl -sSLO https://raw.githubusercontent.com/rust-lang/rust/master/src/tools/linkchecker/linkcheck.sh
sh linkcheck.sh --all --path ../src/doc cargo
-
- success:
- permissions:
- contents: none
- name: bors build finished
- needs:
- - build_std
- - clippy
- - docs
- - lockfile
- - resolver
- - rustfmt
- - test
- - test_gitoxide
- runs-on: ubuntu-latest
- if: "success() && github.event_name == 'push' && github.ref == 'refs/heads/auto-cargo'"
- steps:
- - run: echo ok
- failure:
- permissions:
- contents: none
- name: bors build finished
- needs:
- - build_std
- - clippy
- - docs
- - lockfile
- - resolver
- - rustfmt
- - test
- - test_gitoxide
- runs-on: ubuntu-latest
- if: "!success() && github.event_name == 'push' && github.ref == 'refs/heads/auto-cargo'"
- steps:
- - run: exit 1
diff --git a/src/tools/cargo/CHANGELOG.md b/src/tools/cargo/CHANGELOG.md
index 0784b2638..0141302c8 100644
--- a/src/tools/cargo/CHANGELOG.md
+++ b/src/tools/cargo/CHANGELOG.md
@@ -1,16 +1,67 @@
# Changelog
+## Cargo 1.73 (2023-10-05)
+[45782b6b...HEAD](https://github.com/rust-lang/cargo/compare/45782b6b...HEAD)
+
+### Added
+
+### Changed
+
+### Fixed
+
+### Nightly only
+
+- Fixed invalid package names generated by `-Zscript`.
+ [#12349](https://github.com/rust-lang/cargo/pull/12349)
+- `-Zscript` now errors out on unsupported commands — `publish` and `package`.
+ [#12350](https://github.com/rust-lang/cargo/pull/12350)
+
+### Documentation
+
+- Use heading attributes to control the fragment.
+ [#12339](https://github.com/rust-lang/cargo/pull/12339)
+- Use "number" instead of "digit" when explaining Cargo's use of semver.
+ [#12340](https://github.com/rust-lang/cargo/pull/12340)
+- contrib: Add some more detail about how publishing works.
+ [#12344](https://github.com/rust-lang/cargo/pull/12344)
+- Clarify "Package ID" and "Source ID" in `cargo metadata` are opaque strings.
+ [#12313](https://github.com/rust-lang/cargo/pull/12313)
+- Added `profile.strip` to configuration docs.
+ [#12337](https://github.com/rust-lang/cargo/pull/12337)
+- Multiple versions that differ only in the metadata tag are disallowed on crates.io.
+ [#12335](https://github.com/rust-lang/cargo/pull/12335)
+
+### Internal
+
+- Updated to `criterion` 0.5.1.
+ [#12338](https://github.com/rust-lang/cargo/pull/12338)
+- ci: automatically test new packages by using `--workspace`.
+ [#12342](https://github.com/rust-lang/cargo/pull/12342)
+- ci: automatically update dependencies monthly with Renovate.
+ [#12341](https://github.com/rust-lang/cargo/pull/12341)
+
## Cargo 1.72 (2023-08-24)
-[64fb38c9...HEAD](https://github.com/rust-lang/cargo/compare/64fb38c9...HEAD)
+[64fb38c9...rust-1.72.0](https://github.com/rust-lang/cargo/compare/64fb38c9...rust-1.72.0)
### Added
+- ❗ Enable `-Zdoctest-in-workspace` by default. When running each documentation
+ test, the working directory is set to the root directory of the package the
+ test belongs to.
+ [docs](https://doc.rust-lang.org/nightly/cargo/commands/cargo-test.html#working-directory-of-tests)
+ [#12221](https://github.com/rust-lang/cargo/pull/12221)
+ [#12288](https://github.com/rust-lang/cargo/pull/12288)
- Add support of the "default" keyword to reset previously set `build.jobs`
parallelism back to the default.
[#12222](https://github.com/rust-lang/cargo/pull/12222)
### Changed
+- ❗ Turned feature name validation check to a hard error. The warning was
+ added in Rust 1.49. These extended characters aren't allowed on crates.io, so
+ this should only impact users of other registries, or people who don't publish
+ to a registry.
+ [#12291](https://github.com/rust-lang/cargo/pull/12291)
- Cargo now warns when an edition 2021 package is in a virtual workspace and
`workspace.resolver` is not set. It is recommended to set the resolver
version for workspaces explicitly.
@@ -42,8 +93,29 @@
([eRFC 3424](https://github.com/rust-lang/rfcs/blob/master/text/3424-cargo-script.md))
[docs](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#script)
[#12245](https://github.com/rust-lang/cargo/pull/12245)
+ [#12255](https://github.com/rust-lang/cargo/pull/12255)
+ [#12258](https://github.com/rust-lang/cargo/pull/12258)
+ [#12262](https://github.com/rust-lang/cargo/pull/12262)
+ [#12268](https://github.com/rust-lang/cargo/pull/12268)
+ [#12269](https://github.com/rust-lang/cargo/pull/12269)
+ [#12281](https://github.com/rust-lang/cargo/pull/12281)
+ [#12282](https://github.com/rust-lang/cargo/pull/12282)
+ [#12283](https://github.com/rust-lang/cargo/pull/12283)
+ [#12284](https://github.com/rust-lang/cargo/pull/12284)
+ [#12287](https://github.com/rust-lang/cargo/pull/12287)
+ [#12289](https://github.com/rust-lang/cargo/pull/12289)
+ [#12303](https://github.com/rust-lang/cargo/pull/12303)
+ [#12305](https://github.com/rust-lang/cargo/pull/12305)
+ [#12308](https://github.com/rust-lang/cargo/pull/12308)
- Automatically inherit workspace lints when running `cargo new`/`cargo init`.
[#12174](https://github.com/rust-lang/cargo/pull/12174)
+- Removed `-Zjobserver-per-rustc` again.
+ [#12285](https://github.com/rust-lang/cargo/pull/12285)
+- Added `.toml` file extension restriction for `-Zconfig-include`.
+ [#12298](https://github.com/rust-lang/cargo/pull/12298)
+- Added `-Znext-lockfile-bump` to prepare for the next lockfile bump.
+ [#12279](https://github.com/rust-lang/cargo/pull/12279)
+ [#12302](https://github.com/rust-lang/cargo/pull/12302)
### Documentation
@@ -55,6 +127,18 @@
[#12192](https://github.com/rust-lang/cargo/pull/12192)
[#12239](https://github.com/rust-lang/cargo/pull/12239)
[#12247](https://github.com/rust-lang/cargo/pull/12247)
+- Added more documentation for `Source` download functions.
+ [#12319](https://github.com/rust-lang/cargo/pull/12319)
+- Added READMEs for the credential helpers.
+ [#12322](https://github.com/rust-lang/cargo/pull/12322)
+- Fixed version requirement example in Dependency Resolution.
+ [#12267](https://github.com/rust-lang/cargo/pull/12267)
+- Clarify the default behavior of cargo-install.
+ [#12276](https://github.com/rust-lang/cargo/pull/12276)
+- Clarify the use of "default" branch instead of `main` by default.
+ [#12251](https://github.com/rust-lang/cargo/pull/12251)
+- Provide guidance on version requirements.
+ [#12323](https://github.com/rust-lang/cargo/pull/12323)
### Internal
@@ -62,9 +146,13 @@
[#12236](https://github.com/rust-lang/cargo/pull/12236)
- Updated to `curl-sys` 0.4.63, which corresponds to curl 8.1.2.
[#12218](https://github.com/rust-lang/cargo/pull/12218)
+- Updated to `openssl` 0.10.55.
+ [#12300](https://github.com/rust-lang/cargo/pull/12300)
+- Updated several dependencies.
+ [#12261](https://github.com/rust-lang/cargo/pull/12261)
- Removed unused features from `windows-sys` dependency.
[#12176](https://github.com/rust-lang/cargo/pull/12176)
-- Refactored compiler invocations
+- Refactored compiler invocations.
[#12211](https://github.com/rust-lang/cargo/pull/12211)
- Refactored git and registry sources, and registry data.
[#12203](https://github.com/rust-lang/cargo/pull/12203)
@@ -83,6 +171,18 @@
[#12199](https://github.com/rust-lang/cargo/pull/12199)
- Migrated print-ban from test to clippy
[#12246](https://github.com/rust-lang/cargo/pull/12246)
+- Switched to `OnceLock` for interning uses.
+ [#12217](https://github.com/rust-lang/cargo/pull/12217)
+- Removed a unnecessary `.clone`.
+ [#12213](https://github.com/rust-lang/cargo/pull/12213)
+- Don't try to compile `cargo-credential-gnome-secret` on non-Linux platforms.
+ [#12321](https://github.com/rust-lang/cargo/pull/12321)
+- Use macro to remove duplication of workspace inheritable fields getters.
+ [#12317](https://github.com/rust-lang/cargo/pull/12317)
+- Extracted and rearranged registry API items to their own modules.
+ [#12290](https://github.com/rust-lang/cargo/pull/12290)
+- Show a better error when container tests fail.
+ [#12264](https://github.com/rust-lang/cargo/pull/12264)
## Cargo 1.71 (2023-07-13)
[84b7041f...rust-1.71.0](https://github.com/rust-lang/cargo/compare/84b7041f...rust-1.71.0)
@@ -94,8 +194,6 @@
[#11958](https://github.com/rust-lang/cargo/pull/11958)
- Added `workspace_default_members` to the output of `cargo metadata`.
[#11978](https://github.com/rust-lang/cargo/pull/11978)
-- `cargo add` now considers `rust-version` when selecting packages.
- [#12078](https://github.com/rust-lang/cargo/pull/12078)
- Automatically inherit workspace fields when running `cargo new`/`cargo init`.
[#12069](https://github.com/rust-lang/cargo/pull/12069)
@@ -173,6 +271,8 @@
[#11981](https://github.com/rust-lang/cargo/pull/11981)
- Added `-Zmsrv-policy` feature flag placeholder.
[#12043](https://github.com/rust-lang/cargo/pull/12043)
+- `cargo add` now considers `rust-version` when selecting packages with `-Zmsrv-policy`.
+ [#12078](https://github.com/rust-lang/cargo/pull/12078)
### Documentation
diff --git a/src/tools/cargo/Cargo.lock b/src/tools/cargo/Cargo.lock
index fe365bbcb..371504138 100644
--- a/src/tools/cargo/Cargo.lock
+++ b/src/tools/cargo/Cargo.lock
@@ -39,6 +39,12 @@ dependencies = [
]
[[package]]
+name = "anes"
+version = "0.1.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
+
+[[package]]
name = "anstream"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -74,7 +80,7 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
dependencies = [
- "windows-sys 0.48.0",
+ "windows-sys",
]
[[package]]
@@ -84,14 +90,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
dependencies = [
"anstyle",
- "windows-sys 0.48.0",
+ "windows-sys",
]
[[package]]
name = "anyhow"
-version = "1.0.71"
+version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
+checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854"
[[package]]
name = "arc-swap"
@@ -106,17 +112,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
-name = "atty"
-version = "0.2.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-dependencies = [
- "hermit-abi 0.1.19",
- "libc",
- "winapi",
-]
-
-[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -174,9 +169,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.3.2"
+version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded"
+checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
[[package]]
name = "bitmaps"
@@ -262,20 +257,23 @@ dependencies = [
[[package]]
name = "cargo"
-version = "0.73.0"
+version = "0.74.0"
dependencies = [
"anyhow",
"base64",
"bytesize",
- "cargo-platform 0.1.3",
+ "cargo-credential",
+ "cargo-credential-libsecret",
+ "cargo-credential-macos-keychain",
+ "cargo-credential-wincred",
+ "cargo-platform 0.1.4",
"cargo-test-macro",
"cargo-test-support",
"cargo-util",
- "clap 4.3.3",
+ "clap",
"crates-io",
"curl",
"curl-sys",
- "env_logger 0.10.0",
"filetime",
"flate2",
"fwdansi",
@@ -288,7 +286,7 @@ dependencies = [
"hmac",
"home 0.5.5",
"http-auth",
- "humantime 2.1.0",
+ "humantime",
"ignore",
"im-rc",
"indexmap",
@@ -297,14 +295,12 @@ dependencies = [
"lazycell",
"libc",
"libgit2-sys",
- "log",
"memchr",
"opener",
"openssl",
"os_info",
"pasetors",
"pathdiff",
- "pretty_env_logger",
"pulldown-cmark",
"rand",
"rustfix",
@@ -318,27 +314,40 @@ dependencies = [
"shell-escape",
"snapbox",
"strip-ansi-escapes",
- "syn 2.0.18",
+ "syn 2.0.28",
"tar",
"tempfile",
"termcolor",
"time",
"toml",
"toml_edit",
+ "tracing",
+ "tracing-subscriber",
+ "unicase",
"unicode-width",
"unicode-xid",
"url",
"walkdir",
- "windows-sys 0.48.0",
+ "windows-sys",
]
[[package]]
name = "cargo-credential"
-version = "0.2.0"
+version = "0.3.0"
+dependencies = [
+ "anyhow",
+ "libc",
+ "serde",
+ "serde_json",
+ "snapbox",
+ "thiserror",
+ "time",
+ "windows-sys",
+]
[[package]]
name = "cargo-credential-1password"
-version = "0.2.0"
+version = "0.3.0"
dependencies = [
"cargo-credential",
"serde",
@@ -346,16 +355,17 @@ dependencies = [
]
[[package]]
-name = "cargo-credential-gnome-secret"
-version = "0.2.0"
+name = "cargo-credential-libsecret"
+version = "0.3.1"
dependencies = [
+ "anyhow",
"cargo-credential",
- "pkg-config",
+ "libloading",
]
[[package]]
name = "cargo-credential-macos-keychain"
-version = "0.2.0"
+version = "0.3.0"
dependencies = [
"cargo-credential",
"security-framework",
@@ -363,10 +373,10 @@ dependencies = [
[[package]]
name = "cargo-credential-wincred"
-version = "0.2.0"
+version = "0.3.0"
dependencies = [
"cargo-credential",
- "windows-sys 0.48.0",
+ "windows-sys",
]
[[package]]
@@ -380,7 +390,7 @@ dependencies = [
[[package]]
name = "cargo-platform"
-version = "0.1.3"
+version = "0.1.4"
dependencies = [
"serde",
]
@@ -412,12 +422,12 @@ dependencies = [
"time",
"toml",
"url",
- "windows-sys 0.48.0",
+ "windows-sys",
]
[[package]]
name = "cargo-util"
-version = "0.2.5"
+version = "0.2.6"
dependencies = [
"anyhow",
"core-foundation",
@@ -425,14 +435,14 @@ dependencies = [
"hex",
"jobserver",
"libc",
- "log",
"miow",
"same-file",
"sha2",
"shell-escape",
"tempfile",
+ "tracing",
"walkdir",
- "windows-sys 0.48.0",
+ "windows-sys",
]
[[package]]
@@ -470,34 +480,49 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
-name = "clap"
-version = "2.34.0"
+name = "ciborium"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
+checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926"
dependencies = [
- "bitflags 1.3.2",
- "textwrap",
- "unicode-width",
+ "ciborium-io",
+ "ciborium-ll",
+ "serde",
+]
+
+[[package]]
+name = "ciborium-io"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656"
+
+[[package]]
+name = "ciborium-ll"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b"
+dependencies = [
+ "ciborium-io",
+ "half",
]
[[package]]
name = "clap"
-version = "4.3.3"
+version = "4.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca8f255e4b8027970e78db75e78831229c9815fdbfa67eb1a1b777a62e24b4a0"
+checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
-version = "4.3.3"
+version = "4.3.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "acd4f3c17c83b0ba34ffbc4f8bbd74f079413f747f84a6f89292f138057e36ab"
+checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1"
dependencies = [
"anstream",
"anstyle",
- "bitflags 1.3.2",
"clap_lex",
"strsim",
"terminal_size",
@@ -563,13 +588,13 @@ dependencies = [
[[package]]
name = "crates-io"
-version = "0.37.0"
+version = "0.38.0"
dependencies = [
- "anyhow",
"curl",
"percent-encoding",
"serde",
"serde_json",
+ "thiserror",
"url",
]
@@ -584,24 +609,24 @@ dependencies = [
[[package]]
name = "criterion"
-version = "0.3.6"
+version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f"
+checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
dependencies = [
- "atty",
+ "anes",
"cast",
- "clap 2.34.0",
+ "ciborium",
+ "clap",
"criterion-plot",
- "csv",
+ "is-terminal",
"itertools",
- "lazy_static",
"num-traits",
+ "once_cell",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
- "serde_cbor",
"serde_derive",
"serde_json",
"tinytemplate",
@@ -610,9 +635,9 @@ dependencies = [
[[package]]
name = "criterion-plot"
-version = "0.4.5"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876"
+checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
"itertools",
@@ -684,43 +709,12 @@ dependencies = [
]
[[package]]
-name = "csv"
-version = "1.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086"
-dependencies = [
- "csv-core",
- "itoa 1.0.6",
- "ryu",
- "serde",
-]
-
-[[package]]
-name = "csv-core"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
-dependencies = [
- "memchr",
-]
-
-[[package]]
name = "ct-codecs"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3b7eb4404b8195a9abb6356f4ac07d8ba267045c8d6d220ac4dc992e6cc75df"
[[package]]
-name = "ctor"
-version = "0.1.26"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
-dependencies = [
- "quote",
- "syn 1.0.109",
-]
-
-[[package]]
name = "curl"
version = "0.4.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -737,9 +731,9 @@ dependencies = [
[[package]]
name = "curl-sys"
-version = "0.4.63+curl-8.1.2"
+version = "0.4.65+curl-8.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aeb0fef7046022a1e2ad67a004978f0e3cacb9e3123dc62ce768f92197b771dc"
+checksum = "961ba061c9ef2fe34bbd12b807152d96f0badd2bebe7b90ce6c8c8b7572a0986"
dependencies = [
"cc",
"libc",
@@ -837,30 +831,10 @@ dependencies = [
]
[[package]]
-name = "env_logger"
-version = "0.7.1"
+name = "equivalent"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
-dependencies = [
- "atty",
- "humantime 1.3.0",
- "log",
- "regex",
- "termcolor",
-]
-
-[[package]]
-name = "env_logger"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
-dependencies = [
- "humantime 2.1.0",
- "is-terminal",
- "log",
- "regex",
- "termcolor",
-]
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
@@ -870,7 +844,7 @@ checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
dependencies = [
"errno-dragonfly",
"libc",
- "windows-sys 0.48.0",
+ "windows-sys",
]
[[package]]
@@ -884,6 +858,18 @@ dependencies = [
]
[[package]]
+name = "escargot"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "768064bd3a0e2bedcba91dc87ace90beea91acc41b6a01a3ca8e9aa8827461bf"
+dependencies = [
+ "log",
+ "once_cell",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
name = "fastrand"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -893,6 +879,12 @@ dependencies = [
]
[[package]]
+name = "fastrand"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
+
+[[package]]
name = "ff"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -917,7 +909,7 @@ dependencies = [
"cfg-if",
"libc",
"redox_syscall 0.2.16",
- "windows-sys 0.48.0",
+ "windows-sys",
]
[[package]]
@@ -1172,7 +1164,7 @@ version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f216df1c33e6e1555923eff0096858a879e8aaadd35b5d788641e4e8064c892"
dependencies = [
- "bitflags 2.3.2",
+ "bitflags 2.3.3",
"bstr",
"gix-path",
"libc",
@@ -1269,7 +1261,7 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd0ade1e80ab1f079703d1824e1daf73009096386aa7fd2f0477f6e4ac0a558e"
dependencies = [
- "bitflags 2.3.2",
+ "bitflags 2.3.3",
"bstr",
"gix-features",
"gix-path",
@@ -1277,9 +1269,9 @@ dependencies = [
[[package]]
name = "gix-hash"
-version = "0.11.2"
+version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee181c85d3955f54c4426e6bfaeeada4428692e1a39b8788c2ac7785fc301dd8"
+checksum = "a0dd58cdbe7ffa4032fc111864c80d5f8cecd9a2c9736c97ae7e5be834188272"
dependencies = [
"hex",
"thiserror",
@@ -1287,12 +1279,12 @@ dependencies = [
[[package]]
name = "gix-hashtable"
-version = "0.2.1"
+version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd259bd0d96e6153e357a8cdaca76c48e103fd34208b6c0ce77b1ad995834bd2"
+checksum = "9e133bc56d938eaec1c675af7c681a51de9662b0ada779f45607b967a10da77a"
dependencies = [
"gix-hash",
- "hashbrown 0.13.2",
+ "hashbrown 0.14.0",
"parking_lot",
]
@@ -1314,7 +1306,7 @@ version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "616ba958fabfb11263fa042c35690d48a6c7be4e9277e2c7e24ff263b3fe7b82"
dependencies = [
- "bitflags 2.3.2",
+ "bitflags 2.3.3",
"bstr",
"btoi",
"filetime",
@@ -1358,7 +1350,7 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "945c3ef1e912e44a5f405fc9e924edf42000566a1b257ed52cb1293300f6f08c"
dependencies = [
- "bitflags 2.3.2",
+ "bitflags 2.3.3",
"gix-commitgraph",
"gix-hash",
"gix-object",
@@ -1458,7 +1450,7 @@ dependencies = [
"gix-command",
"gix-config-value",
"parking_lot",
- "rustix",
+ "rustix 0.37.20",
"thiserror",
]
@@ -1559,7 +1551,7 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2b7b38b766eb95dcc5350a9c450030b69892c0902fa35f4a6d0809273bd9dae"
dependencies = [
- "bitflags 2.3.2",
+ "bitflags 2.3.3",
"gix-path",
"libc",
"windows",
@@ -1631,7 +1623,7 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbcfcb150c7ef553d76988467d223254045bdcad0dc6724890f32fbe96415da5"
dependencies = [
- "fastrand",
+ "fastrand 1.9.0",
]
[[package]]
@@ -1724,18 +1716,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hashbrown"
-version = "0.13.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e"
-
-[[package]]
-name = "hermit-abi"
-version = "0.1.19"
+version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
-dependencies = [
- "libc",
-]
+checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
[[package]]
name = "hermit-abi"
@@ -1748,9 +1731,9 @@ dependencies = [
[[package]]
name = "hermit-abi"
-version = "0.3.1"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
+checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
[[package]]
name = "hex"
@@ -1782,14 +1765,14 @@ version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
dependencies = [
- "windows-sys 0.48.0",
+ "windows-sys",
]
[[package]]
name = "home"
-version = "0.5.6"
+version = "0.5.7"
dependencies = [
- "windows-sys 0.48.0",
+ "windows-sys",
]
[[package]]
@@ -1803,15 +1786,6 @@ dependencies = [
[[package]]
name = "humantime"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
-dependencies = [
- "quick-error 1.2.3",
-]
-
-[[package]]
-name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
@@ -1869,12 +1843,12 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "1.9.3"
+version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
+checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
dependencies = [
- "autocfg",
- "hashbrown 0.12.3",
+ "equivalent",
+ "hashbrown 0.14.0",
]
[[package]]
@@ -1902,9 +1876,9 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2"
dependencies = [
- "hermit-abi 0.3.1",
+ "hermit-abi 0.3.2",
"libc",
- "windows-sys 0.48.0",
+ "windows-sys",
]
[[package]]
@@ -1913,10 +1887,10 @@ version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f"
dependencies = [
- "hermit-abi 0.3.1",
+ "hermit-abi 0.3.2",
"io-lifetimes",
- "rustix",
- "windows-sys 0.48.0",
+ "rustix 0.37.20",
+ "windows-sys",
]
[[package]]
@@ -1987,9 +1961,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
-version = "0.2.146"
+version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b"
+checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "libgit2-sys"
@@ -2006,6 +1980,16 @@ dependencies = [
]
[[package]]
+name = "libloading"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb"
+dependencies = [
+ "cfg-if",
+ "windows-sys",
+]
+
+[[package]]
name = "libm"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2054,6 +2038,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519"
[[package]]
+name = "linux-raw-sys"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
+
+[[package]]
name = "lock_api"
version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2070,6 +2060,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
[[package]]
+name = "matchers"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
+dependencies = [
+ "regex-automata",
+]
+
+[[package]]
name = "maybe-async"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2134,11 +2133,11 @@ dependencies = [
[[package]]
name = "miow"
-version = "0.5.0"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52ffbca2f655e33c08be35d87278e5b18b89550a37dbd598c20db92f6a471123"
+checksum = "359f76430b20a79f9e20e115b3428614e654f04fab314482fc0fda0ebd3c6044"
dependencies = [
- "windows-sys 0.42.0",
+ "windows-sys",
]
[[package]]
@@ -2158,6 +2157,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]]
+name = "normpath"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec60c60a693226186f5d6edf073232bfb6464ed97eb22cf3b01c1e8198fd97f5"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.46.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84"
+dependencies = [
+ "overload",
+ "winapi",
+]
+
+[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2200,11 +2218,12 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "opener"
-version = "0.5.2"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "293c15678e37254c15bd2f092314abb4e51d7fdde05c2021279c12631b54f005"
+checksum = "6c62dcb6174f9cb326eac248f07e955d5d559c272730b6c03e396b443b562788"
dependencies = [
"bstr",
+ "normpath",
"winapi",
]
@@ -2231,7 +2250,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.18",
+ "syn 2.0.28",
]
[[package]]
@@ -2294,13 +2313,10 @@ dependencies = [
]
[[package]]
-name = "output_vt100"
-version = "0.1.3"
+name = "overload"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66"
-dependencies = [
- "winapi",
-]
+checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
name = "p384"
@@ -2429,7 +2445,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
- "syn 2.0.18",
+ "syn 2.0.28",
]
[[package]]
@@ -2444,6 +2460,12 @@ dependencies = [
]
[[package]]
+name = "pin-project-lite"
+version = "0.2.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57"
+
+[[package]]
name = "pkcs8"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2495,27 +2517,15 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "pretty_assertions"
-version = "1.3.0"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a25e9bcb20aa780fd0bb16b72403a9064d6b3f22f026946029acb941a50af755"
+checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66"
dependencies = [
- "ctor",
"diff",
- "output_vt100",
"yansi",
]
[[package]]
-name = "pretty_env_logger"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d"
-dependencies = [
- "env_logger 0.7.1",
- "log",
-]
-
-[[package]]
name = "primeorder"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2526,9 +2536,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.60"
+version = "1.0.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406"
+checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
dependencies = [
"unicode-ident",
]
@@ -2587,9 +2597,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quote"
-version = "1.0.28"
+version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
+checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
dependencies = [
"proc-macro2",
]
@@ -2698,6 +2708,9 @@ name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
+dependencies = [
+ "regex-syntax 0.6.29",
+]
[[package]]
name = "regex-syntax"
@@ -2760,8 +2773,21 @@ dependencies = [
"errno",
"io-lifetimes",
"libc",
- "linux-raw-sys",
- "windows-sys 0.48.0",
+ "linux-raw-sys 0.3.8",
+ "windows-sys",
+]
+
+[[package]]
+name = "rustix"
+version = "0.38.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ee020b1716f0a80e2ace9b03441a749e402e86712f15f16fe8a8f75afac732f"
+dependencies = [
+ "bitflags 2.3.3",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.5",
+ "windows-sys",
]
[[package]]
@@ -2793,11 +2819,11 @@ dependencies = [
[[package]]
name = "schannel"
-version = "0.1.21"
+version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3"
+checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
dependencies = [
- "windows-sys 0.42.0",
+ "windows-sys",
]
[[package]]
@@ -2822,9 +2848,9 @@ dependencies = [
[[package]]
name = "security-framework"
-version = "2.9.1"
+version = "2.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8"
+checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de"
dependencies = [
"bitflags 1.3.2",
"core-foundation",
@@ -2845,9 +2871,9 @@ dependencies = [
[[package]]
name = "semver"
-version = "1.0.17"
+version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
+checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
dependencies = [
"serde",
]
@@ -2861,9 +2887,9 @@ dependencies = [
[[package]]
name = "serde"
-version = "1.0.164"
+version = "1.0.171"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d"
+checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9"
dependencies = [
"serde_derive",
]
@@ -2879,40 +2905,30 @@ dependencies = [
]
[[package]]
-name = "serde_cbor"
-version = "0.11.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
-dependencies = [
- "half",
- "serde",
-]
-
-[[package]]
name = "serde_derive"
-version = "1.0.164"
+version = "1.0.171"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68"
+checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.18",
+ "syn 2.0.28",
]
[[package]]
name = "serde_ignored"
-version = "0.1.7"
+version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94eb4a4087ba8bdf14a9208ac44fddbf55c01a6195f7edfc511ddaff6cae45a6"
+checksum = "80c31d5c53fd39f208e770f5a20a0bb214dee2a8d0d8adba18e19ad95a482ca5"
dependencies = [
"serde",
]
[[package]]
name = "serde_json"
-version = "1.0.96"
+version = "1.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1"
+checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c"
dependencies = [
"itoa 1.0.6",
"ryu",
@@ -2921,9 +2937,9 @@ dependencies = [
[[package]]
name = "serde_spanned"
-version = "0.6.2"
+version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d"
+checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
dependencies = [
"serde",
]
@@ -2947,9 +2963,9 @@ checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
[[package]]
name = "sha2"
-version = "0.10.6"
+version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
+checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -2957,6 +2973,15 @@ dependencies = [
]
[[package]]
+name = "sharded-slab"
+version = "0.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
name = "shell-escape"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3023,6 +3048,7 @@ dependencies = [
"anstyle",
"content_inspector",
"dunce",
+ "escargot",
"filetime",
"normalize-line-endings",
"similar",
@@ -3100,9 +3126,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.18"
+version = "2.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e"
+checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567"
dependencies = [
"proc-macro2",
"quote",
@@ -3133,16 +3159,15 @@ dependencies = [
[[package]]
name = "tempfile"
-version = "3.6.0"
+version = "3.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6"
+checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998"
dependencies = [
- "autocfg",
"cfg-if",
- "fastrand",
+ "fastrand 2.0.0",
"redox_syscall 0.3.5",
- "rustix",
- "windows-sys 0.48.0",
+ "rustix 0.38.6",
+ "windows-sys",
]
[[package]]
@@ -3160,37 +3185,28 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237"
dependencies = [
- "rustix",
- "windows-sys 0.48.0",
-]
-
-[[package]]
-name = "textwrap"
-version = "0.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
-dependencies = [
- "unicode-width",
+ "rustix 0.37.20",
+ "windows-sys",
]
[[package]]
name = "thiserror"
-version = "1.0.40"
+version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
+checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.40"
+version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
+checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.18",
+ "syn 2.0.28",
]
[[package]]
@@ -3259,9 +3275,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
-version = "0.7.4"
+version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec"
+checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542"
dependencies = [
"serde",
"serde_spanned",
@@ -3271,18 +3287,18 @@ dependencies = [
[[package]]
name = "toml_datetime"
-version = "0.6.2"
+version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f"
+checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
-version = "0.19.10"
+version = "0.19.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739"
+checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a"
dependencies = [
"indexmap",
"serde",
@@ -3292,6 +3308,68 @@ dependencies = [
]
[[package]]
+name = "tracing"
+version = "0.1.37"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
+dependencies = [
+ "cfg-if",
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.26"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.28",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
+dependencies = [
+ "lazy_static",
+ "log",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
name = "typenum"
version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3375,6 +3453,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
+name = "valuable"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d"
+
+[[package]]
name = "varisat"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3540,7 +3624,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
- "syn 2.0.18",
+ "syn 2.0.28",
"wasm-bindgen-shared",
]
@@ -3562,7 +3646,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.18",
+ "syn 2.0.28",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -3625,21 +3709,6 @@ dependencies = [
[[package]]
name = "windows-sys"
-version = "0.42.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
-dependencies = [
- "windows_aarch64_gnullvm 0.42.2",
- "windows_aarch64_msvc 0.42.2",
- "windows_i686_gnu 0.42.2",
- "windows_i686_msvc 0.42.2",
- "windows_x86_64_gnu 0.42.2",
- "windows_x86_64_gnullvm 0.42.2",
- "windows_x86_64_msvc 0.42.2",
-]
-
-[[package]]
-name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
@@ -3653,104 +3722,62 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
dependencies = [
- "windows_aarch64_gnullvm 0.48.0",
- "windows_aarch64_msvc 0.48.0",
- "windows_i686_gnu 0.48.0",
- "windows_i686_msvc 0.48.0",
- "windows_x86_64_gnu 0.48.0",
- "windows_x86_64_gnullvm 0.48.0",
- "windows_x86_64_msvc 0.48.0",
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
-
-[[package]]
-name = "windows_aarch64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
-
-[[package]]
-name = "windows_aarch64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_i686_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
-
-[[package]]
-name = "windows_i686_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
-
-[[package]]
-name = "windows_i686_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
-
-[[package]]
-name = "windows_x86_64_gnu"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.42.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
-
-[[package]]
-name = "windows_x86_64_msvc"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "winnow"
-version = "0.4.6"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699"
+checksum = "81fac9742fd1ad1bd9643b991319f72dd031016d44b77039a26977eb667141e7"
dependencies = [
"memchr",
]
@@ -3760,21 +3787,23 @@ name = "xtask-build-man"
version = "0.0.0"
[[package]]
-name = "xtask-stale-label"
+name = "xtask-bump-check"
version = "0.0.0"
dependencies = [
- "toml_edit",
+ "anyhow",
+ "cargo",
+ "cargo-util",
+ "clap",
+ "git2",
+ "tracing",
+ "tracing-subscriber",
]
[[package]]
-name = "xtask-unpublished"
+name = "xtask-stale-label"
version = "0.0.0"
dependencies = [
- "anyhow",
- "cargo",
- "clap 4.3.3",
- "env_logger 0.10.0",
- "log",
+ "toml_edit",
]
[[package]]
diff --git a/src/tools/cargo/Cargo.toml b/src/tools/cargo/Cargo.toml
index 7e383be69..0e189a6d4 100644
--- a/src/tools/cargo/Cargo.toml
+++ b/src/tools/cargo/Cargo.toml
@@ -10,98 +10,106 @@ exclude = [
"target/", # exclude bench testing
]
+[workspace.package]
+edition = "2021"
+license = "MIT OR Apache-2.0"
+
[workspace.dependencies]
-anyhow = "1.0.47"
-base64 = "0.21.0"
-bytesize = "1.0"
+anyhow = "1.0.72"
+base64 = "0.21.2"
+bytesize = "1.2"
cargo = { path = "" }
-cargo-credential = { version = "0.2.0", path = "credential/cargo-credential" }
-cargo-platform = { path = "crates/cargo-platform", version = "0.1.3" }
+cargo-credential = { version = "0.3.0", path = "credential/cargo-credential" }
+cargo-credential-libsecret = { version = "0.3.1", path = "credential/cargo-credential-libsecret" }
+cargo-credential-wincred = { version = "0.3.0", path = "credential/cargo-credential-wincred" }
+cargo-credential-macos-keychain = { version = "0.3.0", path = "credential/cargo-credential-macos-keychain" }
+cargo-platform = { path = "crates/cargo-platform", version = "0.1.4" }
cargo-test-macro = { path = "crates/cargo-test-macro" }
cargo-test-support = { path = "crates/cargo-test-support" }
-cargo-util = { version = "0.2.5", path = "crates/cargo-util" }
+cargo-util = { version = "0.2.6", path = "crates/cargo-util" }
cargo_metadata = "0.14.0"
-clap = "4.2.0"
-core-foundation = { version = "0.9.0", features = ["mac_os_10_7_support"] }
-crates-io = { version = "0.37.0", path = "crates/crates-io" }
-criterion = { version = "0.3.5", features = ["html_reports"] }
+clap = "4.3.19"
+core-foundation = { version = "0.9.3", features = ["mac_os_10_7_support"] }
+crates-io = { version = "0.38.0", path = "crates/crates-io" }
+criterion = { version = "0.5.1", features = ["html_reports"] }
curl = "0.4.44"
-curl-sys = "0.4.63"
-env_logger = "0.10.0"
-filetime = "0.2.9"
-flate2 = { version = "1.0.3", default-features = false, features = ["zlib"] }
+curl-sys = "0.4.65"
+filetime = "0.2.21"
+flate2 = { version = "1.0.26", default-features = false, features = ["zlib"] }
fwdansi = "1.1.0"
-git2 = "0.17.1"
+git2 = "0.17.2"
git2-curl = "0.18.0"
gix = { version = "0.45.1", default-features = false, features = ["blocking-http-transport-curl", "progress-tree"] }
gix-features-for-configuration-only = { version = "0.30.0", package = "gix-features", features = [ "parallel" ] }
-glob = "0.3.0"
-handlebars = { version = "3.2.1", features = ["dir_source"] }
-hex = "0.4.2"
+glob = "0.3.1"
+handlebars = { version = "3.5.5", features = ["dir_source"] }
+hex = "0.4.3"
hmac = "0.12.1"
home = "0.5.5"
-http-auth = { version = "0.1.6", default-features = false }
-humantime = "2.0.0"
-ignore = "0.4.7"
-im-rc = "15.0.0"
-indexmap = "1"
+http-auth = { version = "0.1.8", default-features = false }
+humantime = "2.1.0"
+ignore = "0.4.20"
+im-rc = "15.1.0"
+indexmap = "2"
itertools = "0.10.0"
jobserver = "0.1.26"
-lazy_static = "1.3.0"
-lazycell = "1.2.0"
-libc = "0.2.144"
-libgit2-sys = "0.15.1"
-log = "0.4.17"
-memchr = "2.1.3"
-miow = "0.5.0"
-opener = "0.5"
+lazy_static = "1.4.0"
+lazycell = "1.3.0"
+libc = "0.2.147"
+libgit2-sys = "0.15.2"
+libloading = "0.8.0"
+memchr = "2.5.0"
+miow = "0.6.0"
+opener = "0.6.1"
openssl ="0.10.55"
-os_info = "3.5.0"
-pasetors = { version = "0.6.4", features = ["v3", "paserk", "std", "serde"] }
+os_info = "3.7.0"
+pasetors = { version = "0.6.7", features = ["v3", "paserk", "std", "serde"] }
pathdiff = "0.2"
-percent-encoding = "2.0"
-pkg-config = "0.3.19"
-pretty_assertions = "1.3.0"
-pretty_env_logger = "0.4"
-proptest = "1.1.0"
-pulldown-cmark = { version = "0.9.2", default-features = false }
+percent-encoding = "2.3"
+pkg-config = "0.3.27"
+pretty_assertions = "1.4.0"
+proptest = "1.2.0"
+pulldown-cmark = { version = "0.9.3", default-features = false }
rand = "0.8.5"
-rustfix = "0.6.0"
+rustfix = "0.6.1"
same-file = "1.0.6"
-security-framework = "2.0.0"
-semver = { version = "1.0.3", features = ["serde"] }
-serde = "1.0.123"
+security-framework = "2.9.2"
+semver = { version = "1.0.18", features = ["serde"] }
+serde = "1.0.171"
serde-value = "0.7.0"
-serde_ignored = "0.1.0"
-serde_json = "1.0.59"
+serde_ignored = "0.1.9"
+serde_json = "1.0.104"
sha1 = "0.10.5"
-sha2 = "0.10.6"
-shell-escape = "0.1.4"
-snapbox = { version = "0.4.0", features = ["diff", "path"] }
-strip-ansi-escapes = "0.1.0"
-syn = { version = "2.0.14", features = ["extra-traits", "full"] }
+sha2 = "0.10.7"
+shell-escape = "0.1.5"
+snapbox = { version = "0.4.11", features = ["diff", "path"] }
+strip-ansi-escapes = "0.1.1"
+syn = { version = "2.0.28", features = ["extra-traits", "full"] }
tar = { version = "0.4.39", default-features = false }
-tempfile = "3.1.0"
-termcolor = "1.1.2"
-time = { version = "0.3", features = ["parsing", "formatting"] }
-toml = "0.7.0"
-toml_edit = "0.19.0"
-unicode-width = "0.1.5"
-unicode-xid = "0.2.0"
-url = "2.2.2"
-varisat = "0.2.1"
-walkdir = "2.3.1"
+tempfile = "3.7.0"
+termcolor = "1.2.0"
+thiserror = "1.0.44"
+time = { version = "0.3", features = ["parsing", "formatting", "serde"] }
+toml = "0.7.6"
+toml_edit = "0.19.14"
+tracing = "0.1.37"
+tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
+unicase = "2.6.0"
+unicode-width = "0.1.10"
+unicode-xid = "0.2.4"
+url = "2.4.0"
+varisat = "0.2.2"
+walkdir = "2.3.3"
windows-sys = "0.48"
[package]
name = "cargo"
-version = "0.73.0"
-edition = "2021"
-license = "MIT OR Apache-2.0"
+version = "0.74.0"
+edition.workspace = true
+license.workspace = true
homepage = "https://crates.io"
repository = "https://github.com/rust-lang/cargo"
documentation = "https://docs.rs/cargo"
-readme = "README.md"
description = """
Cargo, a package manager for Rust.
"""
@@ -115,12 +123,15 @@ anyhow.workspace = true
base64.workspace = true
bytesize.workspace = true
cargo-platform.workspace = true
+cargo-credential.workspace = true
+cargo-credential-libsecret.workspace = true
+cargo-credential-macos-keychain.workspace = true
+cargo-credential-wincred.workspace = true
cargo-util.workspace = true
clap = { workspace = true, features = ["wrap_help"] }
crates-io.workspace = true
curl = { workspace = true, features = ["http2"] }
curl-sys.workspace = true
-env_logger.workspace = true
filetime.workspace = true
flate2.workspace = true
git2.workspace = true
@@ -141,13 +152,11 @@ jobserver.workspace = true
lazycell.workspace = true
libc.workspace = true
libgit2-sys.workspace = true
-log.workspace = true
memchr.workspace = true
opener.workspace = true
os_info.workspace = true
pasetors.workspace = true
pathdiff.workspace = true
-pretty_env_logger = { workspace = true, optional = true }
pulldown-cmark.workspace = true
rand.workspace = true
rustfix.workspace = true
@@ -166,6 +175,9 @@ termcolor.workspace = true
time.workspace = true
toml.workspace = true
toml_edit.workspace = true
+tracing.workspace = true
+tracing-subscriber.workspace = true
+unicase.workspace = true
unicode-width.workspace = true
unicode-xid.workspace = true
url.workspace = true
@@ -181,10 +193,12 @@ fwdansi.workspace = true
workspace = true
features = [
"Win32_Foundation",
+ "Win32_Security",
"Win32_Storage_FileSystem",
+ "Win32_System_IO",
"Win32_System_Console",
- "Win32_System_Threading",
"Win32_System_JobObjects",
+ "Win32_System_Threading",
]
[dev-dependencies]
@@ -205,6 +219,5 @@ doc = false
[features]
vendored-openssl = ["openssl/vendored"]
vendored-libgit2 = ["libgit2-sys/vendored"]
-pretty-env-logger = ["pretty_env_logger"]
# This is primarily used by rust-lang/rust distributing cargo the executable.
all-static = ['vendored-openssl', 'curl/static-curl', 'curl/force-system-lib-on-osx']
diff --git a/src/tools/cargo/benches/benchsuite/Cargo.toml b/src/tools/cargo/benches/benchsuite/Cargo.toml
index 782e6c101..c15798787 100644
--- a/src/tools/cargo/benches/benchsuite/Cargo.toml
+++ b/src/tools/cargo/benches/benchsuite/Cargo.toml
@@ -1,8 +1,8 @@
[package]
name = "benchsuite"
version = "0.0.0"
-edition = "2021"
-license = "MIT OR Apache-2.0"
+edition.workspace = true
+license.workspace = true
homepage = "https://github.com/rust-lang/cargo"
repository = "https://github.com/rust-lang/cargo"
description = "Benchmarking suite for Cargo."
diff --git a/src/tools/cargo/benches/capture/Cargo.toml b/src/tools/cargo/benches/capture/Cargo.toml
index 6319a0130..e42fe70e2 100644
--- a/src/tools/cargo/benches/capture/Cargo.toml
+++ b/src/tools/cargo/benches/capture/Cargo.toml
@@ -1,8 +1,8 @@
[package]
name = "capture"
version = "0.1.0"
-edition = "2021"
-license = "MIT OR Apache-2.0"
+edition.workspace = true
+license.workspace = true
description = "Tool for capturing a real-world workspace for benchmarking."
publish = false
diff --git a/src/tools/cargo/ci/validate-version-bump.sh b/src/tools/cargo/ci/validate-version-bump.sh
index 9b54fdaaf..659b54c0e 100755
--- a/src/tools/cargo/ci/validate-version-bump.sh
+++ b/src/tools/cargo/ci/validate-version-bump.sh
@@ -16,46 +16,7 @@ set -euo pipefail
base_sha=$(git rev-parse "${BASE_SHA:-HEAD~1}")
head_sha=$(git rev-parse "${HEAD_SHA:-HEAD}")
-echo "Base branch is $base_sha"
-echo "Current head is $head_sha"
+echo "Base revision is $base_sha"
+echo "Head revision is $head_sha"
-# Gets crate names of members that has been changed from $bash_sha to $head_sha.
-changed_crates=$(
- git diff --name-only "$base_sha" "$head_sha" -- crates/ credential/ benches/ \
- | cut -d'/' -f2 \
- | sort -u
-)
-
-if [ -z "$changed_crates" ]
-then
- echo "No file changed in member crates."
- exit 0
-fi
-
-# Checks publish status for only crates with code changes.
-publish_status_table=$(
- echo "$changed_crates" \
- | xargs printf -- '--package %s\n' \
- | xargs cargo unpublished
-)
-
-# "yes" -> code changed but no version difference -> need a bump
-# Prints 2nd column (sep by space), which is the name of the crate.
-crates_need_bump=$(
- echo "$publish_status_table" \
- | { grep '| yes ' || true; } \
- | awk '{print $2}'
-)
-
-if [ -z "$crates_need_bump" ]
-then
- echo "No version bump needed for member crates."
- exit 0
-fi
-
-echo "Detected changes in these crates but no version bump found:"
-echo "$crates_need_bump"
-echo
-echo "Please bump at least one patch version for each corresponding Cargo.toml:"
-echo 'Run "cargo unpublished" to read the publish status table for details.'
-exit 1
+cargo bump-check --base-rev "$base_sha" --head-rev "$head_sha"
diff --git a/src/tools/cargo/crates/cargo-platform/Cargo.toml b/src/tools/cargo/crates/cargo-platform/Cargo.toml
index 423cf491d..e7f22cf87 100644
--- a/src/tools/cargo/crates/cargo-platform/Cargo.toml
+++ b/src/tools/cargo/crates/cargo-platform/Cargo.toml
@@ -1,8 +1,8 @@
[package]
name = "cargo-platform"
-version = "0.1.3"
-edition = "2021"
-license = "MIT OR Apache-2.0"
+version = "0.1.4"
+edition.workspace = true
+license.workspace = true
homepage = "https://github.com/rust-lang/cargo"
repository = "https://github.com/rust-lang/cargo"
documentation = "https://docs.rs/cargo-platform"
diff --git a/src/tools/cargo/crates/cargo-test-macro/Cargo.toml b/src/tools/cargo/crates/cargo-test-macro/Cargo.toml
index e40602ae3..b5da0522f 100644
--- a/src/tools/cargo/crates/cargo-test-macro/Cargo.toml
+++ b/src/tools/cargo/crates/cargo-test-macro/Cargo.toml
@@ -1,8 +1,8 @@
[package]
name = "cargo-test-macro"
version = "0.1.0"
-edition = "2021"
-license = "MIT OR Apache-2.0"
+edition.workspace = true
+license.workspace = true
homepage = "https://github.com/rust-lang/cargo"
repository = "https://github.com/rust-lang/cargo"
documentation = "https://github.com/rust-lang/cargo"
diff --git a/src/tools/cargo/crates/cargo-test-support/Cargo.toml b/src/tools/cargo/crates/cargo-test-support/Cargo.toml
index 305c809a8..085041aff 100644
--- a/src/tools/cargo/crates/cargo-test-support/Cargo.toml
+++ b/src/tools/cargo/crates/cargo-test-support/Cargo.toml
@@ -1,8 +1,8 @@
[package]
name = "cargo-test-support"
version = "0.1.0"
-license = "MIT OR Apache-2.0"
-edition = "2021"
+license.workspace = true
+edition.workspace = true
publish = false
[lib]
diff --git a/src/tools/cargo/crates/cargo-test-support/containers/sshd/Dockerfile b/src/tools/cargo/crates/cargo-test-support/containers/sshd/Dockerfile
index b52eefbad..f25212770 100644
--- a/src/tools/cargo/crates/cargo-test-support/containers/sshd/Dockerfile
+++ b/src/tools/cargo/crates/cargo-test-support/containers/sshd/Dockerfile
@@ -1,4 +1,4 @@
-FROM alpine:3.17
+FROM alpine:3.18
RUN apk add --no-cache openssh git
RUN ssh-keygen -A
diff --git a/src/tools/cargo/crates/cargo-test-support/src/compare.rs b/src/tools/cargo/crates/cargo-test-support/src/compare.rs
index 96ce52afc..21eb64d28 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/compare.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/compare.rs
@@ -192,6 +192,7 @@ fn substitute_macros(input: &str) -> String {
("[CHECKING]", " Checking"),
("[COMPLETED]", " Completed"),
("[CREATED]", " Created"),
+ ("[CREDENTIAL]", " Credential"),
("[DOWNGRADING]", " Downgrading"),
("[FINISHED]", " Finished"),
("[ERROR]", "error:"),
diff --git a/src/tools/cargo/crates/cargo-test-support/src/registry.rs b/src/tools/cargo/crates/cargo-test-support/src/registry.rs
index 910f95bfa..27c319656 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/registry.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/registry.rs
@@ -104,6 +104,8 @@ pub struct RegistryBuilder {
not_found_handler: RequestCallback,
/// If nonzero, the git index update to be delayed by the given number of seconds.
delayed_index_update: usize,
+ /// Credential provider in configuration
+ credential_provider: Option<String>,
}
pub struct TestRegistry {
@@ -172,6 +174,7 @@ impl RegistryBuilder {
custom_responders: HashMap::new(),
not_found_handler: Box::new(not_found),
delayed_index_update: 0,
+ credential_provider: None,
}
}
@@ -266,6 +269,13 @@ impl RegistryBuilder {
self
}
+ /// The credential provider to configure for this registry.
+ #[must_use]
+ pub fn credential_provider(mut self, provider: &[&str]) -> Self {
+ self.credential_provider = Some(format!("['{}']", provider.join("','")));
+ self
+ }
+
/// Initializes the registry.
#[must_use]
pub fn build(self) -> TestRegistry {
@@ -336,6 +346,18 @@ impl RegistryBuilder {
.as_bytes(),
)
.unwrap();
+ if let Some(p) = &self.credential_provider {
+ append(
+ &config_path,
+ &format!(
+ "
+ credential-provider = {p}
+ "
+ )
+ .as_bytes(),
+ )
+ .unwrap()
+ }
} else {
append(
&config_path,
@@ -351,6 +373,20 @@ impl RegistryBuilder {
.as_bytes(),
)
.unwrap();
+
+ if let Some(p) = &self.credential_provider {
+ append(
+ &config_path,
+ &format!(
+ "
+ [registry]
+ credential-provider = {p}
+ "
+ )
+ .as_bytes(),
+ )
+ .unwrap()
+ }
}
}
diff --git a/src/tools/cargo/crates/cargo-util/Cargo.toml b/src/tools/cargo/crates/cargo-util/Cargo.toml
index 614581037..99a59422d 100644
--- a/src/tools/cargo/crates/cargo-util/Cargo.toml
+++ b/src/tools/cargo/crates/cargo-util/Cargo.toml
@@ -1,8 +1,8 @@
[package]
name = "cargo-util"
-version = "0.2.5"
-edition = "2021"
-license = "MIT OR Apache-2.0"
+version = "0.2.6"
+edition.workspace = true
+license.workspace = true
homepage = "https://github.com/rust-lang/cargo"
repository = "https://github.com/rust-lang/cargo"
description = "Miscellaneous support code used by Cargo."
@@ -14,10 +14,10 @@ filetime.workspace = true
hex.workspace = true
jobserver.workspace = true
libc.workspace = true
-log.workspace = true
same-file.workspace = true
shell-escape.workspace = true
tempfile.workspace = true
+tracing.workspace = true
walkdir.workspace = true
[target.'cfg(target_os = "macos")'.dependencies]
diff --git a/src/tools/cargo/crates/cargo-util/src/paths.rs b/src/tools/cargo/crates/cargo-util/src/paths.rs
index 4a917821b..ce6755859 100644
--- a/src/tools/cargo/crates/cargo-util/src/paths.rs
+++ b/src/tools/cargo/crates/cargo-util/src/paths.rs
@@ -237,7 +237,7 @@ pub fn mtime_recursive(path: &Path) -> Result<FileTime> {
Err(e) => {
// Ignore errors while walking. If Cargo can't access it, the
// build script probably can't access it, either.
- log::debug!("failed to determine mtime while walking directory: {}", e);
+ tracing::debug!("failed to determine mtime while walking directory: {}", e);
None
}
})
@@ -252,7 +252,7 @@ pub fn mtime_recursive(path: &Path) -> Result<FileTime> {
// I'm not sure when this is really possible (maybe a
// race with unlinking?). Regardless, if Cargo can't
// read it, the build script probably can't either.
- log::debug!(
+ tracing::debug!(
"failed to determine mtime while fetching symlink metadata of {}: {}",
e.path().display(),
err
@@ -271,7 +271,7 @@ pub fn mtime_recursive(path: &Path) -> Result<FileTime> {
// Can't access the symlink target. If Cargo can't
// access it, the build script probably can't access
// it either.
- log::debug!(
+ tracing::debug!(
"failed to determine mtime of symlink target for {}: {}",
e.path().display(),
err
@@ -286,7 +286,7 @@ pub fn mtime_recursive(path: &Path) -> Result<FileTime> {
// I'm not sure when this is really possible (maybe a
// race with unlinking?). Regardless, if Cargo can't
// read it, the build script probably can't either.
- log::debug!(
+ tracing::debug!(
"failed to determine mtime while fetching metadata of {}: {}",
e.path().display(),
err
@@ -314,7 +314,7 @@ pub fn set_invocation_time(path: &Path) -> Result<FileTime> {
"This file has an mtime of when this was started.",
)?;
let ft = mtime(&timestamp)?;
- log::debug!("invocation time for {:?} is {}", path, ft);
+ tracing::debug!("invocation time for {:?} is {}", path, ft);
Ok(ft)
}
@@ -508,7 +508,7 @@ pub fn link_or_copy(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> Result<()>
}
fn _link_or_copy(src: &Path, dst: &Path) -> Result<()> {
- log::debug!("linking {} to {}", src.display(), dst.display());
+ tracing::debug!("linking {} to {}", src.display(), dst.display());
if same_file::is_same_file(src, dst).unwrap_or(false) {
return Ok(());
}
@@ -567,7 +567,7 @@ fn _link_or_copy(src: &Path, dst: &Path) -> Result<()> {
};
link_result
.or_else(|err| {
- log::debug!("link failed {}. falling back to fs::copy", err);
+ tracing::debug!("link failed {}. falling back to fs::copy", err);
fs::copy(src, dst).map(|_| ())
})
.with_context(|| {
@@ -598,8 +598,8 @@ pub fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<u64> {
pub fn set_file_time_no_err<P: AsRef<Path>>(path: P, time: FileTime) {
let path = path.as_ref();
match filetime::set_file_times(path, time, time) {
- Ok(()) => log::debug!("set file mtime {} to {}", path.display(), time),
- Err(e) => log::warn!(
+ Ok(()) => tracing::debug!("set file mtime {} to {}", path.display(), time),
+ Err(e) => tracing::warn!(
"could not set mtime of {} to {}: {:?}",
path.display(),
time,
@@ -621,7 +621,7 @@ pub fn strip_prefix_canonical<P: AsRef<Path>>(
let safe_canonicalize = |path: &Path| match path.canonicalize() {
Ok(p) => p,
Err(e) => {
- log::warn!("cannot canonicalize {:?}: {:?}", path, e);
+ tracing::warn!("cannot canonicalize {:?}: {:?}", path, e);
path.to_path_buf()
}
};
diff --git a/src/tools/cargo/crates/cargo-util/src/process_builder.rs b/src/tools/cargo/crates/cargo-util/src/process_builder.rs
index 76392f256..b197b95b1 100644
--- a/src/tools/cargo/crates/cargo-util/src/process_builder.rs
+++ b/src/tools/cargo/crates/cargo-util/src/process_builder.rs
@@ -449,7 +449,7 @@ impl ProcessBuilder {
arg.push(tmp.path());
let mut cmd = self.build_command_without_args();
cmd.arg(arg);
- log::debug!("created argfile at {} for {self}", tmp.path().display());
+ tracing::debug!("created argfile at {} for {self}", tmp.path().display());
let cap = self.get_args().map(|arg| arg.len() + 1).sum::<usize>();
let mut buf = Vec::with_capacity(cap);
@@ -558,7 +558,7 @@ fn piped(cmd: &mut Command, pipe_stdin: bool) -> &mut Command {
fn close_tempfile_and_log_error(file: NamedTempFile) {
file.close().unwrap_or_else(|e| {
- log::warn!("failed to close temporary file: {e}");
+ tracing::warn!("failed to close temporary file: {e}");
});
}
diff --git a/src/tools/cargo/crates/crates-io/Cargo.toml b/src/tools/cargo/crates/crates-io/Cargo.toml
index 034c2fca5..139b8aa97 100644
--- a/src/tools/cargo/crates/crates-io/Cargo.toml
+++ b/src/tools/cargo/crates/crates-io/Cargo.toml
@@ -1,8 +1,8 @@
[package]
name = "crates-io"
-version = "0.37.0"
-edition = "2021"
-license = "MIT OR Apache-2.0"
+version = "0.38.0"
+edition.workspace = true
+license.workspace = true
repository = "https://github.com/rust-lang/cargo"
description = """
Helpers for interacting with crates.io
@@ -13,9 +13,9 @@ name = "crates_io"
path = "lib.rs"
[dependencies]
-anyhow.workspace = true
curl.workspace = true
percent-encoding.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
+thiserror.workspace = true
url.workspace = true
diff --git a/src/tools/cargo/crates/crates-io/lib.rs b/src/tools/cargo/crates/crates-io/lib.rs
index 243808098..6ce39cefd 100644
--- a/src/tools/cargo/crates/crates-io/lib.rs
+++ b/src/tools/cargo/crates/crates-io/lib.rs
@@ -1,18 +1,18 @@
#![allow(clippy::all)]
use std::collections::BTreeMap;
-use std::fmt;
use std::fs::File;
use std::io::prelude::*;
use std::io::{Cursor, SeekFrom};
use std::time::Instant;
-use anyhow::{bail, format_err, Context, Result};
use curl::easy::{Easy, List};
use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
use serde::{Deserialize, Serialize};
use url::Url;
+pub type Result<T> = std::result::Result<T, Error>;
+
pub struct Registry {
/// The base URL for issuing API requests.
host: String,
@@ -125,67 +125,62 @@ struct Crates {
meta: TotalCrates,
}
-#[derive(Debug)]
-pub enum ResponseError {
- Curl(curl::Error),
+/// Error returned when interacting with a registry.
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+ /// Error from libcurl.
+ #[error(transparent)]
+ Curl(#[from] curl::Error),
+
+ /// Error from seriailzing the request payload and deserialzing the
+ /// response body (like response body didn't match expected structure).
+ #[error(transparent)]
+ Json(#[from] serde_json::Error),
+
+ /// Error from IO. Mostly from reading the tarball to upload.
+ #[error("failed to seek tarball")]
+ Io(#[from] std::io::Error),
+
+ /// Response body was not valid utf8.
+ #[error("invalid response body from server")]
+ Utf8(#[from] std::string::FromUtf8Error),
+
+ /// Error from API response containing JSON field `errors.details`.
+ #[error(
+ "the remote server responded with an error{}: {}",
+ status(*code),
+ errors.join(", "),
+ )]
Api {
code: u32,
+ headers: Vec<String>,
errors: Vec<String>,
},
+
+ /// Error from API response which didn't have pre-programmed `errors.details`.
+ #[error(
+ "failed to get a 200 OK response, got {code}\nheaders:\n\t{}\nbody:\n{body}",
+ headers.join("\n\t"),
+ )]
Code {
code: u32,
headers: Vec<String>,
body: String,
},
- Other(anyhow::Error),
-}
-
-impl std::error::Error for ResponseError {
- fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
- match self {
- ResponseError::Curl(..) => None,
- ResponseError::Api { .. } => None,
- ResponseError::Code { .. } => None,
- ResponseError::Other(e) => Some(e.as_ref()),
- }
- }
-}
-impl fmt::Display for ResponseError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- ResponseError::Curl(e) => write!(f, "{}", e),
- ResponseError::Api { code, errors } => {
- f.write_str("the remote server responded with an error")?;
- if *code != 200 {
- write!(f, " (status {} {})", code, reason(*code))?;
- };
- write!(f, ": {}", errors.join(", "))
- }
- ResponseError::Code {
- code,
- headers,
- body,
- } => write!(
- f,
- "failed to get a 200 OK response, got {}\n\
- headers:\n\
- \t{}\n\
- body:\n\
- {}",
- code,
- headers.join("\n\t"),
- body
- ),
- ResponseError::Other(..) => write!(f, "invalid response from server"),
- }
- }
-}
-
-impl From<curl::Error> for ResponseError {
- fn from(error: curl::Error) -> Self {
- ResponseError::Curl(error)
- }
+ /// Reason why the token was invalid.
+ #[error("{0}")]
+ InvalidToken(&'static str),
+
+ /// Server was unavailable and timeouted. Happened when uploading a way
+ /// too large tarball to crates.io.
+ #[error(
+ "Request timed out after 30 seconds. If you're trying to \
+ upload a crate it may be too large. If the crate is under \
+ 10MB in size, you can email help@crates.io for assistance.\n\
+ Total size was {0}."
+ )]
+ Timeout(u64),
}
impl Registry {
@@ -221,10 +216,9 @@ impl Registry {
}
fn token(&self) -> Result<&str> {
- let token = match self.token.as_ref() {
- Some(s) => s,
- None => bail!("no upload token found, please run `cargo login`"),
- };
+ let token = self.token.as_ref().ok_or_else(|| {
+ Error::InvalidToken("no upload token found, please run `cargo login`")
+ })?;
check_token(token)?;
Ok(token)
}
@@ -270,12 +264,8 @@ impl Registry {
// This checks the length using seeking instead of metadata, because
// on some filesystems, getting the metadata will fail because
// the file was renamed in ops::package.
- let tarball_len = tarball
- .seek(SeekFrom::End(0))
- .with_context(|| "failed to seek tarball")?;
- tarball
- .seek(SeekFrom::Start(0))
- .with_context(|| "failed to seek tarball")?;
+ let tarball_len = tarball.seek(SeekFrom::End(0))?;
+ tarball.seek(SeekFrom::Start(0))?;
let header = {
let mut w = Vec::new();
w.extend(&(json.len() as u32).to_le_bytes());
@@ -300,18 +290,12 @@ impl Registry {
let body = self
.handle(&mut |buf| body.read(buf).unwrap_or(0))
.map_err(|e| match e {
- ResponseError::Code { code, .. }
+ Error::Code { code, .. }
if code == 503
&& started.elapsed().as_secs() >= 29
&& self.host_is_crates_io() =>
{
- format_err!(
- "Request timed out after 30 seconds. If you're trying to \
- upload a crate it may be too large. If the crate is under \
- 10MB in size, you can email help@crates.io for assistance.\n\
- Total size was {}.",
- tarball_len
- )
+ Error::Timeout(tarball_len)
}
_ => e.into(),
})?;
@@ -410,10 +394,7 @@ impl Registry {
}
}
- fn handle(
- &mut self,
- read: &mut dyn FnMut(&mut [u8]) -> usize,
- ) -> std::result::Result<String, ResponseError> {
+ fn handle(&mut self, read: &mut dyn FnMut(&mut [u8]) -> usize) -> Result<String> {
let mut headers = Vec::new();
let mut body = Vec::new();
{
@@ -427,28 +408,29 @@ impl Registry {
// Headers contain trailing \r\n, trim them to make it easier
// to work with.
let s = String::from_utf8_lossy(data).trim().to_string();
+ // Don't let server sneak extra lines anywhere.
+ if s.contains('\n') {
+ return true;
+ }
headers.push(s);
true
})?;
handle.perform()?;
}
- let body = match String::from_utf8(body) {
- Ok(body) => body,
- Err(..) => {
- return Err(ResponseError::Other(format_err!(
- "response body was not valid utf-8"
- )))
- }
- };
+ let body = String::from_utf8(body)?;
let errors = serde_json::from_str::<ApiErrorList>(&body)
.ok()
.map(|s| s.errors.into_iter().map(|s| s.detail).collect::<Vec<_>>());
match (self.handle.response_code()?, errors) {
(0, None) | (200, None) => Ok(body),
- (code, Some(errors)) => Err(ResponseError::Api { code, errors }),
- (code, None) => Err(ResponseError::Code {
+ (code, Some(errors)) => Err(Error::Api {
+ code,
+ headers,
+ errors,
+ }),
+ (code, None) => Err(Error::Code {
code,
headers,
body,
@@ -457,6 +439,15 @@ impl Registry {
}
}
+fn status(code: u32) -> String {
+ if code == 200 {
+ String::new()
+ } else {
+ let reason = reason(code);
+ format!(" (status {code} {reason})")
+ }
+}
+
fn reason(code: u32) -> &'static str {
// Taken from https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
match code {
@@ -520,7 +511,7 @@ pub fn is_url_crates_io(url: &str) -> bool {
/// registries only create tokens in that format so that is as less restricted as possible.
pub fn check_token(token: &str) -> Result<()> {
if token.is_empty() {
- bail!("please provide a non-empty token");
+ return Err(Error::InvalidToken("please provide a non-empty token"));
}
if token.bytes().all(|b| {
// This is essentially the US-ASCII limitation of
@@ -531,9 +522,9 @@ pub fn check_token(token: &str) -> Result<()> {
}) {
Ok(())
} else {
- Err(anyhow::anyhow!(
+ Err(Error::InvalidToken(
"token contains invalid characters.\nOnly printable ISO-8859-1 characters \
- are allowed as it is sent in a HTTPS header."
+ are allowed as it is sent in a HTTPS header.",
))
}
}
diff --git a/src/tools/cargo/crates/home/Cargo.toml b/src/tools/cargo/crates/home/Cargo.toml
index 6c65ecc18..03bd555a2 100644
--- a/src/tools/cargo/crates/home/Cargo.toml
+++ b/src/tools/cargo/crates/home/Cargo.toml
@@ -1,9 +1,9 @@
[package]
name = "home"
-version = "0.5.6" # also update `html_root_url` in `src/lib.rs`
+version = "0.5.7" # also update `html_root_url` in `src/lib.rs`
authors = ["Brian Anderson <andersrb@gmail.com>"]
documentation = "https://docs.rs/home"
-edition = "2018"
+edition.workspace = true
include = [
"/src",
"/Cargo.toml",
@@ -11,8 +11,7 @@ include = [
"/LICENSE-*",
"/README.md",
]
-license = "MIT OR Apache-2.0"
-readme = "README.md"
+license.workspace = true
repository = "https://github.com/rust-lang/cargo"
description = "Shared definitions of home directories."
diff --git a/src/tools/cargo/crates/mdman/Cargo.toml b/src/tools/cargo/crates/mdman/Cargo.toml
index 812f1393a..ba1d4b462 100644
--- a/src/tools/cargo/crates/mdman/Cargo.toml
+++ b/src/tools/cargo/crates/mdman/Cargo.toml
@@ -1,8 +1,8 @@
[package]
name = "mdman"
version = "0.0.0"
-edition = "2021"
-license = "MIT OR Apache-2.0"
+edition.workspace = true
+license.workspace = true
description = "Creates a man page page from markdown."
publish = false
diff --git a/src/tools/cargo/crates/resolver-tests/Cargo.toml b/src/tools/cargo/crates/resolver-tests/Cargo.toml
index e0efb9b6d..5e69d7367 100644
--- a/src/tools/cargo/crates/resolver-tests/Cargo.toml
+++ b/src/tools/cargo/crates/resolver-tests/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "resolver-tests"
version = "0.0.0"
-edition = "2018"
+edition.workspace = true
publish = false
[dependencies]
diff --git a/src/tools/cargo/crates/semver-check/Cargo.toml b/src/tools/cargo/crates/semver-check/Cargo.toml
index f7b8c7d48..17e696566 100644
--- a/src/tools/cargo/crates/semver-check/Cargo.toml
+++ b/src/tools/cargo/crates/semver-check/Cargo.toml
@@ -2,7 +2,7 @@
name = "semver-check"
version = "0.0.0"
authors = ["Eric Huss"]
-edition = "2021"
+edition.workspace = true
publish = false
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
diff --git a/src/tools/cargo/crates/semver-check/src/main.rs b/src/tools/cargo/crates/semver-check/src/main.rs
index fa4639eb7..1ba405f57 100644
--- a/src/tools/cargo/crates/semver-check/src/main.rs
+++ b/src/tools/cargo/crates/semver-check/src/main.rs
@@ -7,6 +7,11 @@
//! An example with the word "MINOR" at the top is expected to successfully
//! build against the before and after. Otherwise it should fail. A comment of
//! "// Error:" will check that the given message appears in the error output.
+//!
+//! The code block can also include the annotations:
+//! - `run-fail`: The test should fail at runtime, not compiletime.
+//! - `dont-deny`: By default tests have a `#![deny(warnings)]`. This option
+//! avoids this attribute. Note that `#![allow(unused)]` is always added.
use std::error::Error;
use std::fs;
@@ -57,7 +62,13 @@ fn doit() -> Result<(), Box<dyn Error>> {
if line.trim() == "```" {
break;
}
- block.push(line);
+ // Support rustdoc/mdbook hidden lines.
+ let line = line.strip_prefix("# ").unwrap_or(line);
+ if line == "#" {
+ block.push("");
+ } else {
+ block.push(line);
+ }
}
None => {
return Err(format!(
diff --git a/src/tools/cargo/crates/xtask-build-man/Cargo.toml b/src/tools/cargo/crates/xtask-build-man/Cargo.toml
index 6d02aa2c3..bec10c48c 100644
--- a/src/tools/cargo/crates/xtask-build-man/Cargo.toml
+++ b/src/tools/cargo/crates/xtask-build-man/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "xtask-build-man"
version = "0.0.0"
-edition = "2021"
+edition.workspace = true
publish = false
[dependencies]
diff --git a/src/tools/cargo/crates/xtask-bump-check/Cargo.toml b/src/tools/cargo/crates/xtask-bump-check/Cargo.toml
new file mode 100644
index 000000000..e965ad09e
--- /dev/null
+++ b/src/tools/cargo/crates/xtask-bump-check/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "xtask-bump-check"
+version = "0.0.0"
+edition.workspace = true
+publish = false
+
+[dependencies]
+anyhow.workspace = true
+cargo.workspace = true
+cargo-util.workspace = true
+clap.workspace = true
+git2.workspace = true
+tracing.workspace = true
+tracing-subscriber.workspace = true
diff --git a/src/tools/cargo/crates/xtask-unpublished/src/main.rs b/src/tools/cargo/crates/xtask-bump-check/src/main.rs
index 1942a3621..0461ab91a 100644
--- a/src/tools/cargo/crates/xtask-unpublished/src/main.rs
+++ b/src/tools/cargo/crates/xtask-bump-check/src/main.rs
@@ -1,7 +1,8 @@
mod xtask;
fn main() {
- env_logger::init_from_env("CARGO_LOG");
+ setup_logger();
+
let cli = xtask::cli();
let matches = cli.get_matches();
@@ -13,3 +14,14 @@ fn main() {
cargo::exit_with_error(e, &mut config.shell())
}
}
+
+// In sync with `src/bin/cargo/main.rs@setup_logger`.
+fn setup_logger() {
+ let env = tracing_subscriber::EnvFilter::from_env("CARGO_LOG");
+
+ tracing_subscriber::fmt()
+ .with_ansi(std::io::IsTerminal::is_terminal(&std::io::stderr()))
+ .with_writer(std::io::stderr)
+ .with_env_filter(env)
+ .init();
+}
diff --git a/src/tools/cargo/crates/xtask-bump-check/src/xtask.rs b/src/tools/cargo/crates/xtask-bump-check/src/xtask.rs
new file mode 100644
index 000000000..f89152331
--- /dev/null
+++ b/src/tools/cargo/crates/xtask-bump-check/src/xtask.rs
@@ -0,0 +1,423 @@
+//! ```text
+//! NAME
+//! xtask-bump-check
+//!
+//! SYNOPSIS
+//! xtask-bump-check --base-rev <REV> --head-rev <REV>
+//!
+//! DESCRIPTION
+//! Checks if there is any member got changed since a base commit
+//! but forgot to bump its version.
+//! ```
+
+use std::collections::HashMap;
+use std::fmt::Write;
+use std::fs;
+use std::task;
+
+use cargo::core::dependency::Dependency;
+use cargo::core::registry::PackageRegistry;
+use cargo::core::Package;
+use cargo::core::QueryKind;
+use cargo::core::Registry;
+use cargo::core::SourceId;
+use cargo::core::Workspace;
+use cargo::util::command_prelude::*;
+use cargo::util::ToSemver;
+use cargo::CargoResult;
+use cargo_util::ProcessBuilder;
+
+const UPSTREAM_BRANCH: &str = "master";
+const STATUS: &str = "BumpCheck";
+
+pub fn cli() -> clap::Command {
+ clap::Command::new("xtask-bump-check")
+ .arg(
+ opt(
+ "verbose",
+ "Use verbose output (-vv very verbose/build.rs output)",
+ )
+ .short('v')
+ .action(ArgAction::Count)
+ .global(true),
+ )
+ .arg_quiet()
+ .arg(
+ opt("color", "Coloring: auto, always, never")
+ .value_name("WHEN")
+ .global(true),
+ )
+ .arg(opt("base-rev", "Git revision to lookup for a baseline"))
+ .arg(opt("head-rev", "Git revision with changes"))
+ .arg(flag("frozen", "Require Cargo.lock and cache are up to date").global(true))
+ .arg(flag("locked", "Require Cargo.lock is up to date").global(true))
+ .arg(flag("offline", "Run without accessing the network").global(true))
+ .arg(multi_opt("config", "KEY=VALUE", "Override a configuration value").global(true))
+ .arg(
+ Arg::new("unstable-features")
+ .help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details")
+ .short('Z')
+ .value_name("FLAG")
+ .action(ArgAction::Append)
+ .global(true),
+ )
+}
+
+pub fn exec(args: &clap::ArgMatches, config: &mut cargo::util::Config) -> cargo::CliResult {
+ config_configure(config, args)?;
+
+ bump_check(args, config)?;
+
+ Ok(())
+}
+
+fn config_configure(config: &mut Config, args: &ArgMatches) -> CliResult {
+ let verbose = args.verbose();
+ // quiet is unusual because it is redefined in some subcommands in order
+ // to provide custom help text.
+ let quiet = args.flag("quiet");
+ let color = args.get_one::<String>("color").map(String::as_str);
+ let frozen = args.flag("frozen");
+ let locked = args.flag("locked");
+ let offline = args.flag("offline");
+ let mut unstable_flags = vec![];
+ if let Some(values) = args.get_many::<String>("unstable-features") {
+ unstable_flags.extend(values.cloned());
+ }
+ let mut config_args = vec![];
+ if let Some(values) = args.get_many::<String>("config") {
+ config_args.extend(values.cloned());
+ }
+ config.configure(
+ verbose,
+ quiet,
+ color,
+ frozen,
+ locked,
+ offline,
+ &None,
+ &unstable_flags,
+ &config_args,
+ )?;
+ Ok(())
+}
+
+/// Main entry of `xtask-bump-check`.
+///
+/// Assumption: version number are incremental. We never have point release for old versions.
+fn bump_check(args: &clap::ArgMatches, config: &mut cargo::util::Config) -> CargoResult<()> {
+ let ws = args.workspace(config)?;
+ let repo = git2::Repository::open(ws.root())?;
+ let base_commit = get_base_commit(config, args, &repo)?;
+ let head_commit = get_head_commit(args, &repo)?;
+ let referenced_commit = get_referenced_commit(&repo, &base_commit)?;
+ let changed_members = changed(&ws, &repo, &base_commit, &head_commit)?;
+ let status = |msg: &str| config.shell().status(STATUS, msg);
+
+ status(&format!("base commit `{}`", base_commit.id()))?;
+ status(&format!("head commit `{}`", head_commit.id()))?;
+
+ let mut needs_bump = Vec::new();
+
+ check_crates_io(config, &changed_members, &mut needs_bump)?;
+
+ if let Some(referenced_commit) = referenced_commit.as_ref() {
+ status(&format!("compare against `{}`", referenced_commit.id()))?;
+ for referenced_member in checkout_ws(&ws, &repo, referenced_commit)?.members() {
+ let pkg_name = referenced_member.name().as_str();
+ let Some(changed_member) = changed_members.get(pkg_name) else {
+ tracing::trace!("skipping {pkg_name}, may be removed or not published");
+ continue;
+ };
+
+ if changed_member.version() <= referenced_member.version() {
+ needs_bump.push(*changed_member);
+ }
+ }
+ }
+
+ if !needs_bump.is_empty() {
+ needs_bump.sort();
+ needs_bump.dedup();
+ let mut msg = String::new();
+ msg.push_str("Detected changes in these crates but no version bump found:\n");
+ for pkg in needs_bump {
+ writeln!(&mut msg, " {}@{}", pkg.name(), pkg.version())?;
+ }
+ msg.push_str("\nPlease bump at least one patch version in each corresponding Cargo.toml.");
+ anyhow::bail!(msg)
+ }
+
+ // Tracked by https://github.com/obi1kenobi/cargo-semver-checks/issues/511
+ let exclude_args = [
+ "--exclude",
+ "cargo-credential-1password",
+ "--exclude",
+ "cargo-credential-libsecret",
+ "--exclude",
+ "cargo-credential-macos-keychain",
+ "--exclude",
+ "cargo-credential-wincred",
+ ];
+
+ // Even when we test against baseline-rev, we still need to make sure a
+ // change doesn't violate SemVer rules aginst crates.io releases. The
+ // possibility of this happening is nearly zero but no harm to check twice.
+ let mut cmd = ProcessBuilder::new("cargo");
+ cmd.arg("semver-checks")
+ .arg("check-release")
+ .arg("--workspace")
+ .args(&exclude_args);
+ config.shell().status("Running", &cmd)?;
+ cmd.exec()?;
+
+ if let Some(referenced_commit) = referenced_commit.as_ref() {
+ let mut cmd = ProcessBuilder::new("cargo");
+ cmd.arg("semver-checks")
+ .arg("--workspace")
+ .arg("--baseline-rev")
+ .arg(referenced_commit.id().to_string())
+ .args(&exclude_args);
+ config.shell().status("Running", &cmd)?;
+ cmd.exec()?;
+ }
+
+ status("no version bump needed for member crates.")?;
+
+ return Ok(());
+}
+
+/// Returns the commit of upstream `master` branch if `base-rev` is missing.
+fn get_base_commit<'a>(
+ config: &Config,
+ args: &clap::ArgMatches,
+ repo: &'a git2::Repository,
+) -> CargoResult<git2::Commit<'a>> {
+ let base_commit = match args.get_one::<String>("base-rev") {
+ Some(sha) => {
+ let obj = repo.revparse_single(sha)?;
+ obj.peel_to_commit()?
+ }
+ None => {
+ let upstream_branches = repo
+ .branches(Some(git2::BranchType::Remote))?
+ .filter_map(|r| r.ok())
+ .filter(|(b, _)| {
+ b.name()
+ .ok()
+ .flatten()
+ .unwrap_or_default()
+ .ends_with(&format!("/{UPSTREAM_BRANCH}"))
+ })
+ .map(|(b, _)| b)
+ .collect::<Vec<_>>();
+ if upstream_branches.is_empty() {
+ anyhow::bail!(
+ "could not find `base-sha` for `{UPSTREAM_BRANCH}`, pass it in directly"
+ );
+ }
+ let upstream_ref = upstream_branches[0].get();
+ if upstream_branches.len() > 1 {
+ let name = upstream_ref.name().expect("name is valid UTF-8");
+ let _ = config.shell().warn(format!(
+ "multiple `{UPSTREAM_BRANCH}` found, picking {name}"
+ ));
+ }
+ upstream_ref.peel_to_commit()?
+ }
+ };
+ Ok(base_commit)
+}
+
+/// Returns `HEAD` of the Git repository if `head-rev` is missing.
+fn get_head_commit<'a>(
+ args: &clap::ArgMatches,
+ repo: &'a git2::Repository,
+) -> CargoResult<git2::Commit<'a>> {
+ let head_commit = match args.get_one::<String>("head-rev") {
+ Some(sha) => {
+ let head_obj = repo.revparse_single(sha)?;
+ head_obj.peel_to_commit()?
+ }
+ None => {
+ let head_ref = repo.head()?;
+ head_ref.peel_to_commit()?
+ }
+ };
+ Ok(head_commit)
+}
+
+/// Gets the referenced commit to compare if version bump needed.
+///
+/// * When merging into nightly, check the version with beta branch
+/// * When merging into beta, check the version with stable branch
+/// * When merging into stable, check against crates.io registry directly
+fn get_referenced_commit<'a>(
+ repo: &'a git2::Repository,
+ base: &git2::Commit<'a>,
+) -> CargoResult<Option<git2::Commit<'a>>> {
+ let [beta, stable] = beta_and_stable_branch(&repo)?;
+ let rev_id = base.id();
+ let stable_commit = stable.get().peel_to_commit()?;
+ let beta_commit = beta.get().peel_to_commit()?;
+
+ let referenced_commit = if rev_id == stable_commit.id() {
+ None
+ } else if rev_id == beta_commit.id() {
+ tracing::trace!("stable branch from `{}`", stable.name().unwrap().unwrap());
+ Some(stable_commit)
+ } else {
+ tracing::trace!("beta branch from `{}`", beta.name().unwrap().unwrap());
+ Some(beta_commit)
+ };
+
+ Ok(referenced_commit)
+}
+
+/// Get the current beta and stable branch in cargo repository.
+///
+/// Assumptions:
+///
+/// * The repository contains the full history of `<remote>/rust-1.*.0` branches.
+/// * The version part of `<remote>/rust-1.*.0` always ends with a zero.
+/// * The maximum version is for beta channel, and the second one is for stable.
+fn beta_and_stable_branch(repo: &git2::Repository) -> CargoResult<[git2::Branch<'_>; 2]> {
+ let mut release_branches = Vec::new();
+ for branch in repo.branches(Some(git2::BranchType::Remote))? {
+ let (branch, _) = branch?;
+ let name = branch.name()?.unwrap();
+ let Some((_, version)) = name.split_once("/rust-") else {
+ tracing::trace!("branch `{name}` is not in the format of `<remote>/rust-<semver>`");
+ continue;
+ };
+ let Ok(version) = version.to_semver() else {
+ tracing::trace!("branch `{name}` is not a valid semver: `{version}`");
+ continue;
+ };
+ release_branches.push((version, branch));
+ }
+ release_branches.sort_unstable_by(|a, b| a.0.cmp(&b.0));
+ release_branches.dedup_by(|a, b| a.0 == b.0);
+
+ let beta = release_branches.pop().unwrap();
+ let stable = release_branches.pop().unwrap();
+
+ assert_eq!(beta.0.major, 1);
+ assert_eq!(beta.0.patch, 0);
+ assert_eq!(stable.0.major, 1);
+ assert_eq!(stable.0.patch, 0);
+ assert_ne!(beta.0.minor, stable.0.minor);
+
+ Ok([beta.1, stable.1])
+}
+
+/// Lists all changed workspace members between two commits.
+fn changed<'r, 'ws>(
+ ws: &'ws Workspace<'_>,
+ repo: &'r git2::Repository,
+ base_commit: &git2::Commit<'r>,
+ head: &git2::Commit<'r>,
+) -> CargoResult<HashMap<&'ws str, &'ws Package>> {
+ let root_pkg_name = ws.current()?.name(); // `cargo` crate.
+ let ws_members = ws
+ .members()
+ .filter(|pkg| pkg.name() != root_pkg_name) // Only take care of sub crates here.
+ .filter(|pkg| pkg.publish() != &Some(vec![])) // filter out `publish = false`
+ .map(|pkg| {
+ // Having relative package root path so that we can compare with
+ // paths of changed files to determine which package has changed.
+ let relative_pkg_root = pkg.root().strip_prefix(ws.root()).unwrap();
+ (relative_pkg_root, pkg)
+ })
+ .collect::<Vec<_>>();
+ let base_tree = base_commit.as_object().peel_to_tree()?;
+ let head_tree = head.as_object().peel_to_tree()?;
+ let diff = repo.diff_tree_to_tree(Some(&base_tree), Some(&head_tree), Default::default())?;
+
+ let mut changed_members = HashMap::new();
+
+ for delta in diff.deltas() {
+ let old = delta.old_file().path().unwrap();
+ let new = delta.new_file().path().unwrap();
+ for (ref pkg_root, pkg) in ws_members.iter() {
+ if old.starts_with(pkg_root) || new.starts_with(pkg_root) {
+ changed_members.insert(pkg.name().as_str(), *pkg);
+ break;
+ }
+ }
+ }
+
+ tracing::trace!("changed_members: {:?}", changed_members.keys());
+ Ok(changed_members)
+}
+
+/// Compares version against published crates on crates.io.
+///
+/// Assumption: We always release a version larger than all existing versions.
+fn check_crates_io<'a>(
+ config: &Config,
+ changed_members: &HashMap<&'a str, &'a Package>,
+ needs_bump: &mut Vec<&'a Package>,
+) -> CargoResult<()> {
+ let source_id = SourceId::crates_io(config)?;
+ let mut registry = PackageRegistry::new(config)?;
+ let _lock = config.acquire_package_cache_lock()?;
+ registry.lock_patches();
+ config.shell().status(
+ STATUS,
+ format_args!("compare against `{}`", source_id.display_registry_name()),
+ )?;
+ for (name, member) in changed_members {
+ let current = member.version();
+ let version_req = format!(">={current}");
+ let query = Dependency::parse(*name, Some(&version_req), source_id)?;
+ let possibilities = loop {
+ // Exact to avoid returning all for path/git
+ match registry.query_vec(&query, QueryKind::Exact) {
+ task::Poll::Ready(res) => {
+ break res?;
+ }
+ task::Poll::Pending => registry.block_until_ready()?,
+ }
+ };
+ if possibilities.is_empty() {
+ tracing::trace!("dep `{name}` has no version greater than or equal to `{current}`");
+ } else {
+ tracing::trace!(
+ "`{name}@{current}` needs a bump because its should have a version newer than crates.io: {:?}`",
+ possibilities
+ .iter()
+ .map(|s| format!("{}@{}", s.name(), s.version()))
+ .collect::<Vec<_>>(),
+ );
+ needs_bump.push(member);
+ }
+ }
+
+ Ok(())
+}
+
+/// Checkouts a temporary workspace to do further version comparsions.
+fn checkout_ws<'cfg, 'a>(
+ ws: &Workspace<'cfg>,
+ repo: &'a git2::Repository,
+ referenced_commit: &git2::Commit<'a>,
+) -> CargoResult<Workspace<'cfg>> {
+ let repo_path = repo.path().as_os_str().to_str().unwrap();
+ // Put it under `target/cargo-<short-id>`
+ let short_id = &referenced_commit.id().to_string()[..7];
+ let checkout_path = ws.target_dir().join(format!("cargo-{short_id}"));
+ let checkout_path = checkout_path.as_path_unlocked();
+ let _ = fs::remove_dir_all(checkout_path);
+ let new_repo = git2::build::RepoBuilder::new()
+ .clone_local(git2::build::CloneLocal::Local)
+ .clone(repo_path, checkout_path)?;
+ let obj = new_repo.find_object(referenced_commit.id(), None)?;
+ new_repo.reset(&obj, git2::ResetType::Hard, None)?;
+ Workspace::new(&checkout_path.join("Cargo.toml"), ws.config())
+}
+
+#[test]
+fn verify_cli() {
+ cli().debug_assert();
+}
diff --git a/src/tools/cargo/crates/xtask-stale-label/Cargo.toml b/src/tools/cargo/crates/xtask-stale-label/Cargo.toml
index af3218e96..b1f54a2f1 100644
--- a/src/tools/cargo/crates/xtask-stale-label/Cargo.toml
+++ b/src/tools/cargo/crates/xtask-stale-label/Cargo.toml
@@ -1,7 +1,7 @@
[package]
name = "xtask-stale-label"
version = "0.0.0"
-edition = "2021"
+edition.workspace = true
publish = false
[dependencies]
diff --git a/src/tools/cargo/crates/xtask-stale-label/src/main.rs b/src/tools/cargo/crates/xtask-stale-label/src/main.rs
index 37675979c..88c044b5b 100644
--- a/src/tools/cargo/crates/xtask-stale-label/src/main.rs
+++ b/src/tools/cargo/crates/xtask-stale-label/src/main.rs
@@ -34,7 +34,7 @@ fn main() {
for (label, value) in autolabel.iter() {
let Some(trigger_files) = value.get("trigger_files") else {
- continue
+ continue;
};
let trigger_files = trigger_files.as_array().expect("an array");
let missing_files: Vec<_> = trigger_files
diff --git a/src/tools/cargo/crates/xtask-unpublished/Cargo.toml b/src/tools/cargo/crates/xtask-unpublished/Cargo.toml
deleted file mode 100644
index 541a34dea..000000000
--- a/src/tools/cargo/crates/xtask-unpublished/Cargo.toml
+++ /dev/null
@@ -1,12 +0,0 @@
-[package]
-name = "xtask-unpublished"
-version = "0.0.0"
-edition = "2021"
-publish = false
-
-[dependencies]
-anyhow.workspace = true
-cargo.workspace = true
-clap.workspace = true
-env_logger.workspace = true
-log.workspace = true
diff --git a/src/tools/cargo/crates/xtask-unpublished/src/xtask.rs b/src/tools/cargo/crates/xtask-unpublished/src/xtask.rs
deleted file mode 100644
index f1086951f..000000000
--- a/src/tools/cargo/crates/xtask-unpublished/src/xtask.rs
+++ /dev/null
@@ -1,200 +0,0 @@
-//! `xtask-unpublished` outputs a table with publish status --- a local version
-//! and a version on crates.io for comparisons.
-//!
-//! This aims to help developers check if there is any crate required a new
-//! publish, as well as detect if a version bump is needed in CI pipeline.
-
-use std::collections::HashSet;
-
-use cargo::core::registry::PackageRegistry;
-use cargo::core::QueryKind;
-use cargo::core::Registry;
-use cargo::core::SourceId;
-use cargo::ops::Packages;
-use cargo::util::command_prelude::*;
-
-pub fn cli() -> clap::Command {
- clap::Command::new("xtask-unpublished")
- .arg_package_spec_simple("Package to inspect the published status")
- .arg(
- opt(
- "verbose",
- "Use verbose output (-vv very verbose/build.rs output)",
- )
- .short('v')
- .action(ArgAction::Count)
- .global(true),
- )
- .arg_quiet()
- .arg(
- opt("color", "Coloring: auto, always, never")
- .value_name("WHEN")
- .global(true),
- )
- .arg(flag("frozen", "Require Cargo.lock and cache are up to date").global(true))
- .arg(flag("locked", "Require Cargo.lock is up to date").global(true))
- .arg(flag("offline", "Run without accessing the network").global(true))
- .arg(multi_opt("config", "KEY=VALUE", "Override a configuration value").global(true))
- .arg(
- Arg::new("unstable-features")
- .help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details")
- .short('Z')
- .value_name("FLAG")
- .action(ArgAction::Append)
- .global(true),
- )
-}
-
-pub fn exec(args: &clap::ArgMatches, config: &mut cargo::util::Config) -> cargo::CliResult {
- config_configure(config, args)?;
-
- unpublished(args, config)?;
-
- Ok(())
-}
-
-fn config_configure(config: &mut Config, args: &ArgMatches) -> CliResult {
- let verbose = args.verbose();
- // quiet is unusual because it is redefined in some subcommands in order
- // to provide custom help text.
- let quiet = args.flag("quiet");
- let color = args.get_one::<String>("color").map(String::as_str);
- let frozen = args.flag("frozen");
- let locked = args.flag("locked");
- let offline = args.flag("offline");
- let mut unstable_flags = vec![];
- if let Some(values) = args.get_many::<String>("unstable-features") {
- unstable_flags.extend(values.cloned());
- }
- let mut config_args = vec![];
- if let Some(values) = args.get_many::<String>("config") {
- config_args.extend(values.cloned());
- }
- config.configure(
- verbose,
- quiet,
- color,
- frozen,
- locked,
- offline,
- &None,
- &unstable_flags,
- &config_args,
- )?;
- Ok(())
-}
-
-fn unpublished(args: &clap::ArgMatches, config: &mut cargo::util::Config) -> cargo::CliResult {
- let ws = args.workspace(config)?;
-
- let members_to_inspect: HashSet<_> = {
- let pkgs = args.packages_from_flags()?;
- if let Packages::Packages(_) = pkgs {
- HashSet::from_iter(pkgs.get_packages(&ws)?)
- } else {
- HashSet::from_iter(ws.members())
- }
- };
-
- let mut results = Vec::new();
- {
- let mut registry = PackageRegistry::new(config)?;
- let _lock = config.acquire_package_cache_lock()?;
- registry.lock_patches();
- let source_id = SourceId::crates_io(config)?;
-
- for member in members_to_inspect {
- let name = member.name();
- let current = member.version();
- if member.publish() == &Some(vec![]) {
- log::trace!("skipping {name}, `publish = false`");
- continue;
- }
-
- let version_req = format!("<={current}");
- let query =
- cargo::core::dependency::Dependency::parse(name, Some(&version_req), source_id)?;
- let possibilities = loop {
- // Exact to avoid returning all for path/git
- match registry.query_vec(&query, QueryKind::Exact) {
- std::task::Poll::Ready(res) => {
- break res?;
- }
- std::task::Poll::Pending => registry.block_until_ready()?,
- }
- };
- let (last, published) = possibilities
- .iter()
- .map(|s| s.version())
- .max()
- .map(|last| (last.to_string(), last == current))
- .unwrap_or(("-".to_string(), false));
-
- results.push(vec![
- name.to_string(),
- last,
- current.to_string(),
- if published { "yes" } else { "no" }.to_string(),
- ]);
- }
- }
- results.sort();
-
- if results.is_empty() {
- return Ok(());
- }
-
- results.insert(
- 0,
- vec![
- "name".to_owned(),
- "crates.io".to_owned(),
- "local".to_owned(),
- "published?".to_owned(),
- ],
- );
-
- output_table(results);
-
- Ok(())
-}
-
-/// Outputs a markdown table like this.
-///
-/// ```text
-/// | name | crates.io | local | published? |
-/// |------------------|-----------|--------|------------|
-/// | cargo | 0.70.1 | 0.72.0 | no |
-/// | cargo-platform | 0.1.2 | 0.1.2 | yes |
-/// | cargo-util | - | 0.2.4 | no |
-/// | crates-io | 0.36.0 | 0.36.0 | yes |
-/// | home | - | 0.5.6 | no |
-/// ```
-fn output_table(table: Vec<Vec<String>>) {
- let header = table.first().unwrap();
- let paddings = table.iter().fold(vec![0; header.len()], |mut widths, row| {
- for (width, field) in widths.iter_mut().zip(row) {
- *width = usize::max(*width, field.len());
- }
- widths
- });
-
- let print = |row: &[_]| {
- for (field, pad) in row.iter().zip(&paddings) {
- print!("| {field:pad$} ");
- }
- println!("|");
- };
-
- print(header);
-
- paddings.iter().for_each(|fill| print!("|-{:-<fill$}-", ""));
- println!("|");
-
- table.iter().skip(1).for_each(|r| print(r));
-}
-
-#[test]
-fn verify_cli() {
- cli().debug_assert();
-}
diff --git a/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml b/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml
index 8db40e577..a607e6da1 100644
--- a/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml
@@ -1,8 +1,8 @@
[package]
name = "cargo-credential-1password"
-version = "0.2.0"
-edition = "2021"
-license = "MIT OR Apache-2.0"
+version = "0.3.0"
+edition.workspace = true
+license.workspace = true
repository = "https://github.com/rust-lang/cargo"
description = "A Cargo credential process that stores tokens in a 1password vault."
diff --git a/src/tools/cargo/credential/cargo-credential-1password/src/main.rs b/src/tools/cargo/credential/cargo-credential-1password/src/main.rs
index 4f512b717..a2607fd2f 100644
--- a/src/tools/cargo/credential/cargo-credential-1password/src/main.rs
+++ b/src/tools/cargo/credential/cargo-credential-1password/src/main.rs
@@ -1,6 +1,8 @@
//! Cargo registry 1password credential process.
-use cargo_credential::{Credential, Error};
+use cargo_credential::{
+ Action, CacheControl, Credential, CredentialResponse, Error, RegistryInfo, Secret,
+};
use serde::Deserialize;
use std::io::Read;
use std::process::{Command, Stdio};
@@ -38,13 +40,13 @@ struct Url {
}
impl OnePasswordKeychain {
- fn new() -> Result<OnePasswordKeychain, Error> {
- let mut args = std::env::args().skip(1);
+ fn new(args: &[&str]) -> Result<OnePasswordKeychain, Error> {
+ let mut args = args.iter();
let mut action = false;
let mut account = None;
let mut vault = None;
while let Some(arg) = args.next() {
- match arg.as_str() {
+ match *arg {
"--account" => {
account = Some(args.next().ok_or("--account needs an arg")?);
}
@@ -63,7 +65,10 @@ impl OnePasswordKeychain {
}
}
}
- Ok(OnePasswordKeychain { account, vault })
+ Ok(OnePasswordKeychain {
+ account: account.map(|s| s.to_string()),
+ vault: vault.map(|s| s.to_string()),
+ })
}
fn signin(&self) -> Result<Option<String>, Error> {
@@ -73,9 +78,8 @@ impl OnePasswordKeychain {
return Ok(None);
}
let mut cmd = Command::new("op");
- cmd.args(&["signin", "--raw"]);
+ cmd.args(["signin", "--raw"]);
cmd.stdout(Stdio::piped());
- self.with_tty(&mut cmd)?;
let mut child = cmd
.spawn()
.map_err(|e| format!("failed to spawn `op`: {}", e))?;
@@ -121,19 +125,6 @@ impl OnePasswordKeychain {
cmd
}
- fn with_tty(&self, cmd: &mut Command) -> Result<(), Error> {
- #[cfg(unix)]
- const IN_DEVICE: &str = "/dev/tty";
- #[cfg(windows)]
- const IN_DEVICE: &str = "CONIN$";
- let stdin = std::fs::OpenOptions::new()
- .read(true)
- .write(true)
- .open(IN_DEVICE)?;
- cmd.stdin(stdin);
- Ok(())
- }
-
fn run_cmd(&self, mut cmd: Command) -> Result<String, Error> {
cmd.stdout(Stdio::piped());
let mut child = cmd
@@ -196,12 +187,12 @@ impl OnePasswordKeychain {
&self,
session: &Option<String>,
id: &str,
- token: &str,
+ token: Secret<&str>,
_name: Option<&str>,
) -> Result<(), Error> {
let cmd = self.make_cmd(
session,
- &["item", "edit", id, &format!("password={}", token)],
+ &["item", "edit", id, &format!("password={}", token.expose())],
);
self.run_cmd(cmd)?;
Ok(())
@@ -211,21 +202,21 @@ impl OnePasswordKeychain {
&self,
session: &Option<String>,
index_url: &str,
- token: &str,
+ token: Secret<&str>,
name: Option<&str>,
) -> Result<(), Error> {
let title = match name {
Some(name) => format!("Cargo registry token for {}", name),
None => "Cargo registry token".to_string(),
};
- let mut cmd = self.make_cmd(
+ let cmd = self.make_cmd(
session,
&[
"item",
"create",
"--category",
"Login",
- &format!("password={}", token),
+ &format!("password={}", token.expose()),
&format!("url={}", index_url),
"--title",
&title,
@@ -233,15 +224,11 @@ impl OnePasswordKeychain {
CARGO_TAG,
],
);
- // For unknown reasons, `op item create` seems to not be happy if
- // stdin is not a tty. Otherwise it returns with a 0 exit code without
- // doing anything.
- self.with_tty(&mut cmd)?;
self.run_cmd(cmd)?;
Ok(())
}
- fn get_token(&self, session: &Option<String>, id: &str) -> Result<String, Error> {
+ fn get_token(&self, session: &Option<String>, id: &str) -> Result<Secret<String>, Error> {
let cmd = self.make_cmd(session, &["item", "get", "--format=json", id]);
let buffer = self.run_cmd(cmd)?;
let item: Login = serde_json::from_str(&buffer)
@@ -250,7 +237,8 @@ impl OnePasswordKeychain {
match password {
Some(password) => password
.value
- .ok_or_else(|| format!("missing password value for entry").into()),
+ .map(Secret::from)
+ .ok_or("missing password value for entry".into()),
None => Err("could not find password field".into()),
}
}
@@ -262,53 +250,58 @@ impl OnePasswordKeychain {
}
}
-impl Credential for OnePasswordKeychain {
- fn name(&self) -> &'static str {
- env!("CARGO_PKG_NAME")
- }
-
- fn get(&self, index_url: &str) -> Result<String, Error> {
- let session = self.signin()?;
- if let Some(id) = self.search(&session, index_url)? {
- self.get_token(&session, &id)
- } else {
- return Err(format!(
- "no 1password entry found for registry `{}`, try `cargo login` to add a token",
- index_url
- )
- .into());
- }
- }
-
- fn store(&self, index_url: &str, token: &str, name: Option<&str>) -> Result<(), Error> {
- let session = self.signin()?;
- // Check if an item already exists.
- if let Some(id) = self.search(&session, index_url)? {
- self.modify(&session, &id, token, name)
- } else {
- self.create(&session, index_url, token, name)
- }
- }
+pub struct OnePasswordCredential {}
- fn erase(&self, index_url: &str) -> Result<(), Error> {
- let session = self.signin()?;
- // Check if an item already exists.
- if let Some(id) = self.search(&session, index_url)? {
- self.delete(&session, &id)?;
- } else {
- eprintln!("not currently logged in to `{}`", index_url);
+impl Credential for OnePasswordCredential {
+ fn perform(
+ &self,
+ registry: &RegistryInfo,
+ action: &Action,
+ args: &[&str],
+ ) -> Result<CredentialResponse, Error> {
+ let op = OnePasswordKeychain::new(args)?;
+ match action {
+ Action::Get(_) => {
+ let session = op.signin()?;
+ if let Some(id) = op.search(&session, registry.index_url)? {
+ op.get_token(&session, &id)
+ .map(|token| CredentialResponse::Get {
+ token,
+ cache: CacheControl::Session,
+ operation_independent: true,
+ })
+ } else {
+ Err(Error::NotFound)
+ }
+ }
+ Action::Login(options) => {
+ let session = op.signin()?;
+ // Check if an item already exists.
+ if let Some(id) = op.search(&session, registry.index_url)? {
+ eprintln!("note: token already exists for `{}`", registry.index_url);
+ let token = cargo_credential::read_token(options, registry)?;
+ op.modify(&session, &id, token.as_deref(), None)?;
+ } else {
+ let token = cargo_credential::read_token(options, registry)?;
+ op.create(&session, registry.index_url, token.as_deref(), None)?;
+ }
+ Ok(CredentialResponse::Login)
+ }
+ Action::Logout => {
+ let session = op.signin()?;
+ // Check if an item already exists.
+ if let Some(id) = op.search(&session, registry.index_url)? {
+ op.delete(&session, &id)?;
+ Ok(CredentialResponse::Logout)
+ } else {
+ Err(Error::NotFound)
+ }
+ }
+ _ => Err(Error::OperationNotSupported),
}
- Ok(())
}
}
fn main() {
- let op = match OnePasswordKeychain::new() {
- Ok(op) => op,
- Err(e) => {
- eprintln!("error: {}", e);
- std::process::exit(1);
- }
- };
- cargo_credential::main(op);
+ cargo_credential::main(OnePasswordCredential {});
}
diff --git a/src/tools/cargo/credential/cargo-credential-gnome-secret/build.rs b/src/tools/cargo/credential/cargo-credential-gnome-secret/build.rs
deleted file mode 100644
index 8bb86ee43..000000000
--- a/src/tools/cargo/credential/cargo-credential-gnome-secret/build.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-fn main() {
- if cfg!(target_os = "linux") {
- // TODO: Consider ignoring errors when libsecret is not installed and
- // switching the impl to UnsupportedCredential (possibly along with a
- // warning?).
- pkg_config::probe_library("libsecret-1").unwrap();
- }
-}
diff --git a/src/tools/cargo/credential/cargo-credential-gnome-secret/src/libsecret.rs b/src/tools/cargo/credential/cargo-credential-gnome-secret/src/libsecret.rs
deleted file mode 100644
index c584eeecf..000000000
--- a/src/tools/cargo/credential/cargo-credential-gnome-secret/src/libsecret.rs
+++ /dev/null
@@ -1,190 +0,0 @@
-//! Implementation of the libsecret credential helper.
-
-use cargo_credential::{Credential, Error};
-use std::ffi::{CStr, CString};
-use std::os::raw::{c_char, c_int};
-use std::ptr::{null, null_mut};
-
-#[allow(non_camel_case_types)]
-type gchar = c_char;
-
-#[allow(non_camel_case_types)]
-type gboolean = c_int;
-
-type GQuark = u32;
-
-#[repr(C)]
-struct GError {
- domain: GQuark,
- code: c_int,
- message: *mut gchar,
-}
-
-#[repr(C)]
-struct GCancellable {
- _private: [u8; 0],
-}
-
-#[repr(C)]
-struct SecretSchema {
- name: *const gchar,
- flags: SecretSchemaFlags,
- attributes: [SecretSchemaAttribute; 32],
-}
-
-#[repr(C)]
-#[derive(Copy, Clone)]
-struct SecretSchemaAttribute {
- name: *const gchar,
- attr_type: SecretSchemaAttributeType,
-}
-
-#[repr(C)]
-enum SecretSchemaFlags {
- None = 0,
-}
-
-#[repr(C)]
-#[derive(Copy, Clone)]
-enum SecretSchemaAttributeType {
- String = 0,
-}
-
-extern "C" {
- fn secret_password_store_sync(
- schema: *const SecretSchema,
- collection: *const gchar,
- label: *const gchar,
- password: *const gchar,
- cancellable: *mut GCancellable,
- error: *mut *mut GError,
- ...
- ) -> gboolean;
- fn secret_password_clear_sync(
- schema: *const SecretSchema,
- cancellable: *mut GCancellable,
- error: *mut *mut GError,
- ...
- ) -> gboolean;
- fn secret_password_lookup_sync(
- schema: *const SecretSchema,
- cancellable: *mut GCancellable,
- error: *mut *mut GError,
- ...
- ) -> *mut gchar;
-}
-
-pub struct GnomeSecret;
-
-fn label(index_url: &str) -> CString {
- CString::new(format!("cargo-registry:{}", index_url)).unwrap()
-}
-
-fn schema() -> SecretSchema {
- let mut attributes = [SecretSchemaAttribute {
- name: null(),
- attr_type: SecretSchemaAttributeType::String,
- }; 32];
- attributes[0] = SecretSchemaAttribute {
- name: b"url\0".as_ptr() as *const gchar,
- attr_type: SecretSchemaAttributeType::String,
- };
- SecretSchema {
- name: b"org.rust-lang.cargo.registry\0".as_ptr() as *const gchar,
- flags: SecretSchemaFlags::None,
- attributes,
- }
-}
-
-impl Credential for GnomeSecret {
- fn name(&self) -> &'static str {
- env!("CARGO_PKG_NAME")
- }
-
- fn get(&self, index_url: &str) -> Result<String, Error> {
- let mut error: *mut GError = null_mut();
- let attr_url = CString::new("url").unwrap();
- let index_url_c = CString::new(index_url).unwrap();
- let schema = schema();
- unsafe {
- let token_c = secret_password_lookup_sync(
- &schema,
- null_mut(),
- &mut error,
- attr_url.as_ptr(),
- index_url_c.as_ptr(),
- null() as *const gchar,
- );
- if !error.is_null() {
- return Err(format!(
- "failed to get token: {}",
- CStr::from_ptr((*error).message).to_str()?
- )
- .into());
- }
- if token_c.is_null() {
- return Err(format!("cannot find token for {}", index_url).into());
- }
- let token = CStr::from_ptr(token_c)
- .to_str()
- .map_err(|e| format!("expected utf8 token: {}", e))?
- .to_string();
- Ok(token)
- }
- }
-
- fn store(&self, index_url: &str, token: &str, name: Option<&str>) -> Result<(), Error> {
- let label = label(name.unwrap_or(index_url));
- let token = CString::new(token).unwrap();
- let mut error: *mut GError = null_mut();
- let attr_url = CString::new("url").unwrap();
- let index_url_c = CString::new(index_url).unwrap();
- let schema = schema();
- unsafe {
- secret_password_store_sync(
- &schema,
- b"default\0".as_ptr() as *const gchar,
- label.as_ptr(),
- token.as_ptr(),
- null_mut(),
- &mut error,
- attr_url.as_ptr(),
- index_url_c.as_ptr(),
- null() as *const gchar,
- );
- if !error.is_null() {
- return Err(format!(
- "failed to store token: {}",
- CStr::from_ptr((*error).message).to_str()?
- )
- .into());
- }
- }
- Ok(())
- }
-
- fn erase(&self, index_url: &str) -> Result<(), Error> {
- let schema = schema();
- let mut error: *mut GError = null_mut();
- let attr_url = CString::new("url").unwrap();
- let index_url_c = CString::new(index_url).unwrap();
- unsafe {
- secret_password_clear_sync(
- &schema,
- null_mut(),
- &mut error,
- attr_url.as_ptr(),
- index_url_c.as_ptr(),
- null() as *const gchar,
- );
- if !error.is_null() {
- return Err(format!(
- "failed to erase token: {}",
- CStr::from_ptr((*error).message).to_str()?
- )
- .into());
- }
- }
- Ok(())
- }
-}
diff --git a/src/tools/cargo/credential/cargo-credential-gnome-secret/src/main.rs b/src/tools/cargo/credential/cargo-credential-gnome-secret/src/main.rs
deleted file mode 100644
index 1d2ecc61f..000000000
--- a/src/tools/cargo/credential/cargo-credential-gnome-secret/src/main.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-//! Cargo registry gnome libsecret credential process.
-
-#[cfg(target_os = "linux")]
-mod libsecret;
-#[cfg(not(target_os = "linux"))]
-use cargo_credential::UnsupportedCredential as GnomeSecret;
-#[cfg(target_os = "linux")]
-use libsecret::GnomeSecret;
-
-fn main() {
- cargo_credential::main(GnomeSecret);
-}
diff --git a/src/tools/cargo/credential/cargo-credential-gnome-secret/Cargo.toml b/src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml
index 63b3e95cc..1bd4bb7d0 100644
--- a/src/tools/cargo/credential/cargo-credential-gnome-secret/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml
@@ -1,13 +1,12 @@
[package]
-name = "cargo-credential-gnome-secret"
-version = "0.2.0"
-edition = "2021"
-license = "MIT OR Apache-2.0"
+name = "cargo-credential-libsecret"
+version = "0.3.1"
+edition.workspace = true
+license.workspace = true
repository = "https://github.com/rust-lang/cargo"
description = "A Cargo credential process that stores tokens with GNOME libsecret."
[dependencies]
+anyhow.workspace = true
cargo-credential.workspace = true
-
-[build-dependencies]
-pkg-config.workspace = true
+libloading.workspace = true
diff --git a/src/tools/cargo/credential/cargo-credential-gnome-secret/README.md b/src/tools/cargo/credential/cargo-credential-libsecret/README.md
index 7a4b02838..f169323e0 100644
--- a/src/tools/cargo/credential/cargo-credential-gnome-secret/README.md
+++ b/src/tools/cargo/credential/cargo-credential-libsecret/README.md
@@ -1,4 +1,4 @@
-# cargo-credential-gnome-secret
+# cargo-credential-libsecret
This is the implementation for the Cargo credential helper for [GNOME libsecret].
See the [credential-process] documentation for how to use this.
diff --git a/src/tools/cargo/credential/cargo-credential-libsecret/src/lib.rs b/src/tools/cargo/credential/cargo-credential-libsecret/src/lib.rs
new file mode 100644
index 000000000..f83b424ee
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-libsecret/src/lib.rs
@@ -0,0 +1,235 @@
+#[cfg(target_os = "linux")]
+mod linux {
+ //! Implementation of the libsecret credential helper.
+
+ use anyhow::Context;
+ use cargo_credential::{
+ read_token, Action, CacheControl, Credential, CredentialResponse, Error, RegistryInfo,
+ Secret,
+ };
+ use libloading::{Library, Symbol};
+ use std::ffi::{CStr, CString};
+ use std::os::raw::{c_char, c_int};
+ use std::ptr::{null, null_mut};
+
+ #[allow(non_camel_case_types)]
+ type gchar = c_char;
+
+ #[allow(non_camel_case_types)]
+ type gboolean = c_int;
+
+ type GQuark = u32;
+
+ #[repr(C)]
+ struct GError {
+ domain: GQuark,
+ code: c_int,
+ message: *mut gchar,
+ }
+
+ #[repr(C)]
+ struct GCancellable {
+ _private: [u8; 0],
+ }
+
+ #[repr(C)]
+ struct SecretSchema {
+ name: *const gchar,
+ flags: SecretSchemaFlags,
+ attributes: [SecretSchemaAttribute; 32],
+ }
+
+ #[repr(C)]
+ #[derive(Copy, Clone)]
+ struct SecretSchemaAttribute {
+ name: *const gchar,
+ attr_type: SecretSchemaAttributeType,
+ }
+
+ #[repr(C)]
+ enum SecretSchemaFlags {
+ None = 0,
+ }
+
+ #[repr(C)]
+ #[derive(Copy, Clone)]
+ enum SecretSchemaAttributeType {
+ String = 0,
+ }
+
+ type SecretPasswordStoreSync = extern "C" fn(
+ schema: *const SecretSchema,
+ collection: *const gchar,
+ label: *const gchar,
+ password: *const gchar,
+ cancellable: *mut GCancellable,
+ error: *mut *mut GError,
+ ...
+ ) -> gboolean;
+ type SecretPasswordClearSync = extern "C" fn(
+ schema: *const SecretSchema,
+ cancellable: *mut GCancellable,
+ error: *mut *mut GError,
+ ...
+ ) -> gboolean;
+ type SecretPasswordLookupSync = extern "C" fn(
+ schema: *const SecretSchema,
+ cancellable: *mut GCancellable,
+ error: *mut *mut GError,
+ ...
+ ) -> *mut gchar;
+
+ pub struct LibSecretCredential;
+
+ fn label(index_url: &str) -> CString {
+ CString::new(format!("cargo-registry:{}", index_url)).unwrap()
+ }
+
+ fn schema() -> SecretSchema {
+ let mut attributes = [SecretSchemaAttribute {
+ name: null(),
+ attr_type: SecretSchemaAttributeType::String,
+ }; 32];
+ attributes[0] = SecretSchemaAttribute {
+ name: b"url\0".as_ptr() as *const gchar,
+ attr_type: SecretSchemaAttributeType::String,
+ };
+ SecretSchema {
+ name: b"org.rust-lang.cargo.registry\0".as_ptr() as *const gchar,
+ flags: SecretSchemaFlags::None,
+ attributes,
+ }
+ }
+
+ impl Credential for LibSecretCredential {
+ fn perform(
+ &self,
+ registry: &RegistryInfo,
+ action: &Action,
+ _args: &[&str],
+ ) -> Result<CredentialResponse, Error> {
+ // Dynamically load libsecret to avoid users needing to install
+ // additional -dev packages when building this provider.
+ let lib;
+ let secret_password_lookup_sync: Symbol<SecretPasswordLookupSync>;
+ let secret_password_store_sync: Symbol<SecretPasswordStoreSync>;
+ let secret_password_clear_sync: Symbol<SecretPasswordClearSync>;
+ unsafe {
+ lib = Library::new("libsecret-1.so").context(
+ "failed to load libsecret: try installing the `libsecret` \
+ or `libsecret-1-0` package with the system package manager",
+ )?;
+ secret_password_lookup_sync = lib
+ .get(b"secret_password_lookup_sync\0")
+ .map_err(Box::new)?;
+ secret_password_store_sync =
+ lib.get(b"secret_password_store_sync\0").map_err(Box::new)?;
+ secret_password_clear_sync =
+ lib.get(b"secret_password_clear_sync\0").map_err(Box::new)?;
+ }
+
+ let index_url_c = CString::new(registry.index_url).unwrap();
+ match action {
+ cargo_credential::Action::Get(_) => {
+ let mut error: *mut GError = null_mut();
+ let attr_url = CString::new("url").unwrap();
+ let schema = schema();
+ unsafe {
+ let token_c = secret_password_lookup_sync(
+ &schema,
+ null_mut(),
+ &mut error,
+ attr_url.as_ptr(),
+ index_url_c.as_ptr(),
+ null() as *const gchar,
+ );
+ if !error.is_null() {
+ return Err(format!(
+ "failed to get token: {}",
+ CStr::from_ptr((*error).message)
+ .to_str()
+ .unwrap_or_default()
+ )
+ .into());
+ }
+ if token_c.is_null() {
+ return Err(Error::NotFound);
+ }
+ let token = Secret::from(
+ CStr::from_ptr(token_c)
+ .to_str()
+ .map_err(|e| format!("expected utf8 token: {}", e))?
+ .to_string(),
+ );
+ Ok(CredentialResponse::Get {
+ token,
+ cache: CacheControl::Session,
+ operation_independent: true,
+ })
+ }
+ }
+ cargo_credential::Action::Login(options) => {
+ let label = label(registry.name.unwrap_or(registry.index_url));
+ let token = CString::new(read_token(options, registry)?.expose()).unwrap();
+ let mut error: *mut GError = null_mut();
+ let attr_url = CString::new("url").unwrap();
+ let schema = schema();
+ unsafe {
+ secret_password_store_sync(
+ &schema,
+ b"default\0".as_ptr() as *const gchar,
+ label.as_ptr(),
+ token.as_ptr(),
+ null_mut(),
+ &mut error,
+ attr_url.as_ptr(),
+ index_url_c.as_ptr(),
+ null() as *const gchar,
+ );
+ if !error.is_null() {
+ return Err(format!(
+ "failed to store token: {}",
+ CStr::from_ptr((*error).message)
+ .to_str()
+ .unwrap_or_default()
+ )
+ .into());
+ }
+ }
+ Ok(CredentialResponse::Login)
+ }
+ cargo_credential::Action::Logout => {
+ let schema = schema();
+ let mut error: *mut GError = null_mut();
+ let attr_url = CString::new("url").unwrap();
+ unsafe {
+ secret_password_clear_sync(
+ &schema,
+ null_mut(),
+ &mut error,
+ attr_url.as_ptr(),
+ index_url_c.as_ptr(),
+ null() as *const gchar,
+ );
+ if !error.is_null() {
+ return Err(format!(
+ "failed to erase token: {}",
+ CStr::from_ptr((*error).message)
+ .to_str()
+ .unwrap_or_default()
+ )
+ .into());
+ }
+ }
+ Ok(CredentialResponse::Logout)
+ }
+ _ => Err(Error::OperationNotSupported),
+ }
+ }
+ }
+}
+
+#[cfg(not(target_os = "linux"))]
+pub use cargo_credential::UnsupportedCredential as LibSecretCredential;
+#[cfg(target_os = "linux")]
+pub use linux::LibSecretCredential;
diff --git a/src/tools/cargo/credential/cargo-credential-macos-keychain/Cargo.toml b/src/tools/cargo/credential/cargo-credential-macos-keychain/Cargo.toml
index 6311b71de..342c771b5 100644
--- a/src/tools/cargo/credential/cargo-credential-macos-keychain/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential-macos-keychain/Cargo.toml
@@ -1,8 +1,8 @@
[package]
name = "cargo-credential-macos-keychain"
-version = "0.2.0"
-edition = "2021"
-license = "MIT OR Apache-2.0"
+version = "0.3.0"
+edition.workspace = true
+license.workspace = true
repository = "https://github.com/rust-lang/cargo"
description = "A Cargo credential process that stores tokens in a macOS keychain."
diff --git a/src/tools/cargo/credential/cargo-credential-macos-keychain/src/lib.rs b/src/tools/cargo/credential/cargo-credential-macos-keychain/src/lib.rs
new file mode 100644
index 000000000..9e6d55472
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-macos-keychain/src/lib.rs
@@ -0,0 +1,81 @@
+//! Cargo registry macos keychain credential process.
+
+#[cfg(target_os = "macos")]
+mod macos {
+ use cargo_credential::{
+ read_token, Action, CacheControl, Credential, CredentialResponse, Error, RegistryInfo,
+ };
+ use security_framework::os::macos::keychain::SecKeychain;
+
+ pub struct MacKeychain;
+
+ /// The account name is not used.
+ const ACCOUNT: &'static str = "";
+ const NOT_FOUND: i32 = -25300; // errSecItemNotFound
+
+ fn registry(index_url: &str) -> String {
+ format!("cargo-registry:{}", index_url)
+ }
+
+ impl Credential for MacKeychain {
+ fn perform(
+ &self,
+ reg: &RegistryInfo<'_>,
+ action: &Action<'_>,
+ _args: &[&str],
+ ) -> Result<CredentialResponse, Error> {
+ let keychain = SecKeychain::default().unwrap();
+ let service_name = registry(reg.index_url);
+ let not_found = security_framework::base::Error::from(NOT_FOUND).code();
+ match action {
+ Action::Get(_) => match keychain.find_generic_password(&service_name, ACCOUNT) {
+ Err(e) if e.code() == not_found => Err(Error::NotFound),
+ Err(e) => Err(Box::new(e).into()),
+ Ok((pass, _)) => {
+ let token = String::from_utf8(pass.as_ref().to_vec()).map_err(Box::new)?;
+ Ok(CredentialResponse::Get {
+ token: token.into(),
+ cache: CacheControl::Session,
+ operation_independent: true,
+ })
+ }
+ },
+ Action::Login(options) => {
+ let token = read_token(options, reg)?;
+ match keychain.find_generic_password(&service_name, ACCOUNT) {
+ Err(e) => {
+ if e.code() == not_found {
+ keychain
+ .add_generic_password(
+ &service_name,
+ ACCOUNT,
+ token.expose().as_bytes(),
+ )
+ .map_err(Box::new)?;
+ }
+ }
+ Ok((_, mut item)) => {
+ item.set_password(token.expose().as_bytes())
+ .map_err(Box::new)?;
+ }
+ }
+ Ok(CredentialResponse::Login)
+ }
+ Action::Logout => match keychain.find_generic_password(&service_name, ACCOUNT) {
+ Err(e) if e.code() == not_found => Err(Error::NotFound),
+ Err(e) => Err(Box::new(e).into()),
+ Ok((_, item)) => {
+ item.delete();
+ Ok(CredentialResponse::Logout)
+ }
+ },
+ _ => Err(Error::OperationNotSupported),
+ }
+ }
+ }
+}
+
+#[cfg(not(target_os = "macos"))]
+pub use cargo_credential::UnsupportedCredential as MacKeychain;
+#[cfg(target_os = "macos")]
+pub use macos::MacKeychain;
diff --git a/src/tools/cargo/credential/cargo-credential-macos-keychain/src/main.rs b/src/tools/cargo/credential/cargo-credential-macos-keychain/src/main.rs
deleted file mode 100644
index 4d6ea96d0..000000000
--- a/src/tools/cargo/credential/cargo-credential-macos-keychain/src/main.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-//! Cargo registry macos keychain credential process.
-
-#[cfg(target_os = "macos")]
-mod macos {
- use cargo_credential::{Credential, Error};
- use security_framework::os::macos::keychain::SecKeychain;
-
- pub(crate) struct MacKeychain;
-
- /// The account name is not used.
- const ACCOUNT: &'static str = "";
-
- fn registry(registry_name: &str) -> String {
- format!("cargo-registry:{}", registry_name)
- }
-
- impl Credential for MacKeychain {
- fn name(&self) -> &'static str {
- env!("CARGO_PKG_NAME")
- }
-
- fn get(&self, index_url: &str) -> Result<String, Error> {
- let keychain = SecKeychain::default().unwrap();
- let service_name = registry(index_url);
- let (pass, _item) = keychain.find_generic_password(&service_name, ACCOUNT)?;
- String::from_utf8(pass.as_ref().to_vec())
- .map_err(|_| "failed to convert token to UTF8".into())
- }
-
- fn store(&self, index_url: &str, token: &str, name: Option<&str>) -> Result<(), Error> {
- let keychain = SecKeychain::default().unwrap();
- let service_name = registry(name.unwrap_or(index_url));
- if let Ok((_pass, mut item)) = keychain.find_generic_password(&service_name, ACCOUNT) {
- item.set_password(token.as_bytes())?;
- } else {
- keychain.add_generic_password(&service_name, ACCOUNT, token.as_bytes())?;
- }
- Ok(())
- }
-
- fn erase(&self, index_url: &str) -> Result<(), Error> {
- let keychain = SecKeychain::default().unwrap();
- let service_name = registry(index_url);
- let (_pass, item) = keychain.find_generic_password(&service_name, ACCOUNT)?;
- item.delete();
- Ok(())
- }
- }
-}
-
-#[cfg(not(target_os = "macos"))]
-use cargo_credential::UnsupportedCredential as MacKeychain;
-#[cfg(target_os = "macos")]
-use macos::MacKeychain;
-
-fn main() {
- cargo_credential::main(MacKeychain);
-}
diff --git a/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml b/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml
index cd168a8a3..8c609dc4e 100644
--- a/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml
@@ -1,8 +1,8 @@
[package]
name = "cargo-credential-wincred"
-version = "0.2.0"
-edition = "2021"
-license = "MIT OR Apache-2.0"
+version = "0.3.0"
+edition.workspace = true
+license.workspace = true
repository = "https://github.com/rust-lang/cargo"
description = "A Cargo credential process that stores tokens with Windows Credential Manager."
diff --git a/src/tools/cargo/credential/cargo-credential-wincred/src/lib.rs b/src/tools/cargo/credential/cargo-credential-wincred/src/lib.rs
new file mode 100644
index 000000000..9200ca58f
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-wincred/src/lib.rs
@@ -0,0 +1,125 @@
+//! Cargo registry windows credential process.
+
+#[cfg(windows)]
+mod win {
+ use cargo_credential::{read_token, Action, CacheControl, CredentialResponse, RegistryInfo};
+ use cargo_credential::{Credential, Error};
+ use std::ffi::OsStr;
+
+ use std::os::windows::ffi::OsStrExt;
+
+ use windows_sys::core::PWSTR;
+ use windows_sys::Win32::Foundation::ERROR_NOT_FOUND;
+ use windows_sys::Win32::Foundation::FILETIME;
+ use windows_sys::Win32::Foundation::TRUE;
+ use windows_sys::Win32::Security::Credentials::CredReadW;
+ use windows_sys::Win32::Security::Credentials::CredWriteW;
+ use windows_sys::Win32::Security::Credentials::CREDENTIALW;
+ use windows_sys::Win32::Security::Credentials::CRED_PERSIST_LOCAL_MACHINE;
+ use windows_sys::Win32::Security::Credentials::CRED_TYPE_GENERIC;
+ use windows_sys::Win32::Security::Credentials::{CredDeleteW, CredFree};
+
+ pub struct WindowsCredential;
+
+ /// Converts a string to a nul-terminated wide UTF-16 byte sequence.
+ fn wstr(s: &str) -> Vec<u16> {
+ let mut wide: Vec<u16> = OsStr::new(s).encode_wide().collect();
+ if wide.iter().any(|b| *b == 0) {
+ panic!("nul byte in wide string");
+ }
+ wide.push(0);
+ wide
+ }
+
+ fn target_name(index_url: &str) -> Vec<u16> {
+ wstr(&format!("cargo-registry:{}", index_url))
+ }
+
+ impl Credential for WindowsCredential {
+ fn perform(
+ &self,
+ registry: &RegistryInfo,
+ action: &Action,
+ _args: &[&str],
+ ) -> Result<CredentialResponse, Error> {
+ match action {
+ Action::Get(_) => {
+ let target_name = target_name(registry.index_url);
+ let mut p_credential: *mut CREDENTIALW = std::ptr::null_mut() as *mut _;
+ let bytes = unsafe {
+ if CredReadW(
+ target_name.as_ptr(),
+ CRED_TYPE_GENERIC,
+ 0,
+ &mut p_credential as *mut _,
+ ) != TRUE
+ {
+ let err = std::io::Error::last_os_error();
+ if err.raw_os_error() == Some(ERROR_NOT_FOUND as i32) {
+ return Err(Error::NotFound);
+ }
+ return Err(Box::new(err).into());
+ }
+ std::slice::from_raw_parts(
+ (*p_credential).CredentialBlob,
+ (*p_credential).CredentialBlobSize as usize,
+ )
+ };
+ let token = String::from_utf8(bytes.to_vec()).map_err(Box::new);
+ unsafe { CredFree(p_credential as *mut _) };
+ Ok(CredentialResponse::Get {
+ token: token?.into(),
+ cache: CacheControl::Session,
+ operation_independent: true,
+ })
+ }
+ Action::Login(options) => {
+ let token = read_token(options, registry)?.expose();
+ let target_name = target_name(registry.index_url);
+ let comment = wstr("Cargo registry token");
+ let credential = CREDENTIALW {
+ Flags: 0,
+ Type: CRED_TYPE_GENERIC,
+ TargetName: target_name.as_ptr() as PWSTR,
+ Comment: comment.as_ptr() as PWSTR,
+ LastWritten: FILETIME {
+ dwLowDateTime: 0,
+ dwHighDateTime: 0,
+ },
+ CredentialBlobSize: token.len() as u32,
+ CredentialBlob: token.as_bytes().as_ptr() as *mut u8,
+ Persist: CRED_PERSIST_LOCAL_MACHINE,
+ AttributeCount: 0,
+ Attributes: std::ptr::null_mut(),
+ TargetAlias: std::ptr::null_mut(),
+ UserName: std::ptr::null_mut(),
+ };
+ let result = unsafe { CredWriteW(&credential, 0) };
+ if result != TRUE {
+ let err = std::io::Error::last_os_error();
+ return Err(Box::new(err).into());
+ }
+ Ok(CredentialResponse::Login)
+ }
+ Action::Logout => {
+ let target_name = target_name(registry.index_url);
+ let result = unsafe { CredDeleteW(target_name.as_ptr(), CRED_TYPE_GENERIC, 0) };
+ if result != TRUE {
+ let err = std::io::Error::last_os_error();
+ if err.raw_os_error() == Some(ERROR_NOT_FOUND as i32) {
+ return Err(Error::NotFound);
+ }
+ return Err(Box::new(err).into());
+ }
+ Ok(CredentialResponse::Logout)
+ }
+ _ => Err(Error::OperationNotSupported),
+ }
+ }
+ }
+}
+
+#[cfg(not(windows))]
+pub use cargo_credential::UnsupportedCredential as WindowsCredential;
+#[cfg(windows)]
+pub use win::WindowsCredential;
diff --git a/src/tools/cargo/credential/cargo-credential-wincred/src/main.rs b/src/tools/cargo/credential/cargo-credential-wincred/src/main.rs
deleted file mode 100644
index 4377172e8..000000000
--- a/src/tools/cargo/credential/cargo-credential-wincred/src/main.rs
+++ /dev/null
@@ -1,122 +0,0 @@
-//! Cargo registry windows credential process.
-
-#[cfg(windows)]
-mod win {
- use cargo_credential::{Credential, Error};
- use std::ffi::OsStr;
- use std::os::windows::ffi::OsStrExt;
-
- use windows_sys::core::PWSTR;
- use windows_sys::Win32::Foundation::ERROR_NOT_FOUND;
- use windows_sys::Win32::Foundation::FILETIME;
- use windows_sys::Win32::Foundation::TRUE;
- use windows_sys::Win32::Security::Credentials::CredDeleteW;
- use windows_sys::Win32::Security::Credentials::CredReadW;
- use windows_sys::Win32::Security::Credentials::CredWriteW;
- use windows_sys::Win32::Security::Credentials::CREDENTIALW;
- use windows_sys::Win32::Security::Credentials::CRED_PERSIST_LOCAL_MACHINE;
- use windows_sys::Win32::Security::Credentials::CRED_TYPE_GENERIC;
-
- pub(crate) struct WindowsCredential;
-
- /// Converts a string to a nul-terminated wide UTF-16 byte sequence.
- fn wstr(s: &str) -> Vec<u16> {
- let mut wide: Vec<u16> = OsStr::new(s).encode_wide().collect();
- if wide.iter().any(|b| *b == 0) {
- panic!("nul byte in wide string");
- }
- wide.push(0);
- wide
- }
-
- fn target_name(registry_name: &str) -> Vec<u16> {
- wstr(&format!("cargo-registry:{}", registry_name))
- }
-
- impl Credential for WindowsCredential {
- fn name(&self) -> &'static str {
- env!("CARGO_PKG_NAME")
- }
-
- fn get(&self, index_url: &str) -> Result<String, Error> {
- let target_name = target_name(index_url);
- let p_credential: *mut CREDENTIALW = std::ptr::null_mut() as *mut _;
- unsafe {
- if CredReadW(
- target_name.as_ptr(),
- CRED_TYPE_GENERIC,
- 0,
- p_credential as *mut _ as *mut _,
- ) != TRUE
- {
- return Err(format!(
- "failed to fetch token: {}",
- std::io::Error::last_os_error()
- )
- .into());
- }
- let bytes = std::slice::from_raw_parts(
- (*p_credential).CredentialBlob,
- (*p_credential).CredentialBlobSize as usize,
- );
- String::from_utf8(bytes.to_vec())
- .map_err(|_| "failed to convert token to UTF8".into())
- }
- }
-
- fn store(&self, index_url: &str, token: &str, name: Option<&str>) -> Result<(), Error> {
- let token = token.as_bytes();
- let target_name = target_name(index_url);
- let comment = match name {
- Some(name) => wstr(&format!("Cargo registry token for {}", name)),
- None => wstr("Cargo registry token"),
- };
- let mut credential = CREDENTIALW {
- Flags: 0,
- Type: CRED_TYPE_GENERIC,
- TargetName: target_name.as_ptr() as PWSTR,
- Comment: comment.as_ptr() as PWSTR,
- LastWritten: FILETIME {
- dwLowDateTime: 0,
- dwHighDateTime: 0,
- },
- CredentialBlobSize: token.len() as u32,
- CredentialBlob: token.as_ptr() as *mut u8,
- Persist: CRED_PERSIST_LOCAL_MACHINE,
- AttributeCount: 0,
- Attributes: std::ptr::null_mut(),
- TargetAlias: std::ptr::null_mut(),
- UserName: std::ptr::null_mut(),
- };
- let result = unsafe { CredWriteW(&mut credential, 0) };
- if result != TRUE {
- let err = std::io::Error::last_os_error();
- return Err(format!("failed to store token: {}", err).into());
- }
- Ok(())
- }
-
- fn erase(&self, index_url: &str) -> Result<(), Error> {
- let target_name = target_name(index_url);
- let result = unsafe { CredDeleteW(target_name.as_ptr(), CRED_TYPE_GENERIC, 0) };
- if result != TRUE {
- let err = std::io::Error::last_os_error();
- if err.raw_os_error() == Some(ERROR_NOT_FOUND as i32) {
- eprintln!("not currently logged in to `{}`", index_url);
- return Ok(());
- }
- return Err(format!("failed to remove token: {}", err).into());
- }
- Ok(())
- }
- }
-}
-
-#[cfg(not(windows))]
-use cargo_credential::UnsupportedCredential as WindowsCredential;
-#[cfg(windows)]
-use win::WindowsCredential;
-
-fn main() {
- cargo_credential::main(WindowsCredential);
-}
diff --git a/src/tools/cargo/credential/cargo-credential/Cargo.toml b/src/tools/cargo/credential/cargo-credential/Cargo.toml
index 2addaf5af..8cd1348be 100644
--- a/src/tools/cargo/credential/cargo-credential/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential/Cargo.toml
@@ -1,9 +1,21 @@
[package]
name = "cargo-credential"
-version = "0.2.0"
-edition = "2021"
-license = "MIT OR Apache-2.0"
+version = "0.3.0"
+edition.workspace = true
+license.workspace = true
repository = "https://github.com/rust-lang/cargo"
description = "A library to assist writing Cargo credential helpers."
[dependencies]
+anyhow.workspace = true
+libc.workspace = true
+serde = { workspace = true, features = ["derive"] }
+serde_json.workspace = true
+thiserror.workspace = true
+time.workspace = true
+
+[target.'cfg(windows)'.dependencies]
+windows-sys = { workspace = true, features = ["Win32_System_Console", "Win32_Foundation"] }
+
+[dev-dependencies]
+snapbox = { workspace = true, features = ["examples"] }
diff --git a/src/tools/cargo/credential/cargo-credential/README.md b/src/tools/cargo/credential/cargo-credential/README.md
index 53dc8e6b7..049b3ba55 100644
--- a/src/tools/cargo/credential/cargo-credential/README.md
+++ b/src/tools/cargo/credential/cargo-credential/README.md
@@ -18,7 +18,7 @@ Create a Cargo project with this as a dependency:
# Add this to your Cargo.toml:
[dependencies]
-cargo-credential = "0.1"
+cargo-credential = "0.3"
```
And then include a `main.rs` binary which implements the `Credential` trait, and calls
diff --git a/src/tools/cargo/credential/cargo-credential/examples/file-provider.rs b/src/tools/cargo/credential/cargo-credential/examples/file-provider.rs
new file mode 100644
index 000000000..d11958536
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential/examples/file-provider.rs
@@ -0,0 +1,90 @@
+//! Example credential provider that stores credentials in a JSON file.
+//! This is not secure
+
+use cargo_credential::{
+ Action, CacheControl, Credential, CredentialResponse, RegistryInfo, Secret,
+};
+use std::{collections::HashMap, fs::File, io::ErrorKind};
+type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
+
+struct FileCredential;
+
+impl Credential for FileCredential {
+ fn perform(
+ &self,
+ registry: &RegistryInfo,
+ action: &Action,
+ _args: &[&str],
+ ) -> Result<CredentialResponse, cargo_credential::Error> {
+ if registry.index_url != "https://github.com/rust-lang/crates.io-index" {
+ // Restrict this provider to only work for crates.io. Cargo will skip it and attempt
+ // another provider for any other registry.
+ //
+ // If a provider supports any registry, then this check should be omitted.
+ return Err(cargo_credential::Error::UrlNotSupported);
+ }
+
+ // `Error::Other` takes a boxed `std::error::Error` type that causes Cargo to show the error.
+ let mut creds = FileCredential::read().map_err(cargo_credential::Error::Other)?;
+
+ match action {
+ Action::Get(_) => {
+ // Cargo requested a token, look it up.
+ if let Some(token) = creds.get(registry.index_url) {
+ Ok(CredentialResponse::Get {
+ token: token.clone(),
+ cache: CacheControl::Session,
+ operation_independent: true,
+ })
+ } else {
+ // Credential providers should respond with `NotFound` when a credential can not be
+ // found, allowing Cargo to attempt another provider.
+ Err(cargo_credential::Error::NotFound)
+ }
+ }
+ Action::Login(login_options) => {
+ // The token for `cargo login` can come from the `login_options` parameter or i
+ // interactively reading from stdin.
+ //
+ // `cargo_credential::read_token` automatically handles this.
+ let token = cargo_credential::read_token(login_options, registry)?;
+ creds.insert(registry.index_url.to_string(), token);
+
+ FileCredential::write(&creds).map_err(cargo_credential::Error::Other)?;
+
+ // Credentials were successfully stored.
+ Ok(CredentialResponse::Login)
+ }
+ Action::Logout => {
+ if creds.remove(registry.index_url).is_none() {
+ // If the user attempts to log out from a registry that has no credentials
+ // stored, then NotFound is the appropriate error.
+ Err(cargo_credential::Error::NotFound)
+ } else {
+ // Credentials were successfully erased.
+ Ok(CredentialResponse::Logout)
+ }
+ }
+ // If a credential provider doesn't support a given operation, it should respond with `OperationNotSupported`.
+ _ => Err(cargo_credential::Error::OperationNotSupported),
+ }
+ }
+}
+
+impl FileCredential {
+ fn read() -> Result<HashMap<String, Secret<String>>, Error> {
+ match File::open("cargo-credentials.json") {
+ Ok(f) => Ok(serde_json::from_reader(f)?),
+ Err(e) if e.kind() == ErrorKind::NotFound => Ok(HashMap::new()),
+ Err(e) => Err(e)?,
+ }
+ }
+ fn write(value: &HashMap<String, Secret<String>>) -> Result<(), Error> {
+ let file = File::create("cargo-credentials.json")?;
+ Ok(serde_json::to_writer_pretty(file, value)?)
+ }
+}
+
+fn main() {
+ cargo_credential::main(FileCredential);
+}
diff --git a/src/tools/cargo/credential/cargo-credential/examples/stdout-redirected.rs b/src/tools/cargo/credential/cargo-credential/examples/stdout-redirected.rs
new file mode 100644
index 000000000..0b9bcc2f7
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential/examples/stdout-redirected.rs
@@ -0,0 +1,25 @@
+//! Provider used for testing redirection of stdout.
+
+use cargo_credential::{Action, Credential, CredentialResponse, Error, RegistryInfo};
+
+struct MyCredential;
+
+impl Credential for MyCredential {
+ fn perform(
+ &self,
+ _registry: &RegistryInfo,
+ _action: &Action,
+ _args: &[&str],
+ ) -> Result<CredentialResponse, Error> {
+ // Informational messages should be sent on stderr.
+ eprintln!("message on stderr should be sent the the parent process");
+
+ // Reading from stdin and writing to stdout will go to the attached console (tty).
+ println!("message from test credential provider");
+ Err(Error::OperationNotSupported)
+ }
+}
+
+fn main() {
+ cargo_credential::main(MyCredential);
+}
diff --git a/src/tools/cargo/credential/cargo-credential/src/error.rs b/src/tools/cargo/credential/cargo-credential/src/error.rs
new file mode 100644
index 000000000..2ebaf9977
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential/src/error.rs
@@ -0,0 +1,206 @@
+use serde::{Deserialize, Serialize};
+use std::error::Error as StdError;
+use thiserror::Error as ThisError;
+
+/// Credential provider error type.
+///
+/// `UrlNotSupported` and `NotFound` errors both cause Cargo
+/// to attempt another provider, if one is available. The other
+/// variants are fatal.
+///
+/// Note: Do not add a tuple variant, as it cannot be serialized.
+#[derive(Serialize, Deserialize, ThisError, Debug)]
+#[serde(rename_all = "kebab-case", tag = "kind")]
+#[non_exhaustive]
+pub enum Error {
+ /// Registry URL is not supported. This should be used if
+ /// the provider only works for some registries. Cargo will
+ /// try another provider, if available
+ #[error("registry not supported")]
+ UrlNotSupported,
+
+ /// Credentials could not be found. Cargo will try another
+ /// provider, if available
+ #[error("credential not found")]
+ NotFound,
+
+ /// The provider doesn't support this operation, such as
+ /// a provider that can't support 'login' / 'logout'
+ #[error("requested operation not supported")]
+ OperationNotSupported,
+
+ /// The provider failed to perform the operation. Other
+ /// providers will not be attempted
+ #[error(transparent)]
+ #[serde(with = "error_serialize")]
+ Other(Box<dyn StdError + Sync + Send>),
+
+ /// A new variant was added to this enum since Cargo was built
+ #[error("unknown error kind; try updating Cargo?")]
+ #[serde(other)]
+ Unknown,
+}
+
+impl From<String> for Error {
+ fn from(err: String) -> Self {
+ Box::new(StringTypedError {
+ message: err.to_string(),
+ source: None,
+ })
+ .into()
+ }
+}
+
+impl From<&str> for Error {
+ fn from(err: &str) -> Self {
+ err.to_string().into()
+ }
+}
+
+impl From<anyhow::Error> for Error {
+ fn from(value: anyhow::Error) -> Self {
+ let mut prev = None;
+ for e in value.chain().rev() {
+ prev = Some(Box::new(StringTypedError {
+ message: e.to_string(),
+ source: prev,
+ }));
+ }
+ Error::Other(prev.unwrap())
+ }
+}
+
+impl<T: StdError + Send + Sync + 'static> From<Box<T>> for Error {
+ fn from(value: Box<T>) -> Self {
+ Error::Other(value)
+ }
+}
+
+/// String-based error type with an optional source
+#[derive(Debug)]
+struct StringTypedError {
+ message: String,
+ source: Option<Box<StringTypedError>>,
+}
+
+impl StdError for StringTypedError {
+ fn source(&self) -> Option<&(dyn StdError + 'static)> {
+ self.source.as_ref().map(|err| err as &dyn StdError)
+ }
+}
+
+impl std::fmt::Display for StringTypedError {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.message.fmt(f)
+ }
+}
+
+/// Serializer / deserializer for any boxed error.
+/// The string representation of the error, and its `source` chain can roundtrip across
+/// the serialization. The actual types are lost (downcast will not work).
+mod error_serialize {
+ use std::error::Error as StdError;
+ use std::ops::Deref;
+
+ use serde::{ser::SerializeStruct, Deserialize, Deserializer, Serializer};
+
+ use crate::error::StringTypedError;
+
+ pub fn serialize<S>(
+ e: &Box<dyn StdError + Send + Sync>,
+ serializer: S,
+ ) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ {
+ let mut state = serializer.serialize_struct("StringTypedError", 2)?;
+ state.serialize_field("message", &format!("{}", e))?;
+
+ // Serialize the source error chain recursively
+ let mut current_source: &dyn StdError = e.deref();
+ let mut sources = Vec::new();
+ while let Some(err) = current_source.source() {
+ sources.push(err.to_string());
+ current_source = err;
+ }
+ state.serialize_field("caused-by", &sources)?;
+ state.end()
+ }
+
+ pub fn deserialize<'de, D>(deserializer: D) -> Result<Box<dyn StdError + Sync + Send>, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ #[derive(Deserialize)]
+ #[serde(rename_all = "kebab-case")]
+ struct ErrorData {
+ message: String,
+ caused_by: Option<Vec<String>>,
+ }
+ let data = ErrorData::deserialize(deserializer)?;
+ let mut prev = None;
+ if let Some(source) = data.caused_by {
+ for e in source.into_iter().rev() {
+ prev = Some(Box::new(StringTypedError {
+ message: e,
+ source: prev,
+ }));
+ }
+ }
+ let e = Box::new(StringTypedError {
+ message: data.message,
+ source: prev,
+ });
+ Ok(e)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::Error;
+
+ #[test]
+ pub fn unknown_kind() {
+ let json = r#"{
+ "kind": "unexpected-kind",
+ "unexpected-content": "test"
+ }"#;
+ let e: Error = serde_json::from_str(&json).unwrap();
+ assert!(matches!(e, Error::Unknown));
+ }
+
+ #[test]
+ pub fn roundtrip() {
+ // Construct an error with context
+ let e = anyhow::anyhow!("E1").context("E2").context("E3");
+ // Convert to a string with contexts.
+ let s1 = format!("{:?}", e);
+ // Convert the error into an `Error`
+ let e: Error = e.into();
+ // Convert that error into JSON
+ let json = serde_json::to_string_pretty(&e).unwrap();
+ // Convert that error back to anyhow
+ let e: anyhow::Error = e.into();
+ let s2 = format!("{:?}", e);
+ assert_eq!(s1, s2);
+
+ // Convert the error back from JSON
+ let e: Error = serde_json::from_str(&json).unwrap();
+ // Convert to back to anyhow
+ let e: anyhow::Error = e.into();
+ let s3 = format!("{:?}", e);
+ assert_eq!(s2, s3);
+
+ assert_eq!(
+ r#"{
+ "kind": "other",
+ "message": "E3",
+ "caused-by": [
+ "E2",
+ "E1"
+ ]
+}"#,
+ json
+ );
+ }
+}
diff --git a/src/tools/cargo/credential/cargo-credential/src/lib.rs b/src/tools/cargo/credential/cargo-credential/src/lib.rs
index c75172242..0fb495ed3 100644
--- a/src/tools/cargo/credential/cargo-credential/src/lib.rs
+++ b/src/tools/cargo/credential/cargo-credential/src/lib.rs
@@ -1,4 +1,4 @@
-//! Helper library for writing Cargo credential processes.
+//! Helper library for writing Cargo credential providers.
//!
//! A credential process should have a `struct` that implements the `Credential` trait.
//! The `main` function should be called with an instance of that struct, such as:
@@ -9,98 +9,270 @@
//! }
//! ```
//!
-//! This will determine the action to perform (get/store/erase) by looking at
-//! the CLI arguments for the first argument that does not start with `-`. It
-//! will then call the corresponding method of the trait to perform the
-//! requested action.
-
-pub type Error = Box<dyn std::error::Error>;
+//! While in the `perform` function, stdin and stdout will be re-attached to the
+//! active console. This allows credential providers to be interactive if necessary.
+//!
+//! ## Error handling
+//! ### [`Error::UrlNotSupported`]
+//! A credential provider may only support some registry URLs. If this is the case
+//! and an unsupported index URL is passed to the provider, it should respond with
+//! [`Error::UrlNotSupported`]. Other credential providers may be attempted by Cargo.
+//!
+//! ### [`Error::NotFound`]
+//! When attempting an [`Action::Get`] or [`Action::Logout`], if a credential can not
+//! be found, the provider should respond with [`Error::NotFound`]. Other credential
+//! providers may be attempted by Cargo.
+//!
+//! ### [`Error::OperationNotSupported`]
+//! A credential provider might not support all operations. For example if the provider
+//! only supports [`Action::Get`], [`Error::OperationNotSupported`] should be returned
+//! for all other requests.
+//!
+//! ### [`Error::Other`]
+//! All other errors go here. The error will be shown to the user in Cargo, including
+//! the full error chain using [`std::error::Error::source`].
+//!
+//! ## Example
+//! ```rust,ignore
+#![doc = include_str!("../examples/file-provider.rs")]
+//! ```
-pub trait Credential {
- /// Returns the name of this credential process.
- fn name(&self) -> &'static str;
+use serde::{Deserialize, Serialize};
+use std::{fmt::Display, io};
+use time::OffsetDateTime;
- /// Retrieves a token for the given registry.
- fn get(&self, index_url: &str) -> Result<String, Error>;
+mod error;
+mod secret;
+mod stdio;
- /// Stores the given token for the given registry.
- fn store(&self, index_url: &str, token: &str, name: Option<&str>) -> Result<(), Error>;
+pub use error::Error;
+pub use secret::Secret;
+use stdio::stdin_stdout_to_console;
- /// Removes the token for the given registry.
- ///
- /// If the user is not logged in, this should print a message to stderr if
- /// possible indicating that the user is not currently logged in, and
- /// return `Ok`.
- fn erase(&self, index_url: &str) -> Result<(), Error>;
+/// Message sent by the credential helper on startup
+#[derive(Serialize, Deserialize, Clone, Debug)]
+pub struct CredentialHello {
+ // Protocol versions supported by the credential process.
+ pub v: Vec<u32>,
}
+/// Credential provider that doesn't support any registries.
pub struct UnsupportedCredential;
-
impl Credential for UnsupportedCredential {
- fn name(&self) -> &'static str {
- "unsupported"
+ fn perform(
+ &self,
+ _registry: &RegistryInfo,
+ _action: &Action,
+ _args: &[&str],
+ ) -> Result<CredentialResponse, Error> {
+ Err(Error::UrlNotSupported)
}
+}
- fn get(&self, _index_url: &str) -> Result<String, Error> {
- Err("unsupported".into())
- }
+/// Message sent by Cargo to the credential helper after the hello
+#[derive(Serialize, Deserialize, Clone, Debug)]
+#[serde(rename_all = "kebab-case")]
+pub struct CredentialRequest<'a> {
+ // Cargo will respond with the highest common protocol supported by both.
+ pub v: u32,
+ #[serde(borrow)]
+ pub registry: RegistryInfo<'a>,
+ #[serde(borrow, flatten)]
+ pub action: Action<'a>,
+ /// Additional command-line arguments passed to the credential provider.
+ pub args: Vec<&'a str>,
+}
- fn store(&self, _index_url: &str, _token: &str, _name: Option<&str>) -> Result<(), Error> {
- Err("unsupported".into())
- }
+#[derive(Serialize, Deserialize, Clone, Debug)]
+#[serde(rename_all = "kebab-case")]
+pub struct RegistryInfo<'a> {
+ /// Registry index url
+ pub index_url: &'a str,
+ /// Name of the registry in configuration. May not be available.
+ /// The crates.io registry will be `crates-io` (`CRATES_IO_REGISTRY`).
+ pub name: Option<&'a str>,
+ /// Headers from attempting to access a registry that resulted in a HTTP 401.
+ #[serde(skip_serializing_if = "Vec::is_empty", default)]
+ pub headers: Vec<String>,
+}
- fn erase(&self, _index_url: &str) -> Result<(), Error> {
- Err("unsupported".into())
+#[derive(Serialize, Deserialize, Clone, Debug)]
+#[non_exhaustive]
+#[serde(tag = "kind", rename_all = "kebab-case")]
+pub enum Action<'a> {
+ #[serde(borrow)]
+ Get(Operation<'a>),
+ Login(LoginOptions<'a>),
+ Logout,
+ #[serde(other)]
+ Unknown,
+}
+
+impl<'a> Display for Action<'a> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Action::Get(_) => f.write_str("get"),
+ Action::Login(_) => f.write_str("login"),
+ Action::Logout => f.write_str("logout"),
+ Action::Unknown => f.write_str("<unknown>"),
+ }
}
}
-/// Runs the credential interaction by processing the command-line and
-/// environment variables.
+#[derive(Serialize, Deserialize, Clone, Debug)]
+#[serde(rename_all = "kebab-case")]
+pub struct LoginOptions<'a> {
+ /// Token passed on the command line via --token or from stdin
+ pub token: Option<Secret<&'a str>>,
+ /// Optional URL that the user can visit to log in to the registry
+ pub login_url: Option<&'a str>,
+}
+
+/// A record of what kind of operation is happening that we should generate a token for.
+#[derive(Serialize, Deserialize, Clone, Debug)]
+#[non_exhaustive]
+#[serde(tag = "operation", rename_all = "kebab-case")]
+pub enum Operation<'a> {
+ /// The user is attempting to fetch a crate.
+ Read,
+ /// The user is attempting to publish a crate.
+ Publish {
+ /// The name of the crate
+ name: &'a str,
+ /// The version of the crate
+ vers: &'a str,
+ /// The checksum of the crate file being uploaded
+ cksum: &'a str,
+ },
+ /// The user is attempting to yank a crate.
+ Yank {
+ /// The name of the crate
+ name: &'a str,
+ /// The version of the crate
+ vers: &'a str,
+ },
+ /// The user is attempting to unyank a crate.
+ Unyank {
+ /// The name of the crate
+ name: &'a str,
+ /// The version of the crate
+ vers: &'a str,
+ },
+ /// The user is attempting to modify the owners of a crate.
+ Owners {
+ /// The name of the crate
+ name: &'a str,
+ },
+ #[serde(other)]
+ Unknown,
+}
+
+/// Message sent by the credential helper
+#[derive(Serialize, Deserialize, Clone, Debug)]
+#[serde(tag = "kind", rename_all = "kebab-case")]
+#[non_exhaustive]
+pub enum CredentialResponse {
+ Get {
+ token: Secret<String>,
+ cache: CacheControl,
+ operation_independent: bool,
+ },
+ Login,
+ Logout,
+ #[serde(other)]
+ Unknown,
+}
+
+#[derive(Serialize, Deserialize, Clone, Debug)]
+#[serde(rename_all = "kebab-case")]
+#[non_exhaustive]
+pub enum CacheControl {
+ /// Do not cache this result.
+ Never,
+ /// Cache this result and use it for subsequent requests in the current Cargo invocation until the specified time.
+ Expires(#[serde(with = "time::serde::timestamp")] OffsetDateTime),
+ /// Cache this result and use it for all subsequent requests in the current Cargo invocation.
+ Session,
+ #[serde(other)]
+ Unknown,
+}
+
+/// Credential process JSON protocol version. Incrementing
+/// this version will prevent new credential providers
+/// from working with older versions of Cargo.
+pub const PROTOCOL_VERSION_1: u32 = 1;
+pub trait Credential {
+ /// Retrieves a token for the given registry.
+ fn perform(
+ &self,
+ registry: &RegistryInfo,
+ action: &Action,
+ args: &[&str],
+ ) -> Result<CredentialResponse, Error>;
+}
+
+/// Runs the credential interaction
pub fn main(credential: impl Credential) {
- let name = credential.name();
- if let Err(e) = doit(credential) {
- eprintln!("{} error: {}", name, e);
- std::process::exit(1);
+ let result = doit(credential).map_err(|e| Error::Other(e));
+ if result.is_err() {
+ serde_json::to_writer(std::io::stdout(), &result)
+ .expect("failed to serialize credential provider error");
+ println!();
}
}
-fn env(name: &str) -> Result<String, Error> {
- std::env::var(name).map_err(|_| format!("environment variable `{}` is not set", name).into())
-}
+fn doit(
+ credential: impl Credential,
+) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
+ let hello = CredentialHello {
+ v: vec![PROTOCOL_VERSION_1],
+ };
+ serde_json::to_writer(std::io::stdout(), &hello)?;
+ println!();
-fn doit(credential: impl Credential) -> Result<(), Error> {
- let which = std::env::args()
- .skip(1)
- .skip_while(|arg| arg.starts_with('-'))
- .next()
- .ok_or_else(|| "first argument must be the {action}")?;
- let index_url = env("CARGO_REGISTRY_INDEX_URL")?;
- let name = std::env::var("CARGO_REGISTRY_NAME_OPT").ok();
- let result = match which.as_ref() {
- "get" => credential.get(&index_url).and_then(|token| {
- println!("{}", token);
- Ok(())
- }),
- "store" => {
- read_token().and_then(|token| credential.store(&index_url, &token, name.as_deref()))
+ loop {
+ let mut buffer = String::new();
+ let len = std::io::stdin().read_line(&mut buffer)?;
+ if len == 0 {
+ return Ok(());
}
- "erase" => credential.erase(&index_url),
- _ => {
- return Err(format!(
- "unexpected command-line argument `{}`, expected get/store/erase",
- which
- )
- .into())
+ let request: CredentialRequest = serde_json::from_str(&buffer)?;
+ if request.v != PROTOCOL_VERSION_1 {
+ return Err(format!("unsupported protocol version {}", request.v).into());
}
- };
- result.map_err(|e| format!("failed to `{}` token: {}", which, e).into())
+
+ let response = stdin_stdout_to_console(|| {
+ credential.perform(&request.registry, &request.action, &request.args)
+ })?;
+
+ serde_json::to_writer(std::io::stdout(), &response)?;
+ println!();
+ }
}
-fn read_token() -> Result<String, Error> {
- let mut buffer = String::new();
- std::io::stdin().read_line(&mut buffer)?;
- if buffer.ends_with('\n') {
- buffer.pop();
+/// Read a line of text from stdin.
+pub fn read_line() -> Result<String, io::Error> {
+ let mut buf = String::new();
+ io::stdin().read_line(&mut buf)?;
+ Ok(buf.trim().to_string())
+}
+
+/// Prompt the user for a token.
+pub fn read_token(
+ login_options: &LoginOptions,
+ registry: &RegistryInfo,
+) -> Result<Secret<String>, Error> {
+ if let Some(token) = &login_options.token {
+ return Ok(token.to_owned());
}
- Ok(buffer)
+
+ if let Some(url) = login_options.login_url {
+ eprintln!("please paste the token found on {url} below");
+ } else if let Some(name) = registry.name {
+ eprintln!("please paste the token for {name} below");
+ } else {
+ eprintln!("please paste the token for {} below", registry.index_url);
+ }
+
+ Ok(Secret::from(read_line().map_err(Box::new)?))
}
diff --git a/src/tools/cargo/credential/cargo-credential/src/secret.rs b/src/tools/cargo/credential/cargo-credential/src/secret.rs
new file mode 100644
index 000000000..1c2314d8e
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential/src/secret.rs
@@ -0,0 +1,101 @@
+use std::fmt;
+use std::ops::Deref;
+
+use serde::{Deserialize, Serialize};
+
+/// A wrapper for values that should not be printed.
+///
+/// This type does not implement `Display`, and has a `Debug` impl that hides
+/// the contained value.
+///
+/// ```
+/// # use cargo_credential::Secret;
+/// let token = Secret::from("super secret string");
+/// assert_eq!(format!("{:?}", token), "Secret { inner: \"REDACTED\" }");
+/// ```
+///
+/// Currently, we write a borrowed `Secret<T>` as `Secret<&T>`.
+/// The [`as_deref`](Secret::as_deref) and [`to_owned`](Secret::to_owned) methods can
+/// be used to convert back and forth between `Secret<String>` and `Secret<&str>`.
+#[derive(Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
+#[serde(transparent)]
+pub struct Secret<T> {
+ inner: T,
+}
+
+impl<T> Secret<T> {
+ /// Unwraps the contained value.
+ ///
+ /// Use of this method marks the boundary of where the contained value is
+ /// hidden.
+ pub fn expose(self) -> T {
+ self.inner
+ }
+
+ /// Converts a `Secret<T>` to a `Secret<&T::Target>`.
+ /// ```
+ /// # use cargo_credential::Secret;
+ /// let owned: Secret<String> = Secret::from(String::from("token"));
+ /// let borrowed: Secret<&str> = owned.as_deref();
+ /// ```
+ pub fn as_deref(&self) -> Secret<&<T as Deref>::Target>
+ where
+ T: Deref,
+ {
+ Secret::from(self.inner.deref())
+ }
+
+ /// Converts a `Secret<T>` to a `Secret<&T>`.
+ pub fn as_ref(&self) -> Secret<&T> {
+ Secret::from(&self.inner)
+ }
+
+ /// Converts a `Secret<T>` to a `Secret<U>` by applying `f` to the contained value.
+ pub fn map<U, F>(self, f: F) -> Secret<U>
+ where
+ F: FnOnce(T) -> U,
+ {
+ Secret::from(f(self.inner))
+ }
+}
+
+impl<T: ToOwned + ?Sized> Secret<&T> {
+ /// Converts a `Secret` containing a borrowed type to a `Secret` containing the
+ /// corresponding owned type.
+ /// ```
+ /// # use cargo_credential::Secret;
+ /// let borrowed: Secret<&str> = Secret::from("token");
+ /// let owned: Secret<String> = borrowed.to_owned();
+ /// ```
+ pub fn to_owned(&self) -> Secret<<T as ToOwned>::Owned> {
+ Secret::from(self.inner.to_owned())
+ }
+}
+
+impl<T, E> Secret<Result<T, E>> {
+ /// Converts a `Secret<Result<T, E>>` to a `Result<Secret<T>, E>`.
+ pub fn transpose(self) -> Result<Secret<T>, E> {
+ self.inner.map(|v| Secret::from(v))
+ }
+}
+
+impl<T: AsRef<str>> Secret<T> {
+ /// Checks if the contained value is empty.
+ pub fn is_empty(&self) -> bool {
+ self.inner.as_ref().is_empty()
+ }
+}
+
+impl<T> From<T> for Secret<T> {
+ fn from(inner: T) -> Self {
+ Self { inner }
+ }
+}
+
+impl<T> fmt::Debug for Secret<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Secret")
+ .field("inner", &"REDACTED")
+ .finish()
+ }
+}
diff --git a/src/tools/cargo/credential/cargo-credential/src/stdio.rs b/src/tools/cargo/credential/cargo-credential/src/stdio.rs
new file mode 100644
index 000000000..25435056f
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential/src/stdio.rs
@@ -0,0 +1,163 @@
+use std::{fs::File, io::Error};
+
+/// Reset stdin and stdout to the attached console / tty for the duration of the closure.
+/// If no console is available, stdin and stdout will be redirected to null.
+pub fn stdin_stdout_to_console<F, T>(f: F) -> Result<T, Error>
+where
+ F: FnOnce() -> T,
+{
+ let open_write = |f| std::fs::OpenOptions::new().write(true).open(f);
+
+ let mut stdin = File::open(imp::IN_DEVICE).or_else(|_| File::open(imp::NULL_DEVICE))?;
+ let mut stdout = open_write(imp::OUT_DEVICE).or_else(|_| open_write(imp::NULL_DEVICE))?;
+
+ let _stdin_guard = imp::ReplacementGuard::new(Stdio::Stdin, &mut stdin)?;
+ let _stdout_guard = imp::ReplacementGuard::new(Stdio::Stdout, &mut stdout)?;
+ Ok(f())
+}
+
+enum Stdio {
+ Stdin,
+ Stdout,
+}
+
+#[cfg(windows)]
+mod imp {
+ use super::Stdio;
+ use std::{fs::File, io::Error, os::windows::prelude::AsRawHandle};
+ use windows_sys::Win32::{
+ Foundation::{HANDLE, INVALID_HANDLE_VALUE},
+ System::Console::{
+ GetStdHandle, SetStdHandle, STD_HANDLE, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
+ },
+ };
+ pub const OUT_DEVICE: &str = "CONOUT$";
+ pub const IN_DEVICE: &str = "CONIN$";
+ pub const NULL_DEVICE: &str = "NUL";
+
+ /// Restores previous stdio when dropped.
+ pub struct ReplacementGuard {
+ std_handle: STD_HANDLE,
+ previous: HANDLE,
+ }
+
+ impl ReplacementGuard {
+ pub(super) fn new(stdio: Stdio, replacement: &mut File) -> Result<ReplacementGuard, Error> {
+ let std_handle = match stdio {
+ Stdio::Stdin => STD_INPUT_HANDLE,
+ Stdio::Stdout => STD_OUTPUT_HANDLE,
+ };
+
+ let previous;
+ unsafe {
+ // Make a copy of the current handle
+ previous = GetStdHandle(std_handle);
+ if previous == INVALID_HANDLE_VALUE {
+ return Err(std::io::Error::last_os_error());
+ }
+
+ // Replace stdin with the replacement handle
+ if SetStdHandle(std_handle, replacement.as_raw_handle() as HANDLE) == 0 {
+ return Err(std::io::Error::last_os_error());
+ }
+ }
+
+ Ok(ReplacementGuard {
+ previous,
+ std_handle,
+ })
+ }
+ }
+
+ impl Drop for ReplacementGuard {
+ fn drop(&mut self) {
+ unsafe {
+ // Put previous handle back in to stdin
+ SetStdHandle(self.std_handle, self.previous);
+ }
+ }
+ }
+}
+
+#[cfg(unix)]
+mod imp {
+ use super::Stdio;
+ use libc::{close, dup, dup2, STDIN_FILENO, STDOUT_FILENO};
+ use std::{fs::File, io::Error, os::fd::AsRawFd};
+ pub const IN_DEVICE: &str = "/dev/tty";
+ pub const OUT_DEVICE: &str = "/dev/tty";
+ pub const NULL_DEVICE: &str = "/dev/null";
+
+ /// Restores previous stdio when dropped.
+ pub struct ReplacementGuard {
+ std_fileno: i32,
+ previous: i32,
+ }
+
+ impl ReplacementGuard {
+ pub(super) fn new(stdio: Stdio, replacement: &mut File) -> Result<ReplacementGuard, Error> {
+ let std_fileno = match stdio {
+ Stdio::Stdin => STDIN_FILENO,
+ Stdio::Stdout => STDOUT_FILENO,
+ };
+
+ let previous;
+ unsafe {
+ // Duplicate the existing stdin file to a new descriptor
+ previous = dup(std_fileno);
+ if previous == -1 {
+ return Err(std::io::Error::last_os_error());
+ }
+ // Replace stdin with the replacement file
+ if dup2(replacement.as_raw_fd(), std_fileno) == -1 {
+ return Err(std::io::Error::last_os_error());
+ }
+ }
+
+ Ok(ReplacementGuard {
+ previous,
+ std_fileno,
+ })
+ }
+ }
+
+ impl Drop for ReplacementGuard {
+ fn drop(&mut self) {
+ unsafe {
+ // Put previous file back in to stdin
+ dup2(self.previous, self.std_fileno);
+ // Close the file descriptor we used as a backup
+ close(self.previous);
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use std::fs::OpenOptions;
+ use std::io::{Seek, Write};
+
+ use super::imp::ReplacementGuard;
+ use super::Stdio;
+
+ #[test]
+ fn stdin() {
+ let tempdir = snapbox::path::PathFixture::mutable_temp().unwrap();
+ let file = tempdir.path().unwrap().join("stdin");
+ let mut file = OpenOptions::new()
+ .read(true)
+ .write(true)
+ .create(true)
+ .open(file)
+ .unwrap();
+
+ writeln!(&mut file, "hello").unwrap();
+ file.seek(std::io::SeekFrom::Start(0)).unwrap();
+ {
+ let _guard = ReplacementGuard::new(Stdio::Stdin, &mut file).unwrap();
+ let line = std::io::stdin().lines().next().unwrap().unwrap();
+ assert_eq!(line, "hello");
+ }
+ }
+}
diff --git a/src/tools/cargo/credential/cargo-credential/tests/examples.rs b/src/tools/cargo/credential/cargo-credential/tests/examples.rs
new file mode 100644
index 000000000..87fdb8de3
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential/tests/examples.rs
@@ -0,0 +1,45 @@
+use std::path::Path;
+
+use snapbox::cmd::Command;
+
+#[test]
+fn stdout_redirected() {
+ let bin = snapbox::cmd::compile_example("stdout-redirected", []).unwrap();
+
+ let hello = r#"{"v":[1]}"#;
+ let get_request = r#"{"v": 1, "registry": {"index-url":"sparse+https://test/","name":"alternative"},"kind": "get","operation": "read","args": []}"#;
+ let err_not_supported = r#"{"Err":{"kind":"operation-not-supported"}}"#;
+
+ Command::new(bin)
+ .stdin(format!("{get_request}\n"))
+ .arg("--cargo-plugin")
+ .assert()
+ .stdout_eq(format!("{hello}\n{err_not_supported}\n"))
+ .stderr_eq("message on stderr should be sent the the parent process\n")
+ .success();
+}
+
+#[test]
+fn file_provider() {
+ let bin = snapbox::cmd::compile_example("file-provider", []).unwrap();
+
+ let hello = r#"{"v":[1]}"#;
+ let login_request = r#"{"v": 1,"registry": {"index-url":"https://github.com/rust-lang/crates.io-index","name":"crates-io"},"kind": "login","token": "s3krit","args": []}"#;
+ let login_response = r#"{"Ok":{"kind":"login"}}"#;
+
+ let get_request = r#"{"v": 1,"registry": {"index-url":"https://github.com/rust-lang/crates.io-index","name":"crates-io"},"kind": "get","operation": "read","args": []}"#;
+ let get_response =
+ r#"{"Ok":{"kind":"get","token":"s3krit","cache":"session","operation_independent":true}}"#;
+
+ let dir = Path::new(env!("CARGO_TARGET_TMPDIR")).join("cargo-credential-tests");
+ std::fs::create_dir(&dir).unwrap();
+ Command::new(bin)
+ .current_dir(&dir)
+ .stdin(format!("{login_request}\n{get_request}\n"))
+ .arg("--cargo-plugin")
+ .assert()
+ .stdout_eq(format!("{hello}\n{login_response}\n{get_response}\n"))
+ .stderr_eq("")
+ .success();
+ std::fs::remove_dir_all(&dir).unwrap();
+}
diff --git a/src/tools/cargo/deny.toml b/src/tools/cargo/deny.toml
index 89d08eacc..383648171 100644
--- a/src/tools/cargo/deny.toml
+++ b/src/tools/cargo/deny.toml
@@ -109,6 +109,7 @@ allow = [
"MPL-2.0",
"Unicode-DFS-2016",
"CC0-1.0",
+ "ISC",
]
# List of explicitly disallowed licenses
# See https://spdx.org/licenses/ for list of possible licenses
diff --git a/src/tools/cargo/publish.py b/src/tools/cargo/publish.py
index 5ace18f72..13077a69b 100755
--- a/src/tools/cargo/publish.py
+++ b/src/tools/cargo/publish.py
@@ -1,6 +1,12 @@
#!/usr/bin/env python3
# This script is used to publish Cargo to crates.io.
+#
+# This is run automatically every 6 weeks by the Release team's automation
+# whose source is at https://github.com/rust-lang/simpleinfra/.
+#
+# See https://doc.crates.io/contrib/process/release.html for more about
+# Cargo's release process.
import os
import re
diff --git a/src/tools/cargo/src/bin/cargo/cli.rs b/src/tools/cargo/src/bin/cargo/cli.rs
index db52bc8f2..eb337d681 100644
--- a/src/tools/cargo/src/bin/cargo/cli.rs
+++ b/src/tools/cargo/src/bin/cargo/cli.rs
@@ -518,12 +518,14 @@ pub fn cli() -> Command {
let usage = if is_rustup {
"cargo [+toolchain] [OPTIONS] [COMMAND]\n cargo [+toolchain] [OPTIONS] -Zscript <MANIFEST_RS> [ARGS]..."
} else {
- "cargo [OPTIONS] [COMMAND]\n cargo [OPTIONS] -Zscript <MANIFEST> [ARGS]..."
+ "cargo [OPTIONS] [COMMAND]\n cargo [OPTIONS] -Zscript <MANIFEST_RS> [ARGS]..."
};
Command::new("cargo")
// Subcommands all count their args' display order independently (from 0),
// which makes their args interspersed with global args. This puts global args last.
- .next_display_order(1000)
+ //
+ // We also want these to come before auto-generated `--help`
+ .next_display_order(800)
.allow_external_subcommands(true)
// Doesn't mix well with our list of common cargo commands. See clap-rs/clap#3108 for
// opening clap up to allow us to style our help template
@@ -586,9 +588,21 @@ See 'cargo help <command>' for more information on a specific command.\n",
.value_hint(clap::ValueHint::DirPath)
.value_parser(clap::builder::ValueParser::path_buf()),
)
- .arg(flag("frozen", "Require Cargo.lock and cache are up to date").global(true))
- .arg(flag("locked", "Require Cargo.lock is up to date").global(true))
- .arg(flag("offline", "Run without accessing the network").global(true))
+ .arg(
+ flag("frozen", "Require Cargo.lock and cache are up to date")
+ .help_heading(heading::MANIFEST_OPTIONS)
+ .global(true),
+ )
+ .arg(
+ flag("locked", "Require Cargo.lock is up to date")
+ .help_heading(heading::MANIFEST_OPTIONS)
+ .global(true),
+ )
+ .arg(
+ flag("offline", "Run without accessing the network")
+ .help_heading(heading::MANIFEST_OPTIONS)
+ .global(true),
+ )
.arg(multi_opt("config", "KEY=VALUE", "Override a configuration value").global(true))
.arg(
Arg::new("unstable-features")
diff --git a/src/tools/cargo/src/bin/cargo/commands/add.rs b/src/tools/cargo/src/bin/cargo/commands/add.rs
index 52fc38b74..56df76268 100644
--- a/src/tools/cargo/src/bin/cargo/commands/add.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/add.rs
@@ -79,8 +79,8 @@ Example uses:
])
.arg_manifest_path()
.arg_package("Package to modify")
- .arg_quiet()
.arg_dry_run("Don't actually write the manifest")
+ .arg_quiet()
.next_help_heading("Source")
.args([
clap::Arg::new("path")
diff --git a/src/tools/cargo/src/bin/cargo/commands/bench.rs b/src/tools/cargo/src/bin/cargo/commands/bench.rs
index 3739d880e..bb2c193b0 100644
--- a/src/tools/cargo/src/bin/cargo/commands/bench.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/bench.rs
@@ -4,7 +4,7 @@ use cargo::ops::{self, TestOptions};
pub fn cli() -> Command {
subcommand("bench")
.about("Execute all benchmarks of a local package")
- .arg_quiet()
+ .next_display_order(0)
.arg(
Arg::new("BENCHNAME")
.action(ArgAction::Set)
@@ -16,6 +16,19 @@ pub fn cli() -> Command {
.num_args(0..)
.last(true),
)
+ .arg(flag("no-run", "Compile, but don't run benchmarks"))
+ .arg(flag(
+ "no-fail-fast",
+ "Run all benchmarks regardless of failure",
+ ))
+ .arg_ignore_rust_version()
+ .arg_message_format()
+ .arg_quiet()
+ .arg_package_spec(
+ "Package to run benchmarks for",
+ "Benchmark all packages in the workspace",
+ "Exclude packages from the benchmark",
+ )
.arg_targets_all(
"Benchmark only this package's library",
"Benchmark only the specified binary",
@@ -28,31 +41,31 @@ pub fn cli() -> Command {
"Benchmark all benches",
"Benchmark all targets",
)
- .arg(flag("no-run", "Compile, but don't run benchmarks"))
- .arg_package_spec(
- "Package to run benchmarks for",
- "Benchmark all packages in the workspace",
- "Exclude packages from the benchmark",
- )
- .arg_jobs()
- .arg_profile("Build artifacts with the specified profile")
.arg_features()
+ .arg_jobs_without_keep_going()
+ .arg(flag("keep-going", "Use `--no-fail-fast` instead").hide(true)) // See rust-lang/cargo#11702
+ .arg_profile("Build artifacts with the specified profile")
.arg_target_triple("Build for the target triple")
.arg_target_dir()
- .arg_manifest_path()
- .arg_ignore_rust_version()
- .arg_message_format()
- .arg(flag(
- "no-fail-fast",
- "Run all benchmarks regardless of failure",
- ))
.arg_unit_graph()
.arg_timings()
+ .arg_manifest_path()
.after_help("Run `cargo help bench` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
+
+ if args.keep_going() {
+ return Err(anyhow::format_err!(
+ "\
+unexpected argument `--keep-going` found
+
+ tip: to run as many benchmarks as possible without failing fast, use `--no-fail-fast`"
+ )
+ .into());
+ }
+
let mut compile_opts = args.compile_options(
config,
CompileMode::Bench,
diff --git a/src/tools/cargo/src/bin/cargo/commands/build.rs b/src/tools/cargo/src/bin/cargo/commands/build.rs
index a78da38a4..e25638aa0 100644
--- a/src/tools/cargo/src/bin/cargo/commands/build.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/build.rs
@@ -7,13 +7,15 @@ pub fn cli() -> Command {
// subcommand aliases are handled in aliased_command()
// .alias("b")
.about("Compile a local package and all of its dependencies")
+ .arg_ignore_rust_version()
+ .arg_future_incompat_report()
+ .arg_message_format()
.arg_quiet()
.arg_package_spec(
"Package to build (see `cargo help pkgid`)",
"Build all packages in the workspace",
"Exclude packages from the build",
)
- .arg_jobs()
.arg_targets_all(
"Build only this package's library",
"Build only the specified binary",
@@ -26,9 +28,10 @@ pub fn cli() -> Command {
"Build all benches",
"Build all targets",
)
+ .arg_features()
.arg_release("Build artifacts in release mode, with optimizations")
.arg_profile("Build artifacts with the specified profile")
- .arg_features()
+ .arg_jobs()
.arg_target_triple("Build for the target triple")
.arg_target_dir()
.arg(
@@ -36,15 +39,13 @@ pub fn cli() -> Command {
"out-dir",
"Copy final artifacts to this directory (unstable)",
)
- .value_name("PATH"),
+ .value_name("PATH")
+ .help_heading(heading::COMPILATION_OPTIONS),
)
- .arg_manifest_path()
- .arg_ignore_rust_version()
- .arg_message_format()
.arg_build_plan()
.arg_unit_graph()
- .arg_future_incompat_report()
.arg_timings()
+ .arg_manifest_path()
.after_help("Run `cargo help build` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/check.rs b/src/tools/cargo/src/bin/cargo/commands/check.rs
index c9f6e0b38..ab6f99048 100644
--- a/src/tools/cargo/src/bin/cargo/commands/check.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/check.rs
@@ -7,13 +7,15 @@ pub fn cli() -> Command {
// subcommand aliases are handled in aliased_command()
// .alias("c")
.about("Check a local package and all of its dependencies for errors")
+ .arg_ignore_rust_version()
+ .arg_future_incompat_report()
+ .arg_message_format()
.arg_quiet()
.arg_package_spec(
"Package(s) to check",
"Check all packages in the workspace",
"Exclude packages from the check",
)
- .arg_jobs()
.arg_targets_all(
"Check only this package's library",
"Check only the specified binary",
@@ -26,17 +28,15 @@ pub fn cli() -> Command {
"Check all benches",
"Check all targets",
)
+ .arg_features()
+ .arg_jobs()
.arg_release("Check artifacts in release mode, with optimizations")
.arg_profile("Check artifacts with the specified profile")
- .arg_features()
.arg_target_triple("Check for the target triple")
.arg_target_dir()
- .arg_manifest_path()
- .arg_ignore_rust_version()
- .arg_message_format()
.arg_unit_graph()
- .arg_future_incompat_report()
.arg_timings()
+ .arg_manifest_path()
.after_help("Run `cargo help check` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/clean.rs b/src/tools/cargo/src/bin/cargo/commands/clean.rs
index 162461c47..9fa3c8527 100644
--- a/src/tools/cargo/src/bin/cargo/commands/clean.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/clean.rs
@@ -6,14 +6,14 @@ use cargo::util::print_available_packages;
pub fn cli() -> Command {
subcommand("clean")
.about("Remove artifacts that cargo has generated in the past")
+ .arg_doc("Whether or not to clean just the documentation directory")
.arg_quiet()
.arg_package_spec_simple("Package to clean artifacts for")
- .arg_manifest_path()
- .arg_target_triple("Target triple to clean output for")
- .arg_target_dir()
.arg_release("Whether or not to clean release artifacts")
.arg_profile("Clean artifacts of the specified profile")
- .arg_doc("Whether or not to clean just the documentation directory")
+ .arg_target_triple("Target triple to clean output for")
+ .arg_target_dir()
+ .arg_manifest_path()
.after_help("Run `cargo help clean` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/doc.rs b/src/tools/cargo/src/bin/cargo/commands/doc.rs
index 932058afb..c3dfe426d 100644
--- a/src/tools/cargo/src/bin/cargo/commands/doc.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/doc.rs
@@ -7,22 +7,24 @@ pub fn cli() -> Command {
// subcommand aliases are handled in aliased_command()
// .alias("d")
.about("Build a package's documentation")
- .arg_quiet()
.arg(flag(
"open",
"Opens the docs in a browser after the operation",
))
- .arg_package_spec(
- "Package to document",
- "Document all packages in the workspace",
- "Exclude packages from the build",
- )
.arg(flag(
"no-deps",
"Don't build documentation for dependencies",
))
.arg(flag("document-private-items", "Document private items"))
- .arg_jobs()
+ .arg_ignore_rust_version()
+ .arg_message_format()
+ .arg_quiet()
+ .arg_package_spec(
+ "Package to document",
+ "Document all packages in the workspace",
+ "Exclude packages from the build",
+ )
+ .arg_features()
.arg_targets_lib_bin_example(
"Document only this package's library",
"Document only the specified binary",
@@ -30,16 +32,14 @@ pub fn cli() -> Command {
"Document only the specified example",
"Document all examples",
)
+ .arg_jobs()
.arg_release("Build artifacts in release mode, with optimizations")
.arg_profile("Build artifacts with the specified profile")
- .arg_features()
.arg_target_triple("Build for the target triple")
.arg_target_dir()
- .arg_manifest_path()
- .arg_message_format()
- .arg_ignore_rust_version()
.arg_unit_graph()
.arg_timings()
+ .arg_manifest_path()
.after_help("Run `cargo help doc` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/fetch.rs b/src/tools/cargo/src/bin/cargo/commands/fetch.rs
index 2fbbc478c..4b1fcb40f 100644
--- a/src/tools/cargo/src/bin/cargo/commands/fetch.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/fetch.rs
@@ -7,8 +7,8 @@ pub fn cli() -> Command {
subcommand("fetch")
.about("Fetch dependencies of a package from the network")
.arg_quiet()
- .arg_manifest_path()
.arg_target_triple("Fetch dependencies for the target triple")
+ .arg_manifest_path()
.after_help("Run `cargo help fetch` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/fix.rs b/src/tools/cargo/src/bin/cargo/commands/fix.rs
index 5238d5852..1f98dd67e 100644
--- a/src/tools/cargo/src/bin/cargo/commands/fix.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/fix.rs
@@ -5,13 +5,35 @@ use cargo::ops;
pub fn cli() -> Command {
subcommand("fix")
.about("Automatically fix lint warnings reported by rustc")
+ .arg(flag("edition", "Fix in preparation for the next edition"))
+ .arg(flag(
+ "edition-idioms",
+ "Fix warnings to migrate to the idioms of an edition",
+ ))
+ .arg(flag(
+ "broken-code",
+ "Fix code even if it already has compiler errors",
+ ))
+ .arg(flag(
+ "allow-no-vcs",
+ "Fix code even if a VCS was not detected",
+ ))
+ .arg(flag(
+ "allow-dirty",
+ "Fix code even if the working directory is dirty",
+ ))
+ .arg(flag(
+ "allow-staged",
+ "Fix code even if the working directory has staged changes",
+ ))
+ .arg_ignore_rust_version()
+ .arg_message_format()
.arg_quiet()
.arg_package_spec(
"Package(s) to fix",
"Fix all packages in the workspace",
"Exclude packages from the fixes",
)
- .arg_jobs()
.arg_targets_all(
"Fix only this package's library",
"Fix only the specified binary",
@@ -24,36 +46,14 @@ pub fn cli() -> Command {
"Fix all benches",
"Fix all targets (default)",
)
+ .arg_features()
+ .arg_jobs()
.arg_release("Fix artifacts in release mode, with optimizations")
.arg_profile("Build artifacts with the specified profile")
- .arg_features()
.arg_target_triple("Fix for the target triple")
.arg_target_dir()
- .arg_manifest_path()
- .arg_message_format()
- .arg(flag(
- "broken-code",
- "Fix code even if it already has compiler errors",
- ))
- .arg(flag("edition", "Fix in preparation for the next edition"))
- .arg(flag(
- "edition-idioms",
- "Fix warnings to migrate to the idioms of an edition",
- ))
- .arg(flag(
- "allow-no-vcs",
- "Fix code even if a VCS was not detected",
- ))
- .arg(flag(
- "allow-dirty",
- "Fix code even if the working directory is dirty",
- ))
- .arg(flag(
- "allow-staged",
- "Fix code even if the working directory has staged changes",
- ))
- .arg_ignore_rust_version()
.arg_timings()
+ .arg_manifest_path()
.after_help("Run `cargo help fix` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/init.rs b/src/tools/cargo/src/bin/cargo/commands/init.rs
index b280d4fe4..fdb3dc208 100644
--- a/src/tools/cargo/src/bin/cargo/commands/init.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/init.rs
@@ -5,10 +5,10 @@ use cargo::ops;
pub fn cli() -> Command {
subcommand("init")
.about("Create a new cargo package in an existing directory")
- .arg_quiet()
.arg(Arg::new("path").action(ArgAction::Set).default_value("."))
- .arg(opt("registry", "Registry to use").value_name("REGISTRY"))
.arg_new_opts()
+ .arg(opt("registry", "Registry to use").value_name("REGISTRY"))
+ .arg_quiet()
.after_help("Run `cargo help init` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/install.rs b/src/tools/cargo/src/bin/cargo/commands/install.rs
index 3bb90c2d5..8abb00190 100644
--- a/src/tools/cargo/src/bin/cargo/commands/install.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/install.rs
@@ -10,7 +10,6 @@ use cargo_util::paths;
pub fn cli() -> Command {
subcommand("install")
.about("Install a Rust binary. Default location is $HOME/.cargo/bin")
- .arg_quiet()
.arg(
Arg::new("crate")
.value_parser(clap::builder::NonEmptyStringValueParser::new())
@@ -23,6 +22,18 @@ pub fn cli() -> Command {
.requires("crate"),
)
.arg(
+ opt("index", "Registry index to install from")
+ .value_name("INDEX")
+ .requires("crate")
+ .conflicts_with_all(&["git", "path", "registry"]),
+ )
+ .arg(
+ opt("registry", "Registry to use")
+ .value_name("REGISTRY")
+ .requires("crate")
+ .conflicts_with_all(&["git", "path", "index"]),
+ )
+ .arg(
opt("git", "Git URL to install the specified crate from")
.value_name("URL")
.conflicts_with_all(&["path", "index", "registry"]),
@@ -47,42 +58,31 @@ pub fn cli() -> Command {
.value_name("PATH")
.conflicts_with_all(&["git", "index", "registry"]),
)
- .arg(flag(
- "list",
- "list all installed packages and their versions",
- ))
- .arg_jobs()
+ .arg(opt("root", "Directory to install packages into").value_name("DIR"))
.arg(flag("force", "Force overwriting existing crates or binaries").short('f'))
.arg(flag("no-track", "Do not save tracking information"))
- .arg_features()
- .arg_profile("Install artifacts with the specified profile")
.arg(flag(
- "debug",
- "Build in debug mode (with the 'dev' profile) instead of release mode",
+ "list",
+ "list all installed packages and their versions",
))
+ .arg_ignore_rust_version()
+ .arg_message_format()
+ .arg_quiet()
.arg_targets_bins_examples(
"Install only the specified binary",
"Install all binaries",
"Install only the specified example",
"Install all examples",
)
+ .arg_features()
+ .arg_jobs()
+ .arg(flag(
+ "debug",
+ "Build in debug mode (with the 'dev' profile) instead of release mode",
+ ))
+ .arg_profile("Install artifacts with the specified profile")
.arg_target_triple("Build for the target triple")
.arg_target_dir()
- .arg(opt("root", "Directory to install packages into").value_name("DIR"))
- .arg(
- opt("index", "Registry index to install from")
- .value_name("INDEX")
- .requires("crate")
- .conflicts_with_all(&["git", "path", "registry"]),
- )
- .arg(
- opt("registry", "Registry to use")
- .value_name("REGISTRY")
- .requires("crate")
- .conflicts_with_all(&["git", "path", "index"]),
- )
- .arg_ignore_rust_version()
- .arg_message_format()
.arg_timings()
.after_help("Run `cargo help install` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/locate_project.rs b/src/tools/cargo/src/bin/cargo/commands/locate_project.rs
index 26c35cd91..69f015300 100644
--- a/src/tools/cargo/src/bin/cargo/commands/locate_project.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/locate_project.rs
@@ -6,8 +6,7 @@ use serde::Serialize;
pub fn cli() -> Command {
subcommand("locate-project")
.about("Print a JSON representation of a Cargo.toml file's location")
- .arg_quiet()
- .arg_manifest_path()
+ .arg(flag("workspace", "Locate Cargo.toml of the workspace root"))
.arg(
opt(
"message-format",
@@ -15,7 +14,8 @@ pub fn cli() -> Command {
)
.value_name("FMT"),
)
- .arg(flag("workspace", "Locate Cargo.toml of the workspace root"))
+ .arg_quiet()
+ .arg_manifest_path()
.after_help("Run `cargo help locate-project` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/login.rs b/src/tools/cargo/src/bin/cargo/commands/login.rs
index 1c8d3ae4c..e51adaa1c 100644
--- a/src/tools/cargo/src/bin/cargo/commands/login.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/login.rs
@@ -4,44 +4,31 @@ use cargo::ops;
pub fn cli() -> Command {
subcommand("login")
- .about(
- "Save an api token from the registry locally. \
- If token is not specified, it will be read from stdin.",
- )
- .arg_quiet()
+ .about("Log in to a registry.")
.arg(Arg::new("token").action(ArgAction::Set))
.arg(opt("registry", "Registry to use").value_name("REGISTRY"))
.arg(
- flag(
- "generate-keypair",
- "Generate a public/secret keypair (unstable)",
- )
- .conflicts_with("token"),
- )
- .arg(
- flag("secret-key", "Prompt for secret key (unstable)")
- .conflicts_with_all(&["generate-keypair", "token"]),
- )
- .arg(
- opt(
- "key-subject",
- "Set the key subject for this registry (unstable)",
- )
- .value_name("SUBJECT")
- .conflicts_with("token"),
+ Arg::new("args")
+ .help("Arguments for the credential provider (unstable)")
+ .num_args(0..)
+ .last(true),
)
+ .arg_quiet()
.after_help("Run `cargo help login` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let registry = args.registry(config)?;
+ let extra_args = args
+ .get_many::<String>("args")
+ .unwrap_or_default()
+ .map(String::as_str)
+ .collect::<Vec<_>>();
ops::registry_login(
config,
args.get_one::<String>("token").map(|s| s.as_str().into()),
registry.as_deref(),
- args.flag("generate-keypair"),
- args.flag("secret-key"),
- args.get_one("key-subject").map(String::as_str),
+ &extra_args,
)?;
Ok(())
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/logout.rs b/src/tools/cargo/src/bin/cargo/commands/logout.rs
index 0b4d8b83f..4320240c6 100644
--- a/src/tools/cargo/src/bin/cargo/commands/logout.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/logout.rs
@@ -4,8 +4,8 @@ use cargo::ops;
pub fn cli() -> Command {
subcommand("logout")
.about("Remove an API token from the registry locally")
- .arg_quiet()
.arg(opt("registry", "Registry to use").value_name("REGISTRY"))
+ .arg_quiet()
.after_help("Run `cargo help logout` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/metadata.rs b/src/tools/cargo/src/bin/cargo/commands/metadata.rs
index fdf59654c..54257dee3 100644
--- a/src/tools/cargo/src/bin/cargo/commands/metadata.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/metadata.rs
@@ -8,8 +8,6 @@ pub fn cli() -> Command {
the concrete used versions including overrides, \
in machine-readable format",
)
- .arg_quiet()
- .arg_features()
.arg(multi_opt(
"filter-platform",
"TRIPLE",
@@ -20,12 +18,14 @@ pub fn cli() -> Command {
"Output information only about the workspace members \
and don't fetch dependencies",
))
- .arg_manifest_path()
.arg(
opt("format-version", "Format version")
.value_name("VERSION")
.value_parser(["1"]),
)
+ .arg_quiet()
+ .arg_features()
+ .arg_manifest_path()
.after_help("Run `cargo help metadata` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/new.rs b/src/tools/cargo/src/bin/cargo/commands/new.rs
index 18cf93d2e..6124444c0 100644
--- a/src/tools/cargo/src/bin/cargo/commands/new.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/new.rs
@@ -5,10 +5,10 @@ use cargo::ops;
pub fn cli() -> Command {
subcommand("new")
.about("Create a new cargo package at <path>")
- .arg_quiet()
.arg(Arg::new("path").action(ArgAction::Set).required(true))
- .arg(opt("registry", "Registry to use").value_name("REGISTRY"))
.arg_new_opts()
+ .arg(opt("registry", "Registry to use").value_name("REGISTRY"))
+ .arg_quiet()
.after_help("Run `cargo help new` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/owner.rs b/src/tools/cargo/src/bin/cargo/commands/owner.rs
index 493072b7b..223327c31 100644
--- a/src/tools/cargo/src/bin/cargo/commands/owner.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/owner.rs
@@ -1,12 +1,11 @@
use crate::command_prelude::*;
use cargo::ops::{self, OwnersOptions};
-use cargo::util::auth::Secret;
+use cargo_credential::Secret;
pub fn cli() -> Command {
subcommand("owner")
.about("Manage the owners of a crate on the registry")
- .arg_quiet()
.arg(Arg::new("crate").action(ArgAction::Set))
.arg(
multi_opt(
@@ -28,6 +27,7 @@ pub fn cli() -> Command {
.arg(opt("index", "Registry index to modify owners for").value_name("INDEX"))
.arg(opt("token", "API token to use when authenticating").value_name("TOKEN"))
.arg(opt("registry", "Registry to use").value_name("REGISTRY"))
+ .arg_quiet()
.after_help("Run `cargo help owner` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/package.rs b/src/tools/cargo/src/bin/cargo/commands/package.rs
index ac6b1fe27..cf4ac795c 100644
--- a/src/tools/cargo/src/bin/cargo/commands/package.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/package.rs
@@ -5,7 +5,6 @@ use cargo::ops::{self, PackageOpts};
pub fn cli() -> Command {
subcommand("package")
.about("Assemble the local package into a distributable tarball")
- .arg_quiet()
.arg(
flag(
"list",
@@ -25,21 +24,29 @@ pub fn cli() -> Command {
"allow-dirty",
"Allow dirty working directories to be packaged",
))
- .arg_target_triple("Build for the target triple")
- .arg_target_dir()
- .arg_features()
+ .arg_quiet()
.arg_package_spec_no_all(
"Package(s) to assemble",
"Assemble all packages in the workspace",
"Don't assemble specified packages",
)
- .arg_manifest_path()
+ .arg_features()
+ .arg_target_triple("Build for the target triple")
+ .arg_target_dir()
.arg_jobs()
+ .arg_manifest_path()
.after_help("Run `cargo help package` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
+ if ws.root_maybe().is_embedded() {
+ return Err(anyhow::format_err!(
+ "{} is unsupported by `cargo package`",
+ ws.root_manifest().display()
+ )
+ .into());
+ }
let specs = args.packages_from_flags()?;
ops::package(
diff --git a/src/tools/cargo/src/bin/cargo/commands/pkgid.rs b/src/tools/cargo/src/bin/cargo/commands/pkgid.rs
index 664db75bd..ba4540cf1 100644
--- a/src/tools/cargo/src/bin/cargo/commands/pkgid.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/pkgid.rs
@@ -6,8 +6,8 @@ use cargo::util::print_available_packages;
pub fn cli() -> Command {
subcommand("pkgid")
.about("Print a fully qualified package specification")
- .arg_quiet()
.arg(Arg::new("spec").action(ArgAction::Set))
+ .arg_quiet()
.arg_package("Argument to get the package ID specifier for")
.arg_manifest_path()
.after_help("Run `cargo help pkgid` for more detailed information.\n")
@@ -15,6 +15,13 @@ pub fn cli() -> Command {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
+ if ws.root_maybe().is_embedded() {
+ return Err(anyhow::format_err!(
+ "{} is unsupported by `cargo pkgid`",
+ ws.root_manifest().display()
+ )
+ .into());
+ }
if args.is_present_with_zero_values("package") {
print_available_packages(&ws)?
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/publish.rs b/src/tools/cargo/src/bin/cargo/commands/publish.rs
index c831d399f..bda240c8c 100644
--- a/src/tools/cargo/src/bin/cargo/commands/publish.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/publish.rs
@@ -5,8 +5,9 @@ use cargo::ops::{self, PublishOpts};
pub fn cli() -> Command {
subcommand("publish")
.about("Upload a package to the registry")
- .arg_quiet()
+ .arg_dry_run("Perform all checks without uploading")
.arg_index()
+ .arg(opt("registry", "Registry to publish to").value_name("REGISTRY"))
.arg(opt("token", "Token to use when uploading").value_name("TOKEN"))
.arg(flag(
"no-verify",
@@ -16,20 +17,26 @@ pub fn cli() -> Command {
"allow-dirty",
"Allow dirty working directories to be packaged",
))
- .arg_target_triple("Build for the target triple")
- .arg_target_dir()
+ .arg_quiet()
.arg_package("Package to publish")
- .arg_manifest_path()
.arg_features()
.arg_jobs()
- .arg_dry_run("Perform all checks without uploading")
- .arg(opt("registry", "Registry to publish to").value_name("REGISTRY"))
+ .arg_target_triple("Build for the target triple")
+ .arg_target_dir()
+ .arg_manifest_path()
.after_help("Run `cargo help publish` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let registry = args.registry(config)?;
let ws = args.workspace(config)?;
+ if ws.root_maybe().is_embedded() {
+ return Err(anyhow::format_err!(
+ "{} is unsupported by `cargo publish`",
+ ws.root_manifest().display()
+ )
+ .into());
+ }
let index = args.index()?;
ops::publish(
diff --git a/src/tools/cargo/src/bin/cargo/commands/remove.rs b/src/tools/cargo/src/bin/cargo/commands/remove.rs
index 50bc8b7e6..798e6fff6 100644
--- a/src/tools/cargo/src/bin/cargo/commands/remove.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/remove.rs
@@ -1,5 +1,6 @@
use cargo::core::dependency::DepKind;
use cargo::core::PackageIdSpec;
+use cargo::core::Resolve;
use cargo::core::Workspace;
use cargo::ops::cargo_remove::remove;
use cargo::ops::cargo_remove::RemoveOptions;
@@ -24,10 +25,8 @@ pub fn cli() -> clap::Command {
.num_args(1..)
.value_name("DEP_ID")
.help("Dependencies to be removed")])
- .arg_package("Package to remove from")
- .arg_manifest_path()
- .arg_quiet()
.arg_dry_run("Don't actually write the manifest")
+ .arg_quiet()
.next_help_heading("Section")
.args([
clap::Arg::new("dev")
@@ -49,6 +48,8 @@ pub fn cli() -> clap::Command {
.value_parser(clap::builder::NonEmptyStringValueParser::new())
.help("Remove as dependency from the given target platform"),
])
+ .arg_package("Package to remove from")
+ .arg_manifest_path()
}
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
@@ -109,9 +110,24 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
// Reload the workspace since we've changed dependencies
let ws = args.workspace(config)?;
- resolve_ws(&ws)?;
- }
+ let resolve = {
+ // HACK: Avoid unused patch warnings by temporarily changing the verbosity.
+ // In rare cases, this might cause index update messages to not show up
+ let verbosity = ws.config().shell().verbosity();
+ ws.config()
+ .shell()
+ .set_verbosity(cargo::core::Verbosity::Quiet);
+ let resolve = resolve_ws(&ws);
+ ws.config().shell().set_verbosity(verbosity);
+ resolve?.1
+ };
+ // Attempt to gc unused patches and re-resolve if anything is removed
+ if gc_unused_patches(&workspace, &resolve)? {
+ let ws = args.workspace(config)?;
+ resolve_ws(&ws)?;
+ }
+ }
Ok(())
}
@@ -229,31 +245,6 @@ fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> {
}
}
- // Clean up the patch section
- if let Some(toml_edit::Item::Table(patch_section_table)) = manifest.get_mut("patch") {
- patch_section_table.set_implicit(true);
-
- // The key in each of the subtables is a source (either a registry or a URL)
- for (source, item) in patch_section_table.iter_mut() {
- if let toml_edit::Item::Table(patch_table) = item {
- patch_table.set_implicit(true);
-
- for (key, item) in patch_table.iter_mut() {
- let package_name =
- Dependency::from_toml(&workspace.root_manifest(), key.get(), item)?.name;
- if !source_has_match(
- &package_name,
- source.get(),
- &dependencies,
- workspace.config(),
- )? {
- *item = toml_edit::Item::None;
- }
- }
- }
- }
- }
-
// Clean up the replace section
if let Some(toml_edit::Item::Table(table)) = manifest.get_mut("replace") {
table.set_implicit(true);
@@ -310,35 +301,46 @@ fn spec_has_match(
Ok(false)
}
-/// Check whether or not a source (URL or registry name) matches any non-workspace dependencies.
-fn source_has_match(
- name: &str,
- source: &str,
- dependencies: &[Dependency],
- config: &Config,
-) -> CargoResult<bool> {
- for dep in dependencies {
- if &dep.name != name {
- continue;
- }
+/// Removes unused patches from the manifest
+fn gc_unused_patches(workspace: &Workspace<'_>, resolve: &Resolve) -> CargoResult<bool> {
+ let mut manifest: toml_edit::Document =
+ cargo_util::paths::read(workspace.root_manifest())?.parse()?;
+ let mut modified = false;
- match dep.source_id(config)? {
- MaybeWorkspace::Other(source_id) => {
- if source_id.is_registry() {
- if source_id.display_registry_name() == source
- || source_id.url().as_str() == source
+ // Clean up the patch section
+ if let Some(toml_edit::Item::Table(patch_section_table)) = manifest.get_mut("patch") {
+ patch_section_table.set_implicit(true);
+
+ for (_, item) in patch_section_table.iter_mut() {
+ if let toml_edit::Item::Table(patch_table) = item {
+ patch_table.set_implicit(true);
+
+ for (key, item) in patch_table.iter_mut() {
+ let dep = Dependency::from_toml(&workspace.root_manifest(), key.get(), item)?;
+
+ // Generate a PackageIdSpec url for querying
+ let url = if let MaybeWorkspace::Other(source_id) =
+ dep.source_id(workspace.config())?
{
- return Ok(true);
- }
- } else if source_id.is_git() {
- if source_id.url().as_str() == source {
- return Ok(true);
+ format!("{}#{}", source_id.url(), dep.name)
+ } else {
+ continue;
+ };
+
+ if PackageIdSpec::query_str(&url, resolve.unused_patches().iter().cloned())
+ .is_ok()
+ {
+ *item = toml_edit::Item::None;
+ modified = true;
}
}
}
- MaybeWorkspace::Workspace(_) => {}
}
}
- Ok(false)
+ if modified {
+ cargo_util::paths::write(workspace.root_manifest(), manifest.to_string().as_bytes())?;
+ }
+
+ Ok(modified)
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/run.rs b/src/tools/cargo/src/bin/cargo/commands/run.rs
index 366e19396..1649f72ac 100644
--- a/src/tools/cargo/src/bin/cargo/commands/run.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/run.rs
@@ -14,7 +14,6 @@ pub fn cli() -> Command {
// subcommand aliases are handled in aliased_command()
// .alias("r")
.about("Run a binary or example of the local package")
- .arg_quiet()
.arg(
Arg::new("args")
.help("Arguments for the binary or example to run")
@@ -22,21 +21,22 @@ pub fn cli() -> Command {
.num_args(0..)
.trailing_var_arg(true),
)
+ .arg_ignore_rust_version()
+ .arg_message_format()
+ .arg_quiet()
+ .arg_package("Package with the target to run")
.arg_targets_bin_example(
"Name of the bin target to run",
"Name of the example target to run",
)
- .arg_package("Package with the target to run")
+ .arg_features()
.arg_jobs()
.arg_release("Build artifacts in release mode, with optimizations")
.arg_profile("Build artifacts with the specified profile")
- .arg_features()
.arg_target_triple("Build for the target triple")
.arg_target_dir()
.arg_manifest_path()
- .arg_message_format()
.arg_unit_graph()
- .arg_ignore_rust_version()
.arg_timings()
.after_help("Run `cargo help run` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/rustc.rs b/src/tools/cargo/src/bin/cargo/commands/rustc.rs
index de73eb80c..0a0364e37 100644
--- a/src/tools/cargo/src/bin/cargo/commands/rustc.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/rustc.rs
@@ -8,15 +8,29 @@ const CRATE_TYPE_ARG_NAME: &str = "crate-type";
pub fn cli() -> Command {
subcommand("rustc")
.about("Compile a package, and pass extra options to the compiler")
- .arg_quiet()
.arg(
Arg::new("args")
.num_args(0..)
.help("Extra rustc flags")
.trailing_var_arg(true),
)
+ .arg(
+ opt(
+ PRINT_ARG_NAME,
+ "Output compiler information without compiling",
+ )
+ .value_name("INFO"),
+ )
+ .arg(multi_opt(
+ CRATE_TYPE_ARG_NAME,
+ "CRATE-TYPE",
+ "Comma separated list of types of crates for the compiler to emit",
+ ))
+ .arg_future_incompat_report()
+ .arg_ignore_rust_version()
+ .arg_message_format()
+ .arg_quiet()
.arg_package("Package to build")
- .arg_jobs()
.arg_targets_all(
"Build only this package's library",
"Build only the specified binary",
@@ -29,29 +43,15 @@ pub fn cli() -> Command {
"Build all benches",
"Build all targets",
)
+ .arg_features()
+ .arg_jobs()
.arg_release("Build artifacts in release mode, with optimizations")
.arg_profile("Build artifacts with the specified profile")
- .arg_features()
.arg_target_triple("Target triple which compiles will be for")
- .arg(
- opt(
- PRINT_ARG_NAME,
- "Output compiler information without compiling",
- )
- .value_name("INFO"),
- )
- .arg(multi_opt(
- CRATE_TYPE_ARG_NAME,
- "CRATE-TYPE",
- "Comma separated list of types of crates for the compiler to emit",
- ))
.arg_target_dir()
- .arg_manifest_path()
- .arg_message_format()
.arg_unit_graph()
- .arg_ignore_rust_version()
- .arg_future_incompat_report()
.arg_timings()
+ .arg_manifest_path()
.after_help("Run `cargo help rustc` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/rustdoc.rs b/src/tools/cargo/src/bin/cargo/commands/rustdoc.rs
index e87f435fd..488256ba7 100644
--- a/src/tools/cargo/src/bin/cargo/commands/rustdoc.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/rustdoc.rs
@@ -5,7 +5,6 @@ use crate::command_prelude::*;
pub fn cli() -> Command {
subcommand("rustdoc")
.about("Build a package's documentation, using specified custom flags.")
- .arg_quiet()
.arg(
Arg::new("args")
.help("Extra rustdoc flags")
@@ -16,8 +15,10 @@ pub fn cli() -> Command {
"open",
"Opens the docs in a browser after the operation",
))
+ .arg_ignore_rust_version()
+ .arg_message_format()
+ .arg_quiet()
.arg_package("Package to document")
- .arg_jobs()
.arg_targets_all(
"Build only this package's library",
"Build only the specified binary",
@@ -30,16 +31,15 @@ pub fn cli() -> Command {
"Build all benches",
"Build all targets",
)
+ .arg_features()
+ .arg_jobs()
.arg_release("Build artifacts in release mode, with optimizations")
.arg_profile("Build artifacts with the specified profile")
- .arg_features()
.arg_target_triple("Build for the target triple")
.arg_target_dir()
- .arg_manifest_path()
- .arg_message_format()
.arg_unit_graph()
- .arg_ignore_rust_version()
.arg_timings()
+ .arg_manifest_path()
.after_help("Run `cargo help rustdoc` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/search.rs b/src/tools/cargo/src/bin/cargo/commands/search.rs
index c55d932cc..656172e77 100644
--- a/src/tools/cargo/src/bin/cargo/commands/search.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/search.rs
@@ -7,9 +7,7 @@ use cargo::ops;
pub fn cli() -> Command {
subcommand("search")
.about("Search packages in crates.io")
- .arg_quiet()
.arg(Arg::new("query").num_args(0..))
- .arg_index()
.arg(
opt(
"limit",
@@ -17,7 +15,9 @@ pub fn cli() -> Command {
)
.value_name("LIMIT"),
)
+ .arg_index()
.arg(opt("registry", "Registry to use").value_name("REGISTRY"))
+ .arg_quiet()
.after_help("Run `cargo help search` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/test.rs b/src/tools/cargo/src/bin/cargo/commands/test.rs
index 607655aaf..80c935d62 100644
--- a/src/tools/cargo/src/bin/cargo/commands/test.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/test.rs
@@ -17,6 +17,12 @@ pub fn cli() -> Command {
.num_args(0..)
.last(true),
)
+ .arg(flag("doc", "Test only this library's documentation"))
+ .arg(flag("no-run", "Compile, but don't run tests"))
+ .arg(flag("no-fail-fast", "Run all tests regardless of failure"))
+ .arg_ignore_rust_version()
+ .arg_future_incompat_report()
+ .arg_message_format()
.arg(
flag(
"quiet",
@@ -24,6 +30,11 @@ pub fn cli() -> Command {
)
.short('q'),
)
+ .arg_package_spec(
+ "Package to run tests for",
+ "Test all packages in the workspace",
+ "Exclude packages from the test",
+ )
.arg_targets_all(
"Test only this package's library unit tests",
"Test only the specified binary",
@@ -34,28 +45,18 @@ pub fn cli() -> Command {
"Test all tests",
"Test only the specified bench target",
"Test all benches",
- "Test all targets",
- )
- .arg(flag("doc", "Test only this library's documentation"))
- .arg(flag("no-run", "Compile, but don't run tests"))
- .arg(flag("no-fail-fast", "Run all tests regardless of failure"))
- .arg_package_spec(
- "Package to run tests for",
- "Test all packages in the workspace",
- "Exclude packages from the test",
+ "Test all targets (does not include doctests)",
)
- .arg_jobs()
+ .arg_features()
+ .arg_jobs_without_keep_going()
+ .arg(flag("keep-going", "Use `--no-fail-fast` instead").hide(true)) // See rust-lang/cargo#11702
.arg_release("Build artifacts in release mode, with optimizations")
.arg_profile("Build artifacts with the specified profile")
- .arg_features()
.arg_target_triple("Build for the target triple")
.arg_target_dir()
- .arg_manifest_path()
- .arg_ignore_rust_version()
- .arg_message_format()
.arg_unit_graph()
- .arg_future_incompat_report()
.arg_timings()
+ .arg_manifest_path()
.after_help(
"Run `cargo help test` for more detailed information.\n\
Run `cargo test -- --help` for test binary options.\n",
@@ -65,6 +66,16 @@ pub fn cli() -> Command {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
+ if args.keep_going() {
+ return Err(anyhow::format_err!(
+ "\
+unexpected argument `--keep-going` found
+
+ tip: to run as many tests as possible without failing fast, use `--no-fail-fast`"
+ )
+ .into());
+ }
+
let mut compile_opts = args.compile_options(
config,
CompileMode::Test,
diff --git a/src/tools/cargo/src/bin/cargo/commands/tree.rs b/src/tools/cargo/src/bin/cargo/commands/tree.rs
index 94bf3fff1..4472765a9 100644
--- a/src/tools/cargo/src/bin/cargo/commands/tree.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/tree.rs
@@ -12,24 +12,12 @@ use std::str::FromStr;
pub fn cli() -> Command {
subcommand("tree")
.about("Display a tree visualization of a dependency graph")
- .arg_quiet()
- .arg_manifest_path()
- .arg_package_spec_no_all(
- "Package to be used as the root of the tree",
- "Display the tree for all packages in the workspace",
- "Exclude specific workspace members",
- )
.arg(
flag("all", "Deprecated, use --no-dedupe instead")
.short('a')
.hide(true),
)
- .arg(flag("all-targets", "Deprecated, use --target=all instead").hide(true))
- .arg_features()
- .arg_target_triple(
- "Filter dependencies matching the given target-triple (default host platform). \
- Pass `all` to include all targets.",
- )
+ .arg_quiet()
.arg(flag("no-dev-dependencies", "Deprecated, use -e=no-dev instead").hide(true))
.arg(
multi_opt(
@@ -96,6 +84,18 @@ pub fn cli() -> Command {
.short('V')
.hide(true),
)
+ .arg_package_spec_no_all(
+ "Package to be used as the root of the tree",
+ "Display the tree for all packages in the workspace",
+ "Exclude specific workspace members",
+ )
+ .arg_features()
+ .arg(flag("all-targets", "Deprecated, use --target=all instead").hide(true))
+ .arg_target_triple(
+ "Filter dependencies matching the given target-triple (default host platform). \
+ Pass `all` to include all targets.",
+ )
+ .arg_manifest_path()
.after_help("Run `cargo help tree` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/uninstall.rs b/src/tools/cargo/src/bin/cargo/commands/uninstall.rs
index 46654b668..398979bf4 100644
--- a/src/tools/cargo/src/bin/cargo/commands/uninstall.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/uninstall.rs
@@ -5,11 +5,14 @@ use cargo::ops;
pub fn cli() -> Command {
subcommand("uninstall")
.about("Remove a Rust binary")
- .arg_quiet()
.arg(Arg::new("spec").num_args(0..))
- .arg_package_spec_simple("Package to uninstall")
- .arg(multi_opt("bin", "NAME", "Only uninstall the binary NAME"))
.arg(opt("root", "Directory to uninstall packages from").value_name("DIR"))
+ .arg_quiet()
+ .arg_package_spec_simple("Package to uninstall")
+ .arg(
+ multi_opt("bin", "NAME", "Only uninstall the binary NAME")
+ .help_heading(heading::TARGET_SELECTION),
+ )
.after_help("Run `cargo help uninstall` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/update.rs b/src/tools/cargo/src/bin/cargo/commands/update.rs
index da33e8d30..31175ef16 100644
--- a/src/tools/cargo/src/bin/cargo/commands/update.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/update.rs
@@ -6,15 +6,15 @@ use cargo::util::print_available_packages;
pub fn cli() -> Command {
subcommand("update")
.about("Update dependencies as recorded in the local lock file")
- .arg_quiet()
- .arg(flag("workspace", "Only update the workspace packages").short('w'))
- .arg_package_spec_simple("Package to update")
- .arg(flag(
- "aggressive",
- "Force updating all dependencies of SPEC as well when used with -p",
- ))
.arg_dry_run("Don't actually write the lockfile")
.arg(
+ flag(
+ "aggressive",
+ "Force updating all dependencies of SPEC as well when used with -p",
+ )
+ .conflicts_with("precise"),
+ )
+ .arg(
opt(
"precise",
"Update a single dependency to exactly PRECISE when used with -p",
@@ -22,6 +22,13 @@ pub fn cli() -> Command {
.value_name("PRECISE")
.requires("package"),
)
+ .arg_quiet()
+ .arg(
+ flag("workspace", "Only update the workspace packages")
+ .short('w')
+ .help_heading(heading::PACKAGE_SELECTION),
+ )
+ .arg_package_spec_simple("Package to update")
.arg_manifest_path()
.after_help("Run `cargo help update` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/vendor.rs b/src/tools/cargo/src/bin/cargo/commands/vendor.rs
index 1fd79ec51..69b4ee380 100644
--- a/src/tools/cargo/src/bin/cargo/commands/vendor.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/vendor.rs
@@ -5,8 +5,6 @@ use std::path::PathBuf;
pub fn cli() -> Command {
subcommand("vendor")
.about("Vendor all dependencies for a project locally")
- .arg_quiet()
- .arg_manifest_path()
.arg(
Arg::new("path")
.action(ArgAction::Set)
@@ -38,6 +36,8 @@ pub fn cli() -> Command {
.arg(flag("relative-path", "Not supported").hide(true))
.arg(flag("only-git-deps", "Not supported").hide(true))
.arg(flag("disallow-duplicates", "Not supported").hide(true))
+ .arg_quiet()
+ .arg_manifest_path()
.after_help("Run `cargo help vendor` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/yank.rs b/src/tools/cargo/src/bin/cargo/commands/yank.rs
index 3dee52279..e6700bd2f 100644
--- a/src/tools/cargo/src/bin/cargo/commands/yank.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/yank.rs
@@ -1,12 +1,11 @@
use crate::command_prelude::*;
use cargo::ops;
-use cargo::util::auth::Secret;
+use cargo_credential::Secret;
pub fn cli() -> Command {
subcommand("yank")
.about("Remove a pushed crate from the index")
- .arg_quiet()
.arg(Arg::new("crate").action(ArgAction::Set))
.arg(
opt("version", "The version to yank or un-yank")
@@ -18,8 +17,9 @@ pub fn cli() -> Command {
"Undo a yank, putting a version back into the index",
))
.arg(opt("index", "Registry index to yank from").value_name("INDEX"))
- .arg(opt("token", "API token to use when authenticating").value_name("TOKEN"))
.arg(opt("registry", "Registry to use").value_name("REGISTRY"))
+ .arg(opt("token", "API token to use when authenticating").value_name("TOKEN"))
+ .arg_quiet()
.after_help("Run `cargo help yank` for more detailed information.\n")
}
diff --git a/src/tools/cargo/src/bin/cargo/main.rs b/src/tools/cargo/src/bin/cargo/main.rs
index 462332fb7..d96c1423d 100644
--- a/src/tools/cargo/src/bin/cargo/main.rs
+++ b/src/tools/cargo/src/bin/cargo/main.rs
@@ -20,10 +20,7 @@ mod commands;
use crate::command_prelude::*;
fn main() {
- #[cfg(feature = "pretty-env-logger")]
- pretty_env_logger::init_custom_env("CARGO_LOG");
- #[cfg(not(feature = "pretty-env-logger"))]
- env_logger::init_from_env("CARGO_LOG");
+ setup_logger();
let mut config = cli::LazyConfig::new();
@@ -40,6 +37,16 @@ fn main() {
}
}
+fn setup_logger() {
+ let env = tracing_subscriber::EnvFilter::from_env("CARGO_LOG");
+
+ tracing_subscriber::fmt()
+ .with_ansi(std::io::IsTerminal::is_terminal(&std::io::stderr()))
+ .with_writer(std::io::stderr)
+ .with_env_filter(env)
+ .init();
+}
+
/// Table for defining the aliases which come builtin in `Cargo`.
/// The contents are structured as: `(alias, aliased_command, description)`.
const BUILTIN_ALIASES: [(&str, &str, &str); 6] = [
diff --git a/src/tools/cargo/src/cargo/core/compiler/build_context/target_info.rs b/src/tools/cargo/src/cargo/core/compiler/build_context/target_info.rs
index e6e41c522..754adcf3c 100644
--- a/src/tools/cargo/src/cargo/core/compiler/build_context/target_info.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/build_context/target_info.rs
@@ -185,6 +185,12 @@ impl TargetInfo {
.args(&rustflags)
.env_remove("RUSTC_LOG");
+ // Removes `FD_CLOEXEC` set by `jobserver::Client` to pass jobserver
+ // as environment variables specify.
+ if let Some(client) = config.jobserver_from_env() {
+ process.inherit_jobserver(client);
+ }
+
if let CompileKind::Target(target) = kind {
process.arg("--target").arg(target.rustc_target());
}
@@ -1065,7 +1071,7 @@ impl RustDocFingerprint {
if fingerprint.rustc_vv == actual_rustdoc_target_data.rustc_vv {
return Ok(());
} else {
- log::debug!(
+ tracing::debug!(
"doc fingerprint changed:\noriginal:\n{}\nnew:\n{}",
fingerprint.rustc_vv,
actual_rustdoc_target_data.rustc_vv
@@ -1073,11 +1079,11 @@ impl RustDocFingerprint {
}
}
Err(e) => {
- log::debug!("could not deserialize {:?}: {}", fingerprint_path, e);
+ tracing::debug!("could not deserialize {:?}: {}", fingerprint_path, e);
}
};
// Fingerprint does not match, delete the doc directories and write a new fingerprint.
- log::debug!(
+ tracing::debug!(
"fingerprint {:?} mismatch, clearing doc directories",
fingerprint_path
);
diff --git a/src/tools/cargo/src/cargo/core/compiler/context/compilation_files.rs b/src/tools/cargo/src/cargo/core/compiler/context/compilation_files.rs
index 1c9d28461..126e17112 100644
--- a/src/tools/cargo/src/cargo/core/compiler/context/compilation_files.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/context/compilation_files.rs
@@ -7,7 +7,7 @@ use std::path::{Path, PathBuf};
use std::sync::Arc;
use lazycell::LazyCell;
-use log::debug;
+use tracing::debug;
use super::{BuildContext, CompileKind, Context, FileFlavor, Layout};
use crate::core::compiler::{CompileMode, CompileTarget, CrateType, FileType, Unit};
@@ -26,7 +26,7 @@ use crate::util::{self, CargoResult, StableHasher};
const METADATA_VERSION: u8 = 2;
/// The `Metadata` is a hash used to make unique file names for each unit in a
-/// build. It is also use for symbol mangling.
+/// build. It is also used for symbol mangling.
///
/// For example:
/// - A project may depend on crate `A` and crate `B`, so the package name must be in the file name.
diff --git a/src/tools/cargo/src/cargo/core/compiler/custom_build.rs b/src/tools/cargo/src/cargo/core/compiler/custom_build.rs
index d17462174..85306aaac 100644
--- a/src/tools/cargo/src/cargo/core/compiler/custom_build.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/custom_build.rs
@@ -690,7 +690,17 @@ impl BuildOutput {
continue;
}
let data = match iter.next() {
- Some(val) => val,
+ Some(val) => {
+ if val.starts_with(":") {
+ // Line started with `cargo::`.
+ bail!("unsupported output in {}: `{}`\n\
+ Found a `cargo::key=value` build directive which is reserved for future use.\n\
+ Either change the directive to `cargo:key=value` syntax (note the single `:`) or upgrade your version of Rust.\n\
+ See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script \
+ for more information about build script outputs.", whence, line);
+ }
+ val
+ }
None => continue,
};
diff --git a/src/tools/cargo/src/cargo/core/compiler/fingerprint/mod.rs b/src/tools/cargo/src/cargo/core/compiler/fingerprint/mod.rs
index aa8be50f7..2e6fb7eed 100644
--- a/src/tools/cargo/src/cargo/core/compiler/fingerprint/mod.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/fingerprint/mod.rs
@@ -366,10 +366,10 @@ use std::time::SystemTime;
use anyhow::{bail, format_err, Context as _};
use cargo_util::{paths, ProcessBuilder};
use filetime::FileTime;
-use log::{debug, info};
use serde::de;
use serde::ser;
use serde::{Deserialize, Serialize};
+use tracing::{debug, info};
use crate::core::compiler::unit_graph::UnitDep;
use crate::core::Package;
@@ -1815,7 +1815,7 @@ pub fn parse_dep_info(
let info = match EncodedDepInfo::parse(&data) {
Some(info) => info,
None => {
- log::warn!("failed to parse cargo's dep-info at {:?}", dep_info);
+ tracing::warn!("failed to parse cargo's dep-info at {:?}", dep_info);
return Ok(None);
}
};
@@ -1857,14 +1857,27 @@ where
Err(..) => return Some(StaleItem::MissingFile(reference.to_path_buf())),
};
+ let skipable_dirs = if let Ok(cargo_home) = home::cargo_home() {
+ let skipable_dirs: Vec<_> = ["git", "registry"]
+ .into_iter()
+ .map(|subfolder| cargo_home.join(subfolder))
+ .collect();
+ Some(skipable_dirs)
+ } else {
+ None
+ };
+
for path in paths {
let path = path.as_ref();
- // Assuming anything in cargo_home is immutable (see also #9455 about marking it readonly)
- // which avoids rebuilds when CI caches $CARGO_HOME/registry/{index, cache} and
- // $CARGO_HOME/git/db across runs, keeping the content the same but changing the mtime.
- if let Ok(true) = home::cargo_home().map(|home| path.starts_with(home)) {
- continue;
+ // Assuming anything in cargo_home/{git, registry} is immutable
+ // (see also #9455 about marking the src directory readonly) which avoids rebuilds when CI
+ // caches $CARGO_HOME/registry/{index, cache} and $CARGO_HOME/git/db across runs, keeping
+ // the content the same but changing the mtime.
+ if let Some(ref skipable_dirs) = skipable_dirs {
+ if skipable_dirs.iter().any(|dir| path.starts_with(dir)) {
+ continue;
+ }
}
let path_mtime = match mtime_cache.entry(path.to_path_buf()) {
Entry::Occupied(o) => *o.get(),
diff --git a/src/tools/cargo/src/cargo/core/compiler/future_incompat.rs b/src/tools/cargo/src/cargo/core/compiler/future_incompat.rs
index 955dfb8f2..ccea28b94 100644
--- a/src/tools/cargo/src/cargo/core/compiler/future_incompat.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/future_incompat.rs
@@ -417,7 +417,7 @@ pub fn save_and_display_report(
let current_reports = match OnDiskReports::load(bcx.ws) {
Ok(r) => r,
Err(e) => {
- log::debug!(
+ tracing::debug!(
"saving future-incompatible reports failed to load current reports: {:?}",
e
);
diff --git a/src/tools/cargo/src/cargo/core/compiler/job_queue/mod.rs b/src/tools/cargo/src/cargo/core/compiler/job_queue/mod.rs
index 6e8866b2b..26fcd4826 100644
--- a/src/tools/cargo/src/cargo/core/compiler/job_queue/mod.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/job_queue/mod.rs
@@ -125,8 +125,8 @@ use std::time::Duration;
use anyhow::{format_err, Context as _};
use cargo_util::ProcessBuilder;
use jobserver::{Acquired, HelperThread};
-use log::{debug, trace};
use semver::Version;
+use tracing::{debug, trace};
pub use self::job::Freshness::{self, Dirty, Fresh};
pub use self::job::{Job, Work};
@@ -840,7 +840,7 @@ impl<'cfg> DrainState<'cfg> {
}
err_state.count += 1;
} else {
- log::warn!("{:?}", new_err.error);
+ tracing::warn!("{:?}", new_err.error);
}
}
diff --git a/src/tools/cargo/src/cargo/core/compiler/mod.rs b/src/tools/cargo/src/cargo/core/compiler/mod.rs
index 31e63c226..7024a2ac5 100644
--- a/src/tools/cargo/src/cargo/core/compiler/mod.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/mod.rs
@@ -65,7 +65,7 @@ use std::sync::Arc;
use anyhow::{Context as _, Error};
use lazycell::LazyCell;
-use log::{debug, trace};
+use tracing::{debug, trace};
pub use self::build_config::{BuildConfig, CompileMode, MessageFormat, TimingOutput};
pub use self::build_context::{
@@ -368,7 +368,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
// See rust-lang/cargo#8348.
if output.hardlink.is_some() && output.path.exists() {
_ = paths::remove_file(&output.path).map_err(|e| {
- log::debug!(
+ tracing::debug!(
"failed to delete previous output file `{:?}`: {e:?}",
output.path
);
diff --git a/src/tools/cargo/src/cargo/core/compiler/output_depinfo.rs b/src/tools/cargo/src/cargo/core/compiler/output_depinfo.rs
index d659d620c..db98adf92 100644
--- a/src/tools/cargo/src/cargo/core/compiler/output_depinfo.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/output_depinfo.rs
@@ -9,7 +9,7 @@ use std::path::{Path, PathBuf};
use super::{fingerprint, Context, FileFlavor, Unit};
use crate::util::{internal, CargoResult};
use cargo_util::paths;
-use log::debug;
+use tracing::debug;
/// Bacially just normalizes a given path and converts it to a string.
fn render_filename<P: AsRef<Path>>(path: P, basedir: Option<&str>) -> CargoResult<String> {
diff --git a/src/tools/cargo/src/cargo/core/compiler/rustdoc.rs b/src/tools/cargo/src/cargo/core/compiler/rustdoc.rs
index f6fdd005a..aa4bd0dd4 100644
--- a/src/tools/cargo/src/cargo/core/compiler/rustdoc.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/rustdoc.rs
@@ -112,7 +112,7 @@ pub fn add_root_urls(
) -> CargoResult<()> {
let config = cx.bcx.config;
if !config.cli_unstable().rustdoc_map {
- log::debug!("`doc.extern-map` ignored, requires -Zrustdoc-map flag");
+ tracing::debug!("`doc.extern-map` ignored, requires -Zrustdoc-map flag");
return Ok(());
}
let map = config.doc_extern_map()?;
@@ -125,7 +125,7 @@ pub fn add_root_urls(
if let Ok(index_url) = config.get_registry_index(name) {
Some((name, index_url))
} else {
- log::warn!(
+ tracing::warn!(
"`doc.extern-map.{}` specifies a registry that is not defined",
name
);
@@ -181,7 +181,7 @@ pub fn add_root_urls(
})?;
Some(url.to_string())
} else {
- log::warn!(
+ tracing::warn!(
"`doc.extern-map.std` is \"local\", but local docs don't appear to exist at {}",
html_root.display()
);
diff --git a/src/tools/cargo/src/cargo/core/compiler/timings.js b/src/tools/cargo/src/cargo/core/compiler/timings.js
index 986070ab0..1b7e29e01 100644
--- a/src/tools/cargo/src/cargo/core/compiler/timings.js
+++ b/src/tools/cargo/src/cargo/core/compiler/timings.js
@@ -75,6 +75,8 @@ function render_pipeline_graph() {
ctx.translate(X_LINE, MARGIN);
// Compute x,y coordinate of each block.
+ // We also populate a map with the count of each unit name to disambiguate if necessary
+ const unitCount = new Map();
UNIT_COORDS = {};
for (i=0; i<units.length; i++) {
let unit = units[i];
@@ -86,6 +88,9 @@ function render_pipeline_graph() {
}
let width = Math.max(px_per_sec * unit.duration, 1.0);
UNIT_COORDS[unit.i] = {x, y, width, rmeta_x};
+
+ const count = unitCount.get(unit.name) || 0;
+ unitCount.set(unit.name, count + 1);
}
// Draw the blocks.
@@ -111,7 +116,10 @@ function render_pipeline_graph() {
ctx.textAlign = 'start';
ctx.textBaseline = 'middle';
ctx.font = '14px sans-serif';
- const label = `${unit.name}${unit.target} ${unit.duration}s`;
+
+ const labelName = (unitCount.get(unit.name) || 0) > 1 ? `${unit.name} (v${unit.version})${unit.target}` : `${unit.name}${unit.target}`;
+ const label = `${labelName}: ${unit.duration}s`;
+
const text_info = ctx.measureText(label);
const label_x = Math.min(x + 5.0, canvas_width - text_info.width - X_LINE);
ctx.fillText(label, label_x, y + BOX_HEIGHT / 2);
diff --git a/src/tools/cargo/src/cargo/core/compiler/timings.rs b/src/tools/cargo/src/cargo/core/compiler/timings.rs
index 0e0dc03ee..57ded9bf8 100644
--- a/src/tools/cargo/src/cargo/core/compiler/timings.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/timings.rs
@@ -122,7 +122,7 @@ impl<'cfg> Timings<'cfg> {
match State::current() {
Ok(state) => Some(state),
Err(e) => {
- log::info!("failed to get CPU state, CPU tracking disabled: {:?}", e);
+ tracing::info!("failed to get CPU state, CPU tracking disabled: {:?}", e);
None
}
}
@@ -276,7 +276,7 @@ impl<'cfg> Timings<'cfg> {
let current = match State::current() {
Ok(s) => s,
Err(e) => {
- log::info!("failed to get CPU state: {:?}", e);
+ tracing::info!("failed to get CPU state: {:?}", e);
return;
}
};
diff --git a/src/tools/cargo/src/cargo/core/compiler/unit_dependencies.rs b/src/tools/cargo/src/cargo/core/compiler/unit_dependencies.rs
index 369fd8318..686822356 100644
--- a/src/tools/cargo/src/cargo/core/compiler/unit_dependencies.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/unit_dependencies.rs
@@ -17,7 +17,7 @@
use std::collections::{HashMap, HashSet};
-use log::trace;
+use tracing::trace;
use crate::core::compiler::artifact::match_artifacts_kind_with_targets;
use crate::core::compiler::unit_graph::{UnitDep, UnitGraph};
diff --git a/src/tools/cargo/src/cargo/core/dependency.rs b/src/tools/cargo/src/cargo/core/dependency.rs
index 0b3aba8ad..c8fee6262 100644
--- a/src/tools/cargo/src/cargo/core/dependency.rs
+++ b/src/tools/cargo/src/cargo/core/dependency.rs
@@ -1,5 +1,4 @@
use cargo_platform::Platform;
-use log::trace;
use semver::VersionReq;
use serde::ser;
use serde::Serialize;
@@ -7,6 +6,7 @@ use std::borrow::Cow;
use std::fmt;
use std::path::PathBuf;
use std::rc::Rc;
+use tracing::trace;
use crate::core::compiler::{CompileKind, CompileTarget};
use crate::core::{PackageId, SourceId, Summary};
diff --git a/src/tools/cargo/src/cargo/core/package.rs b/src/tools/cargo/src/cargo/core/package.rs
index f4ab448d2..c84941462 100644
--- a/src/tools/cargo/src/cargo/core/package.rs
+++ b/src/tools/cargo/src/cargo/core/package.rs
@@ -13,9 +13,9 @@ use bytesize::ByteSize;
use curl::easy::Easy;
use curl::multi::{EasyHandle, Multi};
use lazycell::LazyCell;
-use log::debug;
use semver::Version;
use serde::Serialize;
+use tracing::debug;
use crate::core::compiler::{CompileKind, RustcTargetData};
use crate::core::dependency::DepKind;
@@ -25,7 +25,7 @@ use crate::core::source::MaybePackage;
use crate::core::{Dependency, Manifest, PackageId, SourceId, Target};
use crate::core::{SourceMap, Summary, Workspace};
use crate::util::config::PackageCacheLock;
-use crate::util::errors::{CargoResult, HttpNotSuccessful, DEBUG_HEADERS};
+use crate::util::errors::{CargoResult, HttpNotSuccessful};
use crate::util::interning::InternedString;
use crate::util::network::http::http_handle_and_timeout;
use crate::util::network::http::HttpTimeout;
@@ -748,9 +748,7 @@ impl<'a, 'cfg> Downloads<'a, 'cfg> {
// Headers contain trailing \r\n, trim them to make it easier
// to work with.
let h = String::from_utf8_lossy(data).trim().to_string();
- if DEBUG_HEADERS.iter().any(|p| h.starts_with(p)) {
- downloads.pending[&token].0.headers.borrow_mut().push(h);
- }
+ downloads.pending[&token].0.headers.borrow_mut().push(h);
}
});
true
diff --git a/src/tools/cargo/src/cargo/core/registry.rs b/src/tools/cargo/src/cargo/core/registry.rs
index e20531b70..da3d612d0 100644
--- a/src/tools/cargo/src/cargo/core/registry.rs
+++ b/src/tools/cargo/src/cargo/core/registry.rs
@@ -8,7 +8,7 @@ use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::{CanonicalUrl, Config};
use anyhow::{bail, Context as _};
-use log::{debug, trace};
+use tracing::{debug, trace};
use url::Url;
/// Source of information about a group of packages.
@@ -876,7 +876,7 @@ fn summary_for_patch(
// Since the locked patch did not match anything, try the unlocked one.
let orig_matches =
ready!(source.query_vec(orig_patch, QueryKind::Exact)).unwrap_or_else(|e| {
- log::warn!(
+ tracing::warn!(
"could not determine unlocked summaries for dep {:?}: {:?}",
orig_patch,
e
@@ -895,7 +895,7 @@ fn summary_for_patch(
let name_summaries =
ready!(source.query_vec(&name_only_dep, QueryKind::Exact)).unwrap_or_else(|e| {
- log::warn!(
+ tracing::warn!(
"failed to do name-only summary query for {:?}: {:?}",
name_only_dep,
e
diff --git a/src/tools/cargo/src/cargo/core/resolver/conflict_cache.rs b/src/tools/cargo/src/cargo/core/resolver/conflict_cache.rs
index 10c41761d..fba497506 100644
--- a/src/tools/cargo/src/cargo/core/resolver/conflict_cache.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/conflict_cache.rs
@@ -1,6 +1,6 @@
use std::collections::{BTreeMap, HashMap, HashSet};
-use log::trace;
+use tracing::trace;
use super::types::ConflictMap;
use crate::core::resolver::Context;
diff --git a/src/tools/cargo/src/cargo/core/resolver/context.rs b/src/tools/cargo/src/cargo/core/resolver/context.rs
index 4854dcde7..f19c678a6 100644
--- a/src/tools/cargo/src/cargo/core/resolver/context.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/context.rs
@@ -6,9 +6,9 @@ use crate::core::{Dependency, PackageId, SourceId, Summary};
use crate::util::interning::InternedString;
use crate::util::Graph;
use anyhow::format_err;
-use log::debug;
use std::collections::HashMap;
use std::num::NonZeroU64;
+use tracing::debug;
pub use super::encode::Metadata;
pub use super::encode::{EncodableDependency, EncodablePackageId, EncodableResolve};
diff --git a/src/tools/cargo/src/cargo/core/resolver/dep_cache.rs b/src/tools/cargo/src/cargo/core/resolver/dep_cache.rs
index 54b0ce97f..997533014 100644
--- a/src/tools/cargo/src/cargo/core/resolver/dep_cache.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/dep_cache.rs
@@ -23,10 +23,10 @@ use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use anyhow::Context as _;
-use log::debug;
use std::collections::{BTreeSet, HashMap, HashSet};
use std::rc::Rc;
use std::task::Poll;
+use tracing::debug;
pub struct RegistryQueryer<'a> {
pub registry: &'a mut (dyn Registry + 'a),
diff --git a/src/tools/cargo/src/cargo/core/resolver/encode.rs b/src/tools/cargo/src/cargo/core/resolver/encode.rs
index f73d023b1..1ee0d23f4 100644
--- a/src/tools/cargo/src/cargo/core/resolver/encode.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/encode.rs
@@ -117,13 +117,13 @@ use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::{internal, Graph};
use anyhow::{bail, Context as _};
-use log::debug;
use serde::de;
use serde::ser;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt;
use std::str::FromStr;
+use tracing::debug;
/// The `Cargo.lock` structure.
#[derive(Serialize, Deserialize, Debug)]
@@ -196,13 +196,13 @@ impl EncodableResolve {
let enc_id = EncodablePackageId {
name: pkg.name.clone(),
version: Some(pkg.version.clone()),
- source: pkg.source,
+ source: pkg.source.clone(),
};
if !all_pkgs.insert(enc_id.clone()) {
anyhow::bail!("package `{}` is specified twice in the lockfile", pkg.name);
}
- let id = match pkg.source.as_ref().or_else(|| path_deps.get(&pkg.name)) {
+ let id = match pkg.source.as_deref().or_else(|| path_deps.get(&pkg.name)) {
// We failed to find a local package in the workspace.
// It must have been removed and should be ignored.
None => {
@@ -366,7 +366,7 @@ impl EncodableResolve {
let mut unused_patches = Vec::new();
for pkg in self.patch.unused {
- let id = match pkg.source.as_ref().or_else(|| path_deps.get(&pkg.name)) {
+ let id = match pkg.source.as_deref().or_else(|| path_deps.get(&pkg.name)) {
Some(&src) => PackageId::new(&pkg.name, &pkg.version, src)?,
None => continue,
};
@@ -488,17 +488,95 @@ impl Patch {
pub struct EncodableDependency {
name: String,
version: String,
- source: Option<SourceId>,
+ source: Option<EncodableSourceId>,
checksum: Option<String>,
dependencies: Option<Vec<EncodablePackageId>>,
replace: Option<EncodablePackageId>,
}
+/// Pretty much equivalent to [`SourceId`] with a different serialization method.
+///
+/// The serialization for `SourceId` doesn't do URL encode for parameters.
+/// In contrast, this type is aware of that whenever [`ResolveVersion`] allows
+/// us to do so (v4 or later).
+///
+/// [`EncodableResolve`] turns into a `
+#[derive(Deserialize, Debug, PartialOrd, Ord, Clone)]
+#[serde(transparent)]
+pub struct EncodableSourceId {
+ inner: SourceId,
+ /// We don't care about the deserialization of this, as the `url` crate
+ /// will always decode as the URL was encoded. Only when a [`Resolve`]
+ /// turns into a [`EncodableResolve`] will it set the value accordingly
+ /// via [`encodable_source_id`].
+ #[serde(skip)]
+ encoded: bool,
+}
+
+impl EncodableSourceId {
+ /// Creates a `EncodableSourceId` that always encodes URL params.
+ fn new(inner: SourceId) -> Self {
+ Self {
+ inner,
+ encoded: true,
+ }
+ }
+
+ /// Creates a `EncodableSourceId` that doesn't encode URL params. This is
+ /// for backward compatibility for order lockfile version.
+ fn without_url_encoded(inner: SourceId) -> Self {
+ Self {
+ inner,
+ encoded: false,
+ }
+ }
+
+ /// Encodes the inner [`SourceId`] as a URL.
+ fn as_url(&self) -> impl fmt::Display + '_ {
+ if self.encoded {
+ self.inner.as_encoded_url()
+ } else {
+ self.inner.as_url()
+ }
+ }
+}
+
+impl std::ops::Deref for EncodableSourceId {
+ type Target = SourceId;
+
+ fn deref(&self) -> &Self::Target {
+ &self.inner
+ }
+}
+
+impl ser::Serialize for EncodableSourceId {
+ fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
+ where
+ S: ser::Serializer,
+ {
+ s.collect_str(&self.as_url())
+ }
+}
+
+impl std::hash::Hash for EncodableSourceId {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ self.inner.hash(state)
+ }
+}
+
+impl std::cmp::PartialEq for EncodableSourceId {
+ fn eq(&self, other: &Self) -> bool {
+ self.inner == other.inner
+ }
+}
+
+impl std::cmp::Eq for EncodableSourceId {}
+
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Hash, Clone)]
pub struct EncodablePackageId {
name: String,
version: Option<String>,
- source: Option<SourceId>,
+ source: Option<EncodableSourceId>,
}
impl fmt::Display for EncodablePackageId {
@@ -535,7 +613,8 @@ impl FromStr for EncodablePackageId {
Ok(EncodablePackageId {
name: name.to_string(),
version: version.map(|v| v.to_string()),
- source: source_id,
+ // Default to url encoded.
+ source: source_id.map(EncodableSourceId::new),
})
}
}
@@ -603,7 +682,7 @@ impl ser::Serialize for Resolve {
.map(|id| EncodableDependency {
name: id.name().to_string(),
version: id.version().to_string(),
- source: encode_source(id.source_id()),
+ source: encodable_source_id(id.source_id(), self.version()),
dependencies: None,
replace: None,
checksum: if self.version() >= ResolveVersion::V2 {
@@ -676,7 +755,7 @@ fn encodable_resolve_node(
EncodableDependency {
name: id.name().to_string(),
version: id.version().to_string(),
- source: encode_source(id.source_id()),
+ source: encodable_source_id(id.source_id(), resolve.version()),
dependencies: deps,
replace,
checksum: if resolve.version() >= ResolveVersion::V2 {
@@ -702,7 +781,7 @@ pub fn encodable_package_id(
}
}
}
- let mut source = encode_source(id_to_encode).map(|s| s.with_precise(None));
+ let mut source = encodable_source_id(id_to_encode.with_precise(None), resolve_version);
if let Some(counts) = &state.counts {
let version_counts = &counts[&id.name()];
if version_counts[&id.version()] == 1 {
@@ -719,10 +798,13 @@ pub fn encodable_package_id(
}
}
-fn encode_source(id: SourceId) -> Option<SourceId> {
+fn encodable_source_id(id: SourceId, version: ResolveVersion) -> Option<EncodableSourceId> {
if id.is_path() {
None
} else {
- Some(id)
+ Some(match version {
+ ResolveVersion::V4 => EncodableSourceId::new(id),
+ _ => EncodableSourceId::without_url_encoded(id),
+ })
}
}
diff --git a/src/tools/cargo/src/cargo/core/resolver/features.rs b/src/tools/cargo/src/cargo/core/resolver/features.rs
index 3670e8711..4518f9fe7 100644
--- a/src/tools/cargo/src/cargo/core/resolver/features.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/features.rs
@@ -470,7 +470,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
deferred_weak_dependencies: HashMap::new(),
};
r.do_resolve(specs, cli_features)?;
- log::debug!("features={:#?}", r.activated_features);
+ tracing::debug!("features={:#?}", r.activated_features);
if r.opts.compare {
r.compare();
}
@@ -518,7 +518,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
fk: FeaturesFor,
fvs: &[FeatureValue],
) -> CargoResult<()> {
- log::trace!("activate_pkg {} {}", pkg_id.name(), fk);
+ tracing::trace!("activate_pkg {} {}", pkg_id.name(), fk);
// Add an empty entry to ensure everything is covered. This is intended for
// finding bugs where the resolver missed something it should have visited.
// Remove this in the future if `activated_features` uses an empty default.
@@ -566,7 +566,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
fk: FeaturesFor,
fv: &FeatureValue,
) -> CargoResult<()> {
- log::trace!("activate_fv {} {} {}", pkg_id.name(), fk, fv);
+ tracing::trace!("activate_fv {} {} {}", pkg_id.name(), fk, fv);
match fv {
FeatureValue::Feature(f) => {
self.activate_rec(pkg_id, fk, *f)?;
@@ -593,7 +593,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
fk: FeaturesFor,
feature_to_enable: InternedString,
) -> CargoResult<()> {
- log::trace!(
+ tracing::trace!(
"activate_rec {} {} feat={}",
pkg_id.name(),
fk,
@@ -615,7 +615,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
// TODO: this should only happen for optional dependencies.
// Other cases should be validated by Summary's `build_feature_map`.
// Figure out some way to validate this assumption.
- log::debug!(
+ tracing::debug!(
"pkg {:?} does not define feature {}",
pkg_id,
feature_to_enable
@@ -654,7 +654,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
}
if let Some(to_enable) = &to_enable {
for dep_feature in to_enable {
- log::trace!(
+ tracing::trace!(
"activate deferred {} {} -> {}/{}",
pkg_id.name(),
fk,
@@ -697,7 +697,7 @@ impl<'a, 'cfg> FeatureResolver<'a, 'cfg> {
{
// This is weak, but not yet activated. Defer in case
// something comes along later and enables it.
- log::trace!(
+ tracing::trace!(
"deferring feature {} {} -> {}/{}",
pkg_id.name(),
fk,
diff --git a/src/tools/cargo/src/cargo/core/resolver/mod.rs b/src/tools/cargo/src/cargo/core/resolver/mod.rs
index b9c29fb87..e3da6fe5a 100644
--- a/src/tools/cargo/src/cargo/core/resolver/mod.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/mod.rs
@@ -63,7 +63,7 @@ use std::mem;
use std::rc::Rc;
use std::time::{Duration, Instant};
-use log::{debug, trace};
+use tracing::{debug, trace};
use crate::core::PackageIdSpec;
use crate::core::{Dependency, PackageId, Registry, Summary};
diff --git a/src/tools/cargo/src/cargo/core/resolver/resolve.rs b/src/tools/cargo/src/cargo/core/resolver/resolve.rs
index 8405a1245..18a389773 100644
--- a/src/tools/cargo/src/cargo/core/resolver/resolve.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/resolve.rs
@@ -83,6 +83,8 @@ pub enum ResolveVersion {
/// Unstable. Will collect a certain amount of changes and then go.
///
/// Changes made:
+ ///
+ /// * SourceId URL serialization is aware of URL encoding.
V4,
}
diff --git a/src/tools/cargo/src/cargo/core/source/source_id.rs b/src/tools/cargo/src/cargo/core/source/source_id.rs
index 4064364d5..6bbc07a5d 100644
--- a/src/tools/cargo/src/cargo/core/source/source_id.rs
+++ b/src/tools/cargo/src/cargo/core/source/source_id.rs
@@ -3,7 +3,6 @@ use crate::sources::registry::CRATES_IO_HTTP_INDEX;
use crate::sources::{DirectorySource, CRATES_IO_DOMAIN, CRATES_IO_INDEX, CRATES_IO_REGISTRY};
use crate::sources::{GitSource, PathSource, RegistrySource};
use crate::util::{config, CanonicalUrl, CargoResult, Config, IntoUrl};
-use log::trace;
use serde::de;
use serde::ser;
use std::cmp::{self, Ordering};
@@ -14,6 +13,7 @@ use std::path::{Path, PathBuf};
use std::ptr;
use std::sync::Mutex;
use std::sync::OnceLock;
+use tracing::trace;
use url::Url;
static SOURCE_ID_CACHE: OnceLock<Mutex<HashSet<&'static SourceIdInner>>> = OnceLock::new();
@@ -195,6 +195,15 @@ impl SourceId {
pub fn as_url(&self) -> SourceIdAsUrl<'_> {
SourceIdAsUrl {
inner: &*self.inner,
+ encoded: false,
+ }
+ }
+
+ /// Like [`Self::as_url`] but with URL parameters encoded.
+ pub fn as_encoded_url(&self) -> SourceIdAsUrl<'_> {
+ SourceIdAsUrl {
+ inner: &*self.inner,
+ encoded: true,
}
}
@@ -566,7 +575,10 @@ impl fmt::Display for SourceId {
// Don't replace the URL display for git references,
// because those are kind of expected to be URLs.
write!(f, "{}", self.inner.url)?;
- if let Some(pretty) = reference.pretty_ref() {
+ // TODO(-Znext-lockfile-bump): set it to true when stabilizing
+ // lockfile v4, because we want Source ID serialization to be
+ // consistent with lockfile.
+ if let Some(pretty) = reference.pretty_ref(false) {
write!(f, "?{}", pretty)?;
}
@@ -714,6 +726,7 @@ impl Ord for SourceKind {
/// A `Display`able view into a `SourceId` that will write it as a url
pub struct SourceIdAsUrl<'a> {
inner: &'a SourceIdInner,
+ encoded: bool,
}
impl<'a> fmt::Display for SourceIdAsUrl<'a> {
@@ -731,7 +744,7 @@ impl<'a> fmt::Display for SourceIdAsUrl<'a> {
..
} => {
write!(f, "git+{}", url)?;
- if let Some(pretty) = reference.pretty_ref() {
+ if let Some(pretty) = reference.pretty_ref(self.encoded) {
write!(f, "?{}", pretty)?;
}
if let Some(precise) = precise.as_ref() {
@@ -771,10 +784,13 @@ impl<'a> fmt::Display for SourceIdAsUrl<'a> {
impl GitReference {
/// Returns a `Display`able view of this git reference, or None if using
/// the head of the default branch
- pub fn pretty_ref(&self) -> Option<PrettyRef<'_>> {
+ pub fn pretty_ref(&self, url_encoded: bool) -> Option<PrettyRef<'_>> {
match self {
GitReference::DefaultBranch => None,
- _ => Some(PrettyRef { inner: self }),
+ _ => Some(PrettyRef {
+ inner: self,
+ url_encoded,
+ }),
}
}
}
@@ -782,16 +798,35 @@ impl GitReference {
/// A git reference that can be `Display`ed
pub struct PrettyRef<'a> {
inner: &'a GitReference,
+ url_encoded: bool,
}
impl<'a> fmt::Display for PrettyRef<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match *self.inner {
- GitReference::Branch(ref b) => write!(f, "branch={}", b),
- GitReference::Tag(ref s) => write!(f, "tag={}", s),
- GitReference::Rev(ref s) => write!(f, "rev={}", s),
+ let value: &str;
+ match self.inner {
+ GitReference::Branch(s) => {
+ write!(f, "branch=")?;
+ value = s;
+ }
+ GitReference::Tag(s) => {
+ write!(f, "tag=")?;
+ value = s;
+ }
+ GitReference::Rev(s) => {
+ write!(f, "rev=")?;
+ value = s;
+ }
GitReference::DefaultBranch => unreachable!(),
}
+ if self.url_encoded {
+ for value in url::form_urlencoded::byte_serialize(value.as_bytes()) {
+ write!(f, "{value}")?;
+ }
+ } else {
+ write!(f, "{value}")?;
+ }
+ Ok(())
}
}
@@ -905,6 +940,27 @@ mod tests {
assert_eq!(formatted, "sparse+https://my-crates.io/");
assert_eq!(source_id, deserialized);
}
+
+ #[test]
+ fn gitrefs_roundtrip() {
+ let base = "https://host/path".into_url().unwrap();
+ let branch = GitReference::Branch("*-._+20%30 Z/z#foo=bar&zap[]?to\\()'\"".to_string());
+ let s1 = SourceId::for_git(&base, branch).unwrap();
+ let ser1 = format!("{}", s1.as_encoded_url());
+ let s2 = SourceId::from_url(&ser1).expect("Failed to deserialize");
+ let ser2 = format!("{}", s2.as_encoded_url());
+ // Serializing twice should yield the same result
+ assert_eq!(ser1, ser2, "Serialized forms don't match");
+ // SourceId serializing the same should have the same semantics
+ // This used to not be the case (# was ambiguous)
+ assert_eq!(s1, s2, "SourceId doesn't round-trip");
+ // Freeze the format to match an x-www-form-urlencoded query string
+ // https://url.spec.whatwg.org/#application/x-www-form-urlencoded
+ assert_eq!(
+ ser1,
+ "git+https://host/path?branch=*-._%2B20%2530+Z%2Fz%23foo%3Dbar%26zap%5B%5D%3Fto%5C%28%29%27%22"
+ );
+ }
}
/// Check if `url` equals to the overridden crates.io URL.
diff --git a/src/tools/cargo/src/cargo/core/workspace.rs b/src/tools/cargo/src/cargo/core/workspace.rs
index db9c18010..9ee0cbe04 100644
--- a/src/tools/cargo/src/cargo/core/workspace.rs
+++ b/src/tools/cargo/src/cargo/core/workspace.rs
@@ -7,7 +7,7 @@ use std::rc::Rc;
use anyhow::{anyhow, bail, Context as _};
use glob::glob;
use itertools::Itertools;
-use log::debug;
+use tracing::debug;
use url::Url;
use crate::core::compiler::Unit;
diff --git a/src/tools/cargo/src/cargo/lib.rs b/src/tools/cargo/src/cargo/lib.rs
index a03d51199..9f6edf80d 100644
--- a/src/tools/cargo/src/cargo/lib.rs
+++ b/src/tools/cargo/src/cargo/lib.rs
@@ -147,7 +147,7 @@
use crate::core::shell::Verbosity::Verbose;
use crate::core::Shell;
use anyhow::Error;
-use log::debug;
+use tracing::debug;
pub use crate::util::errors::{AlreadyPrintedError, InternalError, VerboseError};
pub use crate::util::{indented_lines, CargoResult, CliError, CliResult, Config};
diff --git a/src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs b/src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs
index f53a9e934..1247ceda7 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs
@@ -5,7 +5,7 @@
//! rough outline is:
//!
//! 1. Resolve the dependency graph (see [`ops::resolve`]).
-//! 2. Download any packages needed (see [`PackageSet`](crate::core::PackageSet)).
+//! 2. Download any packages needed (see [`PackageSet`].
//! 3. Generate a list of top-level "units" of work for the targets the user
//! requested on the command-line. Each [`Unit`] corresponds to a compiler
//! invocation. This is done in this module ([`UnitGenerator::generate_root_units`]).
@@ -753,7 +753,7 @@ fn remove_duplicate_doc(
.into_iter()
.partition(|unit| cb(unit) && !root_units.contains(unit));
for unit in to_remove {
- log::debug!(
+ tracing::debug!(
"removing duplicate doc due to {} for package {} target `{}`",
reason,
unit.pkg,
diff --git a/src/tools/cargo/src/cargo/ops/cargo_generate_lockfile.rs b/src/tools/cargo/src/cargo/ops/cargo_generate_lockfile.rs
index 6267b08f5..fddf83f19 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_generate_lockfile.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_generate_lockfile.rs
@@ -6,9 +6,9 @@ use crate::ops;
use crate::util::config::Config;
use crate::util::CargoResult;
use anyhow::Context;
-use log::debug;
use std::collections::{BTreeMap, HashSet};
use termcolor::Color::{self, Cyan, Green, Red, Yellow};
+use tracing::debug;
pub struct UpdateOptions<'a> {
pub config: &'a Config,
diff --git a/src/tools/cargo/src/cargo/ops/cargo_new.rs b/src/tools/cargo/src/cargo/ops/cargo_new.rs
index b113671b0..0809cefc3 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_new.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_new.rs
@@ -879,7 +879,7 @@ mod tests {
.arg(&path_of_source_file)
.exec_with_output()
{
- log::warn!("failed to call rustfmt: {:#}", e);
+ tracing::warn!("failed to call rustfmt: {:#}", e);
}
}
}
diff --git a/src/tools/cargo/src/cargo/ops/cargo_package.rs b/src/tools/cargo/src/cargo/ops/cargo_package.rs
index a322afbb3..93469607b 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_package.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_package.rs
@@ -22,9 +22,10 @@ use anyhow::Context as _;
use cargo_util::paths;
use flate2::read::GzDecoder;
use flate2::{Compression, GzBuilder};
-use log::debug;
use serde::Serialize;
use tar::{Archive, Builder, EntryType, Header, HeaderMode};
+use tracing::debug;
+use unicase::Ascii as UncasedAscii;
pub struct PackageOpts<'cfg> {
pub config: &'cfg Config,
@@ -227,58 +228,84 @@ fn build_ar_list(
src_files: Vec<PathBuf>,
vcs_info: Option<VcsInfo>,
) -> CargoResult<Vec<ArchiveFile>> {
- let mut result = Vec::new();
+ let mut result = HashMap::new();
let root = pkg.root();
- for src_file in src_files {
- let rel_path = src_file.strip_prefix(&root)?.to_path_buf();
- check_filename(&rel_path, &mut ws.config().shell())?;
- let rel_str = rel_path
- .to_str()
- .ok_or_else(|| {
- anyhow::format_err!("non-utf8 path in source directory: {}", rel_path.display())
- })?
- .to_string();
+
+ for src_file in &src_files {
+ let rel_path = src_file.strip_prefix(&root)?;
+ check_filename(rel_path, &mut ws.config().shell())?;
+ let rel_str = rel_path.to_str().ok_or_else(|| {
+ anyhow::format_err!("non-utf8 path in source directory: {}", rel_path.display())
+ })?;
match rel_str.as_ref() {
- "Cargo.toml" => {
- result.push(ArchiveFile {
- rel_path: PathBuf::from(ORIGINAL_MANIFEST_FILE),
- rel_str: ORIGINAL_MANIFEST_FILE.to_string(),
- contents: FileContents::OnDisk(src_file),
- });
- result.push(ArchiveFile {
- rel_path,
- rel_str,
- contents: FileContents::Generated(GeneratedFile::Manifest),
- });
- }
"Cargo.lock" => continue,
VCS_INFO_FILE | ORIGINAL_MANIFEST_FILE => anyhow::bail!(
"invalid inclusion of reserved file name {} in package source",
rel_str
),
_ => {
- result.push(ArchiveFile {
- rel_path,
- rel_str,
- contents: FileContents::OnDisk(src_file),
- });
+ result
+ .entry(UncasedAscii::new(rel_str))
+ .or_insert_with(Vec::new)
+ .push(ArchiveFile {
+ rel_path: rel_path.to_owned(),
+ rel_str: rel_str.to_owned(),
+ contents: FileContents::OnDisk(src_file.clone()),
+ });
}
}
}
+
+ // Ensure we normalize for case insensitive filesystems (like on Windows) by removing the
+ // existing entry, regardless of case, and adding in with the correct case
+ if result.remove(&UncasedAscii::new("Cargo.toml")).is_some() {
+ result
+ .entry(UncasedAscii::new(ORIGINAL_MANIFEST_FILE))
+ .or_insert_with(Vec::new)
+ .push(ArchiveFile {
+ rel_path: PathBuf::from(ORIGINAL_MANIFEST_FILE),
+ rel_str: ORIGINAL_MANIFEST_FILE.to_string(),
+ contents: FileContents::OnDisk(pkg.manifest_path().to_owned()),
+ });
+ result
+ .entry(UncasedAscii::new("Cargo.toml"))
+ .or_insert_with(Vec::new)
+ .push(ArchiveFile {
+ rel_path: PathBuf::from("Cargo.toml"),
+ rel_str: "Cargo.toml".to_string(),
+ contents: FileContents::Generated(GeneratedFile::Manifest),
+ });
+ } else {
+ ws.config().shell().warn(&format!(
+ "no `Cargo.toml` file found when packaging `{}` (note the case of the file name).",
+ pkg.name()
+ ))?;
+ }
+
if pkg.include_lockfile() {
- result.push(ArchiveFile {
- rel_path: PathBuf::from("Cargo.lock"),
- rel_str: "Cargo.lock".to_string(),
- contents: FileContents::Generated(GeneratedFile::Lockfile),
- });
+ let rel_str = "Cargo.lock";
+ result
+ .entry(UncasedAscii::new(rel_str))
+ .or_insert_with(Vec::new)
+ .push(ArchiveFile {
+ rel_path: PathBuf::from(rel_str),
+ rel_str: rel_str.to_string(),
+ contents: FileContents::Generated(GeneratedFile::Lockfile),
+ });
}
if let Some(vcs_info) = vcs_info {
- result.push(ArchiveFile {
- rel_path: PathBuf::from(VCS_INFO_FILE),
- rel_str: VCS_INFO_FILE.to_string(),
- contents: FileContents::Generated(GeneratedFile::VcsInfo(vcs_info)),
- });
- }
+ let rel_str = VCS_INFO_FILE;
+ result
+ .entry(UncasedAscii::new(rel_str))
+ .or_insert_with(Vec::new)
+ .push(ArchiveFile {
+ rel_path: PathBuf::from(rel_str),
+ rel_str: rel_str.to_string(),
+ contents: FileContents::Generated(GeneratedFile::VcsInfo(vcs_info)),
+ });
+ }
+
+ let mut result = result.into_values().flatten().collect();
if let Some(license_file) = &pkg.manifest().metadata().license_file {
let license_path = Path::new(license_file);
let abs_file_path = paths::normalize_path(&pkg.root().join(license_path));
diff --git a/src/tools/cargo/src/cargo/ops/cargo_read_manifest.rs b/src/tools/cargo/src/cargo/ops/cargo_read_manifest.rs
index 2dfe90086..d9daea5da 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_read_manifest.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_read_manifest.rs
@@ -9,7 +9,7 @@ use crate::util::important_paths::find_project_manifest_exact;
use crate::util::toml::read_manifest;
use crate::util::Config;
use cargo_util::paths;
-use log::{info, trace};
+use tracing::{info, trace};
pub fn read_package(
path: &Path,
diff --git a/src/tools/cargo/src/cargo/ops/cargo_run.rs b/src/tools/cargo/src/cargo/ops/cargo_run.rs
index 53916715a..adf144ac2 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_run.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_run.rs
@@ -100,6 +100,10 @@ pub fn run(
// by `compile.target_process` (the package's root directory)
process.args(args).cwd(config.cwd());
+ if config.extra_verbose() {
+ process.display_env_vars();
+ }
+
config.shell().status("Running", process.to_string())?;
process.exec_replace()
diff --git a/src/tools/cargo/src/cargo/ops/cargo_test.rs b/src/tools/cargo/src/cargo/ops/cargo_test.rs
index 1ddf7755f..0d0bd800f 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_test.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_test.rs
@@ -126,7 +126,7 @@ fn run_unit_tests(
script_meta,
} in compilation.tests.iter()
{
- let (exe_display, cmd) = cmd_builds(
+ let (exe_display, mut cmd) = cmd_builds(
config,
cwd,
unit,
@@ -136,6 +136,11 @@ fn run_unit_tests(
compilation,
"unittests",
)?;
+
+ if config.extra_verbose() {
+ cmd.display_env_vars();
+ }
+
config
.shell()
.concise(|shell| shell.status("Running", &exe_display))?;
@@ -149,7 +154,7 @@ fn run_unit_tests(
unit: unit.clone(),
kind: test_kind,
};
- report_test_error(ws, &options.compile_opts, &unit_err, e);
+ report_test_error(ws, test_args, &options.compile_opts, &unit_err, e);
errors.push(unit_err);
if !options.no_fail_fast {
return Err(CliError::code(code));
@@ -260,22 +265,29 @@ fn run_doc_tests(
p.arg("--test-args").arg("--quiet");
}
+ p.args(unit.pkg.manifest().lint_rustflags());
+
p.args(args);
if *unstable_opts {
p.arg("-Zunstable-options");
}
+ if config.extra_verbose() {
+ p.display_env_vars();
+ }
+
config
.shell()
.verbose(|shell| shell.status("Running", p.to_string()))?;
+
if let Err(e) = p.exec() {
let code = fail_fast_code(&e);
let unit_err = UnitTestError {
unit: unit.clone(),
kind: TestKind::Doctest,
};
- report_test_error(ws, &options.compile_opts, &unit_err, e);
+ report_test_error(ws, test_args, &options.compile_opts, &unit_err, e);
errors.push(unit_err);
if !options.no_fail_fast {
return Err(CliError::code(code));
@@ -407,6 +419,7 @@ fn no_fail_fast_err(
/// Displays an error on the console about a test failure.
fn report_test_error(
ws: &Workspace<'_>,
+ test_args: &[&str],
opts: &ops::CompileOptions,
unit_err: &UnitTestError,
test_error: anyhow::Error,
@@ -420,13 +433,23 @@ fn report_test_error(
let mut err = format_err!("{}, to rerun pass `{}`", which, unit_err.cli_args(ws, opts));
// Don't show "process didn't exit successfully" for simple errors.
// libtest exits with 101 for normal errors.
- let is_simple = test_error
+ let (is_simple, executed) = test_error
.downcast_ref::<ProcessError>()
.and_then(|proc_err| proc_err.code)
- .map_or(false, |code| code == 101);
+ .map_or((false, false), |code| (code == 101, true));
+
if !is_simple {
err = test_error.context(err);
}
crate::display_error(&err, &mut ws.config().shell());
+
+ let harness: bool = unit_err.unit.target.harness();
+ let nocapture: bool = test_args.contains(&"--nocapture");
+
+ if !is_simple && executed && harness && !nocapture {
+ drop(ws.config().shell().note(
+ "test exited abnormally; to see the full output pass --nocapture to the harness.",
+ ));
+ }
}
diff --git a/src/tools/cargo/src/cargo/ops/fix.rs b/src/tools/cargo/src/cargo/ops/fix.rs
index be24967f8..0e678d61c 100644
--- a/src/tools/cargo/src/cargo/ops/fix.rs
+++ b/src/tools/cargo/src/cargo/ops/fix.rs
@@ -46,10 +46,10 @@ use std::{env, fs, str};
use anyhow::{bail, Context as _};
use cargo_util::{exit_status_to_string, is_simple_exit_code, paths, ProcessBuilder};
-use log::{debug, trace, warn};
use rustfix::diagnostics::Diagnostic;
use rustfix::{self, CodeFix};
use semver::Version;
+use tracing::{debug, trace, warn};
use crate::core::compiler::RustcTargetData;
use crate::core::resolver::features::{DiffMap, FeatureOpts, FeatureResolver, FeaturesFor};
diff --git a/src/tools/cargo/src/cargo/ops/registry/login.rs b/src/tools/cargo/src/cargo/ops/registry/login.rs
index 1e2b3a87b..e52373734 100644
--- a/src/tools/cargo/src/cargo/ops/registry/login.rs
+++ b/src/tools/cargo/src/cargo/ops/registry/login.rs
@@ -5,40 +5,27 @@
//!
//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html#login
-use std::io;
-use std::io::BufRead;
+use std::io::IsTerminal;
-use anyhow::anyhow;
-use anyhow::bail;
-use anyhow::Context as _;
-use pasetors::keys::AsymmetricKeyPair;
-use pasetors::keys::Generate as _;
-use pasetors::paserk::FormatAsPaserk;
-
-use crate::drop_println;
-use crate::ops::RegistryCredentialConfig;
-use crate::sources::CRATES_IO_DOMAIN;
use crate::util::auth;
-use crate::util::auth::paserk_public_from_paserk_secret;
use crate::util::auth::AuthorizationError;
-use crate::util::auth::Secret;
use crate::CargoResult;
use crate::Config;
+use cargo_credential::LoginOptions;
+use cargo_credential::Secret;
use super::get_source_id;
+use super::registry;
pub fn registry_login(
config: &Config,
- token: Option<Secret<&str>>,
+ token_from_cmdline: Option<Secret<&str>>,
reg: Option<&str>,
- generate_keypair: bool,
- secret_key_required: bool,
- key_subject: Option<&str>,
+ args: &[&str],
) -> CargoResult<()> {
let source_ids = get_source_id(config, None, reg)?;
- let reg_cfg = auth::registry_credential_config(config, &source_ids.original)?;
- let login_url = match super::registry(config, token.clone(), None, reg, false, None) {
+ let login_url = match registry(config, token_from_cmdline.clone(), None, reg, false, None) {
Ok((registry, _)) => Some(format!("{}/me", registry.host())),
Err(e) if e.is::<AuthorizationError>() => e
.downcast::<AuthorizationError>()
@@ -47,114 +34,23 @@ pub fn registry_login(
.map(|u| u.to_string()),
Err(e) => return Err(e),
};
- let new_token;
- if generate_keypair || secret_key_required || key_subject.is_some() {
- if !config.cli_unstable().registry_auth {
- let flag = if generate_keypair {
- "generate-keypair"
- } else if secret_key_required {
- "secret-key"
- } else if key_subject.is_some() {
- "key-subject"
- } else {
- unreachable!("how did we get here");
- };
- bail!(
- "the `{flag}` flag is unstable, pass `-Z registry-auth` to enable it\n\
- See https://github.com/rust-lang/cargo/issues/10519 for more \
- information about the `{flag}` flag."
- );
- }
- assert!(token.is_none());
- // we are dealing with asymmetric tokens
- let (old_secret_key, old_key_subject) = match &reg_cfg {
- RegistryCredentialConfig::AsymmetricKey((old_secret_key, old_key_subject)) => {
- (Some(old_secret_key), old_key_subject.clone())
- }
- _ => (None, None),
- };
- let secret_key: Secret<String>;
- if generate_keypair {
- assert!(!secret_key_required);
- let kp = AsymmetricKeyPair::<pasetors::version3::V3>::generate().unwrap();
- secret_key = Secret::default().map(|mut key| {
- FormatAsPaserk::fmt(&kp.secret, &mut key).unwrap();
- key
- });
- } else if secret_key_required {
- assert!(!generate_keypair);
- drop_println!(config, "please paste the API secret key below");
- secret_key = Secret::default()
- .map(|mut line| {
- let input = io::stdin();
- input
- .lock()
- .read_line(&mut line)
- .with_context(|| "failed to read stdin")
- .map(|_| line.trim().to_string())
- })
- .transpose()?;
- } else {
- secret_key = old_secret_key
- .cloned()
- .ok_or_else(|| anyhow!("need a secret_key to set a key_subject"))?;
- }
- if let Some(p) = paserk_public_from_paserk_secret(secret_key.as_deref()) {
- drop_println!(config, "{}", &p);
- } else {
- bail!("not a validly formatted PASERK secret key");
- }
- new_token = RegistryCredentialConfig::AsymmetricKey((
- secret_key,
- match key_subject {
- Some(key_subject) => Some(key_subject.to_string()),
- None => old_key_subject,
- },
- ));
- } else {
- new_token = RegistryCredentialConfig::Token(match token {
- Some(token) => token.owned(),
- None => {
- if let Some(login_url) = login_url {
- drop_println!(
- config,
- "please paste the token found on {} below",
- login_url
- )
- } else {
- drop_println!(
- config,
- "please paste the token for {} below",
- source_ids.original.display_registry_name()
- )
- }
- let mut line = String::new();
- let input = io::stdin();
- input
- .lock()
- .read_line(&mut line)
- .with_context(|| "failed to read stdin")?;
- // Automatically remove `cargo login` from an inputted token to
- // allow direct pastes from `registry.host()`/me.
- Secret::from(line.replace("cargo login", "").trim().to_string())
+ let mut token_from_stdin = None;
+ let token = token_from_cmdline.or_else(|| {
+ if !std::io::stdin().is_terminal() {
+ let token = std::io::read_to_string(std::io::stdin()).unwrap_or_default();
+ if !token.is_empty() {
+ token_from_stdin = Some(token);
}
- });
-
- if let Some(tok) = new_token.as_token() {
- crates_io::check_token(tok.as_ref().expose())?;
}
- }
- if &reg_cfg == &new_token {
- config.shell().status("Login", "already logged in")?;
- return Ok(());
- }
+ token_from_stdin.as_deref().map(Secret::from)
+ });
- auth::login(config, &source_ids.original, new_token)?;
+ let options = LoginOptions {
+ token,
+ login_url: login_url.as_deref(),
+ };
- config.shell().status(
- "Login",
- format!("token for `{}` saved", reg.unwrap_or(CRATES_IO_DOMAIN)),
- )?;
+ auth::login(config, &source_ids.original, options, args)?;
Ok(())
}
diff --git a/src/tools/cargo/src/cargo/ops/registry/logout.rs b/src/tools/cargo/src/cargo/ops/registry/logout.rs
index 59f2d9261..d1f080bae 100644
--- a/src/tools/cargo/src/cargo/ops/registry/logout.rs
+++ b/src/tools/cargo/src/cargo/ops/registry/logout.rs
@@ -11,32 +11,6 @@ use super::get_source_id;
pub fn registry_logout(config: &Config, reg: Option<&str>) -> CargoResult<()> {
let source_ids = get_source_id(config, None, reg)?;
- let reg_cfg = auth::registry_credential_config(config, &source_ids.original)?;
- let reg_name = source_ids.original.display_registry_name();
- if reg_cfg.is_none() {
- config
- .shell()
- .status("Logout", format!("not currently logged in to `{reg_name}`"))?;
- return Ok(());
- }
auth::logout(config, &source_ids.original)?;
- config.shell().status(
- "Logout",
- format!("token for `{reg_name}` has been removed from local storage"),
- )?;
- let location = if source_ids.original.is_crates_io() {
- "<https://crates.io/me>".to_string()
- } else {
- // The URL for the source requires network access to load the config.
- // That could be a fairly heavy operation to perform just to provide a
- // help message, so for now this just provides some generic text.
- // Perhaps in the future this could have an API to fetch the config if
- // it is cached, but avoid network access otherwise?
- format!("the `{reg_name}` website")
- };
- config.shell().note(format!(
- "This does not revoke the token on the registry server.\n \
- If you need to revoke the token, visit {location} and follow the instructions there."
- ))?;
Ok(())
}
diff --git a/src/tools/cargo/src/cargo/ops/registry/mod.rs b/src/tools/cargo/src/cargo/ops/registry/mod.rs
index ecb610ddd..94ef1ead2 100644
--- a/src/tools/cargo/src/cargo/ops/registry/mod.rs
+++ b/src/tools/cargo/src/cargo/ops/registry/mod.rs
@@ -10,18 +10,18 @@ mod search;
mod yank;
use std::collections::HashSet;
-use std::path::PathBuf;
use std::str;
use std::task::Poll;
use anyhow::{bail, format_err, Context as _};
+use cargo_credential::{Operation, Secret};
use crates_io::{self, Registry};
use crate::core::source::Source;
use crate::core::SourceId;
use crate::sources::{RegistrySource, SourceConfigMap};
-use crate::util::auth::{self, Secret};
-use crate::util::config::Config;
+use crate::util::auth;
+use crate::util::config::{Config, PathAndArgs};
use crate::util::errors::CargoResult;
use crate::util::network::http::http_handle;
use crate::util::IntoUrl;
@@ -44,7 +44,7 @@ pub enum RegistryCredentialConfig {
/// The authentication token.
Token(Secret<String>),
/// Process used for fetching a token.
- Process((PathBuf, Vec<String>)),
+ Process(Vec<PathAndArgs>),
/// Secret Key and subject for Asymmetric tokens.
AsymmetricKey((Secret<String>, Option<String>)),
}
@@ -75,7 +75,7 @@ impl RegistryCredentialConfig {
None
}
}
- pub fn as_process(&self) -> Option<&(PathBuf, Vec<String>)> {
+ pub fn as_process(&self) -> Option<&Vec<PathAndArgs>> {
if let Self::Process(v) = self {
Some(v)
} else {
@@ -106,7 +106,7 @@ fn registry(
index: Option<&str>,
registry: Option<&str>,
force_update: bool,
- token_required: Option<auth::Mutation<'_>>,
+ token_required: Option<Operation<'_>>,
) -> CargoResult<(Registry, RegistrySourceIds)> {
let source_ids = get_source_id(config, index, registry)?;
@@ -114,7 +114,7 @@ fn registry(
bail!("command-line argument --index requires --token to be specified");
}
if let Some(token) = token_from_cmdline {
- auth::cache_token(config, &source_ids.original, token);
+ auth::cache_token_from_commandline(config, &source_ids.original, token);
}
let cfg = {
@@ -138,11 +138,13 @@ fn registry(
.api
.ok_or_else(|| format_err!("{} does not support API commands", source_ids.replacement))?;
let token = if token_required.is_some() || cfg.auth_required {
+ let operation = token_required.unwrap_or(Operation::Read);
Some(auth::auth_token(
config,
&source_ids.original,
None,
- token_required,
+ operation,
+ vec![],
)?)
} else {
None
diff --git a/src/tools/cargo/src/cargo/ops/registry/owner.rs b/src/tools/cargo/src/cargo/ops/registry/owner.rs
index e53e07cb8..e29c6400b 100644
--- a/src/tools/cargo/src/cargo/ops/registry/owner.rs
+++ b/src/tools/cargo/src/cargo/ops/registry/owner.rs
@@ -3,12 +3,12 @@
//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html#owners
use anyhow::Context as _;
+use cargo_credential::Operation;
+use cargo_credential::Secret;
use crate::core::Workspace;
use crate::drop_print;
use crate::drop_println;
-use crate::util::auth;
-use crate::util::auth::Secret;
use crate::util::important_paths::find_root_manifest_for_wd;
use crate::CargoResult;
use crate::Config;
@@ -33,7 +33,7 @@ pub fn modify_owners(config: &Config, opts: &OwnersOptions) -> CargoResult<()> {
}
};
- let mutation = auth::Mutation::Owners { name: &name };
+ let operation = Operation::Owners { name: &name };
let (mut registry, _) = super::registry(
config,
@@ -41,7 +41,7 @@ pub fn modify_owners(config: &Config, opts: &OwnersOptions) -> CargoResult<()> {
opts.index.as_deref(),
opts.registry.as_deref(),
true,
- Some(mutation),
+ Some(operation),
)?;
if let Some(ref v) = opts.to_add {
diff --git a/src/tools/cargo/src/cargo/ops/registry/publish.rs b/src/tools/cargo/src/cargo/ops/registry/publish.rs
index 7f4fbbae2..40ca9fd16 100644
--- a/src/tools/cargo/src/cargo/ops/registry/publish.rs
+++ b/src/tools/cargo/src/cargo/ops/registry/publish.rs
@@ -9,6 +9,8 @@ use std::time::Duration;
use anyhow::bail;
use anyhow::Context as _;
+use cargo_credential::Operation;
+use cargo_credential::Secret;
use cargo_util::paths;
use crates_io::NewCrate;
use crates_io::NewCrateDependency;
@@ -28,7 +30,6 @@ use crate::ops::Packages;
use crate::sources::SourceConfigMap;
use crate::sources::CRATES_IO_REGISTRY;
use crate::util::auth;
-use crate::util::auth::Secret;
use crate::util::config::JobsConfig;
use crate::util::Progress;
use crate::util::ProgressStyle;
@@ -113,7 +114,7 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> {
// This is only used to confirm that we can create a token before we build the package.
// This causes the credential provider to be called an extra time, but keeps the same order of errors.
let ver = pkg.version().to_string();
- let mutation = auth::Mutation::PrePublish;
+ let operation = Operation::Read;
let (mut registry, reg_ids) = super::registry(
opts.config,
@@ -121,7 +122,7 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> {
opts.index.as_deref(),
publish_registry.as_deref(),
true,
- Some(mutation).filter(|_| !opts.dry_run),
+ Some(operation).filter(|_| !opts.dry_run),
)?;
verify_dependencies(pkg, &registry, reg_ids.original)?;
@@ -149,16 +150,17 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> {
let hash = cargo_util::Sha256::new()
.update_file(tarball.file())?
.finish_hex();
- let mutation = Some(auth::Mutation::Publish {
+ let operation = Operation::Publish {
name: pkg.name().as_str(),
vers: &ver,
cksum: &hash,
- });
+ };
registry.set_token(Some(auth::auth_token(
&opts.config,
&reg_ids.original,
None,
- mutation,
+ operation,
+ vec![],
)?));
}
diff --git a/src/tools/cargo/src/cargo/ops/registry/yank.rs b/src/tools/cargo/src/cargo/ops/registry/yank.rs
index 7f087570a..8a961b990 100644
--- a/src/tools/cargo/src/cargo/ops/registry/yank.rs
+++ b/src/tools/cargo/src/cargo/ops/registry/yank.rs
@@ -5,10 +5,10 @@
use anyhow::bail;
use anyhow::Context as _;
+use cargo_credential::Operation;
+use cargo_credential::Secret;
use crate::core::Workspace;
-use crate::util::auth;
-use crate::util::auth::Secret;
use crate::util::config::Config;
use crate::util::errors::CargoResult;
use crate::util::important_paths::find_root_manifest_for_wd;
@@ -36,12 +36,12 @@ pub fn yank(
};
let message = if undo {
- auth::Mutation::Unyank {
+ Operation::Unyank {
name: &name,
vers: &version,
}
} else {
- auth::Mutation::Yank {
+ Operation::Yank {
name: &name,
vers: &version,
}
diff --git a/src/tools/cargo/src/cargo/ops/resolve.rs b/src/tools/cargo/src/cargo/ops/resolve.rs
index ea5eded4a..6246311a5 100644
--- a/src/tools/cargo/src/cargo/ops/resolve.rs
+++ b/src/tools/cargo/src/cargo/ops/resolve.rs
@@ -71,8 +71,8 @@ use crate::sources::PathSource;
use crate::util::errors::CargoResult;
use crate::util::{profile, CanonicalUrl};
use anyhow::Context as _;
-use log::{debug, trace};
use std::collections::{HashMap, HashSet};
+use tracing::{debug, trace};
/// Result for `resolve_ws_with_opts`.
pub struct WorkspaceResolve<'cfg> {
diff --git a/src/tools/cargo/src/cargo/ops/tree/graph.rs b/src/tools/cargo/src/cargo/ops/tree/graph.rs
index d01d07f1a..f0dad4e5d 100644
--- a/src/tools/cargo/src/cargo/ops/tree/graph.rs
+++ b/src/tools/cargo/src/cargo/ops/tree/graph.rs
@@ -642,7 +642,7 @@ fn add_feature_rec(
let dep_indexes = match graph.dep_name_map[&package_index].get(dep_name) {
Some(indexes) => indexes.clone(),
None => {
- log::debug!(
+ tracing::debug!(
"enabling feature {} on {}, found {}/{}, \
dep appears to not be enabled",
feature_name,
diff --git a/src/tools/cargo/src/cargo/sources/config.rs b/src/tools/cargo/src/cargo/sources/config.rs
index 4097567bb..c51c1f009 100644
--- a/src/tools/cargo/src/cargo/sources/config.rs
+++ b/src/tools/cargo/src/cargo/sources/config.rs
@@ -10,8 +10,8 @@ use crate::util::config::{self, ConfigRelativePath, OptValue};
use crate::util::errors::CargoResult;
use crate::util::{Config, IntoUrl};
use anyhow::{bail, Context as _};
-use log::debug;
use std::collections::{HashMap, HashSet};
+use tracing::debug;
use url::Url;
/// Represents the entire [`[source]` replacement table][1] in Cargo configuration.
diff --git a/src/tools/cargo/src/cargo/sources/git/known_hosts.rs b/src/tools/cargo/src/cargo/sources/git/known_hosts.rs
index 7b013f99c..0b0dd3208 100644
--- a/src/tools/cargo/src/cargo/sources/git/known_hosts.rs
+++ b/src/tools/cargo/src/cargo/sources/git/known_hosts.rs
@@ -141,7 +141,7 @@ pub fn certificate_check(
let Some(host_key) = cert.as_hostkey() else {
// Return passthrough for TLS X509 certificates to use whatever validation
// was done in git2.
- return Ok(CertificateCheckStatus::CertificatePassthrough)
+ return Ok(CertificateCheckStatus::CertificatePassthrough);
};
// If a nonstandard port is in use, check for that first.
// The fallback to check without a port is handled in the HostKeyNotFound handler.
@@ -342,7 +342,7 @@ fn check_ssh_known_hosts(
};
match parse_known_hosts_line(&line_value.val, location) {
Some(known_host) => known_hosts.push(known_host),
- None => log::warn!(
+ None => tracing::warn!(
"failed to parse known host {} from {}",
line_value.val,
line_value.definition
@@ -611,10 +611,18 @@ impl KnownHost {
}
fn hashed_hostname_matches(host: &str, hashed: &str) -> bool {
- let Some((b64_salt, b64_host)) = hashed.split_once('|') else { return false; };
- let Ok(salt) = STANDARD.decode(b64_salt) else { return false; };
- let Ok(hashed_host) = STANDARD.decode(b64_host) else { return false; };
- let Ok(mut mac) = hmac::Hmac::<sha1::Sha1>::new_from_slice(&salt) else { return false; };
+ let Some((b64_salt, b64_host)) = hashed.split_once('|') else {
+ return false;
+ };
+ let Ok(salt) = STANDARD.decode(b64_salt) else {
+ return false;
+ };
+ let Ok(hashed_host) = STANDARD.decode(b64_host) else {
+ return false;
+ };
+ let Ok(mut mac) = hmac::Hmac::<sha1::Sha1>::new_from_slice(&salt) else {
+ return false;
+ };
mac.update(host.as_bytes());
let result = mac.finalize().into_bytes();
hashed_host == &result[..]
diff --git a/src/tools/cargo/src/cargo/sources/git/oxide.rs b/src/tools/cargo/src/cargo/sources/git/oxide.rs
index e86c63e8e..ec4fcecdd 100644
--- a/src/tools/cargo/src/cargo/sources/git/oxide.rs
+++ b/src/tools/cargo/src/cargo/sources/git/oxide.rs
@@ -6,12 +6,12 @@ use crate::util::{human_readable_bytes, network, MetricsCounter, Progress};
use crate::{CargoResult, Config};
use cargo_util::paths;
use gix::bstr::{BString, ByteSlice};
-use log::debug;
use std::cell::RefCell;
use std::path::Path;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Weak};
use std::time::{Duration, Instant};
+use tracing::debug;
/// For the time being, `repo_path` makes it easy to instantiate a gitoxide repo just for fetching.
/// In future this may change to be the gitoxide repository itself.
diff --git a/src/tools/cargo/src/cargo/sources/git/source.rs b/src/tools/cargo/src/cargo/sources/git/source.rs
index b021d23a0..10796562d 100644
--- a/src/tools/cargo/src/cargo/sources/git/source.rs
+++ b/src/tools/cargo/src/cargo/sources/git/source.rs
@@ -10,9 +10,9 @@ use crate::util::hex::short_hash;
use crate::util::Config;
use anyhow::Context;
use cargo_util::paths::exclude_from_backups_and_indexing;
-use log::trace;
use std::fmt::{self, Debug, Formatter};
use std::task::Poll;
+use tracing::trace;
use url::Url;
/// `GitSource` contains one or more packages gathering from a Git repository.
@@ -164,7 +164,10 @@ impl<'cfg> Debug for GitSource<'cfg> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "git repo at {}", self.remote.url())?;
- match self.manifest_reference.pretty_ref() {
+ // TODO(-Znext-lockfile-bump): set it to true when stabilizing
+ // lockfile v4, because we want Source ID serialization to be
+ // consistent with lockfile.
+ match self.manifest_reference.pretty_ref(false) {
Some(s) => write!(f, " ({})", s),
None => Ok(()),
}
diff --git a/src/tools/cargo/src/cargo/sources/git/utils.rs b/src/tools/cargo/src/cargo/sources/git/utils.rs
index 0c7ce8b64..093631091 100644
--- a/src/tools/cargo/src/cargo/sources/git/utils.rs
+++ b/src/tools/cargo/src/cargo/sources/git/utils.rs
@@ -11,7 +11,6 @@ use anyhow::{anyhow, Context as _};
use cargo_util::{paths, ProcessBuilder};
use curl::easy::List;
use git2::{self, ErrorClass, ObjectType, Oid};
-use log::{debug, info};
use serde::ser;
use serde::Serialize;
use std::borrow::Cow;
@@ -21,6 +20,7 @@ use std::process::Command;
use std::str;
use std::sync::atomic::{AtomicBool, Ordering};
use std::time::{Duration, Instant};
+use tracing::{debug, info};
use url::Url;
/// A file indicates that if present, `git reset` has been done and a repo
@@ -1316,7 +1316,7 @@ fn clean_repo_temp_files(repo: &git2::Repository) {
let pattern = match path.to_str() {
Some(p) => p,
None => {
- log::warn!("cannot convert {path:?} to a string");
+ tracing::warn!("cannot convert {path:?} to a string");
return;
}
};
@@ -1327,8 +1327,10 @@ fn clean_repo_temp_files(repo: &git2::Repository) {
for path in paths {
if let Ok(path) = path {
match paths::remove_file(&path) {
- Ok(_) => log::debug!("removed stale temp git file {path:?}"),
- Err(e) => log::warn!("failed to remove {path:?} while cleaning temp files: {e}"),
+ Ok(_) => tracing::debug!("removed stale temp git file {path:?}"),
+ Err(e) => {
+ tracing::warn!("failed to remove {path:?} while cleaning temp files: {e}")
+ }
}
}
}
diff --git a/src/tools/cargo/src/cargo/sources/path.rs b/src/tools/cargo/src/cargo/sources/path.rs
index bb40ec9b1..1d8ffc35c 100644
--- a/src/tools/cargo/src/cargo/sources/path.rs
+++ b/src/tools/cargo/src/cargo/sources/path.rs
@@ -11,7 +11,7 @@ use anyhow::Context as _;
use cargo_util::paths;
use filetime::FileTime;
use ignore::gitignore::GitignoreBuilder;
-use log::{trace, warn};
+use tracing::{trace, warn};
use walkdir::WalkDir;
/// A source represents one or multiple packages gathering from a given root
@@ -203,7 +203,7 @@ impl<'cfg> PathSource<'cfg> {
let repo = match git2::Repository::discover(root) {
Ok(repo) => repo,
Err(e) => {
- log::debug!(
+ tracing::debug!(
"could not discover git repo at or above {}: {}",
root.display(),
e
@@ -223,7 +223,7 @@ impl<'cfg> PathSource<'cfg> {
let repo_relative_path = match paths::strip_prefix_canonical(root, repo_root) {
Ok(p) => p,
Err(e) => {
- log::warn!(
+ tracing::warn!(
"cannot determine if path `{:?}` is in git repo `{:?}`: {:?}",
root,
repo_root,
diff --git a/src/tools/cargo/src/cargo/sources/registry/download.rs b/src/tools/cargo/src/cargo/sources/registry/download.rs
index a85d87177..08940b3a1 100644
--- a/src/tools/cargo/src/cargo/sources/registry/download.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/download.rs
@@ -4,6 +4,7 @@
//! [`RemoteRegistry`]: super::remote::RemoteRegistry
use anyhow::Context;
+use cargo_credential::Operation;
use cargo_util::registry::make_dep_path;
use cargo_util::Sha256;
@@ -78,7 +79,13 @@ pub(super) fn download(
}
let authorization = if registry_config.auth_required {
- Some(auth::auth_token(config, &pkg.source_id(), None, None)?)
+ Some(auth::auth_token(
+ config,
+ &pkg.source_id(),
+ None,
+ Operation::Read,
+ vec![],
+ )?)
} else {
None
};
diff --git a/src/tools/cargo/src/cargo/sources/registry/http_remote.rs b/src/tools/cargo/src/cargo/sources/registry/http_remote.rs
index c69ef8f9b..52f6f392e 100644
--- a/src/tools/cargo/src/cargo/sources/registry/http_remote.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/http_remote.rs
@@ -4,16 +4,16 @@ use crate::core::{PackageId, SourceId};
use crate::sources::registry::download;
use crate::sources::registry::MaybeLock;
use crate::sources::registry::{LoadResponse, RegistryConfig, RegistryData};
-use crate::util::errors::{CargoResult, HttpNotSuccessful, DEBUG_HEADERS};
+use crate::util::errors::{CargoResult, HttpNotSuccessful};
use crate::util::network::http::http_handle;
use crate::util::network::retry::{Retry, RetryResult};
use crate::util::network::sleep::SleepTracker;
use crate::util::{auth, Config, Filesystem, IntoUrl, Progress, ProgressStyle};
use anyhow::Context;
+use cargo_credential::Operation;
use cargo_util::paths;
use curl::easy::{Easy, List};
use curl::multi::{EasyHandle, Multi};
-use log::{debug, trace};
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::fs::{self, File};
@@ -22,6 +22,7 @@ use std::path::{Path, PathBuf};
use std::str;
use std::task::{ready, Poll};
use std::time::Duration;
+use tracing::{debug, trace};
use url::Url;
// HTTP headers
@@ -96,6 +97,9 @@ pub struct HttpRegistry<'cfg> {
/// Url to get a token for the registry.
login_url: Option<Url>,
+ /// Headers received with an HTTP 401.
+ auth_error_headers: Vec<String>,
+
/// Disables status messages.
quiet: bool,
}
@@ -149,8 +153,8 @@ struct Headers {
last_modified: Option<String>,
etag: Option<String>,
www_authenticate: Vec<String>,
- /// We don't care about these headers. Put them here for debugging purpose.
- others: Vec<String>,
+ /// All headers, including explicit headers above.
+ all: Vec<String>,
}
/// HTTP status code [`HttpRegistry`] cares about.
@@ -221,6 +225,7 @@ impl<'cfg> HttpRegistry<'cfg> {
registry_config: None,
auth_required: false,
login_url: None,
+ auth_error_headers: vec![],
quiet: false,
})
}
@@ -316,7 +321,7 @@ impl<'cfg> HttpRegistry<'cfg> {
&mut handle,
&url,
data,
- download.header_map.take().others,
+ download.header_map.take().all,
)
.into());
}
@@ -389,11 +394,11 @@ impl<'cfg> HttpRegistry<'cfg> {
Ok(json) => {
self.registry_config = Some(json);
}
- Err(e) => log::debug!("failed to decode cached config.json: {}", e),
+ Err(e) => tracing::debug!("failed to decode cached config.json: {}", e),
},
Err(e) => {
if e.kind() != ErrorKind::NotFound {
- log::debug!("failed to read config.json cache: {}", e)
+ tracing::debug!("failed to read config.json cache: {}", e)
}
}
}
@@ -418,7 +423,7 @@ impl<'cfg> HttpRegistry<'cfg> {
self.registry_config = Some(serde_json::from_slice(&raw_data)?);
if paths::create_dir_all(&config_json_path.parent().unwrap()).is_ok() {
if let Err(e) = fs::write(&config_json_path, &raw_data) {
- log::debug!("failed to write config.json cache: {}", e);
+ tracing::debug!("failed to write config.json cache: {}", e);
}
}
Poll::Ready(Ok(self.registry_config.as_ref().unwrap()))
@@ -569,6 +574,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
}
}
}
+ self.auth_error_headers = result.header_map.all;
}
StatusCode::Unauthorized => {
let err = Err(HttpNotSuccessful {
@@ -576,7 +582,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
body: result.data,
url: self.full_url(path),
ip: None,
- headers: result.header_map.others,
+ headers: result.header_map.all,
}
.into());
if self.auth_required {
@@ -639,8 +645,13 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
}
}
if self.auth_required {
- let authorization =
- auth::auth_token(self.config, &self.source_id, self.login_url.as_ref(), None)?;
+ let authorization = auth::auth_token(
+ self.config,
+ &self.source_id,
+ self.login_url.as_ref(),
+ Operation::Read,
+ self.auth_error_headers.clone(),
+ )?;
headers.append(&format!("Authorization: {}", authorization))?;
trace!("including authorization for {}", full_url);
}
@@ -679,15 +690,12 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
tls::with(|downloads| {
if let Some(downloads) = downloads {
let mut header_map = downloads.pending[&token].0.header_map.borrow_mut();
+ header_map.all.push(format!("{tag}: {value}"));
match tag.to_ascii_lowercase().as_str() {
LAST_MODIFIED => header_map.last_modified = Some(value.to_string()),
ETAG => header_map.etag = Some(value.to_string()),
WWW_AUTHENTICATE => header_map.www_authenticate.push(value.to_string()),
- _ => {
- if DEBUG_HEADERS.iter().any(|prefix| tag.starts_with(prefix)) {
- header_map.others.push(format!("{tag}: {value}"));
- }
- }
+ _ => {}
}
}
});
@@ -808,7 +816,9 @@ impl<'cfg> Downloads<'cfg> {
/// Updates the state of the progress bar for downloads.
fn tick(&self) -> CargoResult<()> {
let mut progress = self.progress.borrow_mut();
- let Some(progress) = progress.as_mut() else { return Ok(()); };
+ let Some(progress) = progress.as_mut() else {
+ return Ok(());
+ };
// Since the sparse protocol discovers dependencies as it goes,
// it's not possible to get an accurate progress indication.
diff --git a/src/tools/cargo/src/cargo/sources/registry/index.rs b/src/tools/cargo/src/cargo/sources/registry/index.rs
index 6d565da8f..05bfe71af 100644
--- a/src/tools/cargo/src/cargo/sources/registry/index.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/index.rs
@@ -94,7 +94,6 @@ use crate::util::IntoUrl;
use crate::util::{internal, CargoResult, Config, Filesystem, OptVersionReq, ToSemver};
use anyhow::bail;
use cargo_util::{paths, registry::make_dep_path};
-use log::{debug, info};
use semver::Version;
use serde::Deserialize;
use std::borrow::Cow;
@@ -105,6 +104,7 @@ use std::io::ErrorKind;
use std::path::Path;
use std::str;
use std::task::{ready, Poll};
+use tracing::{debug, info};
/// The current version of [`SummariesCache`].
const CURRENT_CACHE_VERSION: u8 = 3;
@@ -379,7 +379,9 @@ impl<'cfg> RegistryIndex<'cfg> {
pub fn hash(&mut self, pkg: PackageId, load: &mut dyn RegistryData) -> Poll<CargoResult<&str>> {
let req = OptVersionReq::exact(pkg.version());
let summary = self.summaries(&pkg.name(), &req, load)?;
- let summary = ready!(summary).next();
+ let summary = ready!(summary)
+ .filter(|s| s.summary.version() == pkg.version())
+ .next();
Poll::Ready(Ok(summary
.ok_or_else(|| internal(format!("no hash listed for {}", pkg)))?
.summary
@@ -623,10 +625,10 @@ impl<'cfg> RegistryIndex<'cfg> {
load: &mut dyn RegistryData,
) -> Poll<CargoResult<bool>> {
let req = OptVersionReq::exact(pkg.version());
- let found = self
- .summaries(&pkg.name(), &req, load)
- .map_ok(|mut p| p.any(|summary| summary.yanked));
- found
+ let found = ready!(self.summaries(&pkg.name(), &req, load))?
+ .filter(|s| s.summary.version() == pkg.version())
+ .any(|summary| summary.yanked);
+ Poll::Ready(Ok(found))
}
}
@@ -673,23 +675,23 @@ impl Summaries {
index_version = Some(v);
}
Err(e) => {
- log::debug!("failed to parse {:?} cache: {}", relative, e);
+ tracing::debug!("failed to parse {:?} cache: {}", relative, e);
}
},
- Err(e) => log::debug!("cache missing for {:?} error: {}", relative, e),
+ Err(e) => tracing::debug!("cache missing for {:?} error: {}", relative, e),
}
let response = ready!(load.load(root, relative, index_version.as_deref())?);
match response {
LoadResponse::CacheValid => {
- log::debug!("fast path for registry cache of {:?}", relative);
+ tracing::debug!("fast path for registry cache of {:?}", relative);
return Poll::Ready(Ok(cached_summaries));
}
LoadResponse::NotFound => {
if let Err(e) = fs::remove_file(cache_path) {
if e.kind() != ErrorKind::NotFound {
- log::debug!("failed to remove from cache: {}", e);
+ tracing::debug!("failed to remove from cache: {}", e);
}
}
return Poll::Ready(Ok(None));
@@ -701,7 +703,7 @@ impl Summaries {
// This is the fallback path where we actually talk to the registry backend to load
// information. Here we parse every single line in the index (as we need
// to find the versions)
- log::debug!("slow path for {:?}", relative);
+ tracing::debug!("slow path for {:?}", relative);
let mut cache = SummariesCache::default();
let mut ret = Summaries::default();
ret.raw_data = raw_data;
@@ -722,7 +724,11 @@ impl Summaries {
// entries in the cache preventing those newer
// versions from reading them (that is, until the
// cache is rebuilt).
- log::info!("failed to parse {:?} registry package: {}", relative, e);
+ tracing::info!(
+ "failed to parse {:?} registry package: {}",
+ relative,
+ e
+ );
continue;
}
};
@@ -731,7 +737,7 @@ impl Summaries {
ret.versions.insert(version, summary.into());
}
if let Some(index_version) = index_version {
- log::trace!("caching index_version {}", index_version);
+ tracing::trace!("caching index_version {}", index_version);
let cache_bytes = cache.serialize(index_version.as_str());
// Once we have our `cache_bytes` which represents the `Summaries` we're
// about to return, write that back out to disk so future Cargo
@@ -743,7 +749,7 @@ impl Summaries {
let path = Filesystem::new(cache_path.clone());
config.assert_package_cache_locked(&path);
if let Err(e) = fs::write(cache_path, &cache_bytes) {
- log::info!("failed to write cache: {}", e);
+ tracing::info!("failed to write cache: {}", e);
}
}
@@ -906,7 +912,7 @@ impl IndexSummary {
v,
} = serde_json::from_slice(line)?;
let v = v.unwrap_or(1);
- log::trace!("json parsed registry {}/{}", name, vers);
+ tracing::trace!("json parsed registry {}/{}", name, vers);
let pkgid = PackageId::new(name, &vers, source_id)?;
let deps = deps
.into_iter()
diff --git a/src/tools/cargo/src/cargo/sources/registry/mod.rs b/src/tools/cargo/src/cargo/sources/registry/mod.rs
index a0178db55..c1ff8b12e 100644
--- a/src/tools/cargo/src/cargo/sources/registry/mod.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/mod.rs
@@ -195,10 +195,10 @@ use std::task::{ready, Poll};
use anyhow::Context as _;
use cargo_util::paths::{self, exclude_from_backups_and_indexing};
use flate2::read::GzDecoder;
-use log::debug;
use serde::Deserialize;
use serde::Serialize;
use tar::Archive;
+use tracing::debug;
use crate::core::dependency::Dependency;
use crate::core::source::MaybePackage;
@@ -589,9 +589,9 @@ impl<'cfg> RegistrySource<'cfg> {
}
_ => {
if ok == "ok" {
- log::debug!("old `ok` content found, clearing cache");
+ tracing::debug!("old `ok` content found, clearing cache");
} else {
- log::warn!("unrecognized .cargo-ok content, clearing cache: {ok}");
+ tracing::warn!("unrecognized .cargo-ok content, clearing cache: {ok}");
}
// See comment of `unpack_package` about why removing all stuff.
paths::remove_dir_all(dst.as_path_unlocked())?;
@@ -694,6 +694,7 @@ impl<'cfg> RegistrySource<'cfg> {
.summaries(&package.name(), &req, &mut *self.ops)?
.expect("a downloaded dep now pending!?")
.map(|s| s.summary.clone())
+ .filter(|s| s.version() == package.version())
.next()
.expect("summary not found");
if let Some(cksum) = summary_with_cksum.checksum() {
@@ -887,7 +888,7 @@ impl<'cfg> Source for RegistrySource<'cfg> {
impl RegistryConfig {
/// File name of [`RegistryConfig`].
- const NAME: &str = "config.json";
+ const NAME: &'static str = "config.json";
}
/// Get the maximum upack size that Cargo permits
diff --git a/src/tools/cargo/src/cargo/sources/registry/remote.rs b/src/tools/cargo/src/cargo/sources/registry/remote.rs
index 4223b0303..89927181f 100644
--- a/src/tools/cargo/src/cargo/sources/registry/remote.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/remote.rs
@@ -12,13 +12,13 @@ use crate::util::{Config, Filesystem};
use anyhow::Context as _;
use cargo_util::paths;
use lazycell::LazyCell;
-use log::{debug, trace};
use std::cell::{Cell, Ref, RefCell};
use std::fs::File;
use std::mem;
use std::path::Path;
use std::str;
use std::task::{ready, Poll};
+use tracing::{debug, trace};
/// A remote registry is a registry that lives at a remote URL (such as
/// crates.io). The git index is cloned locally, and `.crate` files are
diff --git a/src/tools/cargo/src/cargo/util/auth/asymmetric.rs b/src/tools/cargo/src/cargo/util/auth/asymmetric.rs
deleted file mode 100644
index 50882a745..000000000
--- a/src/tools/cargo/src/cargo/util/auth/asymmetric.rs
+++ /dev/null
@@ -1,155 +0,0 @@
-//! Registry asymmetric authentication support. See [RFC 3231] for more.
-//!
-//! [RFC 3231]: https://rust-lang.github.io/rfcs/3231-cargo-asymmetric-tokens.html
-
-use pasetors::keys::AsymmetricPublicKey;
-use pasetors::keys::AsymmetricSecretKey;
-use pasetors::paserk;
-use pasetors::paserk::FormatAsPaserk;
-use pasetors::version3;
-use pasetors::version3::PublicToken;
-use time::format_description::well_known::Rfc3339;
-use time::OffsetDateTime;
-
-use crate::core::SourceId;
-use crate::ops::RegistryCredentialConfig;
-use crate::CargoResult;
-
-use super::Mutation;
-use super::Secret;
-
-/// The main body of an asymmetric token as describe in RFC 3231.
-#[derive(serde::Serialize)]
-struct Message<'a> {
- iat: &'a str,
- #[serde(skip_serializing_if = "Option::is_none")]
- sub: Option<&'a str>,
- #[serde(skip_serializing_if = "Option::is_none")]
- mutation: Option<&'a str>,
- #[serde(skip_serializing_if = "Option::is_none")]
- name: Option<&'a str>,
- #[serde(skip_serializing_if = "Option::is_none")]
- vers: Option<&'a str>,
- #[serde(skip_serializing_if = "Option::is_none")]
- cksum: Option<&'a str>,
- #[serde(skip_serializing_if = "Option::is_none")]
- challenge: Option<&'a str>,
- /// This field is not yet used. This field can be set to a value >1 to
- /// indicate a breaking change in the token format.
- #[serde(skip_serializing_if = "Option::is_none")]
- v: Option<u8>,
-}
-
-/// The footer of an asymmetric token as describe in RFC 3231.
-#[derive(serde::Serialize)]
-struct Footer<'a> {
- url: &'a str,
- kip: paserk::Id,
-}
-
-/// Checks that a secret key is valid, and returns the associated public key in
-/// Paserk format.
-pub fn paserk_public_from_paserk_secret(secret_key: Secret<&str>) -> Option<String> {
- let secret: Secret<AsymmetricSecretKey<version3::V3>> =
- secret_key.map(|key| key.try_into()).transpose().ok()?;
- let public: AsymmetricPublicKey<version3::V3> = secret
- .as_ref()
- .map(|key| key.try_into())
- .transpose()
- .ok()?
- .expose();
- let mut paserk_pub_key = String::new();
- FormatAsPaserk::fmt(&public, &mut paserk_pub_key).unwrap();
- Some(paserk_pub_key)
-}
-
-/// Generates a public token from a registry's `credential` configuration for
-/// authenticating to a `source_id`
-///
-/// An optional `mutation` for authenticating a mutation operation aganist the
-/// registry.
-pub fn public_token_from_credential(
- credential: RegistryCredentialConfig,
- source_id: &SourceId,
- mutation: Option<&'_ Mutation<'_>>,
-) -> CargoResult<Secret<String>> {
- let RegistryCredentialConfig::AsymmetricKey((secret_key, secret_key_subject)) = credential else {
- anyhow::bail!("credential must be an asymmetric secret key")
- };
-
- let secret: Secret<AsymmetricSecretKey<version3::V3>> =
- secret_key.map(|key| key.as_str().try_into()).transpose()?;
- let public: AsymmetricPublicKey<version3::V3> = secret
- .as_ref()
- .map(|key| key.try_into())
- .transpose()?
- .expose();
- let kip = (&public).try_into()?;
- let iat = OffsetDateTime::now_utc();
-
- let message = Message {
- iat: &iat.format(&Rfc3339)?,
- sub: secret_key_subject.as_deref(),
- mutation: mutation.and_then(|m| {
- Some(match m {
- Mutation::PrePublish => return None,
- Mutation::Publish { .. } => "publish",
- Mutation::Yank { .. } => "yank",
- Mutation::Unyank { .. } => "unyank",
- Mutation::Owners { .. } => "owners",
- })
- }),
- name: mutation.and_then(|m| {
- Some(match m {
- Mutation::PrePublish => return None,
- Mutation::Publish { name, .. }
- | Mutation::Yank { name, .. }
- | Mutation::Unyank { name, .. }
- | Mutation::Owners { name, .. } => *name,
- })
- }),
- vers: mutation.and_then(|m| {
- Some(match m {
- Mutation::PrePublish | Mutation::Owners { .. } => return None,
- Mutation::Publish { vers, .. }
- | Mutation::Yank { vers, .. }
- | Mutation::Unyank { vers, .. } => *vers,
- })
- }),
- cksum: mutation.and_then(|m| {
- Some(match m {
- Mutation::PrePublish
- | Mutation::Yank { .. }
- | Mutation::Unyank { .. }
- | Mutation::Owners { .. } => return None,
- Mutation::Publish { cksum, .. } => *cksum,
- })
- }),
- challenge: None, // todo: PASETO with challenges
- v: None,
- };
-
- let footer = Footer {
- url: &source_id.url().to_string(),
- kip,
- };
-
- let secret = secret
- .map(|secret| {
- PublicToken::sign(
- &secret,
- serde_json::to_string(&message)
- .expect("cannot serialize")
- .as_bytes(),
- Some(
- serde_json::to_string(&footer)
- .expect("cannot serialize")
- .as_bytes(),
- ),
- None,
- )
- })
- .transpose()?;
-
- Ok(secret)
-}
diff --git a/src/tools/cargo/src/cargo/util/auth/mod.rs b/src/tools/cargo/src/cargo/util/auth/mod.rs
index 58309964f..60a356fa0 100644
--- a/src/tools/cargo/src/cargo/util/auth/mod.rs
+++ b/src/tools/cargo/src/cargo/util/auth/mod.rs
@@ -1,143 +1,227 @@
//! Registry authentication support.
-mod asymmetric;
+use crate::{
+ sources::CRATES_IO_REGISTRY,
+ util::{config::ConfigKey, CanonicalUrl, CargoResult, Config, IntoUrl},
+};
+use anyhow::{bail, Context as _};
+use cargo_credential::{
+ Action, CacheControl, Credential, CredentialResponse, LoginOptions, Operation, RegistryInfo,
+ Secret,
+};
-use crate::util::{config, config::ConfigKey, CanonicalUrl, CargoResult, Config, IntoUrl};
-use anyhow::{bail, format_err, Context as _};
-use cargo_util::ProcessError;
use core::fmt;
use serde::Deserialize;
-use std::collections::HashMap;
use std::error::Error;
-use std::io::{Read, Write};
-use std::ops::Deref;
-use std::path::PathBuf;
-use std::process::{Command, Stdio};
+use time::{Duration, OffsetDateTime};
use url::Url;
use crate::core::SourceId;
-use crate::ops::RegistryCredentialConfig;
+use crate::util::config::Value;
+use crate::util::credential::adaptor::BasicProcessCredential;
+use crate::util::credential::paseto::PasetoCredential;
-pub use self::asymmetric::paserk_public_from_paserk_secret;
+use super::{
+ config::{CredentialCacheValue, OptValue, PathAndArgs},
+ credential::process::CredentialProcessCredential,
+ credential::token::TokenCredential,
+};
-use super::config::CredentialCacheValue;
-
-/// A wrapper for values that should not be printed.
-///
-/// This type does not implement `Display`, and has a `Debug` impl that hides
-/// the contained value.
-///
-/// ```
-/// # use cargo::util::auth::Secret;
-/// let token = Secret::from("super secret string");
-/// assert_eq!(format!("{:?}", token), "Secret { inner: \"REDACTED\" }");
-/// ```
+/// `[registries.NAME]` tables.
///
-/// Currently, we write a borrowed `Secret<T>` as `Secret<&T>`.
-/// The [`as_deref`](Secret::as_deref) and [`owned`](Secret::owned) methods can
-/// be used to convert back and forth between `Secret<String>` and `Secret<&str>`.
-#[derive(Default, Clone, PartialEq, Eq)]
-pub struct Secret<T> {
- inner: T,
+/// The values here should be kept in sync with `RegistryConfigExtended`
+#[derive(Deserialize, Clone, Debug)]
+#[serde(rename_all = "kebab-case")]
+pub struct RegistryConfig {
+ pub index: Option<String>,
+ pub token: OptValue<Secret<String>>,
+ pub credential_provider: Option<PathAndArgs>,
+ pub secret_key: OptValue<Secret<String>>,
+ pub secret_key_subject: Option<String>,
+ #[serde(rename = "protocol")]
+ _protocol: Option<String>,
}
-impl<T> Secret<T> {
- /// Unwraps the contained value.
- ///
- /// Use of this method marks the boundary of where the contained value is
- /// hidden.
- pub fn expose(self) -> T {
- self.inner
- }
-
- /// Converts a `Secret<T>` to a `Secret<&T::Target>`.
- /// ```
- /// # use cargo::util::auth::Secret;
- /// let owned: Secret<String> = Secret::from(String::from("token"));
- /// let borrowed: Secret<&str> = owned.as_deref();
- /// ```
- pub fn as_deref(&self) -> Secret<&<T as Deref>::Target>
- where
- T: Deref,
- {
- Secret::from(self.inner.deref())
- }
-
- /// Converts a `Secret<T>` to a `Secret<&T>`.
- pub fn as_ref(&self) -> Secret<&T> {
- Secret::from(&self.inner)
- }
-
- /// Converts a `Secret<T>` to a `Secret<U>` by applying `f` to the contained value.
- pub fn map<U, F>(self, f: F) -> Secret<U>
- where
- F: FnOnce(T) -> U,
- {
- Secret::from(f(self.inner))
- }
+/// The `[registry]` table, which more keys than the `[registries.NAME]` tables.
+///
+/// Note: nesting `RegistryConfig` inside this struct and using `serde(flatten)` *should* work
+/// but fails with "invalid type: sequence, expected a value" when attempting to deserialize.
+#[derive(Deserialize)]
+#[serde(rename_all = "kebab-case")]
+pub struct RegistryConfigExtended {
+ pub index: Option<String>,
+ pub token: OptValue<Secret<String>>,
+ pub credential_provider: Option<PathAndArgs>,
+ pub secret_key: OptValue<Secret<String>>,
+ pub secret_key_subject: Option<String>,
+ #[serde(rename = "default")]
+ _default: Option<String>,
+ #[serde(rename = "global-credential-providers")]
+ _global_credential_providers: Option<Vec<String>>,
}
-impl<T: ToOwned + ?Sized> Secret<&T> {
- /// Converts a `Secret` containing a borrowed type to a `Secret` containing the
- /// corresponding owned type.
- /// ```
- /// # use cargo::util::auth::Secret;
- /// let borrowed: Secret<&str> = Secret::from("token");
- /// let owned: Secret<String> = borrowed.owned();
- /// ```
- pub fn owned(&self) -> Secret<<T as ToOwned>::Owned> {
- Secret::from(self.inner.to_owned())
+impl RegistryConfigExtended {
+ pub fn to_registry_config(self) -> RegistryConfig {
+ RegistryConfig {
+ index: self.index,
+ token: self.token,
+ credential_provider: self.credential_provider,
+ secret_key: self.secret_key,
+ secret_key_subject: self.secret_key_subject,
+ _protocol: None,
+ }
}
}
-impl<T, E> Secret<Result<T, E>> {
- /// Converts a `Secret<Result<T, E>>` to a `Result<Secret<T>, E>`.
- pub fn transpose(self) -> Result<Secret<T>, E> {
- self.inner.map(|v| Secret::from(v))
- }
-}
+/// Get the list of credential providers for a registry source.
+fn credential_provider(config: &Config, sid: &SourceId) -> CargoResult<Vec<Vec<String>>> {
+ let cfg = registry_credential_config_raw(config, sid)?;
+ let allow_cred_proc = config.cli_unstable().credential_process;
+ let default_providers = || {
+ if allow_cred_proc {
+ // Enable the PASETO provider
+ vec![
+ vec!["cargo:token".to_string()],
+ vec!["cargo:paseto".to_string()],
+ ]
+ } else {
+ vec![vec!["cargo:token".to_string()]]
+ }
+ };
+ let global_providers = config
+ .get::<Option<Vec<Value<String>>>>("registry.global-credential-providers")?
+ .filter(|p| !p.is_empty() && allow_cred_proc)
+ .map(|p| {
+ p.iter()
+ .rev()
+ .map(PathAndArgs::from_whitespace_separated_string)
+ .map(|p| resolve_credential_alias(config, p))
+ .collect()
+ })
+ .unwrap_or_else(default_providers);
+ tracing::debug!(?global_providers);
-impl<T: AsRef<str>> Secret<T> {
- /// Checks if the contained value is empty.
- pub fn is_empty(&self) -> bool {
- self.inner.as_ref().is_empty()
- }
-}
+ let providers = match cfg {
+ // If there's a specific provider configured for this registry, use it.
+ Some(RegistryConfig {
+ credential_provider: Some(provider),
+ token,
+ secret_key,
+ ..
+ }) if allow_cred_proc => {
+ if let Some(token) = token {
+ config.shell().warn(format!(
+ "{sid} has a token configured in {} that will be ignored \
+ because a credential-provider is configured for this registry`",
+ token.definition
+ ))?;
+ }
+ if let Some(secret_key) = secret_key {
+ config.shell().warn(format!(
+ "{sid} has a secret-key configured in {} that will be ignored \
+ because a credential-provider is configured for this registry`",
+ secret_key.definition
+ ))?;
+ }
+ vec![resolve_credential_alias(config, provider)]
+ }
-impl<T> From<T> for Secret<T> {
- fn from(inner: T) -> Self {
- Self { inner }
- }
-}
+ // Warning for both `token` and `secret-key`, stating which will be ignored
+ Some(RegistryConfig {
+ token: Some(token),
+ secret_key: Some(secret_key),
+ ..
+ }) if allow_cred_proc => {
+ let token_pos = global_providers
+ .iter()
+ .position(|p| p.first().map(String::as_str) == Some("cargo:token"));
+ let paseto_pos = global_providers
+ .iter()
+ .position(|p| p.first().map(String::as_str) == Some("cargo:paseto"));
+ match (token_pos, paseto_pos) {
+ (Some(token_pos), Some(paseto_pos)) => {
+ if token_pos < paseto_pos {
+ config.shell().warn(format!(
+ "{sid} has a `secret_key` configured in {} that will be ignored \
+ because a `token` is also configured, and the `cargo:token` provider is \
+ configured with higher precedence",
+ secret_key.definition
+ ))?;
+ } else {
+ config.shell().warn(format!("{sid} has a `token` configured in {} that will be ignored \
+ because a `secret_key` is also configured, and the `cargo:paseto` provider is \
+ configured with higher precedence", token.definition))?;
+ }
+ }
+ (_, _) => {
+ // One or both of the below individual warnings will trigger
+ }
+ }
+ global_providers
+ }
-impl<T> fmt::Debug for Secret<T> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_struct("Secret")
- .field("inner", &"REDACTED")
- .finish()
- }
+ // Check if a `token` is configured that will be ignored.
+ Some(RegistryConfig {
+ token: Some(token), ..
+ }) => {
+ if !global_providers
+ .iter()
+ .any(|p| p.first().map(String::as_str) == Some("cargo:token"))
+ {
+ config.shell().warn(format!(
+ "{sid} has a token configured in {} that will be ignored \
+ because the `cargo:token` credential provider is not listed in \
+ `registry.global-credential-providers`",
+ token.definition
+ ))?;
+ }
+ global_providers
+ }
+
+ // Check if a asymmetric token is configured that will be ignored.
+ Some(RegistryConfig {
+ secret_key: Some(token),
+ ..
+ }) if allow_cred_proc => {
+ if !global_providers
+ .iter()
+ .any(|p| p.first().map(String::as_str) == Some("cargo:paseto"))
+ {
+ config.shell().warn(format!(
+ "{sid} has a secret-key configured in {} that will be ignored \
+ because the `cargo:paseto` credential provider is not listed in \
+ `registry.global-credential-providers`",
+ token.definition
+ ))?;
+ }
+ global_providers
+ }
+
+ // If we couldn't find a registry-specific provider, use the fallback provider list.
+ None | Some(RegistryConfig { .. }) => global_providers,
+ };
+ Ok(providers)
}
/// Get the credential configuration for a `SourceId`.
-pub fn registry_credential_config(
+pub fn registry_credential_config_raw(
config: &Config,
sid: &SourceId,
-) -> CargoResult<RegistryCredentialConfig> {
- #[derive(Deserialize)]
- #[serde(rename_all = "kebab-case")]
- struct RegistryConfig {
- index: Option<String>,
- token: Option<String>,
- credential_process: Option<config::PathAndArgs>,
- secret_key: Option<String>,
- secret_key_subject: Option<String>,
- #[serde(rename = "default")]
- _default: Option<String>,
- #[serde(rename = "protocol")]
- _protocol: Option<String>,
+) -> CargoResult<Option<RegistryConfig>> {
+ let mut cache = config.registry_config();
+ if let Some(cfg) = cache.get(&sid) {
+ return Ok(cfg.clone());
}
+ let cfg = registry_credential_config_raw_uncached(config, sid)?;
+ cache.insert(*sid, cfg.clone());
+ return Ok(cfg);
+}
- log::trace!("loading credential config for {}", sid);
+fn registry_credential_config_raw_uncached(
+ config: &Config,
+ sid: &SourceId,
+) -> CargoResult<Option<RegistryConfig>> {
+ tracing::trace!("loading credential config for {}", sid);
config.load_credentials()?;
if !sid.is_remote_registry() {
bail!(
@@ -150,22 +234,9 @@ pub fn registry_credential_config(
// Handle crates.io specially, since it uses different configuration keys.
if sid.is_crates_io() {
config.check_registry_index_not_set()?;
- let RegistryConfig {
- token,
- credential_process,
- secret_key,
- secret_key_subject,
- ..
- } = config.get::<RegistryConfig>("registry")?;
- return registry_credential_config_inner(
- true,
- None,
- token.map(Secret::from),
- credential_process,
- secret_key.map(Secret::from),
- secret_key_subject,
- config,
- );
+ return Ok(config
+ .get::<Option<RegistryConfigExtended>>("registry")?
+ .map(|c| c.to_registry_config()));
}
// Find the SourceId's name by its index URL. If environment variables
@@ -179,6 +250,7 @@ pub fn registry_credential_config(
// This also allows the authorization token for a registry to be set
// without knowing the registry name by using the _INDEX and _TOKEN
// environment variables.
+
let name = {
// Discover names from environment variables.
let index = sid.canonical_url();
@@ -198,14 +270,17 @@ pub fn registry_credential_config(
// Discover names from the configuration only if none were found in the environment.
if names.len() == 0 {
- names = config
- .get::<HashMap<String, RegistryConfig>>("registries")?
- .iter()
- .filter_map(|(k, v)| Some((k, v.index.as_deref()?)))
- .filter_map(|(k, v)| Some((k, CanonicalUrl::new(&v.into_url().ok()?).ok()?)))
- .filter(|(_, v)| v == index)
- .map(|(k, _)| k.to_string())
- .collect();
+ if let Some(registries) = config.values()?.get("registries") {
+ let (registries, _) = registries.table("registries")?;
+ for (name, value) in registries {
+ if let Some(v) = value.table(&format!("registries.{name}"))?.0.get("index") {
+ let (v, _) = v.string(&format!("registries.{name}.index"))?;
+ if index == &CanonicalUrl::new(&v.into_url()?)? {
+ names.push(name.clone());
+ }
+ }
+ }
+ }
}
names.sort();
match names.len() {
@@ -232,99 +307,34 @@ pub fn registry_credential_config(
}
}
- let (token, credential_process, secret_key, secret_key_subject) = if let Some(name) = &name {
- log::debug!("found alternative registry name `{name}` for {sid}");
- let RegistryConfig {
- token,
- secret_key,
- secret_key_subject,
- credential_process,
- ..
- } = config.get::<RegistryConfig>(&format!("registries.{name}"))?;
- (token, credential_process, secret_key, secret_key_subject)
+ if let Some(name) = &name {
+ tracing::debug!("found alternative registry name `{name}` for {sid}");
+ config.get::<Option<RegistryConfig>>(&format!("registries.{name}"))
} else {
- log::debug!("no registry name found for {sid}");
- (None, None, None, None)
- };
-
- registry_credential_config_inner(
- false,
- name.as_deref(),
- token.map(Secret::from),
- credential_process,
- secret_key.map(Secret::from),
- secret_key_subject,
- config,
- )
+ tracing::debug!("no registry name found for {sid}");
+ Ok(None)
+ }
}
-fn registry_credential_config_inner(
- is_crates_io: bool,
- name: Option<&str>,
- token: Option<Secret<String>>,
- credential_process: Option<config::PathAndArgs>,
- secret_key: Option<Secret<String>>,
- secret_key_subject: Option<String>,
- config: &Config,
-) -> CargoResult<RegistryCredentialConfig> {
- let credential_process =
- credential_process.filter(|_| config.cli_unstable().credential_process);
- let secret_key = secret_key.filter(|_| config.cli_unstable().registry_auth);
- let secret_key_subject = secret_key_subject.filter(|_| config.cli_unstable().registry_auth);
- let err_both = |token_key: &str, proc_key: &str| {
- let registry = if is_crates_io {
- "".to_string()
- } else {
- format!(" for registry `{}`", name.unwrap_or("UN-NAMED"))
- };
- Err(format_err!(
- "both `{token_key}` and `{proc_key}` \
- were specified in the config{registry}.\n\
- Only one of these values may be set, remove one or the other to proceed.",
- ))
- };
- Ok(
- match (token, credential_process, secret_key, secret_key_subject) {
- (Some(_), Some(_), _, _) => return err_both("token", "credential-process"),
- (Some(_), _, Some(_), _) => return err_both("token", "secret-key"),
- (_, Some(_), Some(_), _) => return err_both("credential-process", "secret-key"),
- (_, _, None, Some(_)) => {
- let registry = if is_crates_io {
- "".to_string()
- } else {
- format!(" for registry `{}`", name.as_ref().unwrap())
- };
- return Err(format_err!(
- "`secret-key-subject` was set but `secret-key` was not in the config{}.\n\
- Either set the `secret-key` or remove the `secret-key-subject`.",
- registry
- ));
- }
- (Some(token), _, _, _) => RegistryCredentialConfig::Token(token),
- (_, Some(process), _, _) => RegistryCredentialConfig::Process((
- process.path.resolve_program(config),
- process.args,
- )),
- (None, None, Some(key), subject) => {
- RegistryCredentialConfig::AsymmetricKey((key, subject))
- }
- (None, None, None, _) => {
- if !is_crates_io {
- // If we couldn't find a registry-specific credential, try the global credential process.
- if let Some(process) = config
- .get::<Option<config::PathAndArgs>>("registry.credential-process")?
- .filter(|_| config.cli_unstable().credential_process)
- {
- return Ok(RegistryCredentialConfig::Process((
- process.path.resolve_program(config),
- process.args,
- )));
- }
- }
- RegistryCredentialConfig::None
- }
- },
- )
+/// Use the `[credential-alias]` table to see if the provider name has been aliased.
+fn resolve_credential_alias(config: &Config, mut provider: PathAndArgs) -> Vec<String> {
+ if provider.args.is_empty() {
+ let key = format!("credential-alias.{}", provider.path.raw_value());
+ if let Ok(alias) = config.get::<PathAndArgs>(&key) {
+ tracing::debug!("resolving credential alias '{key}' -> '{alias:?}'");
+ provider = alias;
+ }
+ }
+ provider.args.insert(
+ 0,
+ provider
+ .path
+ .resolve_program(config)
+ .to_str()
+ .unwrap()
+ .to_string(),
+ );
+ provider.args
}
#[derive(Debug, PartialEq)]
@@ -403,18 +413,85 @@ my-registry = {{ index = "{}" }}
}
// Store a token in the cache for future calls.
-pub fn cache_token(config: &Config, sid: &SourceId, token: Secret<&str>) {
+pub fn cache_token_from_commandline(config: &Config, sid: &SourceId, token: Secret<&str>) {
let url = sid.canonical_url();
config.credential_cache().insert(
url.clone(),
CredentialCacheValue {
- from_commandline: true,
- independent_of_endpoint: true,
- token_value: token.owned(),
+ token_value: token.to_owned(),
+ expiration: None,
+ operation_independent: true,
},
);
}
+fn credential_action(
+ config: &Config,
+ sid: &SourceId,
+ action: Action<'_>,
+ headers: Vec<String>,
+ args: &[&str],
+) -> CargoResult<CredentialResponse> {
+ let name = if sid.is_crates_io() {
+ Some(CRATES_IO_REGISTRY)
+ } else {
+ sid.alt_registry_key()
+ };
+ let registry = RegistryInfo {
+ index_url: sid.url().as_str(),
+ name,
+ headers,
+ };
+ let providers = credential_provider(config, sid)?;
+ let mut any_not_found = false;
+ for provider in providers {
+ let args: Vec<&str> = provider
+ .iter()
+ .map(String::as_str)
+ .chain(args.iter().map(|s| *s))
+ .collect();
+ let process = args[0];
+ tracing::debug!("attempting credential provider: {args:?}");
+ let provider: Box<dyn Credential> = match process {
+ "cargo:token" => Box::new(TokenCredential::new(config)),
+ "cargo:paseto" => Box::new(PasetoCredential::new(config)),
+ "cargo:token-from-stdout" => Box::new(BasicProcessCredential {}),
+ "cargo:wincred" => Box::new(cargo_credential_wincred::WindowsCredential {}),
+ "cargo:macos-keychain" => Box::new(cargo_credential_macos_keychain::MacKeychain {}),
+ "cargo:libsecret" => Box::new(cargo_credential_libsecret::LibSecretCredential {}),
+ process => Box::new(CredentialProcessCredential::new(process)),
+ };
+ config.shell().verbose(|c| {
+ c.status(
+ "Credential",
+ format!(
+ "{} {action} {}",
+ args.join(" "),
+ sid.display_registry_name()
+ ),
+ )
+ })?;
+ match provider.perform(&registry, &action, &args[1..]) {
+ Ok(response) => return Ok(response),
+ Err(cargo_credential::Error::UrlNotSupported) => {}
+ Err(cargo_credential::Error::NotFound) => any_not_found = true,
+ e => {
+ return e.with_context(|| {
+ format!(
+ "credential provider `{}` failed action `{action}`",
+ args.join(" ")
+ )
+ })
+ }
+ }
+ }
+ if any_not_found {
+ Err(cargo_credential::Error::NotFound.into())
+ } else {
+ anyhow::bail!("no credential providers could handle the request")
+ }
+}
+
/// Returns the token to use for the given registry.
/// If a `login_url` is provided and a token is not available, the
/// login_url will be included in the returned error.
@@ -422,9 +499,10 @@ pub fn auth_token(
config: &Config,
sid: &SourceId,
login_url: Option<&Url>,
- mutation: Option<Mutation<'_>>,
+ operation: Operation<'_>,
+ headers: Vec<String>,
) -> CargoResult<String> {
- match auth_token_optional(config, sid, mutation.as_ref())? {
+ match auth_token_optional(config, sid, operation, headers)? {
Some(token) => Ok(token.expose()),
None => Err(AuthorizationError {
sid: sid.clone(),
@@ -440,285 +518,99 @@ pub fn auth_token(
fn auth_token_optional(
config: &Config,
sid: &SourceId,
- mutation: Option<&'_ Mutation<'_>>,
+ operation: Operation<'_>,
+ headers: Vec<String>,
) -> CargoResult<Option<Secret<String>>> {
+ tracing::trace!("token requested for {}", sid.display_registry_name());
let mut cache = config.credential_cache();
let url = sid.canonical_url();
-
- if let Some(cache_token_value) = cache.get(url) {
- // Tokens for endpoints that do not involve a mutation can always be reused.
- // If the value is put in the cache by the command line, then we reuse it without looking at the configuration.
- if cache_token_value.from_commandline
- || cache_token_value.independent_of_endpoint
- || mutation.is_none()
+ if let Some(cached_token) = cache.get(url) {
+ if cached_token
+ .expiration
+ .map(|exp| OffsetDateTime::now_utc() + Duration::minutes(1) < exp)
+ .unwrap_or(true)
{
- return Ok(Some(cache_token_value.token_value.clone()));
+ if cached_token.operation_independent || matches!(operation, Operation::Read) {
+ tracing::trace!("using token from in-memory cache");
+ return Ok(Some(cached_token.token_value.clone()));
+ }
+ } else {
+ // Remove expired token from the cache
+ cache.remove(url);
}
}
- let credential = registry_credential_config(config, sid)?;
- let (independent_of_endpoint, token) = match credential {
- RegistryCredentialConfig::None => return Ok(None),
- RegistryCredentialConfig::Token(config_token) => (true, config_token),
- RegistryCredentialConfig::Process(process) => {
- // todo: PASETO with process
- let (independent_of_endpoint, token) =
- run_command(config, &process, sid, Action::Get)?.unwrap();
- (independent_of_endpoint, Secret::from(token))
- }
- cred @ RegistryCredentialConfig::AsymmetricKey(..) => {
- let token = asymmetric::public_token_from_credential(cred, sid, mutation)?;
- (false, token)
+ let credential_response = credential_action(config, sid, Action::Get(operation), headers, &[]);
+ if let Some(e) = credential_response.as_ref().err() {
+ if let Some(e) = e.downcast_ref::<cargo_credential::Error>() {
+ if matches!(e, cargo_credential::Error::NotFound) {
+ return Ok(None);
+ }
}
+ }
+ let credential_response = credential_response?;
+
+ let CredentialResponse::Get {
+ token,
+ cache: cache_control,
+ operation_independent,
+ } = credential_response
+ else {
+ bail!("credential provider produced unexpected response for `get` request: {credential_response:?}")
+ };
+ let token = Secret::from(token);
+ tracing::trace!("found token");
+ let expiration = match cache_control {
+ CacheControl::Expires(expiration) => Some(expiration),
+ CacheControl::Session => None,
+ CacheControl::Never | _ => return Ok(Some(token)),
};
- if independent_of_endpoint || mutation.is_none() {
- cache.insert(
- url.clone(),
- CredentialCacheValue {
- from_commandline: false,
- independent_of_endpoint,
- token_value: token.clone(),
- },
- );
- }
+ cache.insert(
+ url.clone(),
+ CredentialCacheValue {
+ token_value: token.clone(),
+ expiration,
+ operation_independent,
+ },
+ );
Ok(Some(token))
}
-/// A record of what kind of operation is happening that we should generate a token for.
-pub enum Mutation<'a> {
- /// Before we generate a crate file for the users attempt to publish,
- /// we need to check if we are configured correctly to generate a token.
- /// This variant is used to make sure that we can generate a token,
- /// to error out early if the token is not configured correctly.
- PrePublish,
- /// The user is attempting to publish a crate.
- Publish {
- /// The name of the crate
- name: &'a str,
- /// The version of the crate
- vers: &'a str,
- /// The checksum of the crate file being uploaded
- cksum: &'a str,
- },
- /// The user is attempting to yank a crate.
- Yank {
- /// The name of the crate
- name: &'a str,
- /// The version of the crate
- vers: &'a str,
- },
- /// The user is attempting to unyank a crate.
- Unyank {
- /// The name of the crate
- name: &'a str,
- /// The version of the crate
- vers: &'a str,
- },
- /// The user is attempting to modify the owners of a crate.
- Owners {
- /// The name of the crate
- name: &'a str,
- },
-}
-
-enum Action {
- Get,
- Store(String),
- Erase,
-}
-
-/// Saves the given token.
-pub fn login(config: &Config, sid: &SourceId, token: RegistryCredentialConfig) -> CargoResult<()> {
- match registry_credential_config(config, sid)? {
- RegistryCredentialConfig::Process(process) => {
- let token = token
- .as_token()
- .expect("credential_process cannot use login with a secret_key")
- .expose()
- .to_owned();
- run_command(config, &process, sid, Action::Store(token))?;
- }
- _ => {
- config::save_credentials(config, Some(token), &sid)?;
- }
- };
- Ok(())
-}
-
-/// Removes the token for the given registry.
+/// Log out from the given registry.
pub fn logout(config: &Config, sid: &SourceId) -> CargoResult<()> {
- match registry_credential_config(config, sid)? {
- RegistryCredentialConfig::Process(process) => {
- run_command(config, &process, sid, Action::Erase)?;
- }
- _ => {
- config::save_credentials(config, None, &sid)?;
+ let credential_response = credential_action(config, sid, Action::Logout, vec![], &[]);
+ if let Some(e) = credential_response.as_ref().err() {
+ if let Some(e) = e.downcast_ref::<cargo_credential::Error>() {
+ if matches!(e, cargo_credential::Error::NotFound) {
+ config.shell().status(
+ "Logout",
+ format!(
+ "not currently logged in to `{}`",
+ sid.display_registry_name()
+ ),
+ )?;
+ return Ok(());
+ }
}
+ }
+ let credential_response = credential_response?;
+ let CredentialResponse::Logout = credential_response else {
+ bail!("credential provider produced unexpected response for `logout` request: {credential_response:?}")
};
Ok(())
}
-fn run_command(
+/// Log in to the given registry.
+pub fn login(
config: &Config,
- process: &(PathBuf, Vec<String>),
sid: &SourceId,
- action: Action,
-) -> CargoResult<Option<(bool, String)>> {
- let index_url = sid.url().as_str();
- let cred_proc;
- let (exe, args) = if process.0.to_str().unwrap_or("").starts_with("cargo:") {
- cred_proc = sysroot_credential(config, process)?;
- &cred_proc
- } else {
- process
+ options: LoginOptions<'_>,
+ args: &[&str],
+) -> CargoResult<()> {
+ let credential_response = credential_action(config, sid, Action::Login(options), vec![], args)?;
+ let CredentialResponse::Login = credential_response else {
+ bail!("credential provider produced unexpected response for `login` request: {credential_response:?}")
};
- if !args.iter().any(|arg| arg.contains("{action}")) {
- let msg = |which| {
- format!(
- "credential process `{}` cannot be used to {}, \
- the credential-process configuration value must pass the \
- `{{action}}` argument in the config to support this command",
- exe.display(),
- which
- )
- };
- match action {
- Action::Get => {}
- Action::Store(_) => bail!(msg("log in")),
- Action::Erase => bail!(msg("log out")),
- }
- }
- // todo: PASETO with process
- let independent_of_endpoint = true;
- let action_str = match action {
- Action::Get => "get",
- Action::Store(_) => "store",
- Action::Erase => "erase",
- };
- let args: Vec<_> = args
- .iter()
- .map(|arg| {
- arg.replace("{action}", action_str)
- .replace("{index_url}", index_url)
- })
- .collect();
-
- let mut cmd = Command::new(&exe);
- cmd.args(args)
- .env(crate::CARGO_ENV, config.cargo_exe()?)
- .env("CARGO_REGISTRY_INDEX_URL", index_url);
- if sid.is_crates_io() {
- cmd.env("CARGO_REGISTRY_NAME_OPT", "crates-io");
- } else if let Some(name) = sid.alt_registry_key() {
- cmd.env("CARGO_REGISTRY_NAME_OPT", name);
- }
- match action {
- Action::Get => {
- cmd.stdout(Stdio::piped());
- }
- Action::Store(_) => {
- cmd.stdin(Stdio::piped());
- }
- Action::Erase => {}
- }
- let mut child = cmd.spawn().with_context(|| {
- let verb = match action {
- Action::Get => "fetch",
- Action::Store(_) => "store",
- Action::Erase => "erase",
- };
- format!(
- "failed to execute `{}` to {} authentication token for registry `{}`",
- exe.display(),
- verb,
- sid.display_registry_name(),
- )
- })?;
- let mut token = None;
- match &action {
- Action::Get => {
- let mut buffer = String::new();
- log::debug!("reading into buffer");
- child
- .stdout
- .as_mut()
- .unwrap()
- .read_to_string(&mut buffer)
- .with_context(|| {
- format!(
- "failed to read token from registry credential process `{}`",
- exe.display()
- )
- })?;
- if let Some(end) = buffer.find('\n') {
- if buffer.len() > end + 1 {
- bail!(
- "credential process `{}` returned more than one line of output; \
- expected a single token",
- exe.display()
- );
- }
- buffer.truncate(end);
- }
- token = Some((independent_of_endpoint, buffer));
- }
- Action::Store(token) => {
- writeln!(child.stdin.as_ref().unwrap(), "{}", token).with_context(|| {
- format!(
- "failed to send token to registry credential process `{}`",
- exe.display()
- )
- })?;
- }
- Action::Erase => {}
- }
- let status = child.wait().with_context(|| {
- format!(
- "registry credential process `{}` exit failure",
- exe.display()
- )
- })?;
- if !status.success() {
- let msg = match action {
- Action::Get => "failed to authenticate to registry",
- Action::Store(_) => "failed to store token to registry",
- Action::Erase => "failed to erase token from registry",
- };
- return Err(ProcessError::new(
- &format!(
- "registry credential process `{}` {} `{}`",
- exe.display(),
- msg,
- sid.display_registry_name()
- ),
- Some(status),
- None,
- )
- .into());
- }
- Ok(token)
-}
-
-/// Gets the path to the libexec processes in the sysroot.
-fn sysroot_credential(
- config: &Config,
- process: &(PathBuf, Vec<String>),
-) -> CargoResult<(PathBuf, Vec<String>)> {
- let cred_name = process.0.to_str().unwrap().strip_prefix("cargo:").unwrap();
- let cargo = config.cargo_exe()?;
- let root = cargo
- .parent()
- .and_then(|p| p.parent())
- .ok_or_else(|| format_err!("expected cargo path {}", cargo.display()))?;
- let exe = root.join("libexec").join(format!(
- "cargo-credential-{}{}",
- cred_name,
- std::env::consts::EXE_SUFFIX
- ));
- let mut args = process.1.clone();
- if !args.iter().any(|arg| arg == "{action}") {
- args.push("{action}".to_string());
- }
- Ok((exe, args))
+ Ok(())
}
diff --git a/src/tools/cargo/src/cargo/util/command_prelude.rs b/src/tools/cargo/src/cargo/util/command_prelude.rs
index 46ed7dd7c..bc707ef6f 100644
--- a/src/tools/cargo/src/cargo/util/command_prelude.rs
+++ b/src/tools/cargo/src/cargo/util/command_prelude.rs
@@ -26,6 +26,14 @@ pub use clap::Command;
use super::config::JobsConfig;
+pub mod heading {
+ pub const PACKAGE_SELECTION: &str = "Package Selection";
+ pub const TARGET_SELECTION: &str = "Target Selection";
+ pub const FEATURE_SELECTION: &str = "Feature Selection";
+ pub const COMPILATION_OPTIONS: &str = "Compilation Options";
+ pub const MANIFEST_OPTIONS: &str = "Manifest Options";
+}
+
pub trait CommandExt: Sized {
fn _arg(self, arg: Arg) -> Self;
@@ -37,8 +45,10 @@ pub trait CommandExt: Sized {
all: &'static str,
exclude: &'static str,
) -> Self {
- self.arg_package_spec_no_all(package, all, exclude)
- ._arg(flag("all", "Alias for --workspace (deprecated)"))
+ self.arg_package_spec_no_all(package, all, exclude)._arg(
+ flag("all", "Alias for --workspace (deprecated)")
+ .help_heading(heading::PACKAGE_SELECTION),
+ )
}
/// Variant of arg_package_spec that does not include the `--all` flag
@@ -51,33 +61,45 @@ pub trait CommandExt: Sized {
exclude: &'static str,
) -> Self {
self.arg_package_spec_simple(package)
- ._arg(flag("workspace", all))
- ._arg(multi_opt("exclude", "SPEC", exclude))
+ ._arg(flag("workspace", all).help_heading(heading::PACKAGE_SELECTION))
+ ._arg(multi_opt("exclude", "SPEC", exclude).help_heading(heading::PACKAGE_SELECTION))
}
fn arg_package_spec_simple(self, package: &'static str) -> Self {
- self._arg(optional_multi_opt("package", "SPEC", package).short('p'))
+ self._arg(
+ optional_multi_opt("package", "SPEC", package)
+ .short('p')
+ .help_heading(heading::PACKAGE_SELECTION),
+ )
}
fn arg_package(self, package: &'static str) -> Self {
self._arg(
optional_opt("package", package)
.short('p')
- .value_name("SPEC"),
+ .value_name("SPEC")
+ .help_heading(heading::PACKAGE_SELECTION),
)
}
fn arg_jobs(self) -> Self {
+ self.arg_jobs_without_keep_going()._arg(
+ flag(
+ "keep-going",
+ "Do not abort the build as soon as there is an error (unstable)",
+ )
+ .help_heading(heading::COMPILATION_OPTIONS),
+ )
+ }
+
+ fn arg_jobs_without_keep_going(self) -> Self {
self._arg(
opt("jobs", "Number of parallel jobs, defaults to # of CPUs.")
.short('j')
.value_name("N")
- .allow_hyphen_values(true),
+ .allow_hyphen_values(true)
+ .help_heading(heading::COMPILATION_OPTIONS),
)
- ._arg(flag(
- "keep-going",
- "Do not abort the build as soon as there is an error (unstable)",
- ))
}
fn arg_targets_all(
@@ -94,11 +116,13 @@ pub trait CommandExt: Sized {
all: &'static str,
) -> Self {
self.arg_targets_lib_bin_example(lib, bin, bins, example, examples)
- ._arg(flag("tests", tests))
- ._arg(optional_multi_opt("test", "NAME", test))
- ._arg(flag("benches", benches))
- ._arg(optional_multi_opt("bench", "NAME", bench))
- ._arg(flag("all-targets", all))
+ ._arg(flag("tests", tests).help_heading(heading::TARGET_SELECTION))
+ ._arg(optional_multi_opt("test", "NAME", test).help_heading(heading::TARGET_SELECTION))
+ ._arg(flag("benches", benches).help_heading(heading::TARGET_SELECTION))
+ ._arg(
+ optional_multi_opt("bench", "NAME", bench).help_heading(heading::TARGET_SELECTION),
+ )
+ ._arg(flag("all-targets", all).help_heading(heading::TARGET_SELECTION))
}
fn arg_targets_lib_bin_example(
@@ -109,11 +133,14 @@ pub trait CommandExt: Sized {
example: &'static str,
examples: &'static str,
) -> Self {
- self._arg(flag("lib", lib))
- ._arg(flag("bins", bins))
- ._arg(optional_multi_opt("bin", "NAME", bin))
- ._arg(flag("examples", examples))
- ._arg(optional_multi_opt("example", "NAME", example))
+ self._arg(flag("lib", lib).help_heading(heading::TARGET_SELECTION))
+ ._arg(flag("bins", bins).help_heading(heading::TARGET_SELECTION))
+ ._arg(optional_multi_opt("bin", "NAME", bin).help_heading(heading::TARGET_SELECTION))
+ ._arg(flag("examples", examples).help_heading(heading::TARGET_SELECTION))
+ ._arg(
+ optional_multi_opt("example", "NAME", example)
+ .help_heading(heading::TARGET_SELECTION),
+ )
}
fn arg_targets_bins_examples(
@@ -123,15 +150,21 @@ pub trait CommandExt: Sized {
example: &'static str,
examples: &'static str,
) -> Self {
- self._arg(optional_multi_opt("bin", "NAME", bin))
- ._arg(flag("bins", bins))
- ._arg(optional_multi_opt("example", "NAME", example))
- ._arg(flag("examples", examples))
+ self._arg(optional_multi_opt("bin", "NAME", bin).help_heading(heading::TARGET_SELECTION))
+ ._arg(flag("bins", bins).help_heading(heading::TARGET_SELECTION))
+ ._arg(
+ optional_multi_opt("example", "NAME", example)
+ .help_heading(heading::TARGET_SELECTION),
+ )
+ ._arg(flag("examples", examples).help_heading(heading::TARGET_SELECTION))
}
fn arg_targets_bin_example(self, bin: &'static str, example: &'static str) -> Self {
- self._arg(optional_multi_opt("bin", "NAME", bin))
- ._arg(optional_multi_opt("example", "NAME", example))
+ self._arg(optional_multi_opt("bin", "NAME", bin).help_heading(heading::TARGET_SELECTION))
+ ._arg(
+ optional_multi_opt("example", "NAME", example)
+ .help_heading(heading::TARGET_SELECTION),
+ )
}
fn arg_features(self) -> Self {
@@ -141,21 +174,36 @@ pub trait CommandExt: Sized {
"FEATURES",
"Space or comma separated list of features to activate",
)
- .short('F'),
+ .short('F')
+ .help_heading(heading::FEATURE_SELECTION),
+ )
+ ._arg(
+ flag("all-features", "Activate all available features")
+ .help_heading(heading::FEATURE_SELECTION),
+ )
+ ._arg(
+ flag(
+ "no-default-features",
+ "Do not activate the `default` feature",
+ )
+ .help_heading(heading::FEATURE_SELECTION),
)
- ._arg(flag("all-features", "Activate all available features"))
- ._arg(flag(
- "no-default-features",
- "Do not activate the `default` feature",
- ))
}
fn arg_release(self, release: &'static str) -> Self {
- self._arg(flag("release", release).short('r'))
+ self._arg(
+ flag("release", release)
+ .short('r')
+ .help_heading(heading::COMPILATION_OPTIONS),
+ )
}
fn arg_profile(self, profile: &'static str) -> Self {
- self._arg(opt("profile", profile).value_name("PROFILE-NAME"))
+ self._arg(
+ opt("profile", profile)
+ .value_name("PROFILE-NAME")
+ .help_heading(heading::COMPILATION_OPTIONS),
+ )
}
fn arg_doc(self, doc: &'static str) -> Self {
@@ -163,17 +211,23 @@ pub trait CommandExt: Sized {
}
fn arg_target_triple(self, target: &'static str) -> Self {
- self._arg(multi_opt("target", "TRIPLE", target))
+ self._arg(multi_opt("target", "TRIPLE", target).help_heading(heading::COMPILATION_OPTIONS))
}
fn arg_target_dir(self) -> Self {
self._arg(
- opt("target-dir", "Directory for all generated artifacts").value_name("DIRECTORY"),
+ opt("target-dir", "Directory for all generated artifacts")
+ .value_name("DIRECTORY")
+ .help_heading(heading::COMPILATION_OPTIONS),
)
}
fn arg_manifest_path(self) -> Self {
- self._arg(opt("manifest-path", "Path to Cargo.toml").value_name("PATH"))
+ self._arg(
+ opt("manifest-path", "Path to Cargo.toml")
+ .value_name("PATH")
+ .help_heading(heading::MANIFEST_OPTIONS),
+ )
}
fn arg_message_format(self) -> Self {
@@ -181,14 +235,17 @@ pub trait CommandExt: Sized {
}
fn arg_build_plan(self) -> Self {
- self._arg(flag(
- "build-plan",
- "Output the build plan in JSON (unstable)",
- ))
+ self._arg(
+ flag("build-plan", "Output the build plan in JSON (unstable)")
+ .help_heading(heading::COMPILATION_OPTIONS),
+ )
}
fn arg_unit_graph(self) -> Self {
- self._arg(flag("unit-graph", "Output build graph in JSON (unstable)"))
+ self._arg(
+ flag("unit-graph", "Output build graph in JSON (unstable)")
+ .help_heading(heading::COMPILATION_OPTIONS),
+ )
}
fn arg_new_opts(self) -> Self {
@@ -252,7 +309,8 @@ pub trait CommandExt: Sized {
"Timing output formats (unstable) (comma separated): html, json",
)
.value_name("FMTS")
- .require_equals(true),
+ .require_equals(true)
+ .help_heading(heading::COMPILATION_OPTIONS),
)
}
}
diff --git a/src/tools/cargo/src/cargo/util/config/mod.rs b/src/tools/cargo/src/cargo/util/config/mod.rs
index 4e6bca302..cf977d38d 100644
--- a/src/tools/cargo/src/cargo/util/config/mod.rs
+++ b/src/tools/cargo/src/cargo/util/config/mod.rs
@@ -70,20 +70,21 @@ use crate::core::compiler::rustdoc::RustdocExternMap;
use crate::core::shell::Verbosity;
use crate::core::{features, CliUnstable, Shell, SourceId, Workspace, WorkspaceRootConfig};
use crate::ops::RegistryCredentialConfig;
-use crate::util::auth::Secret;
use crate::util::errors::CargoResult;
use crate::util::network::http::configure_http_handle;
use crate::util::network::http::http_handle;
-use crate::util::CanonicalUrl;
-use crate::util::{internal, toml as cargo_toml};
+use crate::util::toml as cargo_toml;
+use crate::util::{internal, CanonicalUrl};
use crate::util::{try_canonicalize, validate_package_name};
use crate::util::{FileLock, Filesystem, IntoUrl, IntoUrlWithBase, Rustc};
use anyhow::{anyhow, bail, format_err, Context as _};
+use cargo_credential::Secret;
use cargo_util::paths;
use curl::easy::Easy;
use lazycell::LazyCell;
use serde::de::IntoDeserializer as _;
use serde::Deserialize;
+use time::OffsetDateTime;
use toml_edit::Item;
use url::Url;
@@ -105,6 +106,8 @@ pub use target::{TargetCfgConfig, TargetConfig};
mod environment;
use environment::Env;
+use super::auth::RegistryConfig;
+
// Helper macro for creating typed access methods.
macro_rules! get_value_typed {
($name:ident, $ty:ty, $variant:ident, $expected:expr) => {
@@ -146,13 +149,9 @@ enum WhyLoad {
/// A previously generated authentication token and the data needed to determine if it can be reused.
#[derive(Debug)]
pub struct CredentialCacheValue {
- /// If the command line was used to override the token then it must always be reused,
- /// even if reading the configuration files would lead to a different value.
- pub from_commandline: bool,
- /// If nothing depends on which endpoint is being hit, then we can reuse the token
- /// for any future request even if some of the requests involve mutations.
- pub independent_of_endpoint: bool,
pub token_value: Secret<String>,
+ pub expiration: Option<OffsetDateTime>,
+ pub operation_independent: bool,
}
/// Configuration information for cargo. This is not specific to a build, it is information
@@ -211,6 +210,8 @@ pub struct Config {
/// Cache of credentials from configuration or credential providers.
/// Maps from url to credential value.
credential_cache: LazyCell<RefCell<HashMap<CanonicalUrl, CredentialCacheValue>>>,
+ /// Cache of registry config from from the `[registries]` table.
+ registry_config: LazyCell<RefCell<HashMap<SourceId, Option<RegistryConfig>>>>,
/// Lock, if held, of the global package cache along with the number of
/// acquisitions so far.
package_cache_lock: RefCell<Option<(Option<FileLock>, usize)>>,
@@ -302,6 +303,7 @@ impl Config {
env,
updated_sources: LazyCell::new(),
credential_cache: LazyCell::new(),
+ registry_config: LazyCell::new(),
package_cache_lock: RefCell::new(None),
http_config: LazyCell::new(),
future_incompat_config: LazyCell::new(),
@@ -491,6 +493,13 @@ impl Config {
.borrow_mut()
}
+ /// Cache of already parsed registries from the `[registries]` table.
+ pub(crate) fn registry_config(&self) -> RefMut<'_, HashMap<SourceId, Option<RegistryConfig>>> {
+ self.registry_config
+ .borrow_with(|| RefCell::new(HashMap::new()))
+ .borrow_mut()
+ }
+
/// Gets all config values from disk.
///
/// This will lazy-load the values as necessary. Callers are responsible
@@ -602,7 +611,7 @@ impl Config {
key: &ConfigKey,
vals: &HashMap<String, ConfigValue>,
) -> CargoResult<Option<ConfigValue>> {
- log::trace!("get cv {:?}", key);
+ tracing::trace!("get cv {:?}", key);
if key.is_root() {
// Returning the entire root table (for example `cargo config get`
// with no key). The definition here shouldn't matter.
@@ -810,7 +819,7 @@ impl Config {
///
/// See `get` for more details.
pub fn get_string(&self, key: &str) -> CargoResult<OptValue<String>> {
- self.get::<Option<Value<String>>>(key)
+ self.get::<OptValue<String>>(key)
}
/// Get a config value that is expected to be a path.
@@ -819,7 +828,7 @@ impl Config {
/// directory separators. See `ConfigRelativePath::resolve_program` for
/// more details.
pub fn get_path(&self, key: &str) -> CargoResult<OptValue<PathBuf>> {
- self.get::<Option<Value<ConfigRelativePath>>>(key).map(|v| {
+ self.get::<OptValue<ConfigRelativePath>>(key).map(|v| {
v.map(|v| Value {
val: v.val.resolve_program(self),
definition: v.definition,
@@ -2789,7 +2798,7 @@ fn disables_multiplexing_for_bad_curl(
.iter()
.any(|v| curl_version.starts_with(v))
{
- log::info!("disabling multiplexing with proxy, curl version is {curl_version}");
+ tracing::info!("disabling multiplexing with proxy, curl version is {curl_version}");
http.multiplexing = Some(false);
}
}
diff --git a/src/tools/cargo/src/cargo/util/config/path.rs b/src/tools/cargo/src/cargo/util/config/path.rs
index a90cab2b2..bc53ffcfa 100644
--- a/src/tools/cargo/src/cargo/util/config/path.rs
+++ b/src/tools/cargo/src/cargo/util/config/path.rs
@@ -10,6 +10,10 @@ use std::path::PathBuf;
pub struct ConfigRelativePath(Value<String>);
impl ConfigRelativePath {
+ pub fn new(path: Value<String>) -> ConfigRelativePath {
+ ConfigRelativePath(path)
+ }
+
/// Returns the underlying value.
pub fn value(&self) -> &Value<String> {
&self.0
@@ -49,7 +53,7 @@ impl ConfigRelativePath {
///
/// Typically you should use `ConfigRelativePath::resolve_program` on the path
/// to get the actual program.
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq)]
pub struct PathAndArgs {
pub path: ConfigRelativePath,
pub args: Vec<String>,
@@ -76,3 +80,22 @@ impl<'de> serde::Deserialize<'de> for PathAndArgs {
})
}
}
+
+impl PathAndArgs {
+ /// Construct a PathAndArgs from a string. The string will be split on ascii whitespace,
+ /// with the first item being treated as a `ConfigRelativePath` to the executable, and subsequent
+ /// items as arguments.
+ pub fn from_whitespace_separated_string(p: &Value<String>) -> PathAndArgs {
+ let mut iter = p.val.split_ascii_whitespace().map(str::to_string);
+ let val = iter.next().unwrap_or_default();
+ let args = iter.collect();
+ let crp = Value {
+ val,
+ definition: p.definition.clone(),
+ };
+ PathAndArgs {
+ path: ConfigRelativePath(crp),
+ args,
+ }
+ }
+}
diff --git a/src/tools/cargo/src/cargo/util/config/target.rs b/src/tools/cargo/src/cargo/util/config/target.rs
index cdafe73dd..b8aaf906d 100644
--- a/src/tools/cargo/src/cargo/util/config/target.rs
+++ b/src/tools/cargo/src/cargo/util/config/target.rs
@@ -45,7 +45,7 @@ pub(super) fn load_target_cfgs(config: &Config) -> CargoResult<Vec<(String, Targ
// rebuilds. We may perhaps one day wish to ensure a deterministic
// ordering via the order keys were defined in files perhaps.
let target: BTreeMap<String, TargetCfgConfig> = config.get("target")?;
- log::debug!("Got all targets {:#?}", target);
+ tracing::debug!("Got all targets {:#?}", target);
for (key, cfg) in target {
if key.starts_with("cfg(") {
// Unfortunately this is not able to display the location of the
diff --git a/src/tools/cargo/src/cargo/util/credential/adaptor.rs b/src/tools/cargo/src/cargo/util/credential/adaptor.rs
new file mode 100644
index 000000000..693e653b5
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util/credential/adaptor.rs
@@ -0,0 +1,68 @@
+//! Credential provider that launches an external process that only outputs a credential
+
+use std::{
+ io::Read,
+ process::{Command, Stdio},
+};
+
+use anyhow::Context;
+use cargo_credential::{
+ Action, CacheControl, Credential, CredentialResponse, RegistryInfo, Secret,
+};
+
+pub struct BasicProcessCredential {}
+
+impl Credential for BasicProcessCredential {
+ fn perform(
+ &self,
+ registry: &RegistryInfo<'_>,
+ action: &Action<'_>,
+ args: &[&str],
+ ) -> Result<CredentialResponse, cargo_credential::Error> {
+ match action {
+ Action::Get(_) => {
+ let mut args = args.iter();
+ let exe = args.next()
+ .ok_or("The first argument to `cargo:token-from-stdout` must be a command that prints a token on stdout")?;
+ let args = args.map(|arg| arg.replace("{index_url}", registry.index_url));
+
+ let mut cmd = Command::new(exe);
+ cmd.args(args)
+ .env("CARGO_REGISTRY_INDEX_URL", registry.index_url);
+ if let Some(name) = registry.name {
+ cmd.env("CARGO_REGISTRY_NAME_OPT", name);
+ }
+ cmd.stdout(Stdio::piped());
+ let mut child = cmd.spawn().context("failed to spawn credential process")?;
+ let mut buffer = String::new();
+ child
+ .stdout
+ .take()
+ .unwrap()
+ .read_to_string(&mut buffer)
+ .context("failed to read from credential provider")?;
+ if let Some(end) = buffer.find('\n') {
+ if buffer.len() > end + 1 {
+ return Err(format!(
+ "process `{}` returned more than one line of output; \
+ expected a single token",
+ exe
+ )
+ .into());
+ }
+ buffer.truncate(end);
+ }
+ let status = child.wait().context("credential process never started")?;
+ if !status.success() {
+ return Err(format!("process `{}` failed with status `{status}`", exe).into());
+ }
+ Ok(CredentialResponse::Get {
+ token: Secret::from(buffer),
+ cache: CacheControl::Session,
+ operation_independent: true,
+ })
+ }
+ _ => Err(cargo_credential::Error::OperationNotSupported),
+ }
+ }
+}
diff --git a/src/tools/cargo/src/cargo/util/credential/mod.rs b/src/tools/cargo/src/cargo/util/credential/mod.rs
new file mode 100644
index 000000000..7baf7d2a1
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util/credential/mod.rs
@@ -0,0 +1,8 @@
+//! Built-in Cargo credential providers
+
+#![allow(clippy::print_stderr)]
+
+pub mod adaptor;
+pub mod paseto;
+pub mod process;
+pub mod token;
diff --git a/src/tools/cargo/src/cargo/util/credential/paseto.rs b/src/tools/cargo/src/cargo/util/credential/paseto.rs
new file mode 100644
index 000000000..329d1f11c
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util/credential/paseto.rs
@@ -0,0 +1,224 @@
+//! Credential provider that implements PASETO asymmetric tokens stored in Cargo's config.
+
+use anyhow::Context;
+use cargo_credential::{
+ Action, CacheControl, Credential, CredentialResponse, Error, Operation, RegistryInfo, Secret,
+};
+use clap::Command;
+use pasetors::{
+ keys::{AsymmetricKeyPair, AsymmetricPublicKey, AsymmetricSecretKey, Generate},
+ paserk::FormatAsPaserk,
+};
+use time::{format_description::well_known::Rfc3339, OffsetDateTime};
+use url::Url;
+
+use crate::{
+ core::SourceId,
+ ops::RegistryCredentialConfig,
+ util::{auth::registry_credential_config_raw, command_prelude::opt, config},
+ Config,
+};
+
+/// The main body of an asymmetric token as describe in RFC 3231.
+#[derive(serde::Serialize)]
+struct Message<'a> {
+ iat: &'a str,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ sub: Option<&'a str>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ mutation: Option<&'a str>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ name: Option<&'a str>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ vers: Option<&'a str>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ cksum: Option<&'a str>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ challenge: Option<&'a str>,
+ /// This field is not yet used. This field can be set to a value >1 to indicate a breaking change in the token format.
+ #[serde(skip_serializing_if = "Option::is_none")]
+ v: Option<u8>,
+}
+/// The footer of an asymmetric token as describe in RFC 3231.
+#[derive(serde::Serialize)]
+struct Footer<'a> {
+ url: &'a str,
+ kip: pasetors::paserk::Id,
+}
+
+pub(crate) struct PasetoCredential<'a> {
+ config: &'a Config,
+}
+
+impl<'a> PasetoCredential<'a> {
+ pub fn new(config: &'a Config) -> Self {
+ Self { config }
+ }
+}
+
+impl<'a> Credential for PasetoCredential<'a> {
+ fn perform(
+ &self,
+ registry: &RegistryInfo<'_>,
+ action: &Action<'_>,
+ args: &[&str],
+ ) -> Result<CredentialResponse, Error> {
+ let index_url = Url::parse(registry.index_url).context("parsing index url")?;
+ let sid = if let Some(name) = registry.name {
+ SourceId::for_alt_registry(&index_url, name)
+ } else {
+ SourceId::for_registry(&index_url)
+ }?;
+
+ let reg_cfg = registry_credential_config_raw(self.config, &sid)?;
+
+ let matches = Command::new("cargo:paseto")
+ .no_binary_name(true)
+ .arg(opt("key-subject", "Set the key subject for this registry").value_name("SUBJECT"))
+ .try_get_matches_from(args)
+ .map_err(Box::new)?;
+ let key_subject = matches.get_one("key-subject").map(String::as_str);
+
+ match action {
+ Action::Get(operation) => {
+ let Some(reg_cfg) = reg_cfg else {
+ return Err(Error::NotFound);
+ };
+ let Some(secret_key) = reg_cfg.secret_key.as_ref() else {
+ return Err(Error::NotFound);
+ };
+
+ let secret_key_subject = reg_cfg.secret_key_subject;
+ let secret: Secret<AsymmetricSecretKey<pasetors::version3::V3>> = secret_key
+ .val
+ .as_ref()
+ .map(|key| key.as_str().try_into())
+ .transpose()
+ .context("failed to load private key")?;
+ let public: AsymmetricPublicKey<pasetors::version3::V3> = secret
+ .as_ref()
+ .map(|key| key.try_into())
+ .transpose()
+ .context("failed to load public key from private key")?
+ .expose();
+ let kip: pasetors::paserk::Id = (&public).into();
+
+ let iat = OffsetDateTime::now_utc();
+
+ let message = Message {
+ iat: &iat.format(&Rfc3339).unwrap(),
+ sub: secret_key_subject.as_deref(),
+ mutation: match operation {
+ Operation::Publish { .. } => Some("publish"),
+ Operation::Yank { .. } => Some("yank"),
+ Operation::Unyank { .. } => Some("unyank"),
+ Operation::Owners { .. } => Some("owners"),
+ _ => None,
+ },
+ name: match operation {
+ Operation::Publish { name, .. }
+ | Operation::Yank { name, .. }
+ | Operation::Unyank { name, .. }
+ | Operation::Owners { name, .. } => Some(name),
+ _ => None,
+ },
+ vers: match operation {
+ Operation::Publish { vers, .. }
+ | Operation::Yank { vers, .. }
+ | Operation::Unyank { vers, .. } => Some(vers),
+ _ => None,
+ },
+ cksum: match operation {
+ Operation::Publish { cksum, .. } => Some(cksum),
+ _ => None,
+ },
+ challenge: None, // todo: PASETO with challenges
+ v: None,
+ };
+ let footer = Footer {
+ url: &registry.index_url,
+ kip,
+ };
+
+ // Only read operations can be cached with asymmetric tokens.
+ let cache = match operation {
+ Operation::Read => CacheControl::Session,
+ _ => CacheControl::Never,
+ };
+
+ let token = secret
+ .map(|secret| {
+ pasetors::version3::PublicToken::sign(
+ &secret,
+ serde_json::to_string(&message)
+ .expect("cannot serialize")
+ .as_bytes(),
+ Some(
+ serde_json::to_string(&footer)
+ .expect("cannot serialize")
+ .as_bytes(),
+ ),
+ None,
+ )
+ })
+ .transpose()
+ .context("failed to sign request")?;
+
+ Ok(CredentialResponse::Get {
+ token,
+ cache,
+ operation_independent: false,
+ })
+ }
+ Action::Login(options) => {
+ let old_key_subject = reg_cfg.and_then(|cfg| cfg.secret_key_subject);
+ let new_token;
+ let secret_key: Secret<String>;
+ if let Some(key) = &options.token {
+ secret_key = key.clone().map(str::to_string);
+ } else {
+ let kp = AsymmetricKeyPair::<pasetors::version3::V3>::generate().unwrap();
+ secret_key = Secret::default().map(|mut key| {
+ FormatAsPaserk::fmt(&kp.secret, &mut key).unwrap();
+ key
+ });
+ }
+
+ if let Some(p) = paserk_public_from_paserk_secret(secret_key.as_deref()) {
+ eprintln!("{}", &p);
+ } else {
+ return Err("not a validly formatted PASERK secret key".into());
+ }
+ new_token = RegistryCredentialConfig::AsymmetricKey((
+ secret_key,
+ match key_subject {
+ Some(key_subject) => Some(key_subject.to_string()),
+ None => old_key_subject,
+ },
+ ));
+ config::save_credentials(self.config, Some(new_token), &sid)?;
+ Ok(CredentialResponse::Login)
+ }
+ Action::Logout => {
+ config::save_credentials(self.config, None, &sid)?;
+ Ok(CredentialResponse::Logout)
+ }
+ _ => Err(Error::OperationNotSupported),
+ }
+ }
+}
+
+/// Checks that a secret key is valid, and returns the associated public key in Paserk format.
+pub(crate) fn paserk_public_from_paserk_secret(secret_key: Secret<&str>) -> Option<String> {
+ let secret: Secret<AsymmetricSecretKey<pasetors::version3::V3>> =
+ secret_key.map(|key| key.try_into()).transpose().ok()?;
+ let public: AsymmetricPublicKey<pasetors::version3::V3> = secret
+ .as_ref()
+ .map(|key| key.try_into())
+ .transpose()
+ .ok()?
+ .expose();
+ let mut paserk_pub_key = String::new();
+ FormatAsPaserk::fmt(&public, &mut paserk_pub_key).unwrap();
+ Some(paserk_pub_key)
+}
diff --git a/src/tools/cargo/src/cargo/util/credential/process.rs b/src/tools/cargo/src/cargo/util/credential/process.rs
new file mode 100644
index 000000000..89eac1af6
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util/credential/process.rs
@@ -0,0 +1,80 @@
+//! Credential provider that launches an external process using Cargo's credential
+//! protocol.
+
+use std::{
+ io::{BufRead, BufReader, Write},
+ path::PathBuf,
+ process::{Command, Stdio},
+};
+
+use anyhow::Context;
+use cargo_credential::{
+ Action, Credential, CredentialHello, CredentialRequest, CredentialResponse, RegistryInfo,
+};
+
+pub struct CredentialProcessCredential {
+ path: PathBuf,
+}
+
+impl<'a> CredentialProcessCredential {
+ pub fn new(path: &str) -> Self {
+ Self {
+ path: PathBuf::from(path),
+ }
+ }
+}
+
+impl<'a> Credential for CredentialProcessCredential {
+ fn perform(
+ &self,
+ registry: &RegistryInfo<'_>,
+ action: &Action<'_>,
+ args: &[&str],
+ ) -> Result<CredentialResponse, cargo_credential::Error> {
+ let mut cmd = Command::new(&self.path);
+ cmd.stdout(Stdio::piped());
+ cmd.stdin(Stdio::piped());
+ cmd.arg("--cargo-plugin");
+ tracing::debug!("credential-process: {cmd:?}");
+ let mut child = cmd.spawn().context("failed to spawn credential process")?;
+ let mut output_from_child = BufReader::new(child.stdout.take().unwrap());
+ let mut input_to_child = child.stdin.take().unwrap();
+ let mut buffer = String::new();
+ output_from_child
+ .read_line(&mut buffer)
+ .context("failed to read hello from credential provider")?;
+ let credential_hello: CredentialHello =
+ serde_json::from_str(&buffer).context("failed to deserialize hello")?;
+ tracing::debug!("credential-process > {credential_hello:?}");
+
+ let req = CredentialRequest {
+ v: cargo_credential::PROTOCOL_VERSION_1,
+ action: action.clone(),
+ registry: registry.clone(),
+ args: args.to_vec(),
+ };
+ let request = serde_json::to_string(&req).context("failed to serialize request")?;
+ tracing::debug!("credential-process < {req:?}");
+ writeln!(input_to_child, "{request}").context("failed to write to credential provider")?;
+
+ buffer.clear();
+ output_from_child
+ .read_line(&mut buffer)
+ .context("failed to read response from credential provider")?;
+ let response: Result<CredentialResponse, cargo_credential::Error> =
+ serde_json::from_str(&buffer).context("failed to deserialize response")?;
+ tracing::debug!("credential-process > {response:?}");
+ drop(input_to_child);
+ let status = child.wait().context("credential process never started")?;
+ if !status.success() {
+ return Err(anyhow::anyhow!(
+ "credential process `{}` failed with status {}`",
+ self.path.display(),
+ status
+ )
+ .into());
+ }
+ tracing::trace!("credential process exited successfully");
+ response
+ }
+}
diff --git a/src/tools/cargo/src/cargo/util/credential/token.rs b/src/tools/cargo/src/cargo/util/credential/token.rs
new file mode 100644
index 000000000..7a29e6360
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util/credential/token.rs
@@ -0,0 +1,96 @@
+//! Credential provider that uses plaintext tokens in Cargo's config.
+
+use anyhow::Context;
+use cargo_credential::{Action, CacheControl, Credential, CredentialResponse, Error, RegistryInfo};
+use url::Url;
+
+use crate::{
+ core::SourceId,
+ ops::RegistryCredentialConfig,
+ util::{auth::registry_credential_config_raw, config},
+ Config,
+};
+
+pub struct TokenCredential<'a> {
+ config: &'a Config,
+}
+
+impl<'a> TokenCredential<'a> {
+ pub fn new(config: &'a Config) -> Self {
+ Self { config }
+ }
+}
+
+impl<'a> Credential for TokenCredential<'a> {
+ fn perform(
+ &self,
+ registry: &RegistryInfo<'_>,
+ action: &Action<'_>,
+ _args: &[&str],
+ ) -> Result<CredentialResponse, Error> {
+ let index_url = Url::parse(registry.index_url).context("parsing index url")?;
+ let sid = if let Some(name) = registry.name {
+ SourceId::for_alt_registry(&index_url, name)
+ } else {
+ SourceId::for_registry(&index_url)
+ }?;
+ let previous_token =
+ registry_credential_config_raw(self.config, &sid)?.and_then(|c| c.token);
+
+ match action {
+ Action::Get(_) => {
+ let token = previous_token.ok_or_else(|| Error::NotFound)?.val;
+ Ok(CredentialResponse::Get {
+ token,
+ cache: CacheControl::Session,
+ operation_independent: true,
+ })
+ }
+ Action::Login(options) => {
+ // Automatically remove `cargo login` from an inputted token to
+ // allow direct pastes from `registry.host()`/me.
+ let new_token = cargo_credential::read_token(options, registry)?
+ .map(|line| line.replace("cargo login", "").trim().to_string());
+
+ crates_io::check_token(new_token.as_ref().expose()).map_err(Box::new)?;
+ config::save_credentials(
+ self.config,
+ Some(RegistryCredentialConfig::Token(new_token)),
+ &sid,
+ )?;
+ let _ = self.config.shell().status(
+ "Login",
+ format!("token for `{}` saved", sid.display_registry_name()),
+ );
+ Ok(CredentialResponse::Login)
+ }
+ Action::Logout => {
+ if previous_token.is_none() {
+ return Err(Error::NotFound);
+ }
+ let reg_name = sid.display_registry_name();
+ config::save_credentials(self.config, None, &sid)?;
+ let _ = self.config.shell().status(
+ "Logout",
+ format!("token for `{reg_name}` has been removed from local storage"),
+ );
+ let location = if sid.is_crates_io() {
+ "<https://crates.io/me>".to_string()
+ } else {
+ // The URL for the source requires network access to load the config.
+ // That could be a fairly heavy operation to perform just to provide a
+ // help message, so for now this just provides some generic text.
+ // Perhaps in the future this could have an API to fetch the config if
+ // it is cached, but avoid network access otherwise?
+ format!("the `{reg_name}` website")
+ };
+ eprintln!(
+ "note: This does not revoke the token on the registry server.\n \
+ If you need to revoke the token, visit {location} and follow the instructions there."
+ );
+ Ok(CredentialResponse::Logout)
+ }
+ _ => Err(Error::OperationNotSupported),
+ }
+ }
+}
diff --git a/src/tools/cargo/src/cargo/util/diagnostic_server.rs b/src/tools/cargo/src/cargo/util/diagnostic_server.rs
index 36215735b..f8eeabfc2 100644
--- a/src/tools/cargo/src/cargo/util/diagnostic_server.rs
+++ b/src/tools/cargo/src/cargo/util/diagnostic_server.rs
@@ -11,8 +11,8 @@ use std::thread::{self, JoinHandle};
use anyhow::{Context, Error};
use cargo_util::ProcessBuilder;
-use log::warn;
use serde::{Deserialize, Serialize};
+use tracing::warn;
use crate::core::Edition;
use crate::util::errors::CargoResult;
diff --git a/src/tools/cargo/src/cargo/util/errors.rs b/src/tools/cargo/src/cargo/util/errors.rs
index 5c7eebcdb..9589e1ae3 100644
--- a/src/tools/cargo/src/cargo/util/errors.rs
+++ b/src/tools/cargo/src/cargo/util/errors.rs
@@ -83,8 +83,21 @@ impl HttpNotSuccessful {
}
write!(result, ", got {}\n", self.code).unwrap();
if show_headers {
- if !self.headers.is_empty() {
- write!(result, "debug headers:\n{}\n", self.headers.join("\n")).unwrap();
+ let headers: Vec<_> = self
+ .headers
+ .iter()
+ .filter(|header| {
+ let Some((name, _)) = header.split_once(":") else {
+ return false;
+ };
+ DEBUG_HEADERS.contains(&name.to_ascii_lowercase().trim())
+ })
+ .collect();
+ if !headers.is_empty() {
+ writeln!(result, "debug headers:").unwrap();
+ for header in headers {
+ writeln!(result, "{header}").unwrap();
+ }
}
}
write!(result, "body:\n{body}").unwrap();
diff --git a/src/tools/cargo/src/cargo/util/job.rs b/src/tools/cargo/src/cargo/util/job.rs
index f2bcf94a2..1d68fc433 100644
--- a/src/tools/cargo/src/cargo/util/job.rs
+++ b/src/tools/cargo/src/cargo/util/job.rs
@@ -49,7 +49,7 @@ mod imp {
use std::ptr;
use std::ptr::addr_of;
- use log::info;
+ use tracing::info;
use windows_sys::Win32::Foundation::CloseHandle;
use windows_sys::Win32::Foundation::HANDLE;
diff --git a/src/tools/cargo/src/cargo/util/mod.rs b/src/tools/cargo/src/cargo/util/mod.rs
index df8dcb0ac..26e97e2d2 100644
--- a/src/tools/cargo/src/cargo/util/mod.rs
+++ b/src/tools/cargo/src/cargo/util/mod.rs
@@ -36,6 +36,7 @@ pub mod command_prelude;
pub mod config;
mod counter;
pub mod cpu;
+pub mod credential;
mod dependency_queue;
pub mod diagnostic_server;
pub mod edit_distance;
diff --git a/src/tools/cargo/src/cargo/util/network/http.rs b/src/tools/cargo/src/cargo/util/network/http.rs
index f077ce2b6..73880f60e 100644
--- a/src/tools/cargo/src/cargo/util/network/http.rs
+++ b/src/tools/cargo/src/cargo/util/network/http.rs
@@ -8,8 +8,8 @@ use curl::easy::Easy;
use curl::easy::InfoType;
use curl::easy::SslOpt;
use curl::easy::SslVersion;
-use log::log;
-use log::Level;
+use tracing::debug;
+use tracing::trace;
use crate::util::config::SslVersionConfig;
use crate::util::config::SslVersionConfigRange;
@@ -135,18 +135,25 @@ pub fn configure_http_handle(config: &Config, handle: &mut Easy) -> CargoResult<
if let Some(true) = http.debug {
handle.verbose(true)?;
- log::debug!("{:#?}", curl::Version::get());
+ tracing::debug!("{:#?}", curl::Version::get());
handle.debug_function(|kind, data| {
+ enum LogLevel {
+ Debug,
+ Trace,
+ }
+ use LogLevel::*;
let (prefix, level) = match kind {
- InfoType::Text => ("*", Level::Debug),
- InfoType::HeaderIn => ("<", Level::Debug),
- InfoType::HeaderOut => (">", Level::Debug),
- InfoType::DataIn => ("{", Level::Trace),
- InfoType::DataOut => ("}", Level::Trace),
+ InfoType::Text => ("*", Debug),
+ InfoType::HeaderIn => ("<", Debug),
+ InfoType::HeaderOut => (">", Debug),
+ InfoType::DataIn => ("{", Trace),
+ InfoType::DataOut => ("}", Trace),
InfoType::SslDataIn | InfoType::SslDataOut => return,
_ => return,
};
let starts_with_ignore_case = |line: &str, text: &str| -> bool {
+ let line = line.as_bytes();
+ let text = text.as_bytes();
line[..line.len().min(text.len())].eq_ignore_ascii_case(text)
};
match str::from_utf8(data) {
@@ -159,16 +166,18 @@ pub fn configure_http_handle(config: &Config, handle: &mut Easy) -> CargoResult<
} else if starts_with_ignore_case(line, "set-cookie") {
line = "set-cookie: [REDACTED]";
}
- log!(level, "http-debug: {} {}", prefix, line);
+ match level {
+ Debug => debug!("http-debug: {prefix} {line}"),
+ Trace => trace!("http-debug: {prefix} {line}"),
+ }
}
}
Err(_) => {
- log!(
- level,
- "http-debug: {} ({} bytes of data)",
- prefix,
- data.len()
- );
+ let len = data.len();
+ match level {
+ Debug => debug!("http-debug: {prefix} ({len} bytes of data)"),
+ Trace => trace!("http-debug: {prefix} ({len} bytes of data)"),
+ }
}
}
})?;
diff --git a/src/tools/cargo/src/cargo/util/network/mod.rs b/src/tools/cargo/src/cargo/util/network/mod.rs
index b078fa352..5db594945 100644
--- a/src/tools/cargo/src/cargo/util/network/mod.rs
+++ b/src/tools/cargo/src/cargo/util/network/mod.rs
@@ -29,7 +29,7 @@ macro_rules! try_old_curl {
let result = $e;
if cfg!(target_os = "macos") {
if let Err(e) = result {
- ::log::warn!("ignoring libcurl {} error: {}", $msg, e);
+ ::tracing::warn!("ignoring libcurl {} error: {}", $msg, e);
}
} else {
use ::anyhow::Context;
diff --git a/src/tools/cargo/src/cargo/util/network/sleep.rs b/src/tools/cargo/src/cargo/util/network/sleep.rs
index d4105065e..fab53263b 100644
--- a/src/tools/cargo/src/cargo/util/network/sleep.rs
+++ b/src/tools/cargo/src/cargo/util/network/sleep.rs
@@ -68,7 +68,7 @@ impl<T> SleepTracker<T> {
let now = Instant::now();
let mut result = Vec::new();
while let Some(next) = self.heap.peek() {
- log::debug!("ERIC: now={now:?} next={:?}", next.wakeup);
+ tracing::debug!("ERIC: now={now:?} next={:?}", next.wakeup);
if next.wakeup < now {
result.push(self.heap.pop().unwrap().data);
} else {
diff --git a/src/tools/cargo/src/cargo/util/restricted_names.rs b/src/tools/cargo/src/cargo/util/restricted_names.rs
index be1811a88..2c3eaa9e1 100644
--- a/src/tools/cargo/src/cargo/util/restricted_names.rs
+++ b/src/tools/cargo/src/cargo/util/restricted_names.rs
@@ -87,23 +87,22 @@ pub fn validate_package_name(name: &str, what: &str, help: &str) -> CargoResult<
pub fn sanitize_package_name(name: &str, placeholder: char) -> String {
let mut slug = String::new();
let mut chars = name.chars();
- if let Some(ch) = chars.next() {
- if ch.is_digit(10) {
- slug.push(placeholder);
- slug.push(ch);
- } else if unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' {
+ while let Some(ch) = chars.next() {
+ if (unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_') && !ch.is_digit(10) {
slug.push(ch);
- } else {
- slug.push(placeholder);
+ break;
}
}
- for ch in chars {
+ while let Some(ch) = chars.next() {
if unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-' {
slug.push(ch);
} else {
slug.push(placeholder);
}
}
+ if slug.is_empty() {
+ slug.push_str("package");
+ }
slug
}
diff --git a/src/tools/cargo/src/cargo/util/rustc.rs b/src/tools/cargo/src/cargo/util/rustc.rs
index 3f1da64d4..238145af6 100644
--- a/src/tools/cargo/src/cargo/util/rustc.rs
+++ b/src/tools/cargo/src/cargo/util/rustc.rs
@@ -6,8 +6,8 @@ use std::sync::Mutex;
use anyhow::Context as _;
use cargo_util::{paths, ProcessBuilder, ProcessError};
-use log::{debug, info, warn};
use serde::{Deserialize, Serialize};
+use tracing::{debug, info, warn};
use crate::util::interning::InternedString;
use crate::util::{profile, CargoResult, Config, StableHasher};
diff --git a/src/tools/cargo/src/cargo/util/toml/embedded.rs b/src/tools/cargo/src/cargo/util/toml/embedded.rs
index 8e41010b4..395430c1b 100644
--- a/src/tools/cargo/src/cargo/util/toml/embedded.rs
+++ b/src/tools/cargo/src/cargo/util/toml/embedded.rs
@@ -18,7 +18,7 @@ pub fn expand_manifest(
let comment = match extract_comment(content) {
Ok(comment) => Some(comment),
Err(err) => {
- log::trace!("failed to extract doc comment: {err}");
+ tracing::trace!("failed to extract doc comment: {err}");
None
}
}
@@ -26,7 +26,7 @@ pub fn expand_manifest(
let manifest = match extract_manifest(&comment)? {
Some(manifest) => Some(manifest),
None => {
- log::trace!("failed to extract manifest");
+ tracing::trace!("failed to extract manifest");
None
}
}
@@ -84,7 +84,7 @@ fn expand_manifest_(
.or_insert_with(|| toml::Value::String(DEFAULT_VERSION.to_owned()));
package.entry("edition".to_owned()).or_insert_with(|| {
let _ = config.shell().warn(format_args!(
- "`package.edition` is unspecifiead, defaulting to `{}`",
+ "`package.edition` is unspecified, defaulting to `{}`",
DEFAULT_EDITION
));
toml::Value::String(DEFAULT_EDITION.to_string())
@@ -207,7 +207,11 @@ impl DocFragment {
let syn::Meta::NameValue(nv) = &attr.meta else {
anyhow::bail!("unsupported attr meta for {:?}", attr.meta.path())
};
- let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit), .. }) = &nv.value else {
+ let syn::Expr::Lit(syn::ExprLit {
+ lit: syn::Lit::Str(lit),
+ ..
+ }) = &nv.value
+ else {
anyhow::bail!("only string literals are supported")
};
Ok(Self {
@@ -373,16 +377,21 @@ fn unindent_doc_fragments(docs: &mut [DocFragment]) {
let Some(min_indent) = docs
.iter()
.map(|fragment| {
- fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| {
- if line.chars().all(|c| c.is_whitespace()) {
- min_indent
- } else {
- // Compare against either space or tab, ignoring whether they are
- // mixed or not.
- let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count();
- min_indent.min(whitespace)
- }
- })
+ fragment
+ .doc
+ .as_str()
+ .lines()
+ .fold(usize::MAX, |min_indent, line| {
+ if line.chars().all(|c| c.is_whitespace()) {
+ min_indent
+ } else {
+ // Compare against either space or tab, ignoring whether they are
+ // mixed or not.
+ let whitespace =
+ line.chars().take_while(|c| *c == ' ' || *c == '\t').count();
+ min_indent.min(whitespace)
+ }
+ })
})
.min()
else {
diff --git a/src/tools/cargo/src/cargo/util/toml/mod.rs b/src/tools/cargo/src/cargo/util/toml/mod.rs
index 2202f6b3b..963c4afaa 100644
--- a/src/tools/cargo/src/cargo/util/toml/mod.rs
+++ b/src/tools/cargo/src/cargo/util/toml/mod.rs
@@ -11,12 +11,12 @@ use cargo_platform::Platform;
use cargo_util::paths;
use itertools::Itertools;
use lazycell::LazyCell;
-use log::{debug, trace};
use semver::{self, VersionReq};
use serde::de::IntoDeserializer as _;
use serde::de::{self, Unexpected};
use serde::ser;
use serde::{Deserialize, Serialize};
+use tracing::{debug, trace};
use url::Url;
use crate::core::compiler::{CompileKind, CompileTarget};
@@ -2835,7 +2835,9 @@ fn parse_unstable_lints<T: Deserialize<'static>>(
config: &Config,
warnings: &mut Vec<String>,
) -> CargoResult<Option<T>> {
- let Some(lints) = lints else { return Ok(None); };
+ let Some(lints) = lints else {
+ return Ok(None);
+ };
if !config.cli_unstable().lints {
warn_for_lint_feature(config, warnings);
@@ -2878,7 +2880,9 @@ switch to nightly channel you can pass
}
fn verify_lints(lints: Option<TomlLints>) -> CargoResult<Option<TomlLints>> {
- let Some(lints) = lints else { return Ok(None); };
+ let Some(lints) = lints else {
+ return Ok(None);
+ };
for (tool, lints) in &lints {
let supported = ["rust", "clippy", "rustdoc"];
diff --git a/src/tools/cargo/src/cargo/util/toml_mut/dependency.rs b/src/tools/cargo/src/cargo/util/toml_mut/dependency.rs
index 1b24833c1..2f39b7ab4 100644
--- a/src/tools/cargo/src/cargo/util/toml_mut/dependency.rs
+++ b/src/tools/cargo/src/cargo/util/toml_mut/dependency.rs
@@ -881,7 +881,11 @@ impl GitSource {
impl std::fmt::Display for GitSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let git_ref = self.git_ref();
- if let Some(pretty_ref) = git_ref.pretty_ref() {
+
+ // TODO(-Znext-lockfile-bump): set it to true when stabilizing
+ // lockfile v4, because we want Source ID serialization to be
+ // consistent with lockfile.
+ if let Some(pretty_ref) = git_ref.pretty_ref(false) {
write!(f, "{}?{}", self.git, pretty_ref)
} else {
write!(f, "{}", self.git)
diff --git a/src/tools/cargo/src/doc/contrib/src/implementation/debugging.md b/src/tools/cargo/src/doc/contrib/src/implementation/debugging.md
index e148d72c3..03940e2ff 100644
--- a/src/tools/cargo/src/doc/contrib/src/implementation/debugging.md
+++ b/src/tools/cargo/src/doc/contrib/src/implementation/debugging.md
@@ -2,11 +2,11 @@
## Logging
-Cargo uses the [`env_logger`] crate to display debug log messages. The
-`CARGO_LOG` environment variable can be set to enable debug logging, with a
-value such as `trace`, `debug`, or `warn`. It also supports filtering for
-specific modules. Feel free to use the standard [`log`] macros to help with
-diagnosing problems.
+Cargo uses the [`tracing`] crate to display debug log messages.
+The `CARGO_LOG` environment variable can be set to enable debug logging, with a value such as `trace`, `debug`, or `warn`.
+It also supports filtering for specific modules with comma-separated [directives].
+Feel free to use [shorthand macros] to help with diagnosing problems.
+We're looking forward to making Cargo logging mechanism more structural!
```sh
# Outputs all logs with levels debug and higher
@@ -22,5 +22,6 @@ CARGO_HTTP_DEBUG=true CARGO_LOG=cargo::ops::registry=debug cargo fetch
CARGO_LOG=cargo::core::compiler::fingerprint=trace cargo build
```
-[`env_logger`]: https://docs.rs/env_logger
-[`log`]: https://docs.rs/log
+[`tracing`]: https://docs.rs/tracing
+[directive]: https://docs.rs/tracing_subscriber/filter/struct.EnvFilter.html#directives
+[shorthand macros]: https://docs.rs/tracing/index.html#shorthand-macros
diff --git a/src/tools/cargo/src/doc/contrib/src/process/release.md b/src/tools/cargo/src/doc/contrib/src/process/release.md
index f0de267c8..169d63ed8 100644
--- a/src/tools/cargo/src/doc/contrib/src/process/release.md
+++ b/src/tools/cargo/src/doc/contrib/src/process/release.md
@@ -127,11 +127,38 @@ module].
## crates.io publishing
-Cargo's library is published to [crates.io] as part of the stable release
-process. This is handled by the [Release team] as part of their process. There
-is a [`publish.py` script] that in theory should help with this process. The
-test and build tool crates aren't published.
-
+Cargo's library and its related dependencies (like `cargo-util`) are published
+to [crates.io] as part of the 6-week stable release process by the [Release
+team]. There is a [`publish.py` script] that is used by the Release team's
+automation scripts (see <https://github.com/rust-lang/simpleinfra/>) to handle
+determining which packages to publish. The test and build tool crates aren't
+published. This runs on the specific git commit associated with the cargo
+submodule in the `stable` branch in `rust-lang/rust` at the time of release.
+
+On very rare cases, the Cargo team may decide to manually publish a new
+release to [crates.io]. For example, this may be necessary if there is a
+problem with the current version that only affects API users, and does not
+affect the `cargo` binary shipped in the stable release. In this situation,
+PRs should be merged to the associated stable release branch in the cargo repo
+(like `rust-1.70.0`) that fix the issue and bump the patch version of the
+affected package. Then someone with permissions (currently a subset of the
+Cargo team, or the Release team) should publish it manually using `cargo
+publish`.
+
+Some packages are not published automatically because they are not part of the
+Rust release train. These currently include all of the [`credential`] packages
+and the [`home`] package. These are published manually on an as-needed or
+as-requested basis by whoever has permissions (currently [@ehuss] or the
+Release/Infra team).
+
+In the future, these manual publishing options should be integrated with
+GitHub Actions so that any team member can trigger them. Likely that should
+involve getting Infra to create scoped tokens that can be added as GitHub
+Secrets, and setting up GitHub Actions workflows with the appropriate
+permissions which can be manually triggered to launch a release.
+
+[`home`]: https://github.com/rust-lang/cargo/tree/master/crates/home
+[`credential`]: https://github.com/rust-lang/cargo/tree/master/credential
[`publish.py` script]: https://github.com/rust-lang/cargo/blob/master/publish.py
## Beta backports
diff --git a/src/tools/cargo/src/doc/man/cargo-metadata.md b/src/tools/cargo/src/doc/man/cargo-metadata.md
index 0027a20b1..8efb29a97 100644
--- a/src/tools/cargo/src/doc/man/cargo-metadata.md
+++ b/src/tools/cargo/src/doc/man/cargo-metadata.md
@@ -13,15 +13,34 @@ cargo-metadata --- Machine-readable metadata about the current package
Output JSON to stdout containing information about the workspace members and
resolved dependencies of the current package.
-It is recommended to include the `--format-version` flag to future-proof
-your code to ensure the output is in the format you are expecting.
+The format of the output is subject to change in futures versions of Cargo. It
+is recommended to include the `--format-version` flag to future-proof your code
+to ensure the output is in the format you are expecting. For more on the
+expectations, see ["Compatibility"](#compatibility).
See the [cargo_metadata crate](https://crates.io/crates/cargo_metadata)
for a Rust API for reading the metadata.
## OUTPUT FORMAT
-The output has the following format:
+### Compatibility
+
+Within the same output format version, the compatibility is maintained, except
+some scenarios. The following is a non-exhaustive list of changes that are not
+considersed as incompatibile:
+
+* **Adding new fields** — New fields will be added when needed. Reserving this
+ helps Cargo evolve without bumping the format version too often.
+* **Adding new values for enum-like fields** — Same as adding new fields. It
+ keeps metadata evolving without stagnation.
+* **Changing opaque representations** — The inner representations of some
+ fields are implementation details. For example, fields related to "Package ID"
+ or "Source ID" are treated as opaque identifiers to differentiate packages or
+ sources. Consumers shouldn't rely on those representations unless specified.
+
+### JSON format
+
+The JSON output has the following format:
```javascript
{
@@ -34,7 +53,9 @@ The output has the following format:
"name": "my-package",
/* The version of the package. */
"version": "0.1.0",
- /* The Package ID, a unique identifier for referring to the package. */
+ /* The Package ID, an opaque and unique identifier for referring to the
+ package. See "Compatibility" above for the stability guarantee.
+ */
"id": "my-package 0.1.0 (path+file:///path/to/my-package)",
/* The license value from the manifest, or null. */
"license": "MIT/Apache-2.0",
@@ -42,14 +63,25 @@ The output has the following format:
"license_file": "LICENSE",
/* The description value from the manifest, or null. */
"description": "Package description.",
- /* The source ID of the package. This represents where
- a package is retrieved from.
+ /* The source ID of the package, an "opaque" identifier representing
+ where a package is retrieved from. See "Compatibility" above for
+ the stability guarantee.
+
This is null for path dependencies and workspace members.
+
For other dependencies, it is a string with the format:
- "registry+URL" for registry-based dependencies.
Example: "registry+https://github.com/rust-lang/crates.io-index"
- "git+URL" for git-based dependencies.
Example: "git+https://github.com/rust-lang/cargo?rev=5e85ba14aaa20f8133863373404cb0af69eeef2c#5e85ba14aaa20f8133863373404cb0af69eeef2c"
+ - "sparse+URL" for dependencies from a sparse registry
+ Example: "sparse+https://my-sparse-registry.org"
+
+ The value after the `+` is not explicitly defined, and may change
+ between versions of Cargo and may not directly correlate to other
+ things, such as registry definitions in a config file. New source
+ kinds may be added in the future which will have different `+`
+ prefixed identifiers.
*/
"source": null,
/* Array of dependencies declared in the package's manifest. */
diff --git a/src/tools/cargo/src/doc/man/cargo-test.md b/src/tools/cargo/src/doc/man/cargo-test.md
index 75bf72b30..cba98b20d 100644
--- a/src/tools/cargo/src/doc/man/cargo-test.md
+++ b/src/tools/cargo/src/doc/man/cargo-test.md
@@ -186,7 +186,6 @@ includes an option to control the number of threads used:
{{#options}}
{{> options-jobs }}
-{{> options-keep-going }}
{{> options-future-incompat }}
{{/options}}
diff --git a/src/tools/cargo/src/doc/man/cargo-yank.md b/src/tools/cargo/src/doc/man/cargo-yank.md
index 946e98c85..2d7c117bf 100644
--- a/src/tools/cargo/src/doc/man/cargo-yank.md
+++ b/src/tools/cargo/src/doc/man/cargo-yank.md
@@ -46,7 +46,7 @@ requirements, following a given release being yanked:
|-------------------------------------|-----------------------------------------|------------------|------------------|
| `1.5.0` | Use either `1.5.1` or `1.5.2` | **Return Error** | Use `2.0.0` |
| `1.5.1` | Use either `1.5.0` or `1.5.2` | Use `1.5.0` | Use `2.0.0` |
-| `2.0.0` | Use either `1.5.0`, `1.5.1` or `0.22.2` | Use `1.5.0` | **Return Error** |
+| `2.0.0` | Use either `1.5.0`, `1.5.1` or `1.5.2` | Use `1.5.0` | **Return Error** |
### When to yank
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-metadata.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-metadata.txt
index d9d48fd9a..3d37f6bb8 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-metadata.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-metadata.txt
@@ -10,14 +10,34 @@ DESCRIPTION
Output JSON to stdout containing information about the workspace members
and resolved dependencies of the current package.
- It is recommended to include the --format-version flag to future-proof
- your code to ensure the output is in the format you are expecting.
+ The format of the output is subject to change in futures versions of
+ Cargo. It is recommended to include the --format-version flag to
+ future-proof your code to ensure the output is in the format you are
+ expecting. For more on the expectations, see “Compatibility”.
See the cargo_metadata crate <https://crates.io/crates/cargo_metadata>
for a Rust API for reading the metadata.
OUTPUT FORMAT
- The output has the following format:
+ Compatibility
+ Within the same output format version, the compatibility is maintained,
+ except some scenarios. The following is a non-exhaustive list of changes
+ that are not considersed as incompatibile:
+
+ o Adding new fields — New fields will be added when needed. Reserving
+ this helps Cargo evolve without bumping the format version too often.
+
+ o Adding new values for enum-like fields — Same as adding new fields.
+ It keeps metadata evolving without stagnation.
+
+ o Changing opaque representations — The inner representations of some
+ fields are implementation details. For example, fields related to
+ “Package ID” or “Source ID” are treated as opaque identifiers
+ to differentiate packages or sources. Consumers shouldn’t rely on
+ those representations unless specified.
+
+ JSON format
+ The JSON output has the following format:
{
/* Array of all packages in the workspace.
@@ -29,7 +49,9 @@ OUTPUT FORMAT
"name": "my-package",
/* The version of the package. */
"version": "0.1.0",
- /* The Package ID, a unique identifier for referring to the package. */
+ /* The Package ID, an opaque and unique identifier for referring to the
+ package. See "Compatibility" above for the stability guarantee.
+ */
"id": "my-package 0.1.0 (path+file:///path/to/my-package)",
/* The license value from the manifest, or null. */
"license": "MIT/Apache-2.0",
@@ -37,14 +59,25 @@ OUTPUT FORMAT
"license_file": "LICENSE",
/* The description value from the manifest, or null. */
"description": "Package description.",
- /* The source ID of the package. This represents where
- a package is retrieved from.
+ /* The source ID of the package, an "opaque" identifier representing
+ where a package is retrieved from. See "Compatibility" above for
+ the stability guarantee.
+
This is null for path dependencies and workspace members.
+
For other dependencies, it is a string with the format:
- "registry+URL" for registry-based dependencies.
Example: "registry+https://github.com/rust-lang/crates.io-index"
- "git+URL" for git-based dependencies.
Example: "git+https://github.com/rust-lang/cargo?rev=5e85ba14aaa20f8133863373404cb0af69eeef2c#5e85ba14aaa20f8133863373404cb0af69eeef2c"
+ - "sparse+URL" for dependencies from a sparse registry
+ Example: "sparse+https://my-sparse-registry.org"
+
+ The value after the `+` is not explicitly defined, and may change
+ between versions of Cargo and may not directly correlate to other
+ things, such as registry definitions in a config file. New source
+ kinds may be added in the future which will have different `+`
+ prefixed identifiers.
*/
"source": null,
/* Array of dependencies declared in the package's manifest. */
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-test.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-test.txt
index 7955b0e3d..dc32bdbf7 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-test.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-test.txt
@@ -442,11 +442,6 @@ OPTIONS
If a string default is provided, it sets the value back to defaults.
Should not be 0.
- --keep-going
- Build as many crates in the dependency graph as possible, rather
- than aborting the build on the first one that fails to build.
- Unstable, requires -Zunstable-options.
-
--future-incompat-report
Displays a future-incompat report for any future-incompatible
warnings produced during execution of this command
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-yank.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-yank.txt
index 784e71a1a..ad966ad26 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-yank.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-yank.txt
@@ -50,7 +50,7 @@ DESCRIPTION
| | 1.5.2 | 1.5.0 | 2.0.0 |
+------------------------+----------------------+----------+----------+
| 2.0.0 | Use either 1.5.0, | Use | Return |
- | | 1.5.1 or 0.22.2 | 1.5.0 | Error |
+ | | 1.5.1 or 1.5.2 | 1.5.0 | Error |
+------------------------+----------------------+----------+----------+
When to yank
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-metadata.md b/src/tools/cargo/src/doc/src/commands/cargo-metadata.md
index e9aeac7df..5be6992d7 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-metadata.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-metadata.md
@@ -13,15 +13,34 @@ cargo-metadata --- Machine-readable metadata about the current package
Output JSON to stdout containing information about the workspace members and
resolved dependencies of the current package.
-It is recommended to include the `--format-version` flag to future-proof
-your code to ensure the output is in the format you are expecting.
+The format of the output is subject to change in futures versions of Cargo. It
+is recommended to include the `--format-version` flag to future-proof your code
+to ensure the output is in the format you are expecting. For more on the
+expectations, see ["Compatibility"](#compatibility).
See the [cargo_metadata crate](https://crates.io/crates/cargo_metadata)
for a Rust API for reading the metadata.
## OUTPUT FORMAT
-The output has the following format:
+### Compatibility
+
+Within the same output format version, the compatibility is maintained, except
+some scenarios. The following is a non-exhaustive list of changes that are not
+considersed as incompatibile:
+
+* **Adding new fields** — New fields will be added when needed. Reserving this
+ helps Cargo evolve without bumping the format version too often.
+* **Adding new values for enum-like fields** — Same as adding new fields. It
+ keeps metadata evolving without stagnation.
+* **Changing opaque representations** — The inner representations of some
+ fields are implementation details. For example, fields related to "Package ID"
+ or "Source ID" are treated as opaque identifiers to differentiate packages or
+ sources. Consumers shouldn't rely on those representations unless specified.
+
+### JSON format
+
+The JSON output has the following format:
```javascript
{
@@ -34,7 +53,9 @@ The output has the following format:
"name": "my-package",
/* The version of the package. */
"version": "0.1.0",
- /* The Package ID, a unique identifier for referring to the package. */
+ /* The Package ID, an opaque and unique identifier for referring to the
+ package. See "Compatibility" above for the stability guarantee.
+ */
"id": "my-package 0.1.0 (path+file:///path/to/my-package)",
/* The license value from the manifest, or null. */
"license": "MIT/Apache-2.0",
@@ -42,14 +63,25 @@ The output has the following format:
"license_file": "LICENSE",
/* The description value from the manifest, or null. */
"description": "Package description.",
- /* The source ID of the package. This represents where
- a package is retrieved from.
+ /* The source ID of the package, an "opaque" identifier representing
+ where a package is retrieved from. See "Compatibility" above for
+ the stability guarantee.
+
This is null for path dependencies and workspace members.
+
For other dependencies, it is a string with the format:
- "registry+URL" for registry-based dependencies.
Example: "registry+https://github.com/rust-lang/crates.io-index"
- "git+URL" for git-based dependencies.
Example: "git+https://github.com/rust-lang/cargo?rev=5e85ba14aaa20f8133863373404cb0af69eeef2c#5e85ba14aaa20f8133863373404cb0af69eeef2c"
+ - "sparse+URL" for dependencies from a sparse registry
+ Example: "sparse+https://my-sparse-registry.org"
+
+ The value after the `+` is not explicitly defined, and may change
+ between versions of Cargo and may not directly correlate to other
+ things, such as registry definitions in a config file. New source
+ kinds may be added in the future which will have different `+`
+ prefixed identifiers.
*/
"source": null,
/* Array of dependencies declared in the package's manifest. */
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-test.md b/src/tools/cargo/src/doc/src/commands/cargo-test.md
index e38e9929e..bcf46d601 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-test.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-test.md
@@ -515,12 +515,6 @@ a string <code>default</code> is provided, it sets the value back to defaults.
Should not be 0.</dd>
-<dt class="option-term" id="option-cargo-test---keep-going"><a class="option-anchor" href="#option-cargo-test---keep-going"></a><code>--keep-going</code></dt>
-<dd class="option-desc">Build as many crates in the dependency graph as possible, rather than aborting
-the build on the first one that fails to build. Unstable, requires
-<code>-Zunstable-options</code>.</dd>
-
-
<dt class="option-term" id="option-cargo-test---future-incompat-report"><a class="option-anchor" href="#option-cargo-test---future-incompat-report"></a><code>--future-incompat-report</code></dt>
<dd class="option-desc">Displays a future-incompat report for any future-incompatible warnings
produced during execution of this command</p>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-yank.md b/src/tools/cargo/src/doc/src/commands/cargo-yank.md
index 7fac5bf47..c0be657bc 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-yank.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-yank.md
@@ -46,7 +46,7 @@ requirements, following a given release being yanked:
|-------------------------------------|-----------------------------------------|------------------|------------------|
| `1.5.0` | Use either `1.5.1` or `1.5.2` | **Return Error** | Use `2.0.0` |
| `1.5.1` | Use either `1.5.0` or `1.5.2` | Use `1.5.0` | Use `2.0.0` |
-| `2.0.0` | Use either `1.5.0`, `1.5.1` or `0.22.2` | Use `1.5.0` | **Return Error** |
+| `2.0.0` | Use either `1.5.0`, `1.5.1` or `1.5.2` | Use `1.5.0` | **Return Error** |
### When to yank
diff --git a/src/tools/cargo/src/doc/src/guide/cargo-toml-vs-cargo-lock.md b/src/tools/cargo/src/doc/src/guide/cargo-toml-vs-cargo-lock.md
index 9b0426684..84d697f66 100644
--- a/src/tools/cargo/src/doc/src/guide/cargo-toml-vs-cargo-lock.md
+++ b/src/tools/cargo/src/doc/src/guide/cargo-toml-vs-cargo-lock.md
@@ -35,7 +35,7 @@ regex = { git = "https://github.com/rust-lang/regex.git" }
This package has a single dependency, on the `regex` library. We’ve stated in
this case that we’re relying on a particular Git repository that lives on
GitHub. Since we haven’t specified any other information, Cargo assumes that
-we intend to use the latest commit on the `master` branch to build our package.
+we intend to use the latest commit on the default branch to build our package.
Sound good? Well, there’s one problem: If you build this package today, and
then you send a copy to me, and I build this package tomorrow, something bad
diff --git a/src/tools/cargo/src/doc/src/reference/build-scripts.md b/src/tools/cargo/src/doc/src/reference/build-scripts.md
index 68e8d404f..e7560812b 100644
--- a/src/tools/cargo/src/doc/src/reference/build-scripts.md
+++ b/src/tools/cargo/src/doc/src/reference/build-scripts.md
@@ -34,7 +34,7 @@ The sections below describe how build scripts work, and the [examples
chapter](build-script-examples.md) shows a variety of examples on how to write
scripts.
-> Note: The [`package.build` manifest key](manifest.md#package-build) can be
+> Note: The [`package.build` manifest key](manifest.md#the-build-field) can be
> used to change the name of the build script, or disable it entirely.
### Life Cycle of a Build Script
@@ -132,8 +132,7 @@ one detailed below.
scripts.
-<a id="rustc-link-arg"></a>
-#### `cargo:rustc-link-arg=FLAG`
+#### `cargo:rustc-link-arg=FLAG` {#rustc-link-arg}
The `rustc-link-arg` instruction tells Cargo to pass the [`-C link-arg=FLAG`
option][link-arg] to the compiler, but only when building supported targets
@@ -143,8 +142,7 @@ linker script.
[link-arg]: ../../rustc/codegen-options/index.md#link-arg
-<a id="rustc-link-arg-bin"></a>
-#### `cargo:rustc-link-arg-bin=BIN=FLAG`
+#### `cargo:rustc-link-arg-bin=BIN=FLAG` {#rustc-link-arg-bin}
The `rustc-link-arg-bin` instruction tells Cargo to pass the [`-C
link-arg=FLAG` option][link-arg] to the compiler, but only when building
@@ -152,8 +150,7 @@ the binary target with name `BIN`. Its usage is highly platform specific. It is
to set a linker script or other linker options.
-<a id="rustc-link-arg-bins"></a>
-#### `cargo:rustc-link-arg-bins=FLAG`
+#### `cargo:rustc-link-arg-bins=FLAG` {#rustc-link-arg-bins}
The `rustc-link-arg-bins` instruction tells Cargo to pass the [`-C
link-arg=FLAG` option][link-arg] to the compiler, but only when building a
@@ -161,8 +158,7 @@ binary target. Its usage is highly platform specific. It is useful
to set a linker script or other linker options.
-<a id="rustc-link-lib"></a>
-#### `cargo:rustc-link-lib=LIB`
+#### `cargo:rustc-link-lib=LIB` {#rustc-link-lib}
The `rustc-link-lib` instruction tells Cargo to link the given library using
the compiler's [`-l` flag][option-link]. This is typically used to link a
@@ -187,30 +183,26 @@ The optional `KIND` may be one of `dylib`, `static`, or `framework`. See the
[FFI]: ../../nomicon/ffi.md
-<a id="rustc-link-arg-tests"></a>
-#### `cargo:rustc-link-arg-tests=FLAG`
+#### `cargo:rustc-link-arg-tests=FLAG` {#rustc-link-arg-tests}
The `rustc-link-arg-tests` instruction tells Cargo to pass the [`-C
link-arg=FLAG` option][link-arg] to the compiler, but only when building a
tests target.
-<a id="rustc-link-arg-examples"></a>
-#### `cargo:rustc-link-arg-examples=FLAG`
+#### `cargo:rustc-link-arg-examples=FLAG` {#rustc-link-arg-examples}
The `rustc-link-arg-examples` instruction tells Cargo to pass the [`-C
link-arg=FLAG` option][link-arg] to the compiler, but only when building an examples
target.
-<a id="rustc-link-arg-benches"></a>
-#### `cargo:rustc-link-arg-benches=FLAG`
+#### `cargo:rustc-link-arg-benches=FLAG` {#rustc-link-arg-benches}
The `rustc-link-arg-benches` instruction tells Cargo to pass the [`-C
link-arg=FLAG` option][link-arg] to the compiler, but only when building a benchmark
target.
-<a id="rustc-link-search"></a>
-#### `cargo:rustc-link-search=[KIND=]PATH`
+#### `cargo:rustc-link-search=[KIND=]PATH` {#rustc-link-search}
The `rustc-link-search` instruction tells Cargo to pass the [`-L`
flag][option-search] to the compiler to add a directory to the library search
@@ -228,16 +220,14 @@ is fine).
[option-search]: ../../rustc/command-line-arguments.md#option-l-search-path
-<a id="rustc-flags"></a>
-#### `cargo:rustc-flags=FLAGS`
+#### `cargo:rustc-flags=FLAGS` {#rustc-flags}
The `rustc-flags` instruction tells Cargo to pass the given space-separated
flags to the compiler. This only allows the `-l` and `-L` flags, and is
equivalent to using [`rustc-link-lib`](#rustc-link-lib) and
[`rustc-link-search`](#rustc-link-search).
-<a id="rustc-cfg"></a>
-#### `cargo:rustc-cfg=KEY[="VALUE"]`
+#### `cargo:rustc-cfg=KEY[="VALUE"]` {#rustc-cfg}
The `rustc-cfg` instruction tells Cargo to pass the given value to the
[`--cfg` flag][option-cfg] to the compiler. This may be used for compile-time
@@ -258,8 +248,7 @@ identifier, the value should be a string.
[conditional compilation]: ../../reference/conditional-compilation.md
[option-cfg]: ../../rustc/command-line-arguments.md#option-cfg
-<a id="rustc-env"></a>
-#### `cargo:rustc-env=VAR=VALUE`
+#### `cargo:rustc-env=VAR=VALUE` {#rustc-env}
The `rustc-env` instruction tells Cargo to set the given environment variable
when compiling the package. The value can be then retrieved by the [`env!`
@@ -279,8 +268,7 @@ Cargo][env-cargo].
[env-macro]: ../../std/macro.env.html
[env-cargo]: environment-variables.md#environment-variables-cargo-sets-for-crates
-<a id="rustc-cdylib-link-arg"></a>
-#### `cargo:rustc-cdylib-link-arg=FLAG`
+#### `cargo:rustc-cdylib-link-arg=FLAG` {#rustc-cdylib-link-arg}
The `rustc-cdylib-link-arg` instruction tells Cargo to pass the [`-C
link-arg=FLAG` option][link-arg] to the compiler, but only when building a
@@ -288,8 +276,7 @@ link-arg=FLAG` option][link-arg] to the compiler, but only when building a
to set the shared library version or the runtime-path.
-<a id="cargo-warning"></a>
-#### `cargo:warning=MESSAGE`
+#### `cargo:warning=MESSAGE` {#cargo-warning}
The `warning` instruction tells Cargo to display a warning after the build
script has finished running. Warnings are only shown for `path` dependencies
@@ -334,8 +321,7 @@ FAQ](../faq.md#why-is-cargo-rebuilding-my-code).
[`exclude` and `include` fields]: manifest.md#the-exclude-and-include-fields
-<a id="rerun-if-changed"></a>
-#### `cargo:rerun-if-changed=PATH`
+#### `cargo:rerun-if-changed=PATH` {#rerun-if-changed}
The `rerun-if-changed` instruction tells Cargo to re-run the build script if
the file at the given path has changed. Currently, Cargo only uses the
@@ -354,15 +340,16 @@ automatically handles whether or not the script itself needs to be recompiled,
and of course the script will be re-run after it has been recompiled.
Otherwise, specifying `build.rs` is redundant and unnecessary.
-<a id="rerun-if-env-changed"></a>
-#### `cargo:rerun-if-env-changed=NAME`
+#### `cargo:rerun-if-env-changed=NAME` {#rerun-if-env-changed}
The `rerun-if-env-changed` instruction tells Cargo to re-run the build script
if the value of an environment variable of the given name has changed.
Note that the environment variables here are intended for global environment
-variables like `CC` and such, it is not necessary to use this for environment
-variables like `TARGET` that Cargo sets.
+variables like `CC` and such, it is not possible to use this for environment
+variables like `TARGET` that [Cargo sets for build scripts][build-env]. The
+environment variables in use are those received by `cargo` invocations, not
+those received by the executable of the build script.
### The `links` Manifest Key
diff --git a/src/tools/cargo/src/doc/src/reference/config.md b/src/tools/cargo/src/doc/src/reference/config.md
index 30053bb18..d1f2b04d3 100644
--- a/src/tools/cargo/src/doc/src/reference/config.md
+++ b/src/tools/cargo/src/doc/src/reference/config.md
@@ -132,6 +132,7 @@ panic = 'unwind' # The panic strategy.
incremental = true # Incremental compilation.
codegen-units = 16 # Number of code generation units.
rpath = false # Sets the rpath linking option.
+strip = "none" # Removes symbols or debuginfo.
[profile.<name>.build-override] # Overrides build-script settings.
# Same keys for a normal profile.
[profile.<name>.package.<name>] # Override profile for a package.
@@ -317,6 +318,12 @@ be specified with environment variables of the form
`CARGO_REGISTRIES_<name>_TOKEN` where `<name>` is the name of the registry in
all capital letters.
+> **Note:** Cargo also reads and writes credential files without the `.toml`
+> extension, such as `.cargo/credentials`. Support for the `.toml` extension
+> was added in version 1.39. In version 1.68, Cargo writes to the file with the
+> extension by default. However, for backward compatibility reason, when both
+> files exist, Cargo will read and write the file without the extension.
+
### Configuration keys
This section documents all configuration keys. The description for keys with
@@ -908,6 +915,13 @@ See [panic](profiles.md#panic).
See [rpath](profiles.md#rpath).
+##### `profile.<name>.strip`
+* Type: string
+* default: See profile docs.
+* Environment: `CARGO_PROFILE_<name>_STRIP`
+
+See [strip](profiles.md#strip).
+
#### `[registries]`
diff --git a/src/tools/cargo/src/doc/src/reference/environment-variables.md b/src/tools/cargo/src/doc/src/reference/environment-variables.md
index 353742877..25881d138 100644
--- a/src/tools/cargo/src/doc/src/reference/environment-variables.md
+++ b/src/tools/cargo/src/doc/src/reference/environment-variables.md
@@ -9,7 +9,7 @@ with them:
You can override these environment variables to change Cargo's behavior on your
system:
-* `CARGO_LOG` --- Cargo uses the [`env_logger`] crate to display debug log messages.
+* `CARGO_LOG` --- Cargo uses the [`tracing`] crate to display debug log messages.
The `CARGO_LOG` environment variable can be set to enable debug logging,
with a value such as `trace`, `debug`, or `warn`.
Usually it is only used during debugging. For more details refer to the
@@ -123,6 +123,7 @@ In summary, the supported environment variables are:
* `CARGO_PROFILE_<name>_PANIC` --- The panic strategy to use, see [`profile.<name>.panic`].
* `CARGO_PROFILE_<name>_RPATH` --- The rpath linking option, see [`profile.<name>.rpath`].
* `CARGO_PROFILE_<name>_SPLIT_DEBUGINFO` --- Controls debug file output behavior, see [`profile.<name>.split-debuginfo`].
+* `CARGO_PROFILE_<name>_STRIP` --- Controls stripping of symbols and/or debuginfos, see [`profile.<name>.strip`].
* `CARGO_REGISTRIES_<name>_INDEX` --- URL of a registry index, see [`registries.<name>.index`].
* `CARGO_REGISTRIES_<name>_TOKEN` --- Authentication token of a registry, see [`registries.<name>.token`].
* `CARGO_REGISTRY_DEFAULT` --- Default registry for the `--registry` flag, see [`registry.default`].
@@ -185,6 +186,7 @@ In summary, the supported environment variables are:
[`profile.<name>.panic`]: config.md#profilenamepanic
[`profile.<name>.rpath`]: config.md#profilenamerpath
[`profile.<name>.split-debuginfo`]: config.md#profilenamesplit-debuginfo
+[`profile.<name>.strip`]: config.md#profilenamestrip
[`registries.<name>.index`]: config.md#registriesnameindex
[`registries.<name>.token`]: config.md#registriesnametoken
[`registry.default`]: config.md#registrydefault
@@ -387,7 +389,7 @@ let out_dir = env::var("OUT_DIR").unwrap();
the environment; scripts should use `CARGO_ENCODED_RUSTFLAGS` instead.
* `CARGO_PKG_<var>` --- The package information variables, with the same names and values as are [provided during crate building][variables set for crates].
-[`env_logger`]: https://docs.rs/env_logger
+[`tracing`]: https://docs.rs/tracing
[debug logging]: https://doc.crates.io/contrib/architecture/console.html#debug-logging
[unix-like platforms]: ../../reference/conditional-compilation.html#unix-and-windows
[windows-like platforms]: ../../reference/conditional-compilation.html#unix-and-windows
diff --git a/src/tools/cargo/src/doc/src/reference/external-tools.md b/src/tools/cargo/src/doc/src/reference/external-tools.md
index 7b5110cbe..b2f37ca0b 100644
--- a/src/tools/cargo/src/doc/src/reference/external-tools.md
+++ b/src/tools/cargo/src/doc/src/reference/external-tools.md
@@ -159,7 +159,8 @@ following structure:
"profile": {
/* The optimization level. */
"opt_level": "0",
- /* The debug level, an integer of 0, 1, or 2. If `null`, it implies
+ /* The debug level, an integer of 0, 1, or 2, or a string
+ "line-directives-only" or "line-tables-only". If `null`, it implies
rustc's default of 0.
*/
"debuginfo": 2,
diff --git a/src/tools/cargo/src/doc/src/reference/manifest.md b/src/tools/cargo/src/doc/src/reference/manifest.md
index 5a3d60ccc..5f9d29ff6 100644
--- a/src/tools/cargo/src/doc/src/reference/manifest.md
+++ b/src/tools/cargo/src/doc/src/reference/manifest.md
@@ -53,7 +53,6 @@ Every manifest file consists of the following sections:
* [`[profile]`](profiles.md) --- Compiler settings and optimizations.
* [`[workspace]`](workspaces.md) --- The workspace definition.
-<a id="package-metadata"></a>
### The `[package]` section
The first section in a `Cargo.toml` is `[package]`.
@@ -112,7 +111,6 @@ breaking change.
[Resolver]: resolver.md
[SemVer compatibility]: semver.md
-<a id="the-authors-field-optional"></a>
#### The `authors` field
The optional `authors` field lists in an array the people or organizations that are considered
@@ -135,7 +133,6 @@ user interface.
> field cannot be changed or removed in already-published versions of a
> package.
-<a id="the-edition-field-optional"></a>
#### The `edition` field
The `edition` key is an optional key that affects which [Rust Edition] your package
@@ -198,7 +195,6 @@ description = "A short description of my package"
> **Note**: [crates.io] requires the `description` to be set.
-<a id="the-documentation-field-optional"></a>
#### The `documentation` field
The `documentation` field specifies a URL to a website hosting the crate's
@@ -326,7 +322,6 @@ categories = ["command-line-utilities", "development-tools::cargo-plugins"]
> match one of the strings available at <https://crates.io/category_slugs>, and
> must match exactly.
-<a id="the-workspace--field-optional"></a>
#### The `workspace` field
The `workspace` field can be used to configure the workspace that this package
@@ -347,8 +342,6 @@ table defined. That is, a crate cannot both be a root crate in a workspace
For more information, see the [workspaces chapter](workspaces.md).
-<a id="package-build"></a>
-<a id="the-build-field-optional"></a>
#### The `build` field
The `build` field specifies a file in the package root which is a [build
@@ -368,7 +361,6 @@ The default is `"build.rs"`, which loads the script from a file named
specify a path to a different file or `build = false` to disable automatic
detection of the build script.
-<a id="the-links-field-optional"></a>
#### The `links` field
The `links` field specifies the name of a native library that is being linked
@@ -386,7 +378,6 @@ on Linux) may specify:
links = "git2"
```
-<a id="the-exclude-and-include-fields-optional"></a>
#### The `exclude` and `include` fields
The `exclude` and `include` fields can be used to explicitly specify which
@@ -476,7 +467,6 @@ if any of those files change.
[gitignore]: https://git-scm.com/docs/gitignore
-<a id="the-publish--field-optional"></a>
#### The `publish` field
The `publish` field can be used to prevent a package from being published to a
@@ -501,7 +491,6 @@ publish = ["some-registry-name"]
If publish array contains a single registry, `cargo publish` command will use
it when `--registry` flag is not specified.
-<a id="the-metadata-table-optional"></a>
#### The `metadata` table
Cargo by default will warn about unused keys in `Cargo.toml` to assist in
@@ -624,6 +613,17 @@ more detail.
"#the-patch-section": "overriding-dependencies.html#the-patch-section",
"#using-patch-with-multiple-versions": "overriding-dependencies.html#using-patch-with-multiple-versions",
"#the-replace-section": "overriding-dependencies.html#the-replace-section",
+ "#package-metadata": "manifest.html#the-package-section",
+ "#the-authors-field-optional": "manifest.html#the-authors-field",
+ "#the-edition-field-optional": "manifest.html#the-edition-field",
+ "#the-documentation-field-optional": "manifest.html#the-documentation-field",
+ "#the-workspace--field-optional": "manifest.html#the-workspace-field",
+ "#package-build": "manifest.html#the-build-field",
+ "#the-build-field-optional": "manifest.html#the-build-field",
+ "#the-links-field-optional": "manifest.html#the-links-field",
+ "#the-exclude-and-include-fields-optional": "manifest.html#the-exclude-and-include-fields",
+ "#the-publish--field-optional": "manifest.html#the-publish-field",
+ "#the-metadata-table-optional": "manifest.html#the-metadata-table",
};
var target = fragments[window.location.hash];
if (target) {
diff --git a/src/tools/cargo/src/doc/src/reference/overriding-dependencies.md b/src/tools/cargo/src/doc/src/reference/overriding-dependencies.md
index c8e8fbcb6..c04a7929d 100644
--- a/src/tools/cargo/src/doc/src/reference/overriding-dependencies.md
+++ b/src/tools/cargo/src/doc/src/reference/overriding-dependencies.md
@@ -349,9 +349,9 @@ crate, instead `[patch]` must be used in that situation. As a result usage of a
path override is typically isolated to quick bug fixes rather than larger
changes.
-Note: using a local configuration to override paths will only work for crates
-that have been published to [crates.io]. You cannot use this feature to tell
-Cargo how to find local unpublished crates.
+> **Note**: using a local configuration to override paths will only work for
+> crates that have been published to [crates.io]. You cannot use this feature
+> to tell Cargo how to find local unpublished crates.
[crates.io]: https://crates.io/
diff --git a/src/tools/cargo/src/doc/src/reference/profiles.md b/src/tools/cargo/src/doc/src/reference/profiles.md
index 124203d55..c094aa815 100644
--- a/src/tools/cargo/src/doc/src/reference/profiles.md
+++ b/src/tools/cargo/src/doc/src/reference/profiles.md
@@ -155,9 +155,10 @@ The valid options are:
#### lto
-The `lto` setting controls the [`-C lto` flag] which controls LLVM's [link
-time optimizations]. LTO can produce better optimized code, using
-whole-program analysis, at the cost of longer linking time.
+The `lto` setting controls `rustc`'s [`-C lto`], [`-C linker-plugin-lto`], and
+[`-C embed-bitcode`] options, which control LLVM's [link time optimizations].
+LTO can produce better optimized code, using whole-program analysis, at the cost
+of longer linking time.
The valid options are:
@@ -171,11 +172,15 @@ The valid options are:
similar to "fat".
* `"off"`: Disables LTO.
-See also the [`-C linker-plugin-lto`] `rustc` flag for cross-language LTO.
+See the [linker-plugin-lto chapter] if you are interested in cross-language LTO.
+This is not yet supported natively in Cargo, but can be performed via
+`RUSTFLAGS`.
-[`-C lto` flag]: ../../rustc/codegen-options/index.html#lto
+[`-C lto`]: ../../rustc/codegen-options/index.html#lto
[link time optimizations]: https://llvm.org/docs/LinkTimeOptimization.html
[`-C linker-plugin-lto`]: ../../rustc/codegen-options/index.html#linker-plugin-lto
+[`-C embed-bitcode`]: ../../rustc/codegen-options/index.html#embed-bitcode
+[linker-plugin-lto chapter]: ../../rustc/linker-plugin-lto.html
["thin" LTO]: http://blog.llvm.org/2016/06/thinlto-scalable-and-incremental-lto.html
#### panic
diff --git a/src/tools/cargo/src/doc/src/reference/resolver.md b/src/tools/cargo/src/doc/src/reference/resolver.md
index 151648f43..ffb194c5e 100644
--- a/src/tools/cargo/src/doc/src/reference/resolver.md
+++ b/src/tools/cargo/src/doc/src/reference/resolver.md
@@ -182,11 +182,7 @@ release. Non-numeric components are compared lexicographically.
SemVer has the concept of "version metadata" with a plus in the version, such
as `1.0.0+21AF26D3`. This metadata is usually ignored, and should not be used
in a version requirement. You should never publish multiple versions that
-differ only in the metadata tag (note, this is a [known issue] with
-[crates.io] that currently permits this).
-
-[known issue]: https://github.com/rust-lang/crates.io/issues/1059
-[crates.io]: https://crates.io/
+differ only in the metadata tag.
## Other constraints
@@ -486,6 +482,7 @@ are too loose, it may be possible for new versions to be published that will
break the build.
[SemVer guidelines]: semver.md
+[crates.io]: https://crates.io/
## Troubleshooting
diff --git a/src/tools/cargo/src/doc/src/reference/semver.md b/src/tools/cargo/src/doc/src/reference/semver.md
index 5a53a6554..69d983078 100644
--- a/src/tools/cargo/src/doc/src/reference/semver.md
+++ b/src/tools/cargo/src/doc/src/reference/semver.md
@@ -59,6 +59,8 @@ considered incompatible.
* Items
* [Major: renaming/moving/removing any public items](#item-remove)
* [Minor: adding new public items](#item-new)
+ * Types
+ * [Major: Changing the alignment, layout, or size of a well-defined type](#type-layout)
* Structs
* [Major: adding a private struct field when all current fields are public](#struct-add-private-field-when-public)
* [Major: adding a public field when no private field exists](#struct-add-public-field-when-no-private)
@@ -112,8 +114,7 @@ after it has been modified, and an example usage of the code that could appear
in another project. In a minor change, the example usage should successfully
build with both the before and after versions.
-<a id="item-remove"></a>
-### Major: renaming/moving/removing any public items
+### Major: renaming/moving/removing any public items {#item-remove}
The absence of a publicly exposed [item][items] will cause any uses of that item to
fail to compile.
@@ -145,8 +146,7 @@ Mitigating strategies:
* Mark renamed items as [deprecated], and use a [`pub use`] item to re-export
to the old name.
-<a id="item-new"></a>
-### Minor: adding new public items
+### Minor: adding new public items {#item-new}
Adding new, public [items] is a minor change.
@@ -206,8 +206,752 @@ This is not considered a major change because conventionally glob imports are
a known forwards-compatibility hazard. Glob imports of items from external
crates should be avoided.
-<a id="struct-add-private-field-when-public"></a>
-### Major: adding a private struct field when all current fields are public
+### Major: Changing the alignment, layout, or size of a well-defined type {#type-layout}
+
+It is a breaking change to change the alignment, layout, or size of a type that was previously well-defined.
+
+In general, types that use the [the default representation] do not have a well-defined alignment, layout, or size.
+The compiler is free to alter the alignment, layout, or size, so code should not make any assumptions about it.
+
+> **Note**: It may be possible for external crates to break if they make assumptions about the alignment, layout, or size of a type even if it is not well-defined.
+> This is not considered a SemVer breaking change since those assumptions should not be made.
+
+Some examples of changes that are not a breaking change are (assuming no other rules in this guide are violated):
+
+* Adding, removing, reordering, or changing fields of a default representation struct, union, or enum in such a way that the change follows the other rules in this guide (for example, using `non_exhaustive` to allow those changes, or changes to private fields that are already private).
+ See [struct-add-private-field-when-public](#struct-add-private-field-when-public), [struct-add-public-field-when-no-private](#struct-add-public-field-when-no-private), [struct-private-fields-with-private](#struct-private-fields-with-private), [enum-fields-new](#enum-fields-new).
+* Adding variants to a default representation enum, if the enum uses `non_exhaustive`.
+ This may change the alignment or size of the enumeration, but those are not well-defined.
+ See [enum-variant-new](#enum-variant-new).
+* Adding, removing, reordering, or changing private fields of a `repr(C)` struct, union, or enum, following the other rules in this guide (for example, using `non_exhaustive`, or adding private fields when other private fields already exist).
+ See [repr-c-private-change](#repr-c-private-change).
+* Adding variants to a `repr(C)` enum, if the enum uses `non_exhaustive`.
+ See [repr-c-enum-variant-new](#repr-c-enum-variant-new).
+* Adding `repr(C)` to a default representation struct, union, or enum.
+ See [repr-c-add](#repr-c-add).
+* Adding `repr(<int>)` [primitive representation] to an enum.
+ See [repr-int-enum-add](#repr-int-enum-add).
+* Adding `repr(transparent)` to a default representation struct or enum.
+ See [repr-transparent-add](#repr-transparent-add).
+
+Types that use the [`repr` attribute] can be said to have an alignment and layout that is defined in some way that code may make some assumptions about that may break as a result of changing that type.
+
+In some cases, types with a `repr` attribute may not have an alignment, layout, or size that is well-defined.
+In these cases, it may be safe to make changes to the types, though care should be exercised.
+For example, types with private fields that do not otherwise document their alignment, layout, or size guarantees cannot be relied upon by external crates since the public API does not fully define the alignment, layout, or size of the type.
+
+A common example where a type with *private* fields is well-defined is a type with a single private field with a generic type, using `repr(transparent)`,
+and the prose of the documentation discusses that it is transparent to the generic type.
+For example, see [`UnsafeCell`].
+
+Some examples of breaking changes are:
+
+* Adding `repr(packed)` to a struct or union.
+ See [repr-packed-add](#repr-packed-add).
+* Adding `repr(align)` to a struct, union, or enum.
+ See [repr-align-add](#repr-align-add).
+* Removing `repr(packed)` from a struct or union.
+ See [repr-packed-remove](#repr-packed-remove).
+* Changing the value N of `repr(packed(N))` if that changes the alignment or layout.
+ See [repr-packed-n-change](#repr-packed-n-change).
+* Changing the value N of `repr(align(N))` if that changes the alignment.
+ See [repr-align-n-change](#repr-align-n-change).
+* Removing `repr(align)` from a struct, union, or enum.
+ See [repr-align-remove](#repr-align-remove).
+* Changing the order of public fields of a `repr(C)` type.
+ See [repr-c-shuffle](#repr-c-shuffle).
+* Removing `repr(C)` from a struct, union, or enum.
+ See [repr-c-remove](#repr-c-remove).
+* Removing `repr(<int>)` from an enum.
+ See [repr-int-enum-remove](#repr-int-enum-remove).
+* Changing the primitive representation of a `repr(<int>)` enum.
+ See [repr-int-enum-change](#repr-int-enum-change).
+* Removing `repr(transparent)` from a struct or enum.
+ See [repr-transparent-remove](#repr-transparent-remove).
+
+[the default representation]: ../../reference/type-layout.html#the-default-representation
+[primitive representation]: ../../reference/type-layout.html#primitive-representations
+[`repr` attribute]: ../../reference/type-layout.html#representations
+[`std::mem::transmute`]: ../../std/mem/fn.transmute.html
+[`UnsafeCell`]: ../../std/cell/struct.UnsafeCell.html#memory-layout
+
+#### Minor: `repr(C)` add, remove, or change a private field {#repr-c-private-change}
+
+It is usually safe to add, remove, or change a private field of a `repr(C)` struct, union, or enum, assuming it follows the other guidelines in this guide (see [struct-add-private-field-when-public](#struct-add-private-field-when-public), [struct-add-public-field-when-no-private](#struct-add-public-field-when-no-private), [struct-private-fields-with-private](#struct-private-fields-with-private), [enum-fields-new](#enum-fields-new)).
+
+For example, adding private fields can only be done if there are already other private fields, or it is `non_exhaustive`.
+Public fields may be added if there are private fields, or it is `non_exhaustive`, and the addition does not alter the layout of the other fields.
+
+However, this may change the size and alignment of the type.
+Care should be taken if the size or alignment changes.
+Code should not make assumptions about the size or alignment of types with private fields or `non_exhaustive` unless it has a documented size or alignment.
+
+```rust,ignore
+// MINOR CHANGE
+
+///////////////////////////////////////////////////////////
+// Before
+#[derive(Default)]
+#[repr(C)]
+pub struct Example {
+ pub f1: i32,
+ f2: i32, // a private field
+}
+
+///////////////////////////////////////////////////////////
+// After
+#[derive(Default)]
+#[repr(C)]
+pub struct Example {
+ pub f1: i32,
+ f2: i32,
+ f3: i32, // a new field
+}
+
+///////////////////////////////////////////////////////////
+// Example use of the library that will safely work.
+fn main() {
+ // NOTE: Users should not make assumptions about the size or alignment
+ // since they are not documented.
+ let f = updated_crate::Example::default();
+}
+```
+
+#### Minor: `repr(C)` add enum variant {#repr-c-enum-variant-new}
+
+It is usually safe to add variants to a `repr(C)` enum, if the enum uses `non_exhastive`.
+See [enum-variant-new](#enum-variant-new) for more discussion.
+
+Note that this may be a breaking change since it changes the size and alignment of the type.
+See [repr-c-private-change](#repr-c-private-change) for similar concerns.
+
+```rust,ignore
+// MINOR CHANGE
+
+///////////////////////////////////////////////////////////
+// Before
+#[repr(C)]
+#[non_exhaustive]
+pub enum Example {
+ Variant1 { f1: i16 },
+ Variant2 { f1: i32 },
+}
+
+///////////////////////////////////////////////////////////
+// After
+#[repr(C)]
+#[non_exhaustive]
+pub enum Example {
+ Variant1 { f1: i16 },
+ Variant2 { f1: i32 },
+ Variant3 { f1: i64 }, // added
+}
+
+///////////////////////////////////////////////////////////
+// Example use of the library that will safely work.
+fn main() {
+ // NOTE: Users should not make assumptions about the size or alignment
+ // since they are not specified. For example, this raised the size from 8
+ // to 16 bytes.
+ let f = updated_crate::Example::Variant2 { f1: 123 };
+}
+```
+
+#### Minor: Adding `repr(C)` to a default representation {#repr-c-add}
+
+It is safe to add `repr(C)` to a struct, union, or enum with [the default representation].
+This is safe because users should not make assumptions about the alignment, layout, or size of types with with the default representation.
+
+```rust,ignore
+// MINOR CHANGE
+
+///////////////////////////////////////////////////////////
+// Before
+pub struct Example {
+ pub f1: i32,
+ pub f2: i16,
+}
+
+///////////////////////////////////////////////////////////
+// After
+#[repr(C)] // added
+pub struct Example {
+ pub f1: i32,
+ pub f2: i16,
+}
+
+///////////////////////////////////////////////////////////
+// Example use of the library that will safely work.
+fn main() {
+ let f = updated_crate::Example { f1: 123, f2: 456 };
+}
+```
+
+#### Minor: Adding `repr(<int>)` to an enum {#repr-int-enum-add}
+
+It is safe to add `repr(<int>)` [primitive representation] to an enum with [the default representation].
+This is safe because users should not make assumptions about the alignment, layout, or size of an enum with the default representation.
+
+```rust,ignore
+// MINOR CHANGE
+
+///////////////////////////////////////////////////////////
+// Before
+pub enum E {
+ Variant1,
+ Variant2(i32),
+ Variant3 { f1: f64 },
+}
+
+///////////////////////////////////////////////////////////
+// After
+#[repr(i32)] // added
+pub enum E {
+ Variant1,
+ Variant2(i32),
+ Variant3 { f1: f64 },
+}
+
+///////////////////////////////////////////////////////////
+// Example use of the library that will safely work.
+fn main() {
+ let x = updated_crate::E::Variant3 { f1: 1.23 };
+}
+```
+
+#### Minor: Adding `repr(transparent)` to a default representation struct or enum {#repr-transparent-add}
+
+It is safe to add `repr(transparent)` to a struct or enum with [the default representation].
+This is safe because users should not make assumptions about the alignment, layout, or size of a struct or enum with the default representation.
+
+```rust,ignore
+// MINOR CHANGE
+
+///////////////////////////////////////////////////////////
+// Before
+#[derive(Default)]
+pub struct Example<T>(T);
+
+///////////////////////////////////////////////////////////
+// After
+#[derive(Default)]
+#[repr(transparent)] // added
+pub struct Example<T>(T);
+
+///////////////////////////////////////////////////////////
+// Example use of the library that will safely work.
+fn main() {
+ let x = updated_crate::Example::<i32>::default();
+}
+```
+
+#### Major: Adding `repr(packed)` to a struct or union {#repr-packed-add}
+
+It is a breaking change to add `repr(packed)` to a struct or union.
+Making a type `repr(packed)` makes changes that can break code, such as being invalid to take a reference to a field, or causing truncation of disjoint closure captures.
+
+<!-- TODO: If all fields are private, should this be safe to do? -->
+
+```rust,ignore
+// MAJOR CHANGE
+
+///////////////////////////////////////////////////////////
+// Before
+pub struct Example {
+ pub f1: u8,
+ pub f2: u16,
+}
+
+///////////////////////////////////////////////////////////
+// After
+#[repr(packed)] // added
+pub struct Example {
+ pub f1: u8,
+ pub f2: u16,
+}
+
+///////////////////////////////////////////////////////////
+// Example usage that will break.
+fn main() {
+ let f = updated_crate::Example { f1: 1, f2: 2 };
+ let x = &f.f2; // Error: reference to packed field is unaligned
+}
+```
+
+```rust,ignore
+// MAJOR CHANGE
+
+///////////////////////////////////////////////////////////
+// Before
+pub struct Example(pub i32, pub i32);
+
+///////////////////////////////////////////////////////////
+// After
+#[repr(packed)]
+pub struct Example(pub i32, pub i32);
+
+///////////////////////////////////////////////////////////
+// Example usage that will break.
+fn main() {
+ let mut f = updated_crate::Example(123, 456);
+ let c = || {
+ // Without repr(packed), the closure precisely captures `&f.0`.
+ // With repr(packed), the closure captures `&f` to avoid undefined behavior.
+ let a = f.0;
+ };
+ f.1 = 789; // Error: cannot assign to `f.1` because it is borrowed
+ c();
+}
+```
+
+#### Major: Adding `repr(align)` to a struct, union, or enum {#repr-align-add}
+
+It is a breaking change to add `repr(align)` to a struct, union, or enum.
+Making a type `repr(align)` would break any use of that type in a `repr(packed)` type because that combination is not allowed.
+
+<!-- TODO: This seems like it should be extraordinarily rare. Should there be any exceptions carved out for this? -->
+
+```rust,ignore
+// MAJOR CHANGE
+
+///////////////////////////////////////////////////////////
+// Before
+pub struct Aligned {
+ pub a: i32,
+}
+
+///////////////////////////////////////////////////////////
+// After
+#[repr(align(8))] // added
+pub struct Aligned {
+ pub a: i32,
+}
+
+///////////////////////////////////////////////////////////
+// Example usage that will break.
+use updated_crate::Aligned;
+
+#[repr(packed)]
+pub struct Packed { // Error: packed type cannot transitively contain a `#[repr(align)]` type
+ f1: Aligned,
+}
+
+fn main() {
+ let p = Packed {
+ f1: Aligned { a: 123 },
+ };
+}
+```
+
+#### Major: Removing `repr(packed)` from a struct or union {#repr-packed-remove}
+
+It is a breaking change to remove `repr(packed)` from a struct or union.
+This may change the alignment or layout that extern crates are relying on.
+
+If any fields are public, then removing `repr(packed)` may change the way disjoint closure captures work.
+In some cases, this can cause code to break, similar to those outlined in the [edition guide][edition-closures].
+
+[edition-closures]: ../../edition-guide/rust-2021/disjoint-capture-in-closures.html
+
+```rust,ignore
+// MAJOR CHANGE
+
+///////////////////////////////////////////////////////////
+// Before
+#[repr(C, packed)]
+pub struct Packed {
+ pub a: u8,
+ pub b: u16,
+}
+
+///////////////////////////////////////////////////////////
+// After
+#[repr(C)] // removed packed
+pub struct Packed {
+ pub a: u8,
+ pub b: u16,
+}
+
+///////////////////////////////////////////////////////////
+// Example usage that will break.
+use updated_crate::Packed;
+
+fn main() {
+ let p = Packed { a: 1, b: 2 };
+ // Some assumption about the size of the type.
+ // Without `packed`, this fails since the size is 4.
+ const _: () = assert!(std::mem::size_of::<Packed>() == 3); // Error: evaluation of constant value failed
+}
+```
+
+```rust,ignore
+// MAJOR CHANGE
+
+///////////////////////////////////////////////////////////
+// Before
+#[repr(C, packed)]
+pub struct Packed {
+ pub a: *mut i32,
+ pub b: i32,
+}
+unsafe impl Send for Packed {}
+
+///////////////////////////////////////////////////////////
+// After
+#[repr(C)] // removed packed
+pub struct Packed {
+ pub a: *mut i32,
+ pub b: i32,
+}
+unsafe impl Send for Packed {}
+
+///////////////////////////////////////////////////////////
+// Example usage that will break.
+use updated_crate::Packed;
+
+fn main() {
+ let mut x = 123;
+
+ let p = Packed {
+ a: &mut x as *mut i32,
+ b: 456,
+ };
+
+ // When the structure was packed, the closure captures `p` which is Send.
+ // When `packed` is removed, this ends up capturing `p.a` which is not Send.
+ std::thread::spawn(move || unsafe {
+ *(p.a) += 1; // Error: cannot be sent between threads safely
+ });
+}
+```
+
+#### Major: Changing the value N of `repr(packed(N))` if that changes the alignment or layout {#repr-packed-n-change}
+
+It is a breaking change to change the value of N of `repr(packed(N))` if that changes the alignment or layout.
+This may change the alignment or layout that external crates are relying on.
+
+If the value `N` is lowered below the alignment of a public field, then that would break any code that attempts to take a reference of that field.
+
+Note that some changes to `N` may not change the alignment or layout, for example increasing it when the current value is already equal to the natural alignment of the type.
+
+```rust,ignore
+// MAJOR CHANGE
+
+///////////////////////////////////////////////////////////
+// Before
+#[repr(packed(4))]
+pub struct Packed {
+ pub a: u8,
+ pub b: u32,
+}
+
+///////////////////////////////////////////////////////////
+// After
+#[repr(packed(2))] // changed to 2
+pub struct Packed {
+ pub a: u8,
+ pub b: u32,
+}
+
+///////////////////////////////////////////////////////////
+// Example usage that will break.
+use updated_crate::Packed;
+
+fn main() {
+ let p = Packed { a: 1, b: 2 };
+ let x = &p.b; // Error: reference to packed field is unaligned
+}
+```
+
+#### Major: Changing the value N of `repr(align(N))` if that changes the alignment {#repr-align-n-change}
+
+It is a breaking change to change the value `N` of `repr(align(N))` if that changes the alignment.
+This may change the alignment that external crates are relying on.
+
+This change should be safe to make if the type is not well-defined as discussed in [type layout](#type-layout) (such as having any private fields and having an undocumented alignment or layout).
+
+Note that some changes to `N` may not change the alignment or layout, for example decreasing it when the current value is already equal to or less than the natural alignment of the type.
+
+```rust,ignore
+// MAJOR CHANGE
+
+///////////////////////////////////////////////////////////
+// Before
+#[repr(align(8))]
+pub struct Packed {
+ pub a: u8,
+ pub b: u32,
+}
+
+///////////////////////////////////////////////////////////
+// After
+#[repr(align(4))] // changed to 4
+pub struct Packed {
+ pub a: u8,
+ pub b: u32,
+}
+
+///////////////////////////////////////////////////////////
+// Example usage that will break.
+use updated_crate::Packed;
+
+fn main() {
+ let p = Packed { a: 1, b: 2 };
+ // Some assumption about the size of the type.
+ // The alignment has changed from 8 to 4.
+ const _: () = assert!(std::mem::align_of::<Packed>() == 8); // Error: evaluation of constant value failed
+}
+```
+
+#### Major: Removing `repr(align)` from a struct, union, or enum {#repr-align-remove}
+
+It is a breaking change to remove `repr(align)` from a struct, union, or enum, if their layout was well-defined.
+This may change the alignment or layout that external crates are relying on.
+
+This change should be safe to make if the type is not well-defined as discussed in [type layout](#type-layout) (such as having any private fields and having an undocumented alignment).
+
+```rust,ignore
+// MAJOR CHANGE
+
+///////////////////////////////////////////////////////////
+// Before
+#[repr(C, align(8))]
+pub struct Packed {
+ pub a: u8,
+ pub b: u32,
+}
+
+///////////////////////////////////////////////////////////
+// After
+#[repr(C)] // removed align
+pub struct Packed {
+ pub a: u8,
+ pub b: u32,
+}
+
+///////////////////////////////////////////////////////////
+// Example usage that will break.
+use updated_crate::Packed;
+
+fn main() {
+ let p = Packed { a: 1, b: 2 };
+ // Some assumption about the size of the type.
+ // The alignment has changed from 8 to 4.
+ const _: () = assert!(std::mem::align_of::<Packed>() == 8); // Error: evaluation of constant value failed
+}
+```
+
+#### Major: Changing the order of public fields of a `repr(C)` type {#repr-c-shuffle}
+
+It is a breaking change to change the order of public fields of a `repr(C)` type.
+External crates may be relying on the specific ordering of the fields.
+
+```rust,ignore,run-fail
+// MAJOR CHANGE
+
+///////////////////////////////////////////////////////////
+// Before
+#[repr(C)]
+pub struct SpecificLayout {
+ pub a: u8,
+ pub b: u32,
+}
+
+///////////////////////////////////////////////////////////
+// After
+#[repr(C)]
+pub struct SpecificLayout {
+ pub b: u32, // changed order
+ pub a: u8,
+}
+
+///////////////////////////////////////////////////////////
+// Example usage that will break.
+use updated_crate::SpecificLayout;
+
+extern "C" {
+ // This C function is assuming a specific layout defined in a C header.
+ fn c_fn_get_b(x: &SpecificLayout) -> u32;
+}
+
+fn main() {
+ let p = SpecificLayout { a: 1, b: 2 };
+ unsafe { assert_eq!(c_fn_get_b(&p), 2) } // Error: value not equal to 2
+}
+
+# mod cdep {
+# // This simulates what would normally be something included from a build script.
+# // This definition would be in a C header.
+# #[repr(C)]
+# pub struct SpecificLayout {
+# pub a: u8,
+# pub b: u32,
+# }
+#
+# #[no_mangle]
+# pub fn c_fn_get_b(x: &SpecificLayout) -> u32 {
+# x.b
+# }
+# }
+```
+
+#### Major: Removing `repr(C)` from a struct, union, or enum {#repr-c-remove}
+
+It is a breaking change to remove `repr(C)` from a struct, union, or enum.
+External crates may be relying on the specific layout of the type.
+
+```rust,ignore
+// MAJOR CHANGE
+
+///////////////////////////////////////////////////////////
+// Before
+#[repr(C)]
+pub struct SpecificLayout {
+ pub a: u8,
+ pub b: u32,
+}
+
+///////////////////////////////////////////////////////////
+// After
+// removed repr(C)
+pub struct SpecificLayout {
+ pub a: u8,
+ pub b: u32,
+}
+
+///////////////////////////////////////////////////////////
+// Example usage that will break.
+use updated_crate::SpecificLayout;
+
+extern "C" {
+ // This C function is assuming a specific layout defined in a C header.
+ fn c_fn_get_b(x: &SpecificLayout) -> u32; // Error: is not FFI-safe
+}
+
+fn main() {
+ let p = SpecificLayout { a: 1, b: 2 };
+ unsafe { assert_eq!(c_fn_get_b(&p), 2) }
+}
+
+# mod cdep {
+# // This simulates what would normally be something included from a build script.
+# // This definition would be in a C header.
+# #[repr(C)]
+# pub struct SpecificLayout {
+# pub a: u8,
+# pub b: u32,
+# }
+#
+# #[no_mangle]
+# pub fn c_fn_get_b(x: &SpecificLayout) -> u32 {
+# x.b
+# }
+# }
+```
+
+#### Major: Removing `repr(<int>)` from an enum {#repr-int-enum-remove}
+
+It is a breaking change to remove `repr(<int>)` from an enum.
+External crates may be assuming that the discriminant is a specific size.
+For example, [`std::mem::transmute`] of an enum may fail.
+
+```rust,ignore
+// MAJOR CHANGE
+
+///////////////////////////////////////////////////////////
+// Before
+#[repr(u16)]
+pub enum Example {
+ Variant1,
+ Variant2,
+ Variant3,
+}
+
+///////////////////////////////////////////////////////////
+// After
+// removed repr(u16)
+pub enum Example {
+ Variant1,
+ Variant2,
+ Variant3,
+}
+
+///////////////////////////////////////////////////////////
+// Example usage that will break.
+
+fn main() {
+ let e = updated_crate::Example::Variant2;
+ let i: u16 = unsafe { std::mem::transmute(e) }; // Error: cannot transmute between types of different sizes
+}
+```
+
+#### Major: Changing the primitive representation of a `repr(<int>)` enum {#repr-int-enum-change}
+
+It is a breaking change to change the primitive representation of a `repr(<int>)` enum.
+External crates may be assuming that the discriminant is a specific size.
+For example, [`std::mem::transmute`] of an enum may fail.
+
+```rust,ignore
+// MAJOR CHANGE
+
+///////////////////////////////////////////////////////////
+// Before
+#[repr(u16)]
+pub enum Example {
+ Variant1,
+ Variant2,
+ Variant3,
+}
+
+///////////////////////////////////////////////////////////
+// After
+#[repr(u8)] // changed repr size
+pub enum Example {
+ Variant1,
+ Variant2,
+ Variant3,
+}
+
+///////////////////////////////////////////////////////////
+// Example usage that will break.
+
+fn main() {
+ let e = updated_crate::Example::Variant2;
+ let i: u16 = unsafe { std::mem::transmute(e) }; // Error: cannot transmute between types of different sizes
+}
+```
+
+#### Major: Removing `repr(transparent)` from a struct or enum {#repr-transparent-remove}
+
+It is a breaking change to remove `repr(transparent)` from a struct or enum.
+External crates may be relying on the type having the alignment, layout, or size of the transparent field.
+
+```rust,ignore
+// MAJOR CHANGE
+
+///////////////////////////////////////////////////////////
+// Before
+#[repr(transparent)]
+pub struct Transparent<T>(T);
+
+///////////////////////////////////////////////////////////
+// After
+// removed repr
+pub struct Transparent<T>(T);
+
+///////////////////////////////////////////////////////////
+// Example usage that will break.
+#![deny(improper_ctypes)]
+use updated_crate::Transparent;
+
+extern "C" {
+ fn c_fn() -> Transparent<f64>; // Error: is not FFI-safe
+}
+
+fn main() {}
+```
+
+### Major: adding a private struct field when all current fields are public {#struct-add-private-field-when-public}
When a private field is added to a struct that previously had all public fields,
this will break any code that attempts to construct it with a [struct literal].
@@ -241,8 +985,7 @@ Mitigation strategies:
a struct to prevent users from using struct literal syntax, and instead
provide a constructor method and/or [Default] implementation.
-<a id="struct-add-public-field-when-no-private"></a>
-### Major: adding a public field when no private field exists
+### Major: adding a public field when no private field exists {#struct-add-public-field-when-no-private}
When a public field is added to a struct that has all public fields, this will
break any code that attempts to construct it with a [struct literal].
@@ -276,8 +1019,7 @@ Mitigation strategies:
a struct to prevent users from using struct literal syntax, and instead
provide a constructor method and/or [Default] implementation.
-<a id="struct-private-fields-with-private"></a>
-### Minor: adding or removing private fields when at least one already exists
+### Minor: adding or removing private fields when at least one already exists {#struct-private-fields-with-private}
It is safe to add or remove private fields from a struct when the struct
already has at least one private field.
@@ -335,8 +1077,7 @@ fn main() {
}
```
-<a id="struct-tuple-normal-with-private"></a>
-### Minor: going from a tuple struct with all private fields (with at least one field) to a normal struct, or vice versa
+### Minor: going from a tuple struct with all private fields (with at least one field) to a normal struct, or vice versa {#struct-tuple-normal-with-private}
Changing a tuple struct to a normal struct (or vice-versa) is safe if all
fields are private.
@@ -367,8 +1108,7 @@ fn main() {
This is safe because existing code cannot use a [struct literal] to construct
it, nor match its contents.
-<a id="enum-variant-new"></a>
-### Major: adding new enum variants (without `non_exhaustive`)
+### Major: adding new enum variants (without `non_exhaustive`) {#enum-variant-new}
It is a breaking change to add a new enum variant if the enum does not use the
[`#[non_exhaustive]`][non_exhaustive] attribute.
@@ -404,8 +1144,7 @@ Mitigation strategies:
* When introducing the enum, mark it as [`#[non_exhaustive]`][non_exhaustive]
to force users to use [wildcard patterns] to catch new variants.
-<a id="enum-fields-new"></a>
-### Major: adding new fields to an enum variant
+### Major: adding new fields to an enum variant {#enum-fields-new}
It is a breaking change to add new fields to an enum variant because all
fields are public, and constructors and matching will fail to compile.
@@ -457,8 +1196,7 @@ Mitigation strategies:
}
```
-<a id="trait-new-item-no-default"></a>
-### Major: adding a non-defaulted trait item
+### Major: adding a non-defaulted trait item {#trait-new-item-no-default}
It is a breaking change to add a non-defaulted item to a trait. This will
break any implementors of the trait.
@@ -490,8 +1228,7 @@ Mitigation strategies:
* When introducing the trait, use the [sealed trait] technique to prevent
users outside of the crate from implementing the trait.
-<a id="trait-item-signature"></a>
-### Major: any change to trait item signatures
+### Major: any change to trait item signatures {#trait-item-signature}
It is a breaking change to make any change to a trait item signature. This can
break external implementors of the trait.
@@ -530,8 +1267,7 @@ Mitigation strategies:
* When introducing the trait, use the [sealed trait] technique to prevent
users outside of the crate from implementing the trait.
-<a id="trait-new-default-item"></a>
-### Possibly-breaking: adding a defaulted trait item
+### Possibly-breaking: adding a defaulted trait item {#trait-new-default-item}
It is usually safe to add a defaulted trait item. However, this can sometimes
cause a compile error. For example, this can introduce an ambiguity if a
@@ -581,8 +1317,7 @@ Mitigation strategies:
to require downstream users to add [disambiguation syntax] to select the
correct function when updating the dependency.
-<a id="trait-object-safety"></a>
-### Major: adding a trait item that makes the trait non-object safe
+### Major: adding a trait item that makes the trait non-object safe {#trait-object-safety}
It is a breaking change to add a trait item that changes the trait to not be
[object safe].
@@ -616,8 +1351,7 @@ fn main() {
It is safe to do the converse (making a non-object safe trait into a safe
one).
-<a id="trait-new-parameter-no-default"></a>
-### Major: adding a type parameter without a default
+### Major: adding a type parameter without a default {#trait-new-parameter-no-default}
It is a breaking change to add a type parameter without a default to a trait.
@@ -643,8 +1377,7 @@ impl Trait for Foo {} // Error: missing generics
Mitigating strategies:
* See [adding a defaulted trait type parameter](#trait-new-parameter-default).
-<a id="trait-new-parameter-default"></a>
-### Minor: adding a defaulted trait type parameter
+### Minor: adding a defaulted trait type parameter {#trait-new-parameter-default}
It is safe to add a type parameter to a trait as long as it has a default.
External implementors will use the default without needing to specify the
@@ -669,8 +1402,7 @@ struct Foo;
impl Trait for Foo {}
```
-<a id="impl-item-new"></a>
-### Possibly-breaking change: adding any inherent items
+### Possibly-breaking change: adding any inherent items {#impl-item-new}
Usually adding inherent items to an implementation should be safe because
inherent items take priority over trait items. However, in some cases the
@@ -719,8 +1451,7 @@ Mitigation strategies:
to require downstream users to add [disambiguation syntax] to select the
correct function when updating the dependency.
-<a id="generic-bounds-tighten"></a>
-### Major: tightening generic bounds
+### Major: tightening generic bounds {#generic-bounds-tighten}
It is a breaking change to tighten generic bounds on a type since this can
break users expecting the looser bounds.
@@ -749,8 +1480,7 @@ fn main() {
}
```
-<a id="generic-bounds-loosen"></a>
-### Minor: loosening generic bounds
+### Minor: loosening generic bounds {#generic-bounds-loosen}
It is safe to loosen the generic bounds on a type, as it only expands what is
allowed.
@@ -779,8 +1509,7 @@ fn main() {
}
```
-<a id="generic-new-default"></a>
-### Minor: adding defaulted type parameters
+### Minor: adding defaulted type parameters {#generic-new-default}
It is safe to add a type parameter to a type as long as it has a default. All
existing references will use the default without needing to specify the
@@ -810,8 +1539,7 @@ fn main() {
}
```
-<a id="generic-generalize-identical"></a>
-### Minor: generalizing a type to use generics (with identical types)
+### Minor: generalizing a type to use generics (with identical types) {#generic-generalize-identical}
A struct or enum field can change from a concrete type to a generic type
parameter, provided that the change results in an identical type for all
@@ -840,8 +1568,7 @@ fn main() {
because existing uses of `Foo` are shorthand for `Foo<u8>` which yields the
identical field type.
-<a id="generic-generalize-different"></a>
-### Major: generalizing a type to use generics (with possibly different types)
+### Major: generalizing a type to use generics (with possibly different types) {#generic-generalize-different}
Changing a struct or enum field from a concrete type to a generic type
parameter can break if the type can change.
@@ -866,8 +1593,7 @@ fn main() {
}
```
-<a id="generic-more-generic"></a>
-### Minor: changing a generic type to a more generic type
+### Minor: changing a generic type to a more generic type {#generic-more-generic}
It is safe to change a generic type to a more generic one. For example, the
following adds a generic parameter that defaults to the original type, which
@@ -894,8 +1620,7 @@ fn main() {
}
```
-<a id="fn-change-arity"></a>
-### Major: adding/removing function parameters
+### Major: adding/removing function parameters {#fn-change-arity}
Changing the arity of a function is a breaking change.
@@ -924,8 +1649,7 @@ Mitigating strategies:
with the builder pattern. This allows new fields to be added to the struct
in the future.
-<a id="fn-generic-new"></a>
-### Possibly-breaking: introducing a new function type parameter
+### Possibly-breaking: introducing a new function type parameter {#fn-generic-new}
Usually, adding a non-defaulted type parameter is safe, but in some
cases it can be a breaking change:
@@ -955,8 +1679,7 @@ other ways) that this breakage is usually acceptable. One should take into
account how likely it is that the function in question is being called with
explicit type arguments.
-<a id="fn-generalize-compatible"></a>
-### Minor: generalizing a function to use generics (supporting original type)
+### Minor: generalizing a function to use generics (supporting original type) {#fn-generalize-compatible}
The type of a parameter to a function, or its return value, can be
*generalized* to use generics, including by introducing a new type parameter,
@@ -1053,8 +1776,7 @@ fn main() {
}
```
-<a id="fn-generalize-mismatch"></a>
-### Major: generalizing a function to use generics with type mismatch
+### Major: generalizing a function to use generics with type mismatch {#fn-generalize-mismatch}
It is a breaking change to change a function parameter or return type if the
generic type constrains or changes the types previously allowed. For example,
@@ -1081,8 +1803,7 @@ fn main() {
}
```
-<a id="fn-unsafe-safe"></a>
-### Minor: making an `unsafe` function safe
+### Minor: making an `unsafe` function safe {#fn-unsafe-safe}
A previously `unsafe` function can be made safe without breaking code.
@@ -1122,8 +1843,7 @@ Making a previously `unsafe` associated function or method on structs / enums
safe is also a minor change, while the same is not true for associated
function on traits (see [any change to trait item signatures](#trait-item-signature)).
-<a id="attr-no-std-to-std"></a>
-### Major: switching from `no_std` support to requiring `std`
+### Major: switching from `no_std` support to requiring `std` {#attr-no-std-to-std}
If your library specifically supports a [`no_std`] environment, it is a
breaking change to make a new release that requires `std`.
@@ -1158,8 +1878,7 @@ Mitigation strategies:
optionally enables `std` support, and when the feature is off, the library
can be used in a `no_std` environment.
-<a id="attr-adding-non-exhaustive"></a>
-### Major: adding `non_exhaustive` to an existing enum, variant, or struct with no private fields
+### Major: adding `non_exhaustive` to an existing enum, variant, or struct with no private fields {#attr-adding-non-exhaustive}
Making items [`#[non_exhaustive]`][non_exhaustive] changes how they may
be used outside the crate where they are defined:
@@ -1243,8 +1962,7 @@ Mitigation strategies:
## Tooling and environment compatibility
-<a id="env-new-rust"></a>
-### Possibly-breaking: changing the minimum version of Rust required
+### Possibly-breaking: changing the minimum version of Rust required {#env-new-rust}
Introducing the use of new features in a new release of Rust can break
projects that are using older versions of Rust. This also includes using new
@@ -1271,8 +1989,7 @@ Mitigation strategies:
mechanism for new features. These are currently unstable and only available
in the nightly channel.
-<a id="env-change-requirements"></a>
-### Possibly-breaking: changing the platform and environment requirements
+### Possibly-breaking: changing the platform and environment requirements {#env-change-requirements}
There is a very wide range of assumptions a library makes about the
environment that it runs in, such as the host platform, operating system
@@ -1292,8 +2009,7 @@ Mitigation strategies:
* Document the platforms and environments you specifically support.
* Test your code on a wide range of environments in CI.
-<a id="new-lints"></a>
-### Minor: introducing new lints
+### Minor: introducing new lints {#new-lints}
Some changes to a library may cause new lints to be triggered in users of that library.
This should generally be considered a compatible change.
@@ -1348,8 +2064,7 @@ Mitigating strategies:
### Cargo
-<a id="cargo-feature-add"></a>
-#### Minor: adding a new Cargo feature
+#### Minor: adding a new Cargo feature {#cargo-feature-add}
It is usually safe to add new [Cargo features]. If the feature introduces new
changes that cause a breaking change, this can cause difficulties for projects
@@ -1371,8 +2086,7 @@ consequences of enabling the feature.
std = []
```
-<a id="cargo-feature-remove"></a>
-#### Major: removing a Cargo feature
+#### Major: removing a Cargo feature {#cargo-feature-remove}
It is usually a breaking change to remove [Cargo features]. This will cause
an error for any project that enabled the feature.
@@ -1398,8 +2112,7 @@ Mitigation strategies:
functionality. Document that the feature is deprecated, and remove it in a
future major SemVer release.
-<a id="cargo-feature-remove-another"></a>
-#### Major: removing a feature from a feature list if that changes functionality or public items
+#### Major: removing a feature from a feature list if that changes functionality or public items {#cargo-feature-remove-another}
If removing a feature from another feature, this can break existing users if
they are expecting that functionality to be available through that feature.
@@ -1420,8 +2133,7 @@ default = [] # This may cause packages to fail if they are expecting std to be
std = []
```
-<a id="cargo-remove-opt-dep"></a>
-#### Possibly-breaking: removing an optional dependency
+#### Possibly-breaking: removing an optional dependency {#cargo-remove-opt-dep}
Removing an optional dependency can break a project using your library because
another project may be enabling that dependency via [Cargo features].
@@ -1454,8 +2166,7 @@ Mitigation strategies:
optional dependencies necessary to implement "networking". Then document the
"networking" feature.
-<a id="cargo-change-dep-feature"></a>
-#### Minor: changing dependency features
+#### Minor: changing dependency features {#cargo-change-dep-feature}
It is usually safe to change the features on a dependency, as long as the
feature does not introduce a breaking change.
@@ -1475,8 +2186,7 @@ rand = { version = "0.7.3", features = ["small_rng"] }
rand = "0.7.3"
```
-<a id="cargo-dep-add"></a>
-#### Minor: adding dependencies
+#### Minor: adding dependencies {#cargo-dep-add}
It is usually safe to add new dependencies, as long as the new dependency
does not introduce new requirements that result in a breaking change.
diff --git a/src/tools/cargo/src/doc/src/reference/specifying-dependencies.md b/src/tools/cargo/src/doc/src/reference/specifying-dependencies.md
index 8941e67ad..f044af95b 100644
--- a/src/tools/cargo/src/doc/src/reference/specifying-dependencies.md
+++ b/src/tools/cargo/src/doc/src/reference/specifying-dependencies.md
@@ -22,7 +22,7 @@ time = "0.1.12"
The string `"0.1.12"` is a version requirement. Although it looks like a
specific *version* of the `time` crate, it actually specifies a *range* of
versions and allows [SemVer] compatible updates. An update is allowed if the new
-version number does not modify the left-most non-zero digit in the major, minor,
+version number does not modify the left-most non-zero number in the major, minor,
patch grouping. In this case, if we ran `cargo update -p time`, cargo should
update us to version `0.1.13` if it is the latest `0.1.z` release, but would not
update us to `0.2.0`. If instead we had specified the version string as `1.0`,
diff --git a/src/tools/cargo/src/doc/src/reference/unstable.md b/src/tools/cargo/src/doc/src/reference/unstable.md
index b59319196..55084f88e 100644
--- a/src/tools/cargo/src/doc/src/reference/unstable.md
+++ b/src/tools/cargo/src/doc/src/reference/unstable.md
@@ -399,7 +399,7 @@ the tracking repository, and if it's not there please file a new issue!
This flag is a sibling to the `-Zbuild-std` feature flag. This will configure
the features enabled for the standard library itself when building the standard
library. The default enabled features, at this time, are `backtrace` and
-`panic_unwind`. This flag expects a comma-separated list and, if provided, will
+`panic-unwind`. This flag expects a comma-separated list and, if provided, will
override the default list of features enabled.
### binary-dep-depinfo
@@ -432,9 +432,9 @@ like to stabilize it somehow!
### keep-going
* Tracking Issue: [#10496](https://github.com/rust-lang/cargo/issues/10496)
-`cargo build --keep-going` (and similarly for `check`, `test` etc) will build as
-many crates in the dependency graph as possible, rather than aborting the build
-at the first one that fails to build.
+`cargo build --keep-going` (and similarly for every command involving compilation, like `check` and `doc`)
+will build as many crates in the dependency graph as possible,
+rather than aborting the build at the first one that fails to build.
For example if the current package depends on dependencies `fails` and `works`,
one of which fails to build, `cargo check -j1` may or may not build the one that
@@ -449,6 +449,16 @@ The `-Z unstable-options` command-line option must be used in order to use
cargo check --keep-going -Z unstable-options
```
+While `cargo test` and `cargo bench` commands involve compilation, they do not provide a `--keep-going` flag.
+Both commands already include a similar `--no-fail-fast` flag, allowing running as many tests as possible without stopping at the first failure.
+To "compile" as many tests as possible, use target selection flags like `--tests` to build test binaries separately.
+For example,
+
+```console
+cargo build --tests --keep-going -Zunstable-options
+cargo test --tests --no-fail-fast
+```
+
### config-include
* Tracking Issue: [#7723](https://github.com/rust-lang/cargo/issues/7723)
@@ -1000,7 +1010,7 @@ It is intended for the rare use cases like "cryptographic proof that the central
Both fields can be set with `cargo login --registry=name --private-key --private-key-subject="subject"` which will prompt you to put in the key value.
-A registry can have at most one of `private-key`, `token`, or `credential-process` set.
+A registry can have at most one of `private-key` or `token` set.
All PASETOs will include `iat`, the current time in ISO 8601 format. Cargo will include the following where appropriate:
- `sub` an optional, non-secret string chosen by the registry that is expected to be claimed with every request. The value will be the `private-key-subject` from the `config.toml` file.
@@ -1026,144 +1036,248 @@ If a claim should be expected for the request but is missing in the PASETO then
The `credential-process` feature adds a config setting to fetch registry
authentication tokens by calling an external process.
-Token authentication is used by the [`cargo login`], [`cargo publish`],
-[`cargo owner`], [`cargo yank`], and [`cargo logout`] commands.
-
To use this feature, you must pass the `-Z credential-process` flag on the
-command-line. Additionally, you must remove any current tokens currently saved
-in the [`credentials.toml` file] (which can be done with the [`cargo logout`] command).
+command-line.
#### `credential-process` Configuration
To configure which process to run to fetch the token, specify the process in
-the `registry` table in a [config file]:
+the `registry` table in a [config file] with spaces separating arguments. If the
+path to the provider or its arguments contain spaces, then it mused be defined in
+the `credential-alias` table and referenced instead.
```toml
[registry]
-credential-process = "/usr/bin/cargo-creds"
+global-credential-providers = ["/usr/bin/cargo-creds"]
```
-If you want to use a different process for a specific registry, it can be
+The provider at the end of the list will be attempted first. This ensures
+that when config files are merged, files closer to the project (and ultimatly
+environment variables) have precedence.
+
+In this example, the `my-provider` provider will be attempted first, and if
+it cannot provide credentials, then the `cargo:token` provider will be used.
+
+```toml
+[registry]
+global-credential-providers = ['cargo:token', 'my-provider']
+```
+
+If you want to use a different provider for a specific registry, it can be
specified in the `registries` table:
```toml
[registries.my-registry]
-credential-process = "/usr/bin/cargo-creds"
+credential-provider = "/usr/bin/cargo-creds"
```
-The value can be a string with spaces separating arguments or it can be a TOML
-array of strings.
+The credential provider for crates.io can be specified as:
-Command-line arguments allow special placeholders which will be replaced with
-the corresponding value:
+```toml
+[registry]
+credential-provider = "/usr/bin/cargo-creds"
+```
-* `{name}` --- The name of the registry.
-* `{api_url}` --- The base URL of the registry API endpoints.
-* `{action}` --- The authentication action (described below).
+The value can be a string with spaces separating arguments or it can be a TOML
+array of strings.
-Process names with the prefix `cargo:` are loaded from the `libexec` directory
-next to cargo. Several experimental credential wrappers are included with
-Cargo, and this provides convenient access to them:
+For commonly-used providers, or providers that need to contain spaces in the arguments
+or path, the `credential-alias` table can be used. These aliases can be referenced
+in `credential-provider` or `global-credential-providers`.
```toml
+[credential-alias]
+my-alias = ["/usr/bin/cargo-creds", "--argument"]
+
[registry]
-credential-process = "cargo:macos-keychain"
-```
-
-The current wrappers are:
-
-* `cargo:macos-keychain`: Uses the macOS Keychain to store the token.
-* `cargo:wincred`: Uses the Windows Credential Manager to store the token.
-* `cargo:1password`: Uses the 1password `op` CLI to store the token. You must
- install the `op` CLI from the [1password
- website](https://1password.com/downloads/command-line/). You must run `op
- signin` at least once with the appropriate arguments (such as `op signin
- my.1password.com user@example.com`), unless you provide the sign-in-address
- and email arguments. The master password will be required on each request
- unless the appropriate `OP_SESSION` environment variable is set. It supports
- the following command-line arguments:
- * `--account`: The account shorthand name to use.
- * `--vault`: The vault name to use.
- * `--sign-in-address`: The sign-in-address, which is a web address such as `my.1password.com`.
- * `--email`: The email address to sign in with.
-
-A wrapper is available for GNOME
-[libsecret](https://wiki.gnome.org/Projects/Libsecret) to store tokens on
-Linux systems. Due to build limitations, this wrapper is not available as a
-pre-compiled binary. This can be built and installed manually. First, install
-libsecret using your system package manager (for example, `sudo apt install
-libsecret-1-dev`). Then build and install the wrapper with `cargo install
-cargo-credential-gnome-secret`.
-In the config, use a path to the binary like this:
+global-credential-providers = ["cargo:token", "my-alias"]
+```
+#### Built-in providers
+
+Cargo now includes several built-in credential providers. These providers are
+executed within the Cargo process. They are identified with the `cargo:` prefix.
+
+* `cargo:token` - Uses Cargo's config and `credentials.toml` to store the token (default).
+* `cargo:wincred` - Uses the Windows Credential Manager to store the token.
+* `cargo:macos-keychain` - Uses the macOS Keychain to store the token.
+* `cargo:libsecret` - Uses [libsecret](https://wiki.gnome.org/Projects/Libsecret) to store tokens on Linux systems.
+* `cargo:token-from-stdout <command>` - Launch a subprocess that returns a token
+ on stdout. Newlines will be trimmed. The process inherits the user's stdin and stderr.
+ It should exit 0 on success, and nonzero on error.
+
+ With this form, [`cargo login`] and [`cargo logout`] are not supported and
+ return an error if used.
+
+ The following environment variables will be provided to the executed command:
+
+ * `CARGO` --- Path to the `cargo` binary executing the command.
+ * `CARGO_REGISTRY_INDEX_URL` --- The URL of the registry index.
+ * `CARGO_REGISTRY_NAME_OPT` --- Optional name of the registry. Should not be used as a storage key. Not always available.
+
+* `cargo:paseto` - implements asymmetric token support (RFC3231) as a credential provider.
+
+
+`cargo-credential-1password` uses the 1password `op` CLI to store the token. You must
+install the `op` CLI from the [1password
+website](https://1password.com/downloads/command-line/). You must run `op
+signin` at least once with the appropriate arguments (such as `op signin
+my.1password.com user@example.com`), unless you provide the sign-in-address
+and email arguments. The master password will be required on each request
+unless the appropriate `OP_SESSION` environment variable is set. It supports
+the following command-line arguments:
+* `--account`: The account shorthand name to use.
+* `--vault`: The vault name to use.
+* `--sign-in-address`: The sign-in-address, which is a web address such as `my.1password.com`.
+* `--email`: The email address to sign in with.
+
+Install the provider with `cargo install cargo-credential-1password`
+In the config, add it to `global-credential-providers`:
```toml
[registry]
-credential-process = "cargo-credential-gnome-secret {action}"
+global-credential-providers = ["cargo-credential-1password"]
```
-#### `credential-process` Interface
-
-There are two different kinds of token processes that Cargo supports. The
-simple "basic" kind will only be called by Cargo when it needs a token. This
-is intended for simple and easy integration with password managers, that can
-often use pre-existing tooling. The more advanced "Cargo" kind supports
-different actions passed as a command-line argument. This is intended for more
-pleasant integration experience, at the expense of requiring a Cargo-specific
-process to glue to the password manager. Cargo will determine which kind is
-supported by the `credential-process` definition. If it contains the
-`{action}` argument, then it uses the advanced style, otherwise it assumes it
-only supports the "basic" kind.
-
-##### Basic authenticator
-
-A basic authenticator is a process that returns a token on stdout. Newlines
-will be trimmed. The process inherits the user's stdin and stderr. It should
-exit 0 on success, and nonzero on error.
-
-With this form, [`cargo login`] and [`cargo logout`] are not supported and
-return an error if used.
-
-##### Cargo authenticator
-
-The protocol between the Cargo and the process is very basic, intended to
-ensure the credential process is kept as simple as possible. Cargo will
-execute the process with the `{action}` argument indicating which action to
-perform:
+#### JSON Interface
+When using an external credential provider, Cargo communicates with the credential
+provider using stdin/stdout messages passed as a single line of JSON.
-* `store` --- Store the given token in secure storage.
-* `get` --- Get a token from storage.
-* `erase` --- Remove a token from storage.
+Cargo will always execute the credential provider with the `--cargo-plugin` argument.
+This enables a credential provider executable to have additional functionality beyond
+how Cargo uses it.
-The `cargo login` command uses `store` to save a token. Commands that require
-authentication, like `cargo publish`, uses `get` to retrieve a token. `cargo
-logout` uses the `erase` command to remove a token.
+The messages here have additional newlines added for readability.
+Actual messages must not contain newlines.
-The process inherits the user's stderr, so the process can display messages.
-Some values are passed in via environment variables (see below). The expected
-interactions are:
-
-* `store` --- The token is sent to the process's stdin, terminated by a newline.
- The process should store the token keyed off the registry name. If the
- process fails, it should exit with a nonzero exit status.
+##### Credential hello
+* Sent by: credential provider
+* Purpose: used to identify the supported protocols on process startup
+```javascript
+{
+ "v":[1]
+}
+```
-* `get` --- The process should send the token to its stdout (trailing newline
- will be trimmed). The process inherits the user's stdin, should it need to
- receive input.
+##### Login request
+* Sent by: Cargo
+* Purpose: collect and store credentials
+```javascript
+{
+ // Protocol version
+ "v":1,
+ // Action to perform: login
+ "kind":"login",
+ // Registry information
+ "registry":{"index-url":"sparse+https://registry-url/index/", "name": "my-registry"},
+}
+```
- If the process is unable to fulfill the request, it should exit with a
- nonzero exit code.
+##### Read request
+* Sent by: Cargo
+* Purpose: Get the credential for reading crate information
+```javascript
+{
+ // Protocol version
+ "v":1,
+ // Request kind: get credentials
+ "kind":"get",
+ // Action to perform: read crate information
+ "operation":"read",
+ // Registry information
+ "registry":{"index-url":"sparse+https://registry-url/index/", "name": "my-registry"},
+ // Additional command-line args
+ "args":[]
+}
+```
-* `erase` --- The process should remove the token associated with the registry
- name. If the token is not found, the process should exit with a 0 exit
- status.
+##### Publish request
+* Sent by: Cargo
+* Purpose: Get the credential for publishing a crate
+```javascript
+{
+ // Protocol version
+ "v":1,
+ // Request kind: get credentials
+ "kind":"get",
+ // Action to perform: publish crate
+ "operation":"publish",
+ // Crate name
+ "name":"sample",
+ // Crate version
+ "vers":"0.1.0",
+ // Crate checksum
+ "cksum":"...",
+ // Registry information
+ "registry":{"index-url":"sparse+https://registry-url/index/", "name": "my-registry"},
+ // Additional command-line args
+ "args":[]
+}
+```
-##### Environment
+##### Success response
+* Sent by: credential process
+* Purpose: Gives the credential to Cargo
+```javascript
+{"Ok":{
+ // Response kind: this was a get request kind
+ "kind":"get",
+ // Token to send to the registry
+ "token":"...",
+ // Cache control. Can be one of the following:
+ // * "never"
+ // * "session"
+ // * { "expires": UNIX timestamp }
+ "cache":{"expires":1684251794},
+ // Is the token operation independent?
+ "operation_independent":true
+}}
+```
-The following environment variables will be provided to the executed command:
+##### Failure response
+* Sent by: credential process
+* Purpose: Gives error information to Cargo
+```javascript
+{"Err":{
+ // Error: the credential provider does not support the
+ // registry
+ "kind":"url-not-supported",
+
+ // Error: The credential could not be found in the provider.
+ // using `cargo login --registry ...`.
+ "kind":"not-found",
+
+ // Error: something else has failed
+ "kind":"other",
+ "detail": "free form string error message"
+}}
+```
-* `CARGO` --- Path to the `cargo` binary executing the command.
-* `CARGO_REGISTRY_INDEX_URL` --- The URL of the registry index.
-* `CARGO_REGISTRY_NAME_OPT` --- Optional name of the registry. Should not be used as a storage key. Not always available.
+##### Example communication to request a token for reading:
+1. Cargo spawns the credential process, capturing stdin and stdout.
+2. Credential process sends the Hello message to Cargo
+ ```javascript
+ { "v": [1] }
+ ```
+3. Cargo sends the CredentialRequest message to the credential process (newlines added for readability).
+ ```javascript
+ {
+ "v": 1,
+ "kind": "get",
+ "operation": "read",
+ "registry":{"index-url":"sparse+https://registry-url/index/", "name":"ado2"},
+ "args":[]
+ }
+ ```
+4. Credential process sends the CredentialResponse to Cargo (newlines added for readability).
+ ```javascript
+ {
+ "token": "...",
+ "cache": "session",
+ "operation_independent": false
+ }
+ ```
+5. Credential process exits
+6. Cargo uses the token for the remainder of the session (until Cargo exits) when interacting with this registry.
[`cargo login`]: ../commands/cargo-login.md
[`cargo logout`]: ../commands/cargo-logout.md
diff --git a/src/tools/cargo/src/doc/src/reference/workspaces.md b/src/tools/cargo/src/doc/src/reference/workspaces.md
index 21f8f08f8..36a2e7323 100644
--- a/src/tools/cargo/src/doc/src/reference/workspaces.md
+++ b/src/tools/cargo/src/doc/src/reference/workspaces.md
@@ -56,7 +56,6 @@ version = "0.1.0" # the current version, obeying semver
authors = ["Alice <a@example.com>", "Bob <b@example.com>"]
```
-<a id="virtual-manifest"></a>
#### Virtual workspace
Alternatively, a `Cargo.toml` file can be created with a `[workspace]` section
@@ -68,6 +67,7 @@ you want to keep all the packages organized in separate directories.
# [PROJECT_DIR]/Cargo.toml
[workspace]
members = ["hello_world"]
+resolver = "2"
```
```toml
@@ -75,9 +75,15 @@ members = ["hello_world"]
[package]
name = "hello_world" # the name of the package
version = "0.1.0" # the current version, obeying semver
+edition = "2021" # the edition, will have no effect on a resolver used in the workspace
authors = ["Alice <a@example.com>", "Bob <b@example.com>"]
```
+Note that in a virtual manifest the [`resolver = "2"`](resolver.md#resolver-versions)
+should be specified manually. It is usually deduced from the [`package.edition`][package-edition]
+field which is absent in virtual manifests and the edition field of a member
+won't affect the resolver used by the workspace.
+
### The `members` and `exclude` fields
The `members` and `exclude` fields define which packages are members of
@@ -242,6 +248,7 @@ if that makes sense for the tool in question.
[package]: manifest.md#the-package-section
[`Cargo.lock`]: ../guide/cargo-toml-vs-cargo-lock.md
[package-metadata]: manifest.md#the-metadata-table
+[package-edition]: manifest.md#the-edition-field
[output directory]: ../guide/build-cache.md
[patch]: overriding-dependencies.md#the-patch-section
[replace]: overriding-dependencies.md#the-replace-section
@@ -253,3 +260,17 @@ if that makes sense for the tool in question.
[specifying-dependencies]: specifying-dependencies.md
[features]: features.md
[inheriting-a-dependency-from-a-workspace]: specifying-dependencies.md#inheriting-a-dependency-from-a-workspace
+
+<script>
+(function() {
+ var fragments = {
+ "#virtual-manifest": "workspaces.html#virtual-workspace",
+ };
+ var target = fragments[window.location.hash];
+ if (target) {
+ var url = window.location.toString();
+ var base = url.substring(0, url.lastIndexOf('/'));
+ window.location.replace(base + "/" + target);
+ }
+})();
+</script>
diff --git a/src/tools/cargo/src/etc/_cargo b/src/tools/cargo/src/etc/_cargo
index bdceb10c9..164d7679f 100644
--- a/src/tools/cargo/src/etc/_cargo
+++ b/src/tools/cargo/src/etc/_cargo
@@ -45,9 +45,13 @@ _cargo() {
'(--bench --bin --example --lib)--test=[specify test name]:test name'
)
- parallel=(
+ jobs=(
'(-j --jobs)'{-j+,--jobs=}'[specify number of parallel jobs]:jobs [# of CPUs]'
- '--keep-going[do not abort build on first error]'
+ )
+
+ parallel=(
+ "${jobs[@]}"
+ '--keep-going[do not abort build on first build error]'
)
features=(
@@ -87,7 +91,7 @@ _cargo() {
'*:args:_default'
;;
bench)
- _arguments -s -A "^--" $common $parallel $features $msgfmt $triple $target $manifest \
+ _arguments -s -A "^--" $common $jobs $features $msgfmt $triple $target $manifest \
"${command_scope_spec[@]}" \
'--all-targets[benchmark all targets]' \
"--no-run[compile but don't run]" \
@@ -297,7 +301,7 @@ _cargo() {
;;
test | t)
- _arguments -s -S $common $parallel $features $msgfmt $triple $target $manifest \
+ _arguments -s -S $common $jobs $features $msgfmt $triple $target $manifest \
'--test=[test name]: :_cargo_test_names' \
'--no-fail-fast[run all tests regardless of failure]' \
'--no-run[compile but do not run]' \
diff --git a/src/tools/cargo/src/etc/cargo.bashcomp.sh b/src/tools/cargo/src/etc/cargo.bashcomp.sh
index 2867ec56d..33f225ebf 100644
--- a/src/tools/cargo/src/etc/cargo.bashcomp.sh
+++ b/src/tools/cargo/src/etc/cargo.bashcomp.sh
@@ -41,7 +41,8 @@ _cargo()
local opt_pkg='-p --package'
local opt_feat='-F --features --all-features --no-default-features'
local opt_mani='--manifest-path'
- local opt_parallel='-j --jobs --keep-going'
+ local opt_jobs='-j --jobs'
+ local opt_parallel="$opt_jobs --keep-going"
local opt_force='-f --force'
local opt_sync='-s --sync'
local opt_lock='--frozen --locked --offline'
@@ -49,7 +50,7 @@ _cargo()
local opt___nocmd="$opt_common -V --version --list --explain"
local opt__add="$opt_common -p --package --features --default-features --no-default-features $opt_mani --optional --no-optional --rename --dry-run --path --git --branch --tag --rev --registry --dev --build --target"
- local opt__bench="$opt_common $opt_pkg_spec $opt_feat $opt_mani $opt_lock $opt_parallel $opt_targets --message-format --target --no-run --no-fail-fast --target-dir --ignore-rust-version"
+ local opt__bench="$opt_common $opt_pkg_spec $opt_feat $opt_mani $opt_lock $opt_jobs $opt_targets --message-format --target --no-run --no-fail-fast --target-dir --ignore-rust-version"
local opt__build="$opt_common $opt_pkg_spec $opt_feat $opt_mani $opt_lock $opt_parallel $opt_targets --message-format --target --release --profile --target-dir --ignore-rust-version"
local opt__b="$opt__build"
local opt__check="$opt_common $opt_pkg_spec $opt_feat $opt_mani $opt_lock $opt_parallel $opt_targets --message-format --target --release --profile --target-dir --ignore-rust-version"
@@ -82,7 +83,7 @@ _cargo()
local opt__rustc="$opt_common $opt_pkg $opt_feat $opt_mani $opt_lock $opt_parallel $opt_targets -L --crate-type --extern --message-format --profile --target --release --target-dir --ignore-rust-version"
local opt__rustdoc="$opt_common $opt_pkg $opt_feat $opt_mani $opt_lock $opt_parallel $opt_targets --message-format --target --release --open --target-dir --profile --ignore-rust-version"
local opt__search="$opt_common $opt_lock --limit --index --registry"
- local opt__test="$opt_common $opt_pkg_spec $opt_feat $opt_mani $opt_lock $opt_parallel $opt_targets --message-format --doc --target --no-run --release --no-fail-fast --target-dir --profile --ignore-rust-version"
+ local opt__test="$opt_common $opt_pkg_spec $opt_feat $opt_mani $opt_lock $opt_jobs $opt_targets --message-format --doc --target --no-run --release --no-fail-fast --target-dir --profile --ignore-rust-version"
local opt__t="$opt__test"
local opt__tree="$opt_common $opt_pkg_spec $opt_feat $opt_mani $opt_lock --target -i --invert --prefix --no-dedupe --duplicates -d --charset -f --format -e --edges"
local opt__uninstall="$opt_common $opt_lock $opt_pkg --bin --root"
diff --git a/src/tools/cargo/src/etc/man/cargo-metadata.1 b/src/tools/cargo/src/etc/man/cargo-metadata.1
index f2ebb63c7..95597d413 100644
--- a/src/tools/cargo/src/etc/man/cargo-metadata.1
+++ b/src/tools/cargo/src/etc/man/cargo-metadata.1
@@ -11,13 +11,37 @@ cargo\-metadata \[em] Machine\-readable metadata about the current package
Output JSON to stdout containing information about the workspace members and
resolved dependencies of the current package.
.sp
-It is recommended to include the \fB\-\-format\-version\fR flag to future\-proof
-your code to ensure the output is in the format you are expecting.
+The format of the output is subject to change in futures versions of Cargo. It
+is recommended to include the \fB\-\-format\-version\fR flag to future\-proof your code
+to ensure the output is in the format you are expecting. For more on the
+expectations, see \[lq]Compatibility\[rq]\&.
.sp
See the \fIcargo_metadata crate\fR <https://crates.io/crates/cargo_metadata>
for a Rust API for reading the metadata.
.SH "OUTPUT FORMAT"
-The output has the following format:
+.SS "Compatibility"
+Within the same output format version, the compatibility is maintained, except
+some scenarios. The following is a non\-exhaustive list of changes that are not
+considersed as incompatibile:
+.sp
+.RS 4
+\h'-04'\(bu\h'+02'\fBAdding new fields\fR \[em] New fields will be added when needed. Reserving this
+helps Cargo evolve without bumping the format version too often.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+02'\fBAdding new values for enum\-like fields\fR \[em] Same as adding new fields. It
+keeps metadata evolving without stagnation.
+.RE
+.sp
+.RS 4
+\h'-04'\(bu\h'+02'\fBChanging opaque representations\fR \[em] The inner representations of some
+fields are implementation details. For example, fields related to \[lq]Package ID\[rq]
+or \[lq]Source ID\[rq] are treated as opaque identifiers to differentiate packages or
+sources. Consumers shouldn\[cq]t rely on those representations unless specified.
+.RE
+.SS "JSON format"
+The JSON output has the following format:
.sp
.RS 4
.nf
@@ -31,7 +55,9 @@ The output has the following format:
"name": "my\-package",
/* The version of the package. */
"version": "0.1.0",
- /* The Package ID, a unique identifier for referring to the package. */
+ /* The Package ID, an opaque and unique identifier for referring to the
+ package. See "Compatibility" above for the stability guarantee.
+ */
"id": "my\-package 0.1.0 (path+file:///path/to/my\-package)",
/* The license value from the manifest, or null. */
"license": "MIT/Apache\-2.0",
@@ -39,14 +65,25 @@ The output has the following format:
"license_file": "LICENSE",
/* The description value from the manifest, or null. */
"description": "Package description.",
- /* The source ID of the package. This represents where
- a package is retrieved from.
+ /* The source ID of the package, an "opaque" identifier representing
+ where a package is retrieved from. See "Compatibility" above for
+ the stability guarantee.
+
This is null for path dependencies and workspace members.
+
For other dependencies, it is a string with the format:
\- "registry+URL" for registry\-based dependencies.
Example: "registry+https://github.com/rust\-lang/crates.io\-index"
\- "git+URL" for git\-based dependencies.
Example: "git+https://github.com/rust\-lang/cargo?rev=5e85ba14aaa20f8133863373404cb0af69eeef2c#5e85ba14aaa20f8133863373404cb0af69eeef2c"
+ \- "sparse+URL" for dependencies from a sparse registry
+ Example: "sparse+https://my\-sparse\-registry.org"
+
+ The value after the `+` is not explicitly defined, and may change
+ between versions of Cargo and may not directly correlate to other
+ things, such as registry definitions in a config file. New source
+ kinds may be added in the future which will have different `+`
+ prefixed identifiers.
*/
"source": null,
/* Array of dependencies declared in the package's manifest. */
diff --git a/src/tools/cargo/src/etc/man/cargo-test.1 b/src/tools/cargo/src/etc/man/cargo-test.1
index 802169815..4ca150dbc 100644
--- a/src/tools/cargo/src/etc/man/cargo-test.1
+++ b/src/tools/cargo/src/etc/man/cargo-test.1
@@ -535,13 +535,6 @@ a string \fBdefault\fR is provided, it sets the value back to defaults.
Should not be 0.
.RE
.sp
-\fB\-\-keep\-going\fR
-.RS 4
-Build as many crates in the dependency graph as possible, rather than aborting
-the build on the first one that fails to build. Unstable, requires
-\fB\-Zunstable\-options\fR\&.
-.RE
-.sp
\fB\-\-future\-incompat\-report\fR
.RS 4
Displays a future\-incompat report for any future\-incompatible warnings
diff --git a/src/tools/cargo/src/etc/man/cargo-yank.1 b/src/tools/cargo/src/etc/man/cargo-yank.1
index 107c59e49..2f46dd694 100644
--- a/src/tools/cargo/src/etc/man/cargo-yank.1
+++ b/src/tools/cargo/src/etc/man/cargo-yank.1
@@ -72,7 +72,7 @@ T}
T{
\fB2.0.0\fR
T}:T{
-Use either \fB1.5.0\fR, \fB1.5.1\fR or \fB0.22.2\fR
+Use either \fB1.5.0\fR, \fB1.5.1\fR or \fB1.5.2\fR
T}:T{
Use \fB1.5.0\fR
T}:T{
diff --git a/src/tools/cargo/tests/testsuite/alt_registry.rs b/src/tools/cargo/tests/testsuite/alt_registry.rs
index ac60ca92f..91157cd53 100644
--- a/src/tools/cargo/tests/testsuite/alt_registry.rs
+++ b/src/tools/cargo/tests/testsuite/alt_registry.rs
@@ -1506,3 +1506,44 @@ fn publish_with_transitive_dep() {
.build();
p2.cargo("publish").run();
}
+
+#[cargo_test]
+fn warn_for_unused_fields() {
+ let _ = RegistryBuilder::new()
+ .no_configure_token()
+ .alternative()
+ .build();
+ let p = project()
+ .file("src/lib.rs", "")
+ .file(
+ ".cargo/config.toml",
+ "[registry]
+ unexpected-field = 'foo'
+ [registries.alternative]
+ unexpected-field = 'foo'
+ ",
+ )
+ .build();
+
+ p.cargo("publish --registry alternative")
+ .with_status(101)
+ .with_stderr(
+ "\
+[UPDATING] `alternative` index
+[WARNING] unused config key `registries.alternative.unexpected-field` in `[..]config.toml`
+[ERROR] no token found for `alternative`, please run `cargo login --registry alternative`
+or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN",
+ )
+ .run();
+
+ p.cargo("publish --registry crates-io")
+ .with_status(101)
+ .with_stderr(
+ "\
+[UPDATING] crates.io index
+[WARNING] unused config key `registry.unexpected-field` in `[..]config.toml`
+[ERROR] no token found, please run `cargo login`
+or use environment variable CARGO_REGISTRY_TOKEN",
+ )
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/bench.rs b/src/tools/cargo/tests/testsuite/bench.rs
index 581acbe15..d773308c6 100644
--- a/src/tools/cargo/tests/testsuite/bench.rs
+++ b/src/tools/cargo/tests/testsuite/bench.rs
@@ -314,9 +314,9 @@ fn cargo_bench_failing_test() {
[RUNNING] [..] (target/release/deps/foo-[..][EXE])",
)
.with_stdout_contains("[..]thread '[..]' panicked at[..]")
- .with_stdout_contains("[..]assertion failed[..]")
- .with_stdout_contains("[..]left: `\"hello\"`[..]")
- .with_stdout_contains("[..]right: `\"nope\"`[..]")
+ .with_stdout_contains("[..]assertion [..]failed[..]")
+ .with_stdout_contains("[..]left: [..]\"hello\"[..]")
+ .with_stdout_contains("[..]right: [..]\"nope\"[..]")
.with_stdout_contains("[..]src/main.rs:15[..]")
.with_status(101)
.run();
@@ -1670,3 +1670,58 @@ fn json_artifact_includes_executable_for_benchmark() {
)
.run();
}
+
+#[cargo_test]
+fn cargo_bench_no_keep_going() {
+ let p = project()
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/main.rs", "")
+ .build();
+
+ p.cargo("bench --keep-going")
+ .with_stderr(
+ "\
+error: unexpected argument `--keep-going` found
+
+ tip: to run as many benchmarks as possible without failing fast, use `--no-fail-fast`",
+ )
+ .with_status(101)
+ .run();
+}
+
+#[cargo_test(nightly, reason = "bench")]
+fn cargo_bench_print_env_verbose() {
+ let p = project()
+ .file("Cargo.toml", &basic_manifest("foo", "0.0.1"))
+ .file(
+ "src/main.rs",
+ r#"
+ #![feature(test)]
+ #[cfg(test)]
+ extern crate test;
+
+ fn hello() -> &'static str {
+ "hello"
+ }
+
+ pub fn main() {
+ println!("{}", hello())
+ }
+
+ #[bench]
+ fn bench_hello(_b: &mut test::Bencher) {
+ assert_eq!(hello(), "hello")
+ }
+ "#,
+ )
+ .build();
+ p.cargo("bench -vv")
+ .with_stderr(
+ "\
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `[..]CARGO_MANIFEST_DIR=[CWD][..] rustc[..]`
+[FINISHED] bench [optimized] target(s) in [..]
+[RUNNING] `[..]CARGO_MANIFEST_DIR=[CWD][..] [CWD]/target/release/deps/foo-[..][EXE] --bench`",
+ )
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/build_script.rs b/src/tools/cargo/tests/testsuite/build_script.rs
index 4840356c6..400d10547 100644
--- a/src/tools/cargo/tests/testsuite/build_script.rs
+++ b/src/tools/cargo/tests/testsuite/build_script.rs
@@ -5141,6 +5141,35 @@ for more information about build script outputs.
}
#[cargo_test]
+fn wrong_syntax_with_two_colons() {
+ let p = project()
+ .file("src/lib.rs", "")
+ .file(
+ "build.rs",
+ r#"
+ fn main() {
+ println!("cargo::foo=bar");
+ }
+ "#,
+ )
+ .build();
+
+ p.cargo("build")
+ .with_status(101)
+ .with_stderr(
+ "\
+[COMPILING] foo [..]
+error: unsupported output in build script of `foo v0.0.1 ([ROOT]/foo)`: `cargo::foo=bar`
+Found a `cargo::key=value` build directive which is reserved for future use.
+Either change the directive to `cargo:key=value` syntax (note the single `:`) or upgrade your version of Rust.
+See https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script \
+for more information about build script outputs.
+",
+ )
+ .run();
+}
+
+#[cargo_test]
fn custom_build_closes_stdin() {
// Ensure stdin is closed to prevent deadlock.
// See https://github.com/rust-lang/cargo/issues/11196
diff --git a/src/tools/cargo/tests/testsuite/build_script_env.rs b/src/tools/cargo/tests/testsuite/build_script_env.rs
index bc87b7120..df574600c 100644
--- a/src/tools/cargo/tests/testsuite/build_script_env.rs
+++ b/src/tools/cargo/tests/testsuite/build_script_env.rs
@@ -117,7 +117,10 @@ fn rustc_bootstrap() {
"#;
let p = project()
.file("Cargo.toml", &basic_manifest("has-dashes", "0.0.1"))
- .file("src/lib.rs", "#![feature(rustc_attrs)]")
+ .file(
+ "src/lib.rs",
+ "#![allow(internal_features)] #![feature(rustc_attrs)]",
+ )
.file("build.rs", build_rs)
.build();
// RUSTC_BOOTSTRAP unset on stable should error
@@ -154,7 +157,10 @@ fn rustc_bootstrap() {
// Tests for binaries instead of libraries
let p = project()
.file("Cargo.toml", &basic_manifest("foo", "0.0.1"))
- .file("src/main.rs", "#![feature(rustc_attrs)] fn main() {}")
+ .file(
+ "src/main.rs",
+ "#![allow(internal_features)] #![feature(rustc_attrs)] fn main() {}",
+ )
.file("build.rs", build_rs)
.build();
// nightly should warn when there's no library whether or not RUSTC_BOOTSTRAP is set
diff --git a/src/tools/cargo/tests/testsuite/cargo/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo/help/mod.rs
new file mode 100644
index 000000000..559377b27
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo/help/mod.rs
@@ -0,0 +1,12 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/init/auto_git/stdout.log b/src/tools/cargo/tests/testsuite/cargo/help/stderr.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/auto_git/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo/help/stdout.log
new file mode 100644
index 000000000..26bcd745b
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo/help/stdout.log
@@ -0,0 +1,39 @@
+Rust's package manager
+
+Usage: cargo [..][OPTIONS] [COMMAND]
+ cargo [..][OPTIONS] -Zscript <MANIFEST_RS> [ARGS]...
+
+Options:
+ -V, --version Print version info and exit
+ --list List installed commands
+ --explain <CODE> Run `rustc --explain CODE`
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
+ --color <WHEN> Coloring: auto, always, never
+ -C <DIRECTORY> Change to DIRECTORY before doing anything (nightly-only)
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Some common cargo commands are (see all commands with --list):
+ build, b Compile the current package
+ check, c Analyze the current package and report errors, but don't build object files
+ clean Remove the target directory
+ doc, d Build this package's and its dependencies' documentation
+ new Create a new cargo package
+ init Create a new cargo package in an existing directory
+ add Add dependencies to a manifest file
+ remove Remove dependencies from a manifest file
+ run, r Run a binary or example of the local package
+ test, t Run the tests
+ bench Run the benchmarks
+ update Update dependencies listed in Cargo.lock
+ search Search registry for crates
+ publish Package and upload this package to the registry
+ install Install a Rust binary. Default location is $HOME/.cargo/bin
+ uninstall Uninstall a Rust binary
+
+See 'cargo help <command>' for more information on a specific command.
diff --git a/src/tools/cargo/tests/testsuite/cargo/mod.rs b/src/tools/cargo/tests/testsuite/cargo/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/help/mod.rs
new file mode 100644
index 000000000..0962047f8
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("add")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit/stdout.log b/src/tools/cargo/tests/testsuite/cargo_add/help/stderr.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_add/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_add/help/stdout.log
new file mode 100644
index 000000000..0daba1a94
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/help/stdout.log
@@ -0,0 +1,124 @@
+Add dependencies to a Cargo.toml manifest file
+
+Usage: cargo add [OPTIONS] <DEP>[@<VERSION>] ...
+ cargo add [OPTIONS] --path <PATH> ...
+ cargo add [OPTIONS] --git <URL> ...
+
+Arguments:
+ [DEP_ID]...
+ Reference to a package to add as a dependency
+
+ You can reference a package by:
+ - `<name>`, like `cargo add serde` (latest version will be used)
+ - `<name>@<version-req>`, like `cargo add serde@1` or `cargo add serde@=1.0.38`
+
+Options:
+ --no-default-features
+ Disable the default features
+
+ --default-features
+ Re-enable the default features
+
+ -F, --features <FEATURES>
+ Space or comma separated list of features to activate
+
+ --optional
+ Mark the dependency as optional
+
+ The package name will be exposed as feature of your crate.
+
+ --no-optional
+ Mark the dependency as required
+
+ The package will be removed from your features.
+
+ --rename <NAME>
+ Rename the dependency
+
+ Example uses:
+ - Depending on multiple versions of a crate
+ - Depend on crates with the same name from different registries
+
+ --ignore-rust-version
+ Ignore `rust-version` specification in packages (unstable)
+
+ --dry-run
+ Don't actually write the manifest
+
+ -q, --quiet
+ Do not print cargo log messages
+
+ -v, --verbose...
+ Use verbose output (-vv very verbose/build.rs output)
+
+ --color <WHEN>
+ Coloring: auto, always, never
+
+ --config <KEY=VALUE>
+ Override a configuration value
+
+ -Z <FLAG>
+ Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+
+ -h, --help
+ Print help (see a summary with '-h')
+
+Manifest Options:
+ --manifest-path <PATH>
+ Path to Cargo.toml
+
+ --frozen
+ Require Cargo.lock and cache are up to date
+
+ --locked
+ Require Cargo.lock is up to date
+
+ --offline
+ Run without accessing the network
+
+Package Selection:
+ -p, --package [<SPEC>]
+ Package to modify
+
+Source:
+ --path <PATH>
+ Filesystem path to local crate to add
+
+ --git <URI>
+ Git repository location
+
+ Without any other information, cargo will use latest commit on the main branch.
+
+ --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
+
+ This is the catch all, handling hashes to named references in remote repositories.
+
+ --registry <NAME>
+ Package registry for this dependency
+
+Section:
+ --dev
+ Add as development dependency
+
+ Dev-dependencies are not used when compiling a package for building, but are used for
+ compiling tests, examples, and benchmarks.
+
+ These dependencies are not propagated to other packages which depend on this package.
+
+ --build
+ Add as build dependency
+
+ Build-dependencies are the only dependencies available for use by build scripts
+ (`build.rs` files).
+
+ --target <TARGET>
+ Add as dependency to the given target platform
+
+Run `cargo help add` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/mod.rs
index be7a1546b..de93afbc1 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/mod.rs
@@ -35,6 +35,7 @@ mod git_normalized_name;
mod git_registry;
mod git_rev;
mod git_tag;
+mod help;
mod infer_prerelease;
mod invalid_arg;
mod invalid_git_name;
diff --git a/src/tools/cargo/tests/testsuite/cargo_bench/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_bench/help/mod.rs
new file mode 100644
index 000000000..9338664e5
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_bench/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("bench")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit_nosrc/stdout.log b/src/tools/cargo/tests/testsuite/cargo_bench/help/stderr.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit_nosrc/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_bench/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_bench/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_bench/help/stdout.log
new file mode 100644
index 000000000..5d9484df9
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_bench/help/stdout.log
@@ -0,0 +1,59 @@
+Execute all benchmarks of a local package
+
+Usage: cargo[EXE] bench [OPTIONS] [BENCHNAME] [-- [args]...]
+
+Arguments:
+ [BENCHNAME] If specified, only run benches containing this string in their names
+ [args]... Arguments for the bench binary
+
+Options:
+ --no-run Compile, but don't run benchmarks
+ --no-fail-fast Run all benchmarks regardless of failure
+ --ignore-rust-version Ignore `rust-version` specification in packages
+ --message-format <FMT> Error format
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
+ details
+ -h, --help Print help
+
+Package Selection:
+ -p, --package [<SPEC>] Package to run benchmarks for
+ --workspace Benchmark all packages in the workspace
+ --exclude <SPEC> Exclude packages from the benchmark
+ --all Alias for --workspace (deprecated)
+
+Target Selection:
+ --lib Benchmark only this package's library
+ --bins Benchmark all binaries
+ --bin [<NAME>] Benchmark only the specified binary
+ --examples Benchmark all examples
+ --example [<NAME>] Benchmark only the specified example
+ --tests Benchmark all tests
+ --test [<NAME>] Benchmark only the specified test target
+ --benches Benchmark all benches
+ --bench [<NAME>] Benchmark only the specified bench target
+ --all-targets Benchmark all targets
+
+Feature Selection:
+ -F, --features <FEATURES> Space or comma separated list of features to activate
+ --all-features Activate all available features
+ --no-default-features Do not activate the `default` feature
+
+Compilation Options:
+ -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs.
+ --profile <PROFILE-NAME> Build artifacts with the specified profile
+ --target <TRIPLE> Build for the target triple
+ --target-dir <DIRECTORY> Directory for all generated artifacts
+ --unit-graph Output build graph in JSON (unstable)
+ --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help bench` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_bench/mod.rs b/src/tools/cargo/tests/testsuite/cargo_bench/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_bench/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_build/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_build/help/mod.rs
new file mode 100644
index 000000000..9ca23b478
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_build/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("build")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit/stdout.log b/src/tools/cargo/tests/testsuite/cargo_build/help/stderr.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_build/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_build/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_build/help/stdout.log
new file mode 100644
index 000000000..af906c24f
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_build/help/stdout.log
@@ -0,0 +1,58 @@
+Compile a local package and all of its dependencies
+
+Usage: cargo[EXE] build [OPTIONS]
+
+Options:
+ --ignore-rust-version Ignore `rust-version` specification in packages
+ --future-incompat-report Outputs a future incompatibility report at the end of the build
+ --message-format <FMT> Error format
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
+ details
+ -h, --help Print help
+
+Package Selection:
+ -p, --package [<SPEC>] Package to build (see `cargo help pkgid`)
+ --workspace Build all packages in the workspace
+ --exclude <SPEC> Exclude packages from the build
+ --all Alias for --workspace (deprecated)
+
+Target Selection:
+ --lib Build only this package's library
+ --bins Build all binaries
+ --bin [<NAME>] Build only the specified binary
+ --examples Build all examples
+ --example [<NAME>] Build only the specified example
+ --tests Build all tests
+ --test [<NAME>] Build only the specified test target
+ --benches Build all benches
+ --bench [<NAME>] Build only the specified bench target
+ --all-targets Build all targets
+
+Feature Selection:
+ -F, --features <FEATURES> Space or comma separated list of features to activate
+ --all-features Activate all available features
+ --no-default-features Do not activate the `default` feature
+
+Compilation Options:
+ -r, --release Build artifacts in release mode, with optimizations
+ --profile <PROFILE-NAME> Build artifacts with the specified profile
+ -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs.
+ --keep-going Do not abort the build as soon as there is an error (unstable)
+ --target <TRIPLE> Build for the target triple
+ --target-dir <DIRECTORY> Directory for all generated artifacts
+ --out-dir <PATH> Copy final artifacts to this directory (unstable)
+ --build-plan Output the build plan in JSON (unstable)
+ --unit-graph Output build graph in JSON (unstable)
+ --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help build` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_build/mod.rs b/src/tools/cargo/tests/testsuite/cargo_build/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_build/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_check/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_check/help/mod.rs
new file mode 100644
index 000000000..71571bc95
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_check/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("check")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namenosrc/stdout.log b/src/tools/cargo/tests/testsuite/cargo_check/help/stderr.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namenosrc/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_check/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_check/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_check/help/stdout.log
new file mode 100644
index 000000000..7c87615cd
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_check/help/stdout.log
@@ -0,0 +1,56 @@
+Check a local package and all of its dependencies for errors
+
+Usage: cargo[EXE] check [OPTIONS]
+
+Options:
+ --ignore-rust-version Ignore `rust-version` specification in packages
+ --future-incompat-report Outputs a future incompatibility report at the end of the build
+ --message-format <FMT> Error format
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
+ details
+ -h, --help Print help
+
+Package Selection:
+ -p, --package [<SPEC>] Package(s) to check
+ --workspace Check all packages in the workspace
+ --exclude <SPEC> Exclude packages from the check
+ --all Alias for --workspace (deprecated)
+
+Target Selection:
+ --lib Check only this package's library
+ --bins Check all binaries
+ --bin [<NAME>] Check only the specified binary
+ --examples Check all examples
+ --example [<NAME>] Check only the specified example
+ --tests Check all tests
+ --test [<NAME>] Check only the specified test target
+ --benches Check all benches
+ --bench [<NAME>] Check only the specified bench target
+ --all-targets Check all targets
+
+Feature Selection:
+ -F, --features <FEATURES> Space or comma separated list of features to activate
+ --all-features Activate all available features
+ --no-default-features Do not activate the `default` feature
+
+Compilation Options:
+ -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs.
+ --keep-going Do not abort the build as soon as there is an error (unstable)
+ -r, --release Check artifacts in release mode, with optimizations
+ --profile <PROFILE-NAME> Check artifacts with the specified profile
+ --target <TRIPLE> Check for the target triple
+ --target-dir <DIRECTORY> Directory for all generated artifacts
+ --unit-graph Output build graph in JSON (unstable)
+ --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help check` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_check/mod.rs b/src/tools/cargo/tests/testsuite/cargo_check/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_check/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_clean/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_clean/help/mod.rs
new file mode 100644
index 000000000..7225292b8
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_clean/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("clean")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namesrc/stdout.log b/src/tools/cargo/tests/testsuite/cargo_clean/help/stderr.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namesrc/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_clean/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_clean/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_clean/help/stdout.log
new file mode 100644
index 000000000..fd3c8855c
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_clean/help/stdout.log
@@ -0,0 +1,29 @@
+Remove artifacts that cargo has generated in the past
+
+Usage: cargo[EXE] clean [OPTIONS]
+
+Options:
+ --doc Whether or not to clean just the documentation directory
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Package Selection:
+ -p, --package [<SPEC>] Package to clean artifacts for
+
+Compilation Options:
+ -r, --release Whether or not to clean release artifacts
+ --profile <PROFILE-NAME> Clean artifacts of the specified profile
+ --target <TRIPLE> Target triple to clean output for
+ --target-dir <DIRECTORY> Directory for all generated artifacts
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help clean` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_clean/mod.rs b/src/tools/cargo/tests/testsuite/cargo_clean/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_clean/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_config/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_config/help/mod.rs
new file mode 100644
index 000000000..070238ef0
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_config/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("config")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_nosrc/stdout.log b/src/tools/cargo/tests/testsuite/cargo_config/help/stderr.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_nosrc/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_config/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_config/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_config/help/stdout.log
new file mode 100644
index 000000000..50caca72a
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_config/help/stdout.log
@@ -0,0 +1,18 @@
+Inspect configuration values
+
+Usage: cargo[EXE] config [OPTIONS] <COMMAND>
+
+Commands:
+ get
+
+Options:
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Manifest Options:
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
diff --git a/src/tools/cargo/tests/testsuite/cargo_config.rs b/src/tools/cargo/tests/testsuite/cargo_config/mod.rs
index e367f8e06..dc0a40ed8 100644
--- a/src/tools/cargo/tests/testsuite/cargo_config.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_config/mod.rs
@@ -5,6 +5,8 @@ use cargo_test_support::paths;
use std::fs;
use std::path::PathBuf;
+mod help;
+
fn cargo_process(s: &str) -> cargo_test_support::Execs {
let mut p = cargo_test_support::cargo_process(s);
// Clear out some of the environment added by the default cargo_process so
diff --git a/src/tools/cargo/tests/testsuite/cargo_doc/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_doc/help/mod.rs
new file mode 100644
index 000000000..b0fd4f3e8
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_doc/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("doc")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/init/both_lib_and_bin/stdout.log b/src/tools/cargo/tests/testsuite/cargo_doc/help/stderr.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/both_lib_and_bin/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_doc/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_doc/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_doc/help/stdout.log
new file mode 100644
index 000000000..480e189c1
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_doc/help/stdout.log
@@ -0,0 +1,53 @@
+Build a package's documentation
+
+Usage: cargo[EXE] doc [OPTIONS]
+
+Options:
+ --open Opens the docs in a browser after the operation
+ --no-deps Don't build documentation for dependencies
+ --document-private-items Document private items
+ --ignore-rust-version Ignore `rust-version` specification in packages
+ --message-format <FMT> Error format
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
+ details
+ -h, --help Print help
+
+Package Selection:
+ -p, --package [<SPEC>] Package to document
+ --workspace Document all packages in the workspace
+ --exclude <SPEC> Exclude packages from the build
+ --all Alias for --workspace (deprecated)
+
+Feature Selection:
+ -F, --features <FEATURES> Space or comma separated list of features to activate
+ --all-features Activate all available features
+ --no-default-features Do not activate the `default` feature
+
+Target Selection:
+ --lib Document only this package's library
+ --bins Document all binaries
+ --bin [<NAME>] Document only the specified binary
+ --examples Document all examples
+ --example [<NAME>] Document only the specified example
+
+Compilation Options:
+ -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs.
+ --keep-going Do not abort the build as soon as there is an error (unstable)
+ -r, --release Build artifacts in release mode, with optimizations
+ --profile <PROFILE-NAME> Build artifacts with the specified profile
+ --target <TRIPLE> Build for the target triple
+ --target-dir <DIRECTORY> Directory for all generated artifacts
+ --unit-graph Output build graph in JSON (unstable)
+ --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help doc` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_doc/mod.rs b/src/tools/cargo/tests/testsuite/cargo_doc/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_doc/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_fetch/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_fetch/help/mod.rs
new file mode 100644
index 000000000..79025bc32
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_fetch/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("fetch")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/init/cant_create_library_when_both_binlib_present/stdout.log b/src/tools/cargo/tests/testsuite/cargo_fetch/help/stderr.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/cant_create_library_when_both_binlib_present/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_fetch/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_fetch/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_fetch/help/stdout.log
new file mode 100644
index 000000000..b9bd6c35b
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_fetch/help/stdout.log
@@ -0,0 +1,22 @@
+Fetch dependencies of a package from the network
+
+Usage: cargo[EXE] fetch [OPTIONS]
+
+Options:
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Compilation Options:
+ --target <TRIPLE> Fetch dependencies for the target triple
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help fetch` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_fetch/mod.rs b/src/tools/cargo/tests/testsuite/cargo_fetch/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_fetch/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_fix/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_fix/help/mod.rs
new file mode 100644
index 000000000..2c67e1556
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_fix/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("fix")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/stdout.log b/src/tools/cargo/tests/testsuite/cargo_fix/help/stderr.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_fix/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_fix/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_fix/help/stdout.log
new file mode 100644
index 000000000..c0a98218a
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_fix/help/stdout.log
@@ -0,0 +1,60 @@
+Automatically fix lint warnings reported by rustc
+
+Usage: cargo[EXE] fix [OPTIONS]
+
+Options:
+ --edition Fix in preparation for the next edition
+ --edition-idioms Fix warnings to migrate to the idioms of an edition
+ --broken-code Fix code even if it already has compiler errors
+ --allow-no-vcs Fix code even if a VCS was not detected
+ --allow-dirty Fix code even if the working directory is dirty
+ --allow-staged Fix code even if the working directory has staged changes
+ --ignore-rust-version Ignore `rust-version` specification in packages
+ --message-format <FMT> Error format
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
+ details
+ -h, --help Print help
+
+Package Selection:
+ -p, --package [<SPEC>] Package(s) to fix
+ --workspace Fix all packages in the workspace
+ --exclude <SPEC> Exclude packages from the fixes
+ --all Alias for --workspace (deprecated)
+
+Target Selection:
+ --lib Fix only this package's library
+ --bins Fix all binaries
+ --bin [<NAME>] Fix only the specified binary
+ --examples Fix all examples
+ --example [<NAME>] Fix only the specified example
+ --tests Fix all tests
+ --test [<NAME>] Fix only the specified test target
+ --benches Fix all benches
+ --bench [<NAME>] Fix only the specified bench target
+ --all-targets Fix all targets (default)
+
+Feature Selection:
+ -F, --features <FEATURES> Space or comma separated list of features to activate
+ --all-features Activate all available features
+ --no-default-features Do not activate the `default` feature
+
+Compilation Options:
+ -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs.
+ --keep-going Do not abort the build as soon as there is an error (unstable)
+ -r, --release Fix artifacts in release mode, with optimizations
+ --profile <PROFILE-NAME> Build artifacts with the specified profile
+ --target <TRIPLE> Fix for the target triple
+ --target-dir <DIRECTORY> Directory for all generated artifacts
+ --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help fix` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_fix/mod.rs b/src/tools/cargo/tests/testsuite/cargo_fix/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_fix/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_generate_lockfile/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_generate_lockfile/help/mod.rs
new file mode 100644
index 000000000..0408ce06b
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_generate_lockfile/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("generate-lockfile")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/stdout.log b/src/tools/cargo/tests/testsuite/cargo_generate_lockfile/help/stderr.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_generate_lockfile/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_generate_lockfile/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_generate_lockfile/help/stdout.log
new file mode 100644
index 000000000..07eff888a
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_generate_lockfile/help/stdout.log
@@ -0,0 +1,19 @@
+Generate the lockfile for a package
+
+Usage: cargo[EXE] generate-lockfile [OPTIONS]
+
+Options:
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help generate-lockfile` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_generate_lockfile/mod.rs b/src/tools/cargo/tests/testsuite/cargo_generate_lockfile/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_generate_lockfile/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_git_checkout/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_git_checkout/help/mod.rs
new file mode 100644
index 000000000..5ff877fbb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_git_checkout/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("git-checkout")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/stdout.log b/src/tools/cargo/tests/testsuite/cargo_git_checkout/help/stderr.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_git_checkout/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_git_checkout/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_git_checkout/help/stdout.log
new file mode 100644
index 000000000..675090fd3
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_git_checkout/help/stdout.log
@@ -0,0 +1 @@
+The `git-checkout` command has been removed.
diff --git a/src/tools/cargo/tests/testsuite/cargo_git_checkout/mod.rs b/src/tools/cargo/tests/testsuite/cargo_git_checkout/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_git_checkout/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_help/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_help/help/mod.rs
new file mode 100644
index 000000000..af445cda1
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_help/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("help")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/stdout.log b/src/tools/cargo/tests/testsuite/cargo_help/help/stderr.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_help/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_help/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_help/help/stdout.log
new file mode 100644
index 000000000..a03946b45
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_help/help/stdout.log
@@ -0,0 +1,18 @@
+Displays help for a cargo subcommand
+
+Usage: cargo[EXE] help [OPTIONS] [COMMAND]
+
+Arguments:
+ [COMMAND]
+
+Options:
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Manifest Options:
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
diff --git a/src/tools/cargo/tests/testsuite/cargo_help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_help/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_help/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/init/auto_git/in b/src/tools/cargo/tests/testsuite/cargo_init/auto_git/in
index 1202506b6..1202506b6 120000
--- a/src/tools/cargo/tests/testsuite/init/auto_git/in
+++ b/src/tools/cargo/tests/testsuite/cargo_init/auto_git/in
diff --git a/src/tools/cargo/tests/testsuite/init/auto_git/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/auto_git/mod.rs
index 68c217520..68c217520 100644
--- a/src/tools/cargo/tests/testsuite/init/auto_git/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/auto_git/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/auto_git/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/auto_git/out/Cargo.toml
index dcdb8da2c..dcdb8da2c 100644
--- a/src/tools/cargo/tests/testsuite/init/auto_git/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/auto_git/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/auto_git/out/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/auto_git/out/src/lib.rs
index 7d12d9af8..7d12d9af8 100644
--- a/src/tools/cargo/tests/testsuite/init/auto_git/out/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/auto_git/out/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/auto_git/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/auto_git/stderr.log
index f459bf226..f459bf226 100644
--- a/src/tools/cargo/tests/testsuite/init/auto_git/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/auto_git/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/explicit_bin_with_git/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/auto_git/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/explicit_bin_with_git/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/auto_git/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit/in/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit/in/src/main.rs
index 65fdcf8da..65fdcf8da 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit/in/src/main.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit/in/src/main.rs
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit/mod.rs
index 326bd218a..326bd218a 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit/out/Cargo.toml
index dcdb8da2c..dcdb8da2c 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit/out/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit/out/src/main.rs
index 65fdcf8da..65fdcf8da 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit/out/src/main.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit/out/src/main.rs
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit/stderr.log
index 3847e4e4a..3847e4e4a 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/formats_source/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/formats_source/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit_nosrc/in/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/in/main.rs
index 65fdcf8da..65fdcf8da 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit_nosrc/in/main.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/in/main.rs
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit_nosrc/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/mod.rs
index 1f16fb659..1f16fb659 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit_nosrc/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit_nosrc/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/out/Cargo.toml
index 5c6c9158c..5c6c9158c 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit_nosrc/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit_nosrc/out/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/out/main.rs
index 65fdcf8da..65fdcf8da 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit_nosrc/out/main.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/out/main.rs
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit_nosrc/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/stderr.log
index 3847e4e4a..3847e4e4a 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_explicit_nosrc/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/fossil_autodetect/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/fossil_autodetect/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_explicit_nosrc/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit/in/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit/in/src/main.rs
index 65fdcf8da..65fdcf8da 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit/in/src/main.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit/in/src/main.rs
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit/mod.rs
index 12349a09b..12349a09b 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit/out/Cargo.toml
index dcdb8da2c..dcdb8da2c 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit/out/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit/out/src/main.rs
index 65fdcf8da..65fdcf8da 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit/out/src/main.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit/out/src/main.rs
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit/stderr.log
index 3847e4e4a..3847e4e4a 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/git_autodetect/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/git_autodetect/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namenosrc/in/case.rs b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/in/case.rs
index 65fdcf8da..65fdcf8da 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namenosrc/in/case.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/in/case.rs
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namenosrc/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/mod.rs
index fe65940db..fe65940db 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namenosrc/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namenosrc/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/out/Cargo.toml
index 8da5fe778..8da5fe778 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namenosrc/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namenosrc/out/case.rs b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/out/case.rs
index 65fdcf8da..65fdcf8da 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namenosrc/out/case.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/out/case.rs
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namenosrc/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/stderr.log
index 3847e4e4a..3847e4e4a 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namenosrc/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namenosrc/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namesrc/in/src/case.rs b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/in/src/case.rs
index 65fdcf8da..65fdcf8da 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namesrc/in/src/case.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/in/src/case.rs
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namesrc/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/mod.rs
index d3e8e66df..d3e8e66df 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namesrc/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namesrc/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/out/Cargo.toml
index dec0aaea9..dec0aaea9 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namesrc/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namesrc/out/src/case.rs b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/out/src/case.rs
index 65fdcf8da..65fdcf8da 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namesrc/out/src/case.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/out/src/case.rs
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namesrc/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/stderr.log
index 3847e4e4a..3847e4e4a 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_namesrc/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_namesrc/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_nosrc/in/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/in/main.rs
index 65fdcf8da..65fdcf8da 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_nosrc/in/main.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/in/main.rs
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_nosrc/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/mod.rs
index fe65940db..fe65940db 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_nosrc/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_nosrc/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/out/Cargo.toml
index 5c6c9158c..5c6c9158c 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_nosrc/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_nosrc/out/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/out/main.rs
index 65fdcf8da..65fdcf8da 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_nosrc/out/main.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/out/main.rs
diff --git a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_nosrc/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/stderr.log
index 3847e4e4a..3847e4e4a 100644
--- a/src/tools/cargo/tests/testsuite/init/bin_already_exists_implicit_nosrc/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/inferred_bin_with_git/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/inferred_bin_with_git/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/bin_already_exists_implicit_nosrc/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/both_lib_and_bin/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/both_lib_and_bin/mod.rs
index c9232320a..c9232320a 100644
--- a/src/tools/cargo/tests/testsuite/init/both_lib_and_bin/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/both_lib_and_bin/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/both_lib_and_bin/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/both_lib_and_bin/stderr.log
index 9d635a427..9d635a427 100644
--- a/src/tools/cargo/tests/testsuite/init/both_lib_and_bin/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/both_lib_and_bin/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/inferred_lib_with_git/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/both_lib_and_bin/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/inferred_lib_with_git/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/both_lib_and_bin/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/cant_create_library_when_both_binlib_present/in/case.rs b/src/tools/cargo/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/in/case.rs
index f328e4d9d..f328e4d9d 100644
--- a/src/tools/cargo/tests/testsuite/init/cant_create_library_when_both_binlib_present/in/case.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/in/case.rs
diff --git a/src/tools/cargo/tests/testsuite/init/cant_create_library_when_both_binlib_present/in/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/in/lib.rs
index 59760b549..59760b549 100644
--- a/src/tools/cargo/tests/testsuite/init/cant_create_library_when_both_binlib_present/in/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/in/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/cant_create_library_when_both_binlib_present/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/mod.rs
index 5e9e1b94c..5e9e1b94c 100644
--- a/src/tools/cargo/tests/testsuite/init/cant_create_library_when_both_binlib_present/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/cant_create_library_when_both_binlib_present/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/stderr.log
index c08dce96b..c08dce96b 100644
--- a/src/tools/cargo/tests/testsuite/init/cant_create_library_when_both_binlib_present/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/cant_create_library_when_both_binlib_present/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/in/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/in/lib.rs
index 321163744..321163744 100644
--- a/src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/in/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/in/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/in/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/in/src/lib.rs
index f71455a1a..f71455a1a 100644
--- a/src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/in/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/in/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/mod.rs
index d1cba2ff7..d1cba2ff7 100644
--- a/src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/out/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/out/lib.rs
index 321163744..321163744 100644
--- a/src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/out/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/out/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/out/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/out/src/lib.rs
index f71455a1a..f71455a1a 100644
--- a/src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/out/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/out/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/stderr.log
index 8dbd2aaf0..8dbd2aaf0 100644
--- a/src/tools/cargo/tests/testsuite/init/confused_by_multiple_lib_files/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/invalid_dir_name/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/invalid_dir_name/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/confused_by_multiple_lib_files/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/in/case.rs b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/in/case.rs
index f328e4d9d..f328e4d9d 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/in/case.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/in/case.rs
diff --git a/src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/in/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/in/lib.rs
index 59760b549..59760b549 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/in/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/in/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/mod.rs
index 326bd218a..326bd218a 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/out/Cargo.toml
index 675c888a5..675c888a5 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/out/case.rs b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/out/case.rs
index f328e4d9d..f328e4d9d 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/out/case.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/out/case.rs
diff --git a/src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/out/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/out/lib.rs
index 59760b549..59760b549 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/out/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/out/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/stderr.log
index 3847e4e4a..3847e4e4a 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_binary_when_both_binlib_present/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/lib_already_exists_nosrc/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/lib_already_exists_nosrc/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_both_binlib_present/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/in/case.rs b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/in/case.rs
index 59760b549..59760b549 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/in/case.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/in/case.rs
diff --git a/src/tools/cargo/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/mod.rs
index 326bd218a..326bd218a 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/out/Cargo.toml
index 8da5fe778..8da5fe778 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/out/case.rs b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/out/case.rs
index 59760b549..59760b549 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/out/case.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/out/case.rs
diff --git a/src/tools/cargo/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/stderr.log
index ec428f31c..ec428f31c 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_binary_when_instructed_and_has_lib_file/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/lib_already_exists_src/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/lib_already_exists_src/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_binary_when_instructed_and_has_lib_file/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/in/case.rs b/src/tools/cargo/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/in/case.rs
index f328e4d9d..f328e4d9d 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/in/case.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/in/case.rs
diff --git a/src/tools/cargo/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/mod.rs
index 59c192cb9..59c192cb9 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/out/Cargo.toml
index 2c0464468..2c0464468 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/out/case.rs b/src/tools/cargo/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/out/case.rs
index f328e4d9d..f328e4d9d 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/out/case.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/out/case.rs
diff --git a/src/tools/cargo/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/stderr.log
index bf070e2da..bf070e2da 100644
--- a/src/tools/cargo/tests/testsuite/init/creates_library_when_instructed_and_has_bin_file/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/mercurial_autodetect/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/mercurial_autodetect/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/creates_library_when_instructed_and_has_bin_file/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/empty_dir/.keep b/src/tools/cargo/tests/testsuite/cargo_init/empty_dir/.keep
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/empty_dir/.keep
+++ b/src/tools/cargo/tests/testsuite/cargo_init/empty_dir/.keep
diff --git a/src/tools/cargo/tests/testsuite/init/empty_dir/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/empty_dir/mod.rs
index 074954f01..074954f01 100644
--- a/src/tools/cargo/tests/testsuite/init/empty_dir/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/empty_dir/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/explicit_bin_with_git/in b/src/tools/cargo/tests/testsuite/cargo_init/explicit_bin_with_git/in
index 1202506b6..1202506b6 120000
--- a/src/tools/cargo/tests/testsuite/init/explicit_bin_with_git/in
+++ b/src/tools/cargo/tests/testsuite/cargo_init/explicit_bin_with_git/in
diff --git a/src/tools/cargo/tests/testsuite/init/explicit_bin_with_git/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/explicit_bin_with_git/mod.rs
index 7314e955c..7314e955c 100644
--- a/src/tools/cargo/tests/testsuite/init/explicit_bin_with_git/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/explicit_bin_with_git/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/explicit_bin_with_git/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/explicit_bin_with_git/out/Cargo.toml
index dcdb8da2c..dcdb8da2c 100644
--- a/src/tools/cargo/tests/testsuite/init/explicit_bin_with_git/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/explicit_bin_with_git/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/explicit_bin_with_git/out/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/explicit_bin_with_git/out/src/main.rs
index e7a11a969..e7a11a969 100644
--- a/src/tools/cargo/tests/testsuite/init/explicit_bin_with_git/out/src/main.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/explicit_bin_with_git/out/src/main.rs
diff --git a/src/tools/cargo/tests/testsuite/init/explicit_bin_with_git/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/explicit_bin_with_git/stderr.log
index 3847e4e4a..3847e4e4a 100644
--- a/src/tools/cargo/tests/testsuite/init/explicit_bin_with_git/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/explicit_bin_with_git/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/explicit_bin_with_git/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/explicit_bin_with_git/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/formats_source/in/rustfmt.toml b/src/tools/cargo/tests/testsuite/cargo_init/formats_source/in/rustfmt.toml
index b196eaa2d..b196eaa2d 100644
--- a/src/tools/cargo/tests/testsuite/init/formats_source/in/rustfmt.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/formats_source/in/rustfmt.toml
diff --git a/src/tools/cargo/tests/testsuite/init/formats_source/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/formats_source/mod.rs
index ac1fb6271..ac1fb6271 100644
--- a/src/tools/cargo/tests/testsuite/init/formats_source/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/formats_source/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/formats_source/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/formats_source/out/Cargo.toml
index dcdb8da2c..dcdb8da2c 100644
--- a/src/tools/cargo/tests/testsuite/init/formats_source/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/formats_source/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/formats_source/out/rustfmt.toml b/src/tools/cargo/tests/testsuite/cargo_init/formats_source/out/rustfmt.toml
index b196eaa2d..b196eaa2d 100644
--- a/src/tools/cargo/tests/testsuite/init/formats_source/out/rustfmt.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/formats_source/out/rustfmt.toml
diff --git a/src/tools/cargo/tests/testsuite/init/formats_source/out/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/formats_source/out/src/lib.rs
index 3b9acffd5..3b9acffd5 100644
--- a/src/tools/cargo/tests/testsuite/init/formats_source/out/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/formats_source/out/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/formats_source/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/formats_source/stderr.log
index f459bf226..f459bf226 100644
--- a/src/tools/cargo/tests/testsuite/init/formats_source/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/formats_source/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/no_filename/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/formats_source/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/no_filename/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/formats_source/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/fossil_autodetect/in/.fossil/.keep b/src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/in/.fossil/.keep
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/fossil_autodetect/in/.fossil/.keep
+++ b/src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/in/.fossil/.keep
diff --git a/src/tools/cargo/tests/testsuite/init/fossil_autodetect/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/mod.rs
index d45ba868a..d45ba868a 100644
--- a/src/tools/cargo/tests/testsuite/init/fossil_autodetect/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/fossil_autodetect/out/.fossil-settings/clean-glob b/src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/out/.fossil-settings/clean-glob
index a9d37c560..a9d37c560 100644
--- a/src/tools/cargo/tests/testsuite/init/fossil_autodetect/out/.fossil-settings/clean-glob
+++ b/src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/out/.fossil-settings/clean-glob
diff --git a/src/tools/cargo/tests/testsuite/init/fossil_autodetect/out/.fossil-settings/ignore-glob b/src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/out/.fossil-settings/ignore-glob
index a9d37c560..a9d37c560 100644
--- a/src/tools/cargo/tests/testsuite/init/fossil_autodetect/out/.fossil-settings/ignore-glob
+++ b/src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/out/.fossil-settings/ignore-glob
diff --git a/src/tools/cargo/tests/testsuite/init/fossil_autodetect/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/out/Cargo.toml
index dcdb8da2c..dcdb8da2c 100644
--- a/src/tools/cargo/tests/testsuite/init/fossil_autodetect/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/fossil_autodetect/out/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/out/src/lib.rs
index 7d12d9af8..7d12d9af8 100644
--- a/src/tools/cargo/tests/testsuite/init/fossil_autodetect/out/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/out/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/fossil_autodetect/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/stderr.log
index f459bf226..f459bf226 100644
--- a/src/tools/cargo/tests/testsuite/init/fossil_autodetect/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/path_contains_separator/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/path_contains_separator/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/fossil_autodetect/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/git_autodetect/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/git_autodetect/mod.rs
index aef47bc7d..aef47bc7d 100644
--- a/src/tools/cargo/tests/testsuite/init/git_autodetect/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/git_autodetect/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/git_autodetect/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/git_autodetect/out/Cargo.toml
index 1d9cfe317..1d9cfe317 100644
--- a/src/tools/cargo/tests/testsuite/init/git_autodetect/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/git_autodetect/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/git_autodetect/out/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/git_autodetect/out/src/lib.rs
index 7d12d9af8..7d12d9af8 100644
--- a/src/tools/cargo/tests/testsuite/init/git_autodetect/out/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/git_autodetect/out/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/git_autodetect/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/git_autodetect/stderr.log
index f459bf226..f459bf226 100644
--- a/src/tools/cargo/tests/testsuite/init/git_autodetect/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/git_autodetect/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/pijul_autodetect/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/git_autodetect/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/pijul_autodetect/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/git_autodetect/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/mod.rs
index cd4437c65..cd4437c65 100644
--- a/src/tools/cargo/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/out/Cargo.toml
index a6269fdcd..a6269fdcd 100644
--- a/src/tools/cargo/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/out/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/out/src/lib.rs
index 7d12d9af8..7d12d9af8 100644
--- a/src/tools/cargo/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/out/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/out/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/stderr.log
index f459bf226..f459bf226 100644
--- a/src/tools/cargo/tests/testsuite/init/git_ignore_exists_no_conflicting_entries/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/reserved_name/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/reserved_name/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/git_ignore_exists_no_conflicting_entries/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/help/mod.rs
new file mode 100644
index 000000000..7f00d347a
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("init")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/in/README.md b/src/tools/cargo/tests/testsuite/cargo_init/help/stderr.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/in/README.md
+++ b/src/tools/cargo/tests/testsuite/cargo_init/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/help/stdout.log
new file mode 100644
index 000000000..5dfb02498
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/help/stdout.log
@@ -0,0 +1,31 @@
+Create a new cargo package in an existing directory
+
+Usage: cargo[EXE] init [OPTIONS] [path]
+
+Arguments:
+ [path] [default: .]
+
+Options:
+ --vcs <VCS> Initialize a new repository for the given version control system (git,
+ hg, pijul, or fossil) or do not initialize any version control at all
+ (none), overriding a global configuration. [possible values: git, hg,
+ pijul, fossil, none]
+ --bin Use a binary (application) template [default]
+ --lib Use a library template
+ --edition <YEAR> Edition to set for the crate generated [possible values: 2015, 2018,
+ 2021]
+ --name <NAME> Set the resulting package name, defaults to the directory name
+ --registry <REGISTRY> Registry to use
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Manifest Options:
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help init` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/in/rustfmt.toml b/src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/in/rustfmt.toml
index b196eaa2d..b196eaa2d 100644
--- a/src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/in/rustfmt.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/in/rustfmt.toml
diff --git a/src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/mod.rs
index fd9394049..fd9394049 100644
--- a/src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/out/Cargo.toml
index dcdb8da2c..dcdb8da2c 100644
--- a/src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/out/rustfmt.toml b/src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/out/rustfmt.toml
index b196eaa2d..b196eaa2d 100644
--- a/src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/out/rustfmt.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/out/rustfmt.toml
diff --git a/src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/out/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/out/src/lib.rs
index 7d12d9af8..7d12d9af8 100644
--- a/src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/out/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/out/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/stderr.log
index f459bf226..f459bf226 100644
--- a/src/tools/cargo/tests/testsuite/init/ignores_failure_to_format_source/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/simple_bin/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_bin/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/ignores_failure_to_format_source/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/inferred_bin_with_git/in/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/inferred_bin_with_git/in/main.rs
index f328e4d9d..f328e4d9d 100644
--- a/src/tools/cargo/tests/testsuite/init/inferred_bin_with_git/in/main.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inferred_bin_with_git/in/main.rs
diff --git a/src/tools/cargo/tests/testsuite/init/inferred_bin_with_git/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/inferred_bin_with_git/mod.rs
index 80bec8893..80bec8893 100644
--- a/src/tools/cargo/tests/testsuite/init/inferred_bin_with_git/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inferred_bin_with_git/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/inferred_bin_with_git/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/inferred_bin_with_git/out/Cargo.toml
index 5c6c9158c..5c6c9158c 100644
--- a/src/tools/cargo/tests/testsuite/init/inferred_bin_with_git/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inferred_bin_with_git/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/inferred_bin_with_git/out/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/inferred_bin_with_git/out/main.rs
index f328e4d9d..f328e4d9d 100644
--- a/src/tools/cargo/tests/testsuite/init/inferred_bin_with_git/out/main.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inferred_bin_with_git/out/main.rs
diff --git a/src/tools/cargo/tests/testsuite/init/inferred_bin_with_git/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/inferred_bin_with_git/stderr.log
index 3847e4e4a..3847e4e4a 100644
--- a/src/tools/cargo/tests/testsuite/init/inferred_bin_with_git/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inferred_bin_with_git/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/simple_git/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/inferred_bin_with_git/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_git/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inferred_bin_with_git/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/inferred_lib_with_git/in/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/inferred_lib_with_git/in/lib.rs
index 59760b549..59760b549 100644
--- a/src/tools/cargo/tests/testsuite/init/inferred_lib_with_git/in/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inferred_lib_with_git/in/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/inferred_lib_with_git/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/inferred_lib_with_git/mod.rs
index 80bec8893..80bec8893 100644
--- a/src/tools/cargo/tests/testsuite/init/inferred_lib_with_git/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inferred_lib_with_git/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/inferred_lib_with_git/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/inferred_lib_with_git/out/Cargo.toml
index 39e95fe94..39e95fe94 100644
--- a/src/tools/cargo/tests/testsuite/init/inferred_lib_with_git/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inferred_lib_with_git/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/inferred_lib_with_git/out/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/inferred_lib_with_git/out/lib.rs
index 59760b549..59760b549 100644
--- a/src/tools/cargo/tests/testsuite/init/inferred_lib_with_git/out/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inferred_lib_with_git/out/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/inferred_lib_with_git/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/inferred_lib_with_git/stderr.log
index f459bf226..f459bf226 100644
--- a/src/tools/cargo/tests/testsuite/init/inferred_lib_with_git/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inferred_lib_with_git/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/simple_git_ignore_exists/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/inferred_lib_with_git/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_git_ignore_exists/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inferred_lib_with_git/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/in/Cargo.toml
index b7a2e9036..b7a2e9036 100644
--- a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/in/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/in/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/lib_already_exists_nosrc/in/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/in/README.md
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/lib_already_exists_nosrc/in/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/in/README.md
diff --git a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/in/crates/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/in/crates/foo/src/main.rs
index 43f0dac5e..43f0dac5e 100644
--- a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/in/crates/foo/src/main.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/in/crates/foo/src/main.rs
diff --git a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/in/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/in/src/lib.rs
index 7d12d9af8..7d12d9af8 100644
--- a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/in/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/in/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/mod.rs
index 4e1dda845..4e1dda845 100644
--- a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/out/Cargo.toml
index b7a2e9036..b7a2e9036 100644
--- a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/out/crates/foo/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/out/crates/foo/Cargo.toml
index 137ed1c87..137ed1c87 100644
--- a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/out/crates/foo/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/out/crates/foo/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/out/crates/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/out/crates/foo/src/main.rs
index 43f0dac5e..43f0dac5e 100644
--- a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/out/crates/foo/src/main.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/out/crates/foo/src/main.rs
diff --git a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/out/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/out/src/lib.rs
index 7d12d9af8..7d12d9af8 100644
--- a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/out/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/out/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/stderr.log
index 3847e4e4a..3847e4e4a 100644
--- a/src/tools/cargo/tests/testsuite/init/inherit_workspace_package_table/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/simple_hg/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_hg/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/inherit_workspace_package_table/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/invalid_dir_name/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/invalid_dir_name/mod.rs
index 2b1be9022..2b1be9022 100644
--- a/src/tools/cargo/tests/testsuite/init/invalid_dir_name/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/invalid_dir_name/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/invalid_dir_name/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/invalid_dir_name/stderr.log
index 86d2c665f..86d2c665f 100644
--- a/src/tools/cargo/tests/testsuite/init/invalid_dir_name/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/invalid_dir_name/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/simple_hg_ignore_exists/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/invalid_dir_name/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_hg_ignore_exists/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/invalid_dir_name/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/lib_already_exists_nosrc/out/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_nosrc/in/lib.rs
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/lib_already_exists_nosrc/out/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_nosrc/in/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/lib_already_exists_nosrc/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_nosrc/mod.rs
index d3e8e66df..d3e8e66df 100644
--- a/src/tools/cargo/tests/testsuite/init/lib_already_exists_nosrc/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_nosrc/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/lib_already_exists_nosrc/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_nosrc/out/Cargo.toml
index 39e95fe94..39e95fe94 100644
--- a/src/tools/cargo/tests/testsuite/init/lib_already_exists_nosrc/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_nosrc/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/path_contains_separator/in/.keep b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_nosrc/out/lib.rs
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/path_contains_separator/in/.keep
+++ b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_nosrc/out/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/lib_already_exists_nosrc/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_nosrc/stderr.log
index f459bf226..f459bf226 100644
--- a/src/tools/cargo/tests/testsuite/init/lib_already_exists_nosrc/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_nosrc/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/simple_lib/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_nosrc/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_lib/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_nosrc/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/lib_already_exists_src/in/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_src/in/src/lib.rs
index 59760b549..59760b549 100644
--- a/src/tools/cargo/tests/testsuite/init/lib_already_exists_src/in/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_src/in/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/lib_already_exists_src/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_src/mod.rs
index d3e8e66df..d3e8e66df 100644
--- a/src/tools/cargo/tests/testsuite/init/lib_already_exists_src/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_src/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/lib_already_exists_src/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_src/out/Cargo.toml
index dcdb8da2c..dcdb8da2c 100644
--- a/src/tools/cargo/tests/testsuite/init/lib_already_exists_src/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_src/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/lib_already_exists_src/out/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_src/out/src/lib.rs
index 59760b549..59760b549 100644
--- a/src/tools/cargo/tests/testsuite/init/lib_already_exists_src/out/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_src/out/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/lib_already_exists_src/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_src/stderr.log
index f459bf226..f459bf226 100644
--- a/src/tools/cargo/tests/testsuite/init/lib_already_exists_src/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_src/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/unknown_flags/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_src/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/unknown_flags/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/lib_already_exists_src/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/mercurial_autodetect/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/mercurial_autodetect/mod.rs
index d45ba868a..d45ba868a 100644
--- a/src/tools/cargo/tests/testsuite/init/mercurial_autodetect/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/mercurial_autodetect/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/mercurial_autodetect/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/mercurial_autodetect/out/Cargo.toml
index dcdb8da2c..dcdb8da2c 100644
--- a/src/tools/cargo/tests/testsuite/init/mercurial_autodetect/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/mercurial_autodetect/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/mercurial_autodetect/out/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/mercurial_autodetect/out/src/lib.rs
index 7d12d9af8..7d12d9af8 100644
--- a/src/tools/cargo/tests/testsuite/init/mercurial_autodetect/out/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/mercurial_autodetect/out/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/mercurial_autodetect/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/mercurial_autodetect/stderr.log
index f459bf226..f459bf226 100644
--- a/src/tools/cargo/tests/testsuite/init/mercurial_autodetect/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/mercurial_autodetect/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/with_argument/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/mercurial_autodetect/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/with_argument/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/mercurial_autodetect/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/mod.rs
index 7853a1a0b..a1988a06a 100644
--- a/src/tools/cargo/tests/testsuite/init/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/mod.rs
@@ -18,6 +18,7 @@ mod formats_source;
mod fossil_autodetect;
mod git_autodetect;
mod git_ignore_exists_no_conflicting_entries;
+mod help;
mod ignores_failure_to_format_source;
mod inferred_bin_with_git;
mod inferred_lib_with_git;
diff --git a/src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/in/case.rs b/src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/in/case.rs
index b31221118..b31221118 100644
--- a/src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/in/case.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/in/case.rs
diff --git a/src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/in/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/in/main.rs
index 7937627b9..7937627b9 100644
--- a/src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/in/main.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/in/main.rs
diff --git a/src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/mod.rs
index fdd4476d9..fdd4476d9 100644
--- a/src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/out/case.rs b/src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/out/case.rs
index b31221118..b31221118 100644
--- a/src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/out/case.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/out/case.rs
diff --git a/src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/out/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/out/main.rs
index 7937627b9..7937627b9 100644
--- a/src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/out/main.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/out/main.rs
diff --git a/src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/stderr.log
index 21a1dabee..21a1dabee 100644
--- a/src/tools/cargo/tests/testsuite/init/multibin_project_name_clash/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/pijul_autodetect/in/.pijul/.keep b/src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/pijul_autodetect/in/.pijul/.keep
+++ b/src/tools/cargo/tests/testsuite/cargo_init/multibin_project_name_clash/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/no_filename/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/no_filename/mod.rs
index 8edfd2823..8edfd2823 100644
--- a/src/tools/cargo/tests/testsuite/init/no_filename/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/no_filename/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/no_filename/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/no_filename/stderr.log
index bd087ec90..bd087ec90 100644
--- a/src/tools/cargo/tests/testsuite/init/no_filename/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/no_filename/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/init/with_argument/in/foo/.keep b/src/tools/cargo/tests/testsuite/cargo_init/no_filename/stdout.log
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/init/with_argument/in/foo/.keep
+++ b/src/tools/cargo/tests/testsuite/cargo_init/no_filename/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/in/.keep b/src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/in/.keep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/in/.keep
diff --git a/src/tools/cargo/tests/testsuite/init/path_contains_separator/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/mod.rs
index 0a12f4269..0a12f4269 100644
--- a/src/tools/cargo/tests/testsuite/init/path_contains_separator/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/path_contains_separator/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/out/Cargo.toml
index 11465f1fc..11465f1fc 100644
--- a/src/tools/cargo/tests/testsuite/init/path_contains_separator/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/path_contains_separator/out/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/out/src/main.rs
index e7a11a969..e7a11a969 100644
--- a/src/tools/cargo/tests/testsuite/init/path_contains_separator/out/src/main.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/out/src/main.rs
diff --git a/src/tools/cargo/tests/testsuite/init/path_contains_separator/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/stderr.log
index d7947aea2..d7947aea2 100644
--- a/src/tools/cargo/tests/testsuite/init/path_contains_separator/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/path_contains_separator/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/in/.pijul/.keep b/src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/in/.pijul/.keep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/in/.pijul/.keep
diff --git a/src/tools/cargo/tests/testsuite/init/pijul_autodetect/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/mod.rs
index d45ba868a..d45ba868a 100644
--- a/src/tools/cargo/tests/testsuite/init/pijul_autodetect/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/pijul_autodetect/out/.ignore b/src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/out/.ignore
index 4fffb2f89..4fffb2f89 100644
--- a/src/tools/cargo/tests/testsuite/init/pijul_autodetect/out/.ignore
+++ b/src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/out/.ignore
diff --git a/src/tools/cargo/tests/testsuite/init/pijul_autodetect/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/out/Cargo.toml
index dcdb8da2c..dcdb8da2c 100644
--- a/src/tools/cargo/tests/testsuite/init/pijul_autodetect/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/pijul_autodetect/out/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/out/src/lib.rs
index 7d12d9af8..7d12d9af8 100644
--- a/src/tools/cargo/tests/testsuite/init/pijul_autodetect/out/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/out/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/pijul_autodetect/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/stderr.log
index f459bf226..f459bf226 100644
--- a/src/tools/cargo/tests/testsuite/init/pijul_autodetect/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/pijul_autodetect/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/reserved_name/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/reserved_name/mod.rs
index cc65fd0a1..cc65fd0a1 100644
--- a/src/tools/cargo/tests/testsuite/init/reserved_name/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/reserved_name/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/reserved_name/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/reserved_name/stderr.log
index 748971bdf..748971bdf 100644
--- a/src/tools/cargo/tests/testsuite/init/reserved_name/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/reserved_name/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/reserved_name/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/reserved_name/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/reserved_name/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/simple_bin/in b/src/tools/cargo/tests/testsuite/cargo_init/simple_bin/in
index 1202506b6..1202506b6 120000
--- a/src/tools/cargo/tests/testsuite/init/simple_bin/in
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_bin/in
diff --git a/src/tools/cargo/tests/testsuite/init/simple_bin/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/simple_bin/mod.rs
index eaf0955f9..eaf0955f9 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_bin/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_bin/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/simple_bin/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/simple_bin/out/Cargo.toml
index a6269fdcd..a6269fdcd 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_bin/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_bin/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/simple_bin/out/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/simple_bin/out/src/main.rs
index e7a11a969..e7a11a969 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_bin/out/src/main.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_bin/out/src/main.rs
diff --git a/src/tools/cargo/tests/testsuite/init/simple_bin/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/simple_bin/stderr.log
index 3847e4e4a..3847e4e4a 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_bin/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_bin/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/simple_bin/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/simple_bin/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_bin/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/simple_git/in b/src/tools/cargo/tests/testsuite/cargo_init/simple_git/in
index 1202506b6..1202506b6 120000
--- a/src/tools/cargo/tests/testsuite/init/simple_git/in
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_git/in
diff --git a/src/tools/cargo/tests/testsuite/init/simple_git/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/simple_git/mod.rs
index c373fe2a2..c373fe2a2 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_git/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_git/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/simple_git/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/simple_git/out/Cargo.toml
index dcdb8da2c..dcdb8da2c 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_git/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_git/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/simple_git/out/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/simple_git/out/src/lib.rs
index 7d12d9af8..7d12d9af8 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_git/out/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_git/out/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/simple_git/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/simple_git/stderr.log
index f459bf226..f459bf226 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_git/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_git/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/simple_git/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/simple_git/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_git/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/simple_git_ignore_exists/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/simple_git_ignore_exists/mod.rs
index 142e86efd..142e86efd 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_git_ignore_exists/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_git_ignore_exists/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/simple_git_ignore_exists/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/simple_git_ignore_exists/out/Cargo.toml
index a6269fdcd..a6269fdcd 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_git_ignore_exists/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_git_ignore_exists/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/simple_git_ignore_exists/out/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/simple_git_ignore_exists/out/src/lib.rs
index 7d12d9af8..7d12d9af8 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_git_ignore_exists/out/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_git_ignore_exists/out/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/simple_git_ignore_exists/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/simple_git_ignore_exists/stderr.log
index f459bf226..f459bf226 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_git_ignore_exists/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_git_ignore_exists/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/simple_git_ignore_exists/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/simple_git_ignore_exists/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_git_ignore_exists/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/simple_hg/in b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg/in
index 1202506b6..1202506b6 120000
--- a/src/tools/cargo/tests/testsuite/init/simple_hg/in
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg/in
diff --git a/src/tools/cargo/tests/testsuite/init/simple_hg/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg/mod.rs
index 1d6765453..1d6765453 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_hg/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/simple_hg/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg/out/Cargo.toml
index dcdb8da2c..dcdb8da2c 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_hg/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/simple_hg/out/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg/out/src/lib.rs
index 7d12d9af8..7d12d9af8 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_hg/out/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg/out/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/simple_hg/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg/stderr.log
index f459bf226..f459bf226 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_hg/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/simple_hg/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/simple_hg_ignore_exists/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg_ignore_exists/mod.rs
index d45ba868a..d45ba868a 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_hg_ignore_exists/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg_ignore_exists/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/simple_hg_ignore_exists/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg_ignore_exists/out/Cargo.toml
index dcdb8da2c..dcdb8da2c 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_hg_ignore_exists/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg_ignore_exists/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/simple_hg_ignore_exists/out/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg_ignore_exists/out/src/lib.rs
index 7d12d9af8..7d12d9af8 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_hg_ignore_exists/out/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg_ignore_exists/out/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/simple_hg_ignore_exists/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg_ignore_exists/stderr.log
index f459bf226..f459bf226 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_hg_ignore_exists/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg_ignore_exists/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/simple_hg_ignore_exists/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg_ignore_exists/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_hg_ignore_exists/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/simple_lib/in b/src/tools/cargo/tests/testsuite/cargo_init/simple_lib/in
index 1202506b6..1202506b6 120000
--- a/src/tools/cargo/tests/testsuite/init/simple_lib/in
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_lib/in
diff --git a/src/tools/cargo/tests/testsuite/init/simple_lib/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/simple_lib/mod.rs
index d6bae5167..d6bae5167 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_lib/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_lib/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/simple_lib/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/simple_lib/out/Cargo.toml
index a6269fdcd..a6269fdcd 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_lib/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_lib/out/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/simple_lib/out/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_init/simple_lib/out/src/lib.rs
index 7d12d9af8..7d12d9af8 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_lib/out/src/lib.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_lib/out/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/init/simple_lib/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/simple_lib/stderr.log
index f459bf226..f459bf226 100644
--- a/src/tools/cargo/tests/testsuite/init/simple_lib/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_lib/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/simple_lib/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/simple_lib/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/simple_lib/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/init/unknown_flags/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/mod.rs
index 4289b4b9e..4289b4b9e 100644
--- a/src/tools/cargo/tests/testsuite/init/unknown_flags/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/unknown_flags/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/stderr.log
index 980e8acd8..980e8acd8 100644
--- a/src/tools/cargo/tests/testsuite/init/unknown_flags/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/with_argument/in/foo/.keep b/src/tools/cargo/tests/testsuite/cargo_init/with_argument/in/foo/.keep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/with_argument/in/foo/.keep
diff --git a/src/tools/cargo/tests/testsuite/init/with_argument/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/with_argument/mod.rs
index 0b5e342a1..0b5e342a1 100644
--- a/src/tools/cargo/tests/testsuite/init/with_argument/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/with_argument/mod.rs
diff --git a/src/tools/cargo/tests/testsuite/init/with_argument/out/foo/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/with_argument/out/foo/Cargo.toml
index 1d9cfe317..1d9cfe317 100644
--- a/src/tools/cargo/tests/testsuite/init/with_argument/out/foo/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_init/with_argument/out/foo/Cargo.toml
diff --git a/src/tools/cargo/tests/testsuite/init/with_argument/out/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/with_argument/out/foo/src/main.rs
index e7a11a969..e7a11a969 100644
--- a/src/tools/cargo/tests/testsuite/init/with_argument/out/foo/src/main.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/with_argument/out/foo/src/main.rs
diff --git a/src/tools/cargo/tests/testsuite/init/with_argument/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/with_argument/stderr.log
index 3847e4e4a..3847e4e4a 100644
--- a/src/tools/cargo/tests/testsuite/init/with_argument/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/with_argument/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/with_argument/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/with_argument/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/with_argument/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_install/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_install/help/mod.rs
new file mode 100644
index 000000000..a2c1c724b
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_install/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("install")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_install/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_install/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_install/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_install/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_install/help/stdout.log
new file mode 100644
index 000000000..a07fa47f6
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_install/help/stdout.log
@@ -0,0 +1,56 @@
+Install a Rust binary. Default location is $HOME/.cargo/bin
+
+Usage: cargo[EXE] install [OPTIONS] [crate]...
+
+Arguments:
+ [crate]...
+
+Options:
+ --version <VERSION> Specify a version to install
+ --index <INDEX> Registry index to install from
+ --registry <REGISTRY> Registry to use
+ --git <URL> Git URL to install the specified crate from
+ --branch <BRANCH> Branch to use when installing from git
+ --tag <TAG> Tag to use when installing from git
+ --rev <SHA> Specific commit to use when installing from git
+ --path <PATH> Filesystem path to local crate to install
+ --root <DIR> Directory to install packages into
+ -f, --force Force overwriting existing crates or binaries
+ --no-track Do not save tracking information
+ --list list all installed packages and their versions
+ --ignore-rust-version Ignore `rust-version` specification in packages
+ --message-format <FMT> Error format
+ -q, --quiet Do not print cargo log messages
+ --debug Build in debug mode (with the 'dev' profile) instead of release mode
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
+ details
+ -h, --help Print help
+
+Target Selection:
+ --bin [<NAME>] Install only the specified binary
+ --bins Install all binaries
+ --example [<NAME>] Install only the specified example
+ --examples Install all examples
+
+Feature Selection:
+ -F, --features <FEATURES> Space or comma separated list of features to activate
+ --all-features Activate all available features
+ --no-default-features Do not activate the `default` feature
+
+Compilation Options:
+ -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs.
+ --keep-going Do not abort the build as soon as there is an error (unstable)
+ --profile <PROFILE-NAME> Install artifacts with the specified profile
+ --target <TRIPLE> Build for the target triple
+ --target-dir <DIRECTORY> Directory for all generated artifacts
+ --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json
+
+Manifest Options:
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help install` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_install/mod.rs b/src/tools/cargo/tests/testsuite/cargo_install/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_install/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_locate_project/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_locate_project/help/mod.rs
new file mode 100644
index 000000000..f6b7e8eaf
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_locate_project/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("locate-project")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_locate_project/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_locate_project/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_locate_project/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_locate_project/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_locate_project/help/stdout.log
new file mode 100644
index 000000000..1c6ea7b25
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_locate_project/help/stdout.log
@@ -0,0 +1,22 @@
+Print a JSON representation of a Cargo.toml file's location
+
+Usage: cargo[EXE] locate-project [OPTIONS]
+
+Options:
+ --workspace Locate Cargo.toml of the workspace root
+ --message-format <FMT> Output representation [possible values: json, plain]
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
+ details
+ -h, --help Print help
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help locate-project` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_locate_project/mod.rs b/src/tools/cargo/tests/testsuite/cargo_locate_project/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_locate_project/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_login/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_login/help/mod.rs
new file mode 100644
index 000000000..86b95da15
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_login/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("login")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_login/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_login/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_login/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_login/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_login/help/stdout.log
new file mode 100644
index 000000000..faec55c18
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_login/help/stdout.log
@@ -0,0 +1,23 @@
+Log in to a registry.
+
+Usage: cargo[EXE] login [OPTIONS] [token] [-- [args]...]
+
+Arguments:
+ [token]
+ [args]... Arguments for the credential provider (unstable)
+
+Options:
+ --registry <REGISTRY> Registry to use
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Manifest Options:
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help login` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_login/mod.rs b/src/tools/cargo/tests/testsuite/cargo_login/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_login/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_logout/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_logout/help/mod.rs
new file mode 100644
index 000000000..f895b60dd
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_logout/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("logout")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_logout/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_logout/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_logout/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_logout/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_logout/help/stdout.log
new file mode 100644
index 000000000..fe328d765
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_logout/help/stdout.log
@@ -0,0 +1,19 @@
+Remove an API token from the registry locally
+
+Usage: cargo[EXE] logout [OPTIONS]
+
+Options:
+ --registry <REGISTRY> Registry to use
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Manifest Options:
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help logout` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_logout/mod.rs b/src/tools/cargo/tests/testsuite/cargo_logout/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_logout/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_metadata/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_metadata/help/mod.rs
new file mode 100644
index 000000000..a88c374fe
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_metadata/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("metadata")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_metadata/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_metadata/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_metadata/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_metadata/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_metadata/help/stdout.log
new file mode 100644
index 000000000..939fc40c9
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_metadata/help/stdout.log
@@ -0,0 +1,30 @@
+Output the resolved dependencies of a package, the concrete used versions including overrides, in
+machine-readable format
+
+Usage: cargo[EXE] metadata [OPTIONS]
+
+Options:
+ --filter-platform <TRIPLE> Only include resolve dependencies matching the given target-triple
+ --no-deps Output information only about the workspace members and don't
+ fetch dependencies
+ --format-version <VERSION> Format version [possible values: 1]
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
+ details
+ -h, --help Print help
+
+Feature Selection:
+ -F, --features <FEATURES> Space or comma separated list of features to activate
+ --all-features Activate all available features
+ --no-default-features Do not activate the `default` feature
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help metadata` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_metadata/mod.rs b/src/tools/cargo/tests/testsuite/cargo_metadata/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_metadata/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/help/mod.rs
new file mode 100644
index 000000000..6a1721deb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("new")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_new/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_new/help/stdout.log
new file mode 100644
index 000000000..7252e0da1
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/help/stdout.log
@@ -0,0 +1,31 @@
+Create a new cargo package at <path>
+
+Usage: cargo[EXE] new [OPTIONS] <path>
+
+Arguments:
+ <path>
+
+Options:
+ --vcs <VCS> Initialize a new repository for the given version control system (git,
+ hg, pijul, or fossil) or do not initialize any version control at all
+ (none), overriding a global configuration. [possible values: git, hg,
+ pijul, fossil, none]
+ --bin Use a binary (application) template [default]
+ --lib Use a library template
+ --edition <YEAR> Edition to set for the crate generated [possible values: 2015, 2018,
+ 2021]
+ --name <NAME> Set the resulting package name, defaults to the directory name
+ --registry <REGISTRY> Registry to use
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Manifest Options:
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help new` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/mod.rs
index e895cf883..887316395 100644
--- a/src/tools/cargo/tests/testsuite/cargo_new/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_new/mod.rs
@@ -1,3 +1,4 @@
+mod help;
mod inherit_workspace_lints;
mod inherit_workspace_package_table;
mod inherit_workspace_package_table_with_edition;
diff --git a/src/tools/cargo/tests/testsuite/cargo_owner/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_owner/help/mod.rs
new file mode 100644
index 000000000..20583e5b1
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_owner/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("owner")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_owner/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_owner/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_owner/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_owner/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_owner/help/stdout.log
new file mode 100644
index 000000000..3c8495ff0
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_owner/help/stdout.log
@@ -0,0 +1,27 @@
+Manage the owners of a crate on the registry
+
+Usage: cargo[EXE] owner [OPTIONS] [crate]
+
+Arguments:
+ [crate]
+
+Options:
+ -a, --add <LOGIN> Name of a user or team to invite as an owner
+ -r, --remove <LOGIN> Name of a user or team to remove as an owner
+ -l, --list List owners of a crate
+ --index <INDEX> Registry index to modify owners for
+ --token <TOKEN> API token to use when authenticating
+ --registry <REGISTRY> Registry to use
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Manifest Options:
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help owner` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_owner/mod.rs b/src/tools/cargo/tests/testsuite/cargo_owner/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_owner/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_package/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_package/help/mod.rs
new file mode 100644
index 000000000..4e2f28e4f
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_package/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("package")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_package/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_package/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_package/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_package/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_package/help/stdout.log
new file mode 100644
index 000000000..35e32f313
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_package/help/stdout.log
@@ -0,0 +1,39 @@
+Assemble the local package into a distributable tarball
+
+Usage: cargo[EXE] package [OPTIONS]
+
+Options:
+ -l, --list Print files included in a package without making one
+ --no-verify Don't verify the contents by building them
+ --no-metadata Ignore warnings about a lack of human-usable metadata
+ --allow-dirty Allow dirty working directories to be packaged
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Package Selection:
+ -p, --package [<SPEC>] Package(s) to assemble
+ --workspace Assemble all packages in the workspace
+ --exclude <SPEC> Don't assemble specified packages
+
+Feature Selection:
+ -F, --features <FEATURES> Space or comma separated list of features to activate
+ --all-features Activate all available features
+ --no-default-features Do not activate the `default` feature
+
+Compilation Options:
+ --target <TRIPLE> Build for the target triple
+ --target-dir <DIRECTORY> Directory for all generated artifacts
+ -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs.
+ --keep-going Do not abort the build as soon as there is an error (unstable)
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help package` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_package/mod.rs b/src/tools/cargo/tests/testsuite/cargo_package/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_package/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_pkgid/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_pkgid/help/mod.rs
new file mode 100644
index 000000000..6d182d116
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_pkgid/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("pkgid")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_pkgid/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_pkgid/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_pkgid/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_pkgid/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_pkgid/help/stdout.log
new file mode 100644
index 000000000..ed48bb7ea
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_pkgid/help/stdout.log
@@ -0,0 +1,25 @@
+Print a fully qualified package specification
+
+Usage: cargo[EXE] pkgid [OPTIONS] [spec]
+
+Arguments:
+ [spec]
+
+Options:
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Package Selection:
+ -p, --package [<SPEC>] Argument to get the package ID specifier for
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help pkgid` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_pkgid/mod.rs b/src/tools/cargo/tests/testsuite/cargo_pkgid/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_pkgid/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_publish/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_publish/help/mod.rs
new file mode 100644
index 000000000..183b6aac4
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_publish/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("publish")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_publish/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_publish/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_publish/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_publish/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_publish/help/stdout.log
new file mode 100644
index 000000000..c02522887
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_publish/help/stdout.log
@@ -0,0 +1,39 @@
+Upload a package to the registry
+
+Usage: cargo[EXE] publish [OPTIONS]
+
+Options:
+ --dry-run Perform all checks without uploading
+ --index <INDEX> Registry index URL to upload the package to
+ --registry <REGISTRY> Registry to publish to
+ --token <TOKEN> Token to use when uploading
+ --no-verify Don't verify the contents by building them
+ --allow-dirty Allow dirty working directories to be packaged
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Package Selection:
+ -p, --package [<SPEC>] Package to publish
+
+Feature Selection:
+ -F, --features <FEATURES> Space or comma separated list of features to activate
+ --all-features Activate all available features
+ --no-default-features Do not activate the `default` feature
+
+Compilation Options:
+ -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs.
+ --keep-going Do not abort the build as soon as there is an error (unstable)
+ --target <TRIPLE> Build for the target triple
+ --target-dir <DIRECTORY> Directory for all generated artifacts
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help publish` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_publish/mod.rs b/src/tools/cargo/tests/testsuite/cargo_publish/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_publish/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_read_manifest/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_read_manifest/help/mod.rs
new file mode 100644
index 000000000..d0055f6d8
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_read_manifest/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("read-manifest")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_read_manifest/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_read_manifest/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_read_manifest/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_read_manifest/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_read_manifest/help/stdout.log
new file mode 100644
index 000000000..83db5413d
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_read_manifest/help/stdout.log
@@ -0,0 +1,19 @@
+Print a JSON representation of a Cargo.toml manifest.
+
+Deprecated, use `cargo metadata --no-deps` instead.
+
+Usage: cargo[EXE] read-manifest [OPTIONS]
+
+Options:
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
diff --git a/src/tools/cargo/tests/testsuite/cargo_read_manifest/mod.rs b/src/tools/cargo/tests/testsuite/cargo_read_manifest/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_read_manifest/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/avoid_empty_tables/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/avoid_empty_tables/stderr.log
index dd71023a8..486ef359f 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/avoid_empty_tables/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/avoid_empty_tables/stderr.log
@@ -1,2 +1 @@
Removing clippy from dependencies
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/build/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/build/stderr.log
index f037ebe28..a51bea48c 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/build/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/build/stderr.log
@@ -1,2 +1 @@
Removing semver from build-dependencies
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/dev/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/dev/stderr.log
index c629b26b1..ccabdb193 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/dev/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/dev/stderr.log
@@ -1,2 +1 @@
Removing regex from dev-dependencies
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/Cargo.toml
new file mode 100644
index 000000000..28b028417
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/Cargo.toml
@@ -0,0 +1,8 @@
+# Cargo.toml
+
+[workspace]
+members = ["serde", "serde_derive"]
+
+[patch.crates-io]
+serde = { path = "serde" }
+
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/serde/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/serde/Cargo.toml
new file mode 100644
index 000000000..c05589aba
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/serde/Cargo.toml
@@ -0,0 +1,9 @@
+# serde/Cargo.toml
+
+[package]
+name = "serde"
+version = "1.0.0"
+
+[dependencies]
+serde_derive = { path = "../serde_derive" }
+
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/serde/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/serde/src/lib.rs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/serde/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/serde_derive/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/serde_derive/Cargo.toml
new file mode 100644
index 000000000..2b9b48b50
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/serde_derive/Cargo.toml
@@ -0,0 +1,8 @@
+# serde_derive/Cargo.toml
+
+[package]
+name = "serde_derive"
+version = "1.0.0"
+
+[dev-dependencies]
+serde_json = "1.0.0"
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/serde_derive/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/serde_derive/src/lib.rs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/in/serde_derive/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/mod.rs
new file mode 100644
index 000000000..f66478c5d
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/mod.rs
@@ -0,0 +1,27 @@
+use cargo_test_support::compare::assert_ui;
+use cargo_test_support::curr_dir;
+use cargo_test_support::CargoCommand;
+use cargo_test_support::Project;
+
+#[cargo_test]
+fn case() {
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("serde", "1.0.0").publish();
+ cargo_test_support::registry::Package::new("serde_json", "1.0.0")
+ .dep("serde", "1.0.0")
+ .publish();
+
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = project.root();
+
+ snapbox::cmd::Command::cargo_ui()
+ .current_dir(&project_root)
+ .arg("remove")
+ .args(["--package", "serde", "serde_derive"])
+ .assert()
+ .code(0)
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+
+ assert_ui().subset_matches(curr_dir!().join("out"), &project_root);
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/Cargo.toml
new file mode 100644
index 000000000..28b028417
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/Cargo.toml
@@ -0,0 +1,8 @@
+# Cargo.toml
+
+[workspace]
+members = ["serde", "serde_derive"]
+
+[patch.crates-io]
+serde = { path = "serde" }
+
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/serde/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/serde/Cargo.toml
new file mode 100644
index 000000000..a91d8ebd5
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/serde/Cargo.toml
@@ -0,0 +1,6 @@
+# serde/Cargo.toml
+
+[package]
+name = "serde"
+version = "1.0.0"
+
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/serde/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/serde/src/lib.rs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/serde/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/serde_derive/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/serde_derive/Cargo.toml
new file mode 100644
index 000000000..2b9b48b50
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/serde_derive/Cargo.toml
@@ -0,0 +1,8 @@
+# serde_derive/Cargo.toml
+
+[package]
+name = "serde_derive"
+version = "1.0.0"
+
+[dev-dependencies]
+serde_json = "1.0.0"
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/serde_derive/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/serde_derive/src/lib.rs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/out/serde_derive/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/stderr.log
new file mode 100644
index 000000000..b4377b3a4
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/stderr.log
@@ -0,0 +1 @@
+ Removing serde_derive from dependencies
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/stdout.log b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_keep_used_patch/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/mod.rs
index ec521a5bb..d4d305323 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/mod.rs
@@ -23,6 +23,13 @@ fn case() {
})
.url();
+ let git_project3 = git::new("bar3", |project| {
+ project
+ .file("Cargo.toml", &basic_manifest("bar", "0.1.0"))
+ .file("src/lib.rs", "")
+ })
+ .url();
+
let in_project = project()
.file(
"Cargo.toml",
@@ -38,7 +45,7 @@ fn case() {
bar = {{ git = \"{git_project1}\" }}\n\
\n\
[patch.\"{git_project1}\"]\n\
- bar = {{ git = \"{git_project2}\" }}\n\
+ bar = {{ git = \"{git_project3}\" }}\n\
\n\
[patch.crates-io]\n\
bar = {{ git = \"{git_project2}\" }}\n",
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/out/Cargo.lock b/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/out/Cargo.lock
new file mode 100644
index 000000000..4a1467ba1
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/out/Cargo.lock
@@ -0,0 +1,19 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "bar"
+version = "0.1.0"
+source = "git+[..]"
+
+[[package]]
+name = "my-member"
+version = "0.1.0"
+dependencies = [
+ "bar",
+]
+
+[[package]]
+name = "my-project"
+version = "0.1.0"
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/stderr.log
index 1dd2e7757..ba519ba1b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/stderr.log
@@ -1,3 +1 @@
Removing bar from dependencies
- Updating git repository `[ROOTURL]/bar2`
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_profile/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/gc_profile/stderr.log
index 0e2e38f26..9dee9e2b7 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/gc_profile/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_profile/stderr.log
@@ -1,2 +1 @@
Removing toml from dependencies
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_replace/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/gc_replace/stderr.log
index 0e2e38f26..9dee9e2b7 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/gc_replace/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_replace/stderr.log
@@ -1,2 +1 @@
Removing toml from dependencies
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/help/mod.rs
new file mode 100644
index 000000000..69fb60f03
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("remove")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_remove/help/stdout.log
new file mode 100644
index 000000000..81a2d78b6
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/help/stdout.log
@@ -0,0 +1,29 @@
+Remove dependencies from a Cargo.toml manifest file
+
+Usage: cargo[EXE] remove [OPTIONS] <DEP_ID>...
+
+Arguments:
+ <DEP_ID>... Dependencies to be removed
+
+Options:
+ --dry-run Don't actually write the manifest
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Section:
+ --dev Remove as development dependency
+ --build Remove as build dependency
+ --target <TARGET> Remove as dependency from the given target platform
+
+Package Selection:
+ -p, --package [<SPEC>] Package to remove from
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs
index feb08cea4..ea7902bd8 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs
@@ -2,9 +2,11 @@ mod avoid_empty_tables;
mod build;
mod dev;
mod dry_run;
+mod gc_keep_used_patch;
mod gc_patch;
mod gc_profile;
mod gc_replace;
+mod help;
mod invalid_arg;
mod invalid_dep;
mod invalid_package;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/multiple_deps/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/multiple_deps/stderr.log
index 1eb59aca1..111b1e94a 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/multiple_deps/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/multiple_deps/stderr.log
@@ -1,3 +1,2 @@
Removing docopt from dependencies
Removing semver from dependencies
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/stderr.log
index a3042dcc3..8a69c94f5 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/stderr.log
@@ -1,3 +1,2 @@
Removing regex from dev-dependencies
Removing serde from dev-dependencies
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/stderr.log
index 72c9f9217..d3656ec54 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/stderr.log
@@ -1,2 +1 @@
Removing serde from dev-dependencies
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_feature/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/optional_feature/stderr.log
index 2dc546fa7..ef7354ef1 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/optional_feature/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_feature/stderr.log
@@ -1,2 +1 @@
Removing semver from dependencies
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/package/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/package/stderr.log
index 231026f2b..7083976b1 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/package/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/package/stderr.log
@@ -1,2 +1 @@
Removing docopt from dependencies
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/remove_basic/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/remove_basic/stderr.log
index 231026f2b..7083976b1 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/remove_basic/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/remove_basic/stderr.log
@@ -1,2 +1 @@
Removing docopt from dependencies
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/target/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/target/stderr.log
index 810abd994..8fb1b5000 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/target/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/target/stderr.log
@@ -1,2 +1 @@
Removing dbus from dependencies for target `x86_64-unknown-linux-gnu`
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/target_build/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/target_build/stderr.log
index b06f8f319..673a47ceb 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/target_build/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/target_build/stderr.log
@@ -1,2 +1 @@
Removing semver from build-dependencies for target `x86_64-unknown-linux-gnu`
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/target_dev/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/target_dev/stderr.log
index 68553a3bd..854aff44a 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/target_dev/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/target_dev/stderr.log
@@ -1,2 +1 @@
Removing ncurses from dev-dependencies for target `x86_64-unknown-linux-gnu`
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/stderr.log
index 164f8f4b9..1494b0fc5 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/stderr.log
@@ -1,2 +1 @@
Removing rustc-serialize from dependencies
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/workspace/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/workspace/stderr.log
index f037ebe28..a51bea48c 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/workspace/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/workspace/stderr.log
@@ -1,2 +1 @@
Removing semver from build-dependencies
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/workspace_non_virtual/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/workspace_non_virtual/stderr.log
index f037ebe28..a51bea48c 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/workspace_non_virtual/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/workspace_non_virtual/stderr.log
@@ -1,2 +1 @@
Removing semver from build-dependencies
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/workspace_preserved/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/workspace_preserved/stderr.log
index f037ebe28..a51bea48c 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/workspace_preserved/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/workspace_preserved/stderr.log
@@ -1,2 +1 @@
Removing semver from build-dependencies
- Updating `dummy-registry` index
diff --git a/src/tools/cargo/tests/testsuite/cargo_report/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_report/help/mod.rs
new file mode 100644
index 000000000..3d2975769
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_report/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("report")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_report/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_report/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_report/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_report/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_report/help/stdout.log
new file mode 100644
index 000000000..67819de55
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_report/help/stdout.log
@@ -0,0 +1,20 @@
+Generate and display various kinds of reports
+
+Usage: cargo[EXE] report [OPTIONS] <COMMAND>
+
+Commands:
+ future-incompatibilities Reports any crates which will eventually stop compiling
+
+Options:
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Manifest Options:
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help report` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_report/mod.rs b/src/tools/cargo/tests/testsuite/cargo_report/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_report/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_run/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_run/help/mod.rs
new file mode 100644
index 000000000..0a8a6bde0
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_run/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("run")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_run/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_run/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_run/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_run/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_run/help/stdout.log
new file mode 100644
index 000000000..6ab0e76b1
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_run/help/stdout.log
@@ -0,0 +1,47 @@
+Run a binary or example of the local package
+
+Usage: cargo[EXE] run [OPTIONS] [args]...
+
+Arguments:
+ [args]... Arguments for the binary or example to run
+
+Options:
+ --ignore-rust-version Ignore `rust-version` specification in packages
+ --message-format <FMT> Error format
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
+ details
+ -h, --help Print help
+
+Package Selection:
+ -p, --package [<SPEC>] Package with the target to run
+
+Target Selection:
+ --bin [<NAME>] Name of the bin target to run
+ --example [<NAME>] Name of the example target to run
+
+Feature Selection:
+ -F, --features <FEATURES> Space or comma separated list of features to activate
+ --all-features Activate all available features
+ --no-default-features Do not activate the `default` feature
+
+Compilation Options:
+ -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs.
+ --keep-going Do not abort the build as soon as there is an error (unstable)
+ -r, --release Build artifacts in release mode, with optimizations
+ --profile <PROFILE-NAME> Build artifacts with the specified profile
+ --target <TRIPLE> Build for the target triple
+ --target-dir <DIRECTORY> Directory for all generated artifacts
+ --unit-graph Output build graph in JSON (unstable)
+ --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help run` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_run/mod.rs b/src/tools/cargo/tests/testsuite/cargo_run/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_run/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_rustc/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_rustc/help/mod.rs
new file mode 100644
index 000000000..0a3b31686
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_rustc/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("rustc")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_rustc/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_rustc/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_rustc/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_rustc/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_rustc/help/stdout.log
new file mode 100644
index 000000000..f587c3276
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_rustc/help/stdout.log
@@ -0,0 +1,58 @@
+Compile a package, and pass extra options to the compiler
+
+Usage: cargo[EXE] rustc [OPTIONS] [args]...
+
+Arguments:
+ [args]... Extra rustc flags
+
+Options:
+ --print <INFO> Output compiler information without compiling
+ --crate-type <CRATE-TYPE> Comma separated list of types of crates for the compiler to emit
+ --future-incompat-report Outputs a future incompatibility report at the end of the build
+ --ignore-rust-version Ignore `rust-version` specification in packages
+ --message-format <FMT> Error format
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
+ details
+ -h, --help Print help
+
+Package Selection:
+ -p, --package [<SPEC>] Package to build
+
+Target Selection:
+ --lib Build only this package's library
+ --bins Build all binaries
+ --bin [<NAME>] Build only the specified binary
+ --examples Build all examples
+ --example [<NAME>] Build only the specified example
+ --tests Build all tests
+ --test [<NAME>] Build only the specified test target
+ --benches Build all benches
+ --bench [<NAME>] Build only the specified bench target
+ --all-targets Build all targets
+
+Feature Selection:
+ -F, --features <FEATURES> Space or comma separated list of features to activate
+ --all-features Activate all available features
+ --no-default-features Do not activate the `default` feature
+
+Compilation Options:
+ -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs.
+ --keep-going Do not abort the build as soon as there is an error (unstable)
+ -r, --release Build artifacts in release mode, with optimizations
+ --profile <PROFILE-NAME> Build artifacts with the specified profile
+ --target <TRIPLE> Target triple which compiles will be for
+ --target-dir <DIRECTORY> Directory for all generated artifacts
+ --unit-graph Output build graph in JSON (unstable)
+ --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help rustc` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_rustc/mod.rs b/src/tools/cargo/tests/testsuite/cargo_rustc/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_rustc/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/mod.rs
new file mode 100644
index 000000000..88652749f
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("rustdoc")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stdout.log
new file mode 100644
index 000000000..4cac29e0a
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stdout.log
@@ -0,0 +1,56 @@
+Build a package's documentation, using specified custom flags.
+
+Usage: cargo[EXE] rustdoc [OPTIONS] [args]...
+
+Arguments:
+ [args]... Extra rustdoc flags
+
+Options:
+ --open Opens the docs in a browser after the operation
+ --ignore-rust-version Ignore `rust-version` specification in packages
+ --message-format <FMT> Error format
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
+ details
+ -h, --help Print help
+
+Package Selection:
+ -p, --package [<SPEC>] Package to document
+
+Target Selection:
+ --lib Build only this package's library
+ --bins Build all binaries
+ --bin [<NAME>] Build only the specified binary
+ --examples Build all examples
+ --example [<NAME>] Build only the specified example
+ --tests Build all tests
+ --test [<NAME>] Build only the specified test target
+ --benches Build all benches
+ --bench [<NAME>] Build only the specified bench target
+ --all-targets Build all targets
+
+Feature Selection:
+ -F, --features <FEATURES> Space or comma separated list of features to activate
+ --all-features Activate all available features
+ --no-default-features Do not activate the `default` feature
+
+Compilation Options:
+ -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs.
+ --keep-going Do not abort the build as soon as there is an error (unstable)
+ -r, --release Build artifacts in release mode, with optimizations
+ --profile <PROFILE-NAME> Build artifacts with the specified profile
+ --target <TRIPLE> Build for the target triple
+ --target-dir <DIRECTORY> Directory for all generated artifacts
+ --unit-graph Output build graph in JSON (unstable)
+ --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help rustdoc` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_rustdoc/mod.rs b/src/tools/cargo/tests/testsuite/cargo_rustdoc/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_rustdoc/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_search/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_search/help/mod.rs
new file mode 100644
index 000000000..b580816e7
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_search/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("search")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_search/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_search/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_search/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_search/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_search/help/stdout.log
new file mode 100644
index 000000000..8572064e3
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_search/help/stdout.log
@@ -0,0 +1,24 @@
+Search packages in crates.io
+
+Usage: cargo[EXE] search [OPTIONS] [query]...
+
+Arguments:
+ [query]...
+
+Options:
+ --limit <LIMIT> Limit the number of results (default: 10, max: 100)
+ --index <INDEX> Registry index URL to upload the package to
+ --registry <REGISTRY> Registry to use
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Manifest Options:
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help search` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_search/mod.rs b/src/tools/cargo/tests/testsuite/cargo_search/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_search/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_test/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_test/help/mod.rs
new file mode 100644
index 000000000..ae5b092b7
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_test/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("test")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_test/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_test/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_test/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_test/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_test/help/stdout.log
new file mode 100644
index 000000000..d693dc3c9
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_test/help/stdout.log
@@ -0,0 +1,63 @@
+Execute all unit and integration tests and build examples of a local package
+
+Usage: cargo[EXE] test [OPTIONS] [TESTNAME] [-- [args]...]
+
+Arguments:
+ [TESTNAME] If specified, only run tests containing this string in their names
+ [args]... Arguments for the test binary
+
+Options:
+ --doc Test only this library's documentation
+ --no-run Compile, but don't run tests
+ --no-fail-fast Run all tests regardless of failure
+ --ignore-rust-version Ignore `rust-version` specification in packages
+ --future-incompat-report Outputs a future incompatibility report at the end of the build
+ --message-format <FMT> Error format
+ -q, --quiet Display one character per test instead of one line
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
+ details
+ -h, --help Print help
+
+Package Selection:
+ -p, --package [<SPEC>] Package to run tests for
+ --workspace Test all packages in the workspace
+ --exclude <SPEC> Exclude packages from the test
+ --all Alias for --workspace (deprecated)
+
+Target Selection:
+ --lib Test only this package's library unit tests
+ --bins Test all binaries
+ --bin [<NAME>] Test only the specified binary
+ --examples Test all examples
+ --example [<NAME>] Test only the specified example
+ --tests Test all tests
+ --test [<NAME>] Test only the specified test target
+ --benches Test all benches
+ --bench [<NAME>] Test only the specified bench target
+ --all-targets Test all targets (does not include doctests)
+
+Feature Selection:
+ -F, --features <FEATURES> Space or comma separated list of features to activate
+ --all-features Activate all available features
+ --no-default-features Do not activate the `default` feature
+
+Compilation Options:
+ -j, --jobs <N> Number of parallel jobs, defaults to # of CPUs.
+ -r, --release Build artifacts in release mode, with optimizations
+ --profile <PROFILE-NAME> Build artifacts with the specified profile
+ --target <TRIPLE> Build for the target triple
+ --target-dir <DIRECTORY> Directory for all generated artifacts
+ --unit-graph Output build graph in JSON (unstable)
+ --timings[=<FMTS>] Timing output formats (unstable) (comma separated): html, json
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help test` for more detailed information.
+Run `cargo test -- --help` for test binary options.
diff --git a/src/tools/cargo/tests/testsuite/cargo_test/mod.rs b/src/tools/cargo/tests/testsuite/cargo_test/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_test/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_tree/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_tree/help/mod.rs
new file mode 100644
index 000000000..269ac2cdc
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_tree/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("tree")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_tree/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_tree/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_tree/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_tree/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_tree/help/stdout.log
new file mode 100644
index 000000000..268b6b2ad
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_tree/help/stdout.log
@@ -0,0 +1,45 @@
+Display a tree visualization of a dependency graph
+
+Usage: cargo[EXE] tree [OPTIONS]
+
+Options:
+ -q, --quiet Do not print cargo log messages
+ -e, --edges <KINDS> The kinds of dependencies to display (features, normal, build, dev, all,
+ no-normal, no-build, no-dev, no-proc-macro)
+ -i, --invert [<SPEC>] Invert the tree direction and focus on the given package
+ --prune <SPEC> Prune the given package from the display of the dependency tree
+ --depth <DEPTH> Maximum display depth of the dependency tree
+ --prefix <PREFIX> Change the prefix (indentation) of how each entry is displayed [default:
+ indent] [possible values: depth, indent, none]
+ --no-dedupe Do not de-duplicate (repeats all shared dependencies)
+ -d, --duplicates Show only dependencies which come in multiple versions (implies -i)
+ --charset <CHARSET> Character set to use in output [default: utf8] [possible values: utf8,
+ ascii]
+ -f, --format <FORMAT> Format string used for printing dependencies [default: {p}]
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Package Selection:
+ -p, --package [<SPEC>] Package to be used as the root of the tree
+ --workspace Display the tree for all packages in the workspace
+ --exclude <SPEC> Exclude specific workspace members
+
+Feature Selection:
+ -F, --features <FEATURES> Space or comma separated list of features to activate
+ --all-features Activate all available features
+ --no-default-features Do not activate the `default` feature
+
+Compilation Options:
+ --target <TRIPLE> Filter dependencies matching the given target-triple (default host
+ platform). Pass `all` to include all targets.
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help tree` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_tree/mod.rs b/src/tools/cargo/tests/testsuite/cargo_tree/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_tree/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_uninstall/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_uninstall/help/mod.rs
new file mode 100644
index 000000000..60c4faed0
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_uninstall/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("uninstall")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_uninstall/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_uninstall/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_uninstall/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_uninstall/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_uninstall/help/stdout.log
new file mode 100644
index 000000000..2da1a5d57
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_uninstall/help/stdout.log
@@ -0,0 +1,28 @@
+Remove a Rust binary
+
+Usage: cargo[EXE] uninstall [OPTIONS] [spec]...
+
+Arguments:
+ [spec]...
+
+Options:
+ --root <DIR> Directory to uninstall packages from
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Package Selection:
+ -p, --package [<SPEC>] Package to uninstall
+
+Target Selection:
+ --bin <NAME> Only uninstall the binary NAME
+
+Manifest Options:
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help uninstall` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_uninstall/mod.rs b/src/tools/cargo/tests/testsuite/cargo_uninstall/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_uninstall/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_update/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_update/help/mod.rs
new file mode 100644
index 000000000..ae310977c
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_update/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("update")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_update/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_update/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_update/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_update/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_update/help/stdout.log
new file mode 100644
index 000000000..6cc109151
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_update/help/stdout.log
@@ -0,0 +1,26 @@
+Update dependencies as recorded in the local lock file
+
+Usage: cargo[EXE] update [OPTIONS]
+
+Options:
+ --dry-run Don't actually write the lockfile
+ --aggressive Force updating all dependencies of SPEC as well when used with -p
+ --precise <PRECISE> Update a single dependency to exactly PRECISE when used with -p
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Package Selection:
+ -w, --workspace Only update the workspace packages
+ -p, --package [<SPEC>] Package to update
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help update` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_update/mod.rs b/src/tools/cargo/tests/testsuite/cargo_update/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_update/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_vendor/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_vendor/help/mod.rs
new file mode 100644
index 000000000..c111b99c0
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_vendor/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("vendor")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_vendor/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_vendor/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_vendor/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_vendor/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_vendor/help/stdout.log
new file mode 100644
index 000000000..7f37ab56e
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_vendor/help/stdout.log
@@ -0,0 +1,27 @@
+Vendor all dependencies for a project locally
+
+Usage: cargo[EXE] vendor [OPTIONS] [path]
+
+Arguments:
+ [path] Where to vendor crates (`vendor` by default)
+
+Options:
+ --no-delete Don't delete older crates in the vendor directory
+ -s, --sync <TOML> Additional `Cargo.toml` to sync and vendor
+ --respect-source-config Respect `[source]` config in `.cargo/config`
+ --versioned-dirs Always include version in subdir name
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
+ details
+ -h, --help Print help
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help vendor` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_vendor/mod.rs b/src/tools/cargo/tests/testsuite/cargo_vendor/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_vendor/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_verify_project/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_verify_project/help/mod.rs
new file mode 100644
index 000000000..8f6c9bab1
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_verify_project/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("verify-project")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_verify_project/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_verify_project/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_verify_project/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_verify_project/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_verify_project/help/stdout.log
new file mode 100644
index 000000000..a61534500
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_verify_project/help/stdout.log
@@ -0,0 +1,19 @@
+Check correctness of crate manifest
+
+Usage: cargo[EXE] verify-project [OPTIONS]
+
+Options:
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Manifest Options:
+ --manifest-path <PATH> Path to Cargo.toml
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help verify-project` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_verify_project/mod.rs b/src/tools/cargo/tests/testsuite/cargo_verify_project/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_verify_project/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_version/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_version/help/mod.rs
new file mode 100644
index 000000000..daa8548c6
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_version/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("version")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_version/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_version/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_version/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_version/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_version/help/stdout.log
new file mode 100644
index 000000000..3f79051ad
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_version/help/stdout.log
@@ -0,0 +1,18 @@
+Show version information
+
+Usage: cargo[EXE] version [OPTIONS]
+
+Options:
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Manifest Options:
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help version` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_version/mod.rs b/src/tools/cargo/tests/testsuite/cargo_version/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_version/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/cargo_yank/help/mod.rs b/src/tools/cargo/tests/testsuite/cargo_yank/help/mod.rs
new file mode 100644
index 000000000..12034f152
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_yank/help/mod.rs
@@ -0,0 +1,13 @@
+use cargo_test_support::curr_dir;
+use cargo_test_support::prelude::*;
+
+#[cargo_test]
+fn case() {
+ snapbox::cmd::Command::cargo_ui()
+ .arg("yank")
+ .arg("--help")
+ .assert()
+ .success()
+ .stdout_matches_path(curr_dir!().join("stdout.log"))
+ .stderr_matches_path(curr_dir!().join("stderr.log"));
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_yank/help/stderr.log b/src/tools/cargo/tests/testsuite/cargo_yank/help/stderr.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_yank/help/stderr.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_yank/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_yank/help/stdout.log
new file mode 100644
index 000000000..25b04e6c7
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_yank/help/stdout.log
@@ -0,0 +1,26 @@
+Remove a pushed crate from the index
+
+Usage: cargo[EXE] yank [OPTIONS] [crate]
+
+Arguments:
+ [crate]
+
+Options:
+ --version <VERSION> The version to yank or un-yank
+ --undo Undo a yank, putting a version back into the index
+ --index <INDEX> Registry index to yank from
+ --registry <REGISTRY> Registry to use
+ --token <TOKEN> API token to use when authenticating
+ -q, --quiet Do not print cargo log messages
+ -v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ --color <WHEN> Coloring: auto, always, never
+ --config <KEY=VALUE> Override a configuration value
+ -Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
+ -h, --help Print help
+
+Manifest Options:
+ --frozen Require Cargo.lock and cache are up to date
+ --locked Require Cargo.lock is up to date
+ --offline Run without accessing the network
+
+Run `cargo help yank` for more detailed information.
diff --git a/src/tools/cargo/tests/testsuite/cargo_yank/mod.rs b/src/tools/cargo/tests/testsuite/cargo_yank/mod.rs
new file mode 100644
index 000000000..c0ce11180
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_yank/mod.rs
@@ -0,0 +1 @@
+mod help;
diff --git a/src/tools/cargo/tests/testsuite/credential_process.rs b/src/tools/cargo/tests/testsuite/credential_process.rs
index 8c202c6a3..c010c01cd 100644
--- a/src/tools/cargo/tests/testsuite/credential_process.rs
+++ b/src/tools/cargo/tests/testsuite/credential_process.rs
@@ -1,8 +1,7 @@
//! Tests for credential-process.
-use cargo_test_support::registry::TestRegistry;
+use cargo_test_support::registry::{Package, TestRegistry};
use cargo_test_support::{basic_manifest, cargo_process, paths, project, registry, Project};
-use std::fs::{self, read_to_string};
fn toml_bin(proj: &Project, name: &str) -> String {
proj.bin(name).display().to_string().replace('\\', "\\\\")
@@ -24,7 +23,7 @@ fn gated() {
".cargo/config",
r#"
[registry]
- credential-process = "false"
+ credential-provider = ["false"]
"#,
)
.file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
@@ -65,80 +64,6 @@ or use environment variable CARGO_REGISTRIES_ALTERNATIVE_TOKEN
.run();
}
-#[cargo_test]
-fn warn_both_token_and_process() {
- // Specifying both credential-process and a token in config should issue a warning.
- let _server = registry::RegistryBuilder::new()
- .http_api()
- .http_index()
- .alternative()
- .no_configure_token()
- .build();
- let p = project()
- .file(
- ".cargo/config",
- r#"
- [registries.alternative]
- token = "alternative-sekrit"
- credential-process = "false"
- "#,
- )
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.1.0"
- description = "foo"
- authors = []
- license = "MIT"
- homepage = "https://example.com/"
- "#,
- )
- .file("src/lib.rs", "")
- .build();
-
- p.cargo("publish --no-verify --registry alternative -Z credential-process")
- .masquerade_as_nightly_cargo(&["credential-process"])
- .with_status(101)
- .with_stderr(
- "\
-[UPDATING] [..]
-[ERROR] both `token` and `credential-process` were specified in the config for registry `alternative`.
-Only one of these values may be set, remove one or the other to proceed.
-",
- )
- .run();
-
- // Try with global credential-process, and registry-specific `token`.
- // This should silently use the config token, and not run the "false" exe.
- p.change_file(
- ".cargo/config",
- r#"
- [registry]
- credential-process = "false"
-
- [registries.alternative]
- token = "alternative-sekrit"
- "#,
- );
- p.cargo("publish --no-verify --registry alternative -Z credential-process")
- .masquerade_as_nightly_cargo(&["credential-process"])
- .with_stderr(
- "\
-[UPDATING] [..]
-[PACKAGING] foo v0.1.0 [..]
-[PACKAGED] [..]
-[UPLOADING] foo v0.1.0 [..]
-[UPLOADED] foo v0.1.0 [..]
-note: Waiting [..]
-You may press ctrl-c [..]
-[PUBLISHED] foo v0.1.0 [..]
-",
- )
- .run();
-}
-
/// Setup for a test that will issue a command that needs to fetch a token.
///
/// This does the following:
@@ -158,29 +83,14 @@ fn get_token_test() -> (Project, TestRegistry) {
))
.alternative()
.http_api()
+ .http_index()
+ .auth_required()
.build();
- // The credential process to use.
- let cred_proj = project()
- .at("cred_proj")
- .file("Cargo.toml", &basic_manifest("test-cred", "1.0.0"))
- .file(
- "src/main.rs",
- r#"
- use std::fs::File;
- use std::io::Write;
- fn main() {
- let mut f = File::options()
- .write(true)
- .create(true)
- .append(true)
- .open("runs.log")
- .unwrap();
- write!(f, "+");
- println!("sekrit");
- } "#,
- )
- .build();
- cred_proj.cargo("build").run();
+
+ let provider = build_provider(
+ "test-cred",
+ r#"{"Ok":{"kind":"get","token":"sekrit","cache":"session","operation_independent":false}}"#,
+ );
let p = project()
.file(
@@ -189,10 +99,9 @@ fn get_token_test() -> (Project, TestRegistry) {
r#"
[registries.alternative]
index = "{}"
- credential-process = ["{}"]
+ credential-provider = ["{provider}"]
"#,
server.index_url(),
- toml_bin(&cred_proj, "test-cred")
),
)
.file(
@@ -202,7 +111,6 @@ fn get_token_test() -> (Project, TestRegistry) {
name = "foo"
version = "0.1.0"
description = "foo"
- authors = []
license = "MIT"
homepage = "https://example.com/"
"#,
@@ -217,24 +125,22 @@ fn publish() {
// Checks that credential-process is used for `cargo publish`.
let (p, _t) = get_token_test();
- p.cargo("publish --no-verify --registry alternative -Z credential-process")
+ p.cargo("publish --no-verify --registry alternative -Z credential-process -Z registry-auth")
.masquerade_as_nightly_cargo(&["credential-process"])
.with_stderr(
- "\
-[UPDATING] [..]
+ r#"[UPDATING] [..]
+{"v":1,"registry":{"index-url":"[..]","name":"alternative","headers":[..]},"kind":"get","operation":"read","args":[]}
[PACKAGING] foo v0.1.0 [..]
[PACKAGED] [..]
+{"v":1,"registry":{"index-url":"[..]","name":"alternative"},"kind":"get","operation":"publish","name":"foo","vers":"0.1.0","cksum":"[..]","args":[]}
[UPLOADING] foo v0.1.0 [..]
[UPLOADED] foo v0.1.0 [..]
note: Waiting [..]
You may press ctrl-c [..]
[PUBLISHED] foo v0.1.0 [..]
-",
+"#,
)
.run();
-
- let calls = read_to_string(p.root().join("runs.log")).unwrap().len();
- assert_eq!(calls, 1);
}
#[cargo_test]
@@ -242,15 +148,8 @@ fn basic_unsupported() {
// Non-action commands don't support login/logout.
let registry = registry::RegistryBuilder::new()
.no_configure_token()
+ .credential_provider(&["cargo:token-from-stdout", "false"])
.build();
- cargo_util::paths::append(
- &paths::home().join(".cargo/config"),
- br#"
- [registry]
- credential-process = "false"
- "#,
- )
- .unwrap();
cargo_process("login -Z credential-process abcdefg")
.replace_crates_io(registry.index_url())
@@ -259,9 +158,10 @@ fn basic_unsupported() {
.with_stderr(
"\
[UPDATING] crates.io index
-[ERROR] credential process `false` cannot be used to log in, \
-the credential-process configuration value must pass the \
-`{action}` argument in the config to support this command
+[ERROR] credential provider `cargo:token-from-stdout false` failed action `login`
+
+Caused by:
+ requested operation not supported
",
)
.run();
@@ -272,9 +172,10 @@ the credential-process configuration value must pass the \
.with_status(101)
.with_stderr(
"\
-[ERROR] credential process `false` cannot be used to log out, \
-the credential-process configuration value must pass the \
-`{action}` argument in the config to support this command
+[ERROR] credential provider `cargo:token-from-stdout false` failed action `logout`
+
+Caused by:
+ requested operation not supported
",
)
.run();
@@ -282,222 +183,475 @@ the credential-process configuration value must pass the \
#[cargo_test]
fn login() {
+ let registry = registry::RegistryBuilder::new()
+ .no_configure_token()
+ .credential_provider(&[
+ &build_provider("test-cred", r#"{"Ok": {"kind": "login"}}"#),
+ "cfg1",
+ "--cfg2",
+ ])
+ .build();
+
+ cargo_process("login -Z credential-process abcdefg -- cmd3 --cmd4")
+ .masquerade_as_nightly_cargo(&["credential-process"])
+ .replace_crates_io(registry.index_url())
+ .with_stderr(
+ r#"[UPDATING] [..]
+{"v":1,"registry":{"index-url":"https://github.com/rust-lang/crates.io-index","name":"crates-io"},"kind":"login","token":"abcdefg","login-url":"[..]","args":["cfg1","--cfg2","cmd3","--cmd4"]}
+"#,
+ )
+ .run();
+}
+
+#[cargo_test]
+fn logout() {
let server = registry::RegistryBuilder::new()
.no_configure_token()
+ .credential_provider(&[&build_provider(
+ "test-cred",
+ r#"{"Ok": {"kind": "logout"}}"#,
+ )])
.build();
- // The credential process to use.
+
+ cargo_process("logout -Z credential-process")
+ .masquerade_as_nightly_cargo(&["credential-process"])
+ .replace_crates_io(server.index_url())
+ .with_stderr(
+ r#"{"v":1,"registry":{"index-url":"https://github.com/rust-lang/crates.io-index","name":"crates-io"},"kind":"logout","args":[]}
+"#,
+ )
+ .run();
+}
+
+#[cargo_test]
+fn yank() {
+ let (p, _t) = get_token_test();
+
+ p.cargo("yank --version 0.1.0 --registry alternative -Zcredential-process -Zregistry-auth")
+ .masquerade_as_nightly_cargo(&["credential-process"])
+ .with_stderr(
+ r#"[UPDATING] [..]
+{"v":1,"registry":{"index-url":"[..]","name":"alternative","headers":[..]},"kind":"get","operation":"read","args":[]}
+{"v":1,"registry":{"index-url":"[..]","name":"alternative"},"kind":"get","operation":"yank","name":"foo","vers":"0.1.0","args":[]}
+[YANK] foo@0.1.0
+"#,
+ )
+ .run();
+}
+
+#[cargo_test]
+fn owner() {
+ let (p, _t) = get_token_test();
+
+ p.cargo("owner --add username --registry alternative -Zcredential-process -Zregistry-auth")
+ .masquerade_as_nightly_cargo(&["credential-process"])
+ .with_stderr(
+ r#"[UPDATING] [..]
+{"v":1,"registry":{"index-url":"[..]","name":"alternative","headers":[..]},"kind":"get","operation":"read","args":[]}
+{"v":1,"registry":{"index-url":"[..]","name":"alternative"},"kind":"get","operation":"owners","name":"foo","args":[]}
+[OWNER] completed!
+"#,
+ )
+ .run();
+}
+
+#[cargo_test]
+fn invalid_token_output() {
+ // Error when credential process does not output the expected format for a token.
let cred_proj = project()
.at("cred_proj")
.file("Cargo.toml", &basic_manifest("test-cred", "1.0.0"))
+ .file("src/main.rs", r#"fn main() { print!("a\nb\n"); } "#)
+ .build();
+ cred_proj.cargo("build").run();
+ let _server = registry::RegistryBuilder::new()
+ .alternative()
+ .credential_provider(&[
+ "cargo:token-from-stdout",
+ &toml_bin(&cred_proj, "test-cred"),
+ ])
+ .no_configure_token()
+ .build();
+
+ let p = project()
+ .file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("publish --no-verify --registry alternative -Z credential-process")
+ .masquerade_as_nightly_cargo(&["credential-process"])
+ .with_status(101)
+ .with_stderr(
+ "\
+[UPDATING] [..]
+[ERROR] credential provider `[..]test-cred[EXE]` failed action `get`
+
+Caused by:
+ process `[..]` returned more than one line of output; expected a single token
+",
+ )
+ .run();
+}
+
+/// Builds a credential provider that echos the request from cargo to stderr,
+/// and prints the `response` to stdout.
+fn build_provider(name: &str, response: &str) -> String {
+ // The credential process to use.
+ let cred_proj = project()
+ .at(name)
+ .file("Cargo.toml", &basic_manifest(name, "1.0.0"))
.file(
"src/main.rs",
- r#"
- use std::io::Read;
-
- fn main() {{
- assert_eq!(std::env::var("CARGO_REGISTRY_NAME_OPT").unwrap(), "crates-io");
- assert_eq!(std::env::var("CARGO_REGISTRY_INDEX_URL").unwrap(), "https://github.com/rust-lang/crates.io-index");
- assert_eq!(std::env::args().skip(1).next().unwrap(), "store");
+ &r####"
+ fn main() {
+ println!(r#"{{"v":[1]}}"#);
+ assert_eq!(std::env::args().skip(1).next().unwrap(), "--cargo-plugin");
let mut buffer = String::new();
- std::io::stdin().read_to_string(&mut buffer).unwrap();
- assert_eq!(buffer, "abcdefg\n");
- std::fs::write("token-store", buffer).unwrap();
- }}
- "#,
+ std::io::stdin().read_line(&mut buffer).unwrap();
+ eprint!("{}", buffer);
+ use std::io::Write;
+ std::io::stdout().write_all(r###"[RESPONSE]"###.as_bytes()).unwrap();
+ println!();
+ } "####
+ .replace("[RESPONSE]", response),
)
.build();
cred_proj.cargo("build").run();
+ toml_bin(&cred_proj, name)
+}
+#[cargo_test]
+fn all_not_found() {
+ let server = registry::RegistryBuilder::new()
+ .no_configure_token()
+ .auth_required()
+ .http_index()
+ .build();
+ let not_found = build_provider("not_found", r#"{"Err": {"kind": "not-found"}}"#);
cargo_util::paths::append(
&paths::home().join(".cargo/config"),
format!(
r#"
[registry]
- credential-process = ["{}", "{{action}}"]
+ global-credential-providers = ["not_found"]
+ [credential-alias]
+ not_found = ["{not_found}"]
"#,
- toml_bin(&cred_proj, "test-cred")
)
.as_bytes(),
)
.unwrap();
- cargo_process("login -Z credential-process abcdefg")
- .masquerade_as_nightly_cargo(&["credential-process"])
+ cargo_process("install -v foo -Zcredential-process -Zregistry-auth")
+ .masquerade_as_nightly_cargo(&["credential-process", "registry-auth"])
.replace_crates_io(server.index_url())
+ .with_status(101)
.with_stderr(
- "\
-[UPDATING] [..]
-[LOGIN] token for `crates.io` saved
-",
+ r#"[UPDATING] [..]
+[CREDENTIAL] [..]not_found[..] get crates-io
+{"v":1,"registry":{"index-url":"[..]","name":"crates-io","headers":[[..]"WWW-Authenticate: Cargo login_url=\"https://test-registry-login/me\""[..]]},"kind":"get","operation":"read","args":[]}
+[ERROR] failed to query replaced source registry `crates-io`
+
+Caused by:
+ no token found, please run `cargo login`
+ or use environment variable CARGO_REGISTRY_TOKEN
+"#,
)
.run();
- assert_eq!(
- fs::read_to_string(paths::root().join("token-store")).unwrap(),
- "abcdefg\n"
- );
}
#[cargo_test]
-fn logout() {
+fn all_not_supported() {
let server = registry::RegistryBuilder::new()
.no_configure_token()
+ .auth_required()
+ .http_index()
.build();
- // The credential process to use.
- let cred_proj = project()
- .at("cred_proj")
- .file("Cargo.toml", &basic_manifest("test-cred", "1.0.0"))
- .file(
- "src/main.rs",
- r#"
- use std::io::Read;
-
- fn main() {{
- assert_eq!(std::env::var("CARGO_REGISTRY_NAME_OPT").unwrap(), "crates-io");
- assert_eq!(std::env::var("CARGO_REGISTRY_INDEX_URL").unwrap(), "https://github.com/rust-lang/crates.io-index");
- assert_eq!(std::env::args().skip(1).next().unwrap(), "erase");
- std::fs::write("token-store", "").unwrap();
- eprintln!("token for `crates-io` has been erased!")
- }}
- "#,
- )
- .build();
- cred_proj.cargo("build").run();
-
+ let not_supported =
+ build_provider("not_supported", r#"{"Err": {"kind": "url-not-supported"}}"#);
cargo_util::paths::append(
&paths::home().join(".cargo/config"),
format!(
r#"
[registry]
- credential-process = ["{}", "{{action}}"]
+ global-credential-providers = ["not_supported"]
+ [credential-alias]
+ not_supported = ["{not_supported}"]
"#,
- toml_bin(&cred_proj, "test-cred")
)
.as_bytes(),
)
.unwrap();
- cargo_process("logout -Z credential-process")
- .masquerade_as_nightly_cargo(&["credential-process"])
+ cargo_process("install -v foo -Zcredential-process -Zregistry-auth")
+ .masquerade_as_nightly_cargo(&["credential-process", "registry-auth"])
.replace_crates_io(server.index_url())
+ .with_status(101)
.with_stderr(
- "\
-token for `crates-io` has been erased!
-[LOGOUT] token for `crates-io` has been removed from local storage
-[NOTE] This does not revoke the token on the registry server.
- If you need to revoke the token, visit <https://crates.io/me> \
- and follow the instructions there.
-",
+ r#"[UPDATING] [..]
+[CREDENTIAL] [..]not_supported[..] get crates-io
+{"v":1,"registry":{"index-url":"[..]","name":"crates-io","headers":[[..]"WWW-Authenticate: Cargo login_url=\"https://test-registry-login/me\""[..]]},"kind":"get","operation":"read","args":[]}
+[ERROR] failed to query replaced source registry `crates-io`
+
+Caused by:
+ no credential providers could handle the request
+"#,
)
.run();
- assert_eq!(
- fs::read_to_string(paths::root().join("token-store")).unwrap(),
- ""
- );
}
#[cargo_test]
-fn yank() {
- let (p, _t) = get_token_test();
+fn multiple_providers() {
+ let server = registry::RegistryBuilder::new()
+ .no_configure_token()
+ .build();
- p.cargo("yank --version 0.1.0 --registry alternative -Z credential-process")
+ // Set up two credential providers: the first will fail with "UrlNotSupported"
+ // and Cargo should skip it. The second should succeed.
+ let url_not_supported = build_provider(
+ "url_not_supported",
+ r#"{"Err": {"kind": "url-not-supported"}}"#,
+ );
+
+ let success_provider = build_provider("success_provider", r#"{"Ok": {"kind": "login"}}"#);
+
+ cargo_util::paths::append(
+ &paths::home().join(".cargo/config"),
+ format!(
+ r#"
+ [registry]
+ global-credential-providers = ["success_provider", "url_not_supported"]
+
+ [credential-alias]
+ success_provider = ["{success_provider}"]
+ url_not_supported = ["{url_not_supported}"]
+ "#,
+ )
+ .as_bytes(),
+ )
+ .unwrap();
+
+ cargo_process("login -Z credential-process -v abcdefg")
.masquerade_as_nightly_cargo(&["credential-process"])
+ .replace_crates_io(server.index_url())
.with_stderr(
- "\
-[UPDATING] [..]
-[YANK] foo@0.1.0
-",
+ r#"[UPDATING] [..]
+[CREDENTIAL] [..]url_not_supported[..] login crates-io
+{"v":1,"registry":{"index-url":"https://github.com/rust-lang/crates.io-index","name":"crates-io"},"kind":"login","token":"abcdefg","login-url":"[..]","args":[]}
+[CREDENTIAL] [..]success_provider[..] login crates-io
+{"v":1,"registry":{"index-url":"https://github.com/rust-lang/crates.io-index","name":"crates-io"},"kind":"login","token":"abcdefg","login-url":"[..]","args":[]}
+"#,
)
.run();
}
#[cargo_test]
-fn owner() {
- let (p, _t) = get_token_test();
+fn both_token_and_provider() {
+ let server = registry::RegistryBuilder::new().build();
+ cargo_util::paths::append(
+ &paths::home().join(".cargo/config"),
+ format!(
+ r#"
+ [registry]
+ credential-provider = ["cargo:token"]
+ "#,
+ )
+ .as_bytes(),
+ )
+ .unwrap();
- p.cargo("owner --add username --registry alternative -Z credential-process")
+ cargo_process("login -Z credential-process -v abcdefg")
.masquerade_as_nightly_cargo(&["credential-process"])
+ .replace_crates_io(server.index_url())
.with_stderr(
- "\
-[UPDATING] [..]
-[OWNER] completed!
-",
+ r#"[UPDATING] [..]
+[WARNING] registry `crates-io` has a token configured in [..]credentials.toml that will be ignored because a credential-provider is configured for this registry`
+[CREDENTIAL] cargo:token login crates-io
+[LOGIN] token for `crates-io` saved
+"#,
)
.run();
+ let credentials =
+ std::fs::read_to_string(paths::home().join(".cargo/credentials.toml")).unwrap();
+ assert_eq!(credentials, "[registry]\ntoken = \"abcdefg\"\n");
}
#[cargo_test]
-fn libexec_path() {
- // cargo: prefixed names use the sysroot
- let server = registry::RegistryBuilder::new()
- .no_configure_token()
- .build();
+fn both_asymmetric_and_token() {
+ let server = registry::RegistryBuilder::new().build();
cargo_util::paths::append(
&paths::home().join(".cargo/config"),
- br#"
- [registry]
- credential-process = "cargo:doesnotexist"
- "#,
+ format!(
+ r#"
+ [registry]
+ token = "foo"
+ secret-key = "bar"
+ "#,
+ )
+ .as_bytes(),
)
.unwrap();
- cargo_process("login -Z credential-process abcdefg")
+ cargo_process("login -Z credential-process -v abcdefg")
.masquerade_as_nightly_cargo(&["credential-process"])
.replace_crates_io(server.index_url())
- .with_status(101)
.with_stderr(
- // FIXME: Update "Caused by" error message once rust/pull/87704 is merged.
- // On Windows, changing to a custom executable resolver has changed the
- // error messages.
- &format!("\
-[UPDATING] [..]
-[ERROR] failed to execute `[..]libexec/cargo-credential-doesnotexist[EXE]` to store authentication token for registry `crates-io`
-
-Caused by:
- [..]
-"),
+ r#"[UPDATING] [..]
+[WARNING] registry `crates-io` has a `secret_key` configured in [..]config that will be ignored because a `token` is also configured, and the `cargo:token` provider is configured with higher precedence
+[CREDENTIAL] cargo:token login crates-io
+[LOGIN] token for `crates-io` saved
+"#,
)
.run();
}
#[cargo_test]
-fn invalid_token_output() {
- // Error when credential process does not output the expected format for a token.
- let _server = registry::RegistryBuilder::new()
- .alternative()
+fn token_caching() {
+ let server = registry::RegistryBuilder::new()
.no_configure_token()
+ .no_configure_registry()
+ .token(cargo_test_support::registry::Token::Plaintext(
+ "sekrit".to_string(),
+ ))
+ .alternative()
+ .http_api()
+ .http_index()
.build();
+
+ // Token should not be re-used if it is expired
+ let expired_provider = build_provider(
+ "test-cred",
+ r#"{"Ok":{"kind":"get","token":"sekrit","cache":{"expires":0},"operation_independent":true}}"#,
+ );
+
+ // Token should not be re-used for a different operation if it is not operation_independent
+ let non_independent_provider = build_provider(
+ "test-cred",
+ r#"{"Ok":{"kind":"get","token":"sekrit","cache":"session","operation_independent":false}}"#,
+ );
+
+ let p = project()
+ .file(
+ ".cargo/config",
+ &format!(
+ r#"
+ [registries.alternative]
+ index = "{}"
+ credential-provider = ["{expired_provider}"]
+ "#,
+ server.index_url(),
+ ),
+ )
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ description = "foo"
+ license = "MIT"
+ homepage = "https://example.com/"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ let output = r#"[UPDATING] `alternative` index
+{"v":1,"registry":{"index-url":"[..]","name":"alternative"},"kind":"get","operation":"read","args":[]}
+[PACKAGING] foo v0.1.0 [..]
+[PACKAGED] [..]
+{"v":1,"registry":{"index-url":"[..]","name":"alternative"},"kind":"get","operation":"publish","name":"foo","vers":"0.1.0","cksum":"[..]","args":[]}
+[UPLOADING] foo v0.1.0 [..]
+[UPLOADED] foo v0.1.0 [..]
+note: Waiting [..]
+You may press ctrl-c [..]
+[PUBLISHED] foo v0.1.0 [..]
+"#;
+
+ // The output should contain two JSON messages from the provider in boths cases:
+ // The first because the credential is expired, the second because the provider
+ // indicated that the token was non-operation-independent.
+ p.cargo("publish -Z credential-process --registry alternative --no-verify")
+ .masquerade_as_nightly_cargo(&["credential-process"])
+ .with_stderr(output)
+ .run();
+
+ p.change_file(
+ ".cargo/config",
+ &format!(
+ r#"
+ [registries.alternative]
+ index = "{}"
+ credential-provider = ["{non_independent_provider}"]
+ "#,
+ server.index_url(),
+ ),
+ );
+
+ p.cargo("publish -Z credential-process --registry alternative --no-verify")
+ .masquerade_as_nightly_cargo(&["credential-process"])
+ .with_stderr(output)
+ .run();
+}
+
+#[cargo_test]
+fn basic_provider() {
let cred_proj = project()
.at("cred_proj")
.file("Cargo.toml", &basic_manifest("test-cred", "1.0.0"))
- .file("src/main.rs", r#"fn main() { print!("a\nb\n"); } "#)
+ .file("src/main.rs", r#"fn main() {
+ eprintln!("CARGO={:?}", std::env::var("CARGO").ok());
+ eprintln!("CARGO_REGISTRY_NAME_OPT={:?}", std::env::var("CARGO_REGISTRY_NAME_OPT").ok());
+ eprintln!("CARGO_REGISTRY_INDEX_URL={:?}", std::env::var("CARGO_REGISTRY_INDEX_URL").ok());
+ print!("sekrit");
+ }"#)
.build();
cred_proj.cargo("build").run();
- cargo_util::paths::append(
- &paths::home().join(".cargo/config"),
- format!(
+ let _server = registry::RegistryBuilder::new()
+ .no_configure_token()
+ .credential_provider(&[
+ "cargo:token-from-stdout",
+ &toml_bin(&cred_proj, "test-cred"),
+ ])
+ .token(cargo_test_support::registry::Token::Plaintext(
+ "sekrit".to_string(),
+ ))
+ .alternative()
+ .http_api()
+ .auth_required()
+ .build();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
r#"
- [registry]
- credential-process = ["{}"]
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ [dependencies.bar]
+ version = "0.0.1"
+ registry = "alternative"
"#,
- toml_bin(&cred_proj, "test-cred")
)
- .as_bytes(),
- )
- .unwrap();
-
- let p = project()
- .file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
- .file("src/lib.rs", "")
+ .file("src/main.rs", "fn main() {}")
.build();
+ Package::new("bar", "0.0.1").alternative(true).publish();
- p.cargo("publish --no-verify --registry alternative -Z credential-process")
- .masquerade_as_nightly_cargo(&["credential-process"])
- .with_status(101)
+ p.cargo("check -Z credential-process -Z registry-auth")
+ .masquerade_as_nightly_cargo(&["credential-process", "registry-auth"])
.with_stderr(
"\
-[UPDATING] [..]
-[ERROR] credential process `[..]test-cred[EXE]` returned more than one line of output; expected a single token
+[UPDATING] `alternative` index
+CARGO=Some([..])
+CARGO_REGISTRY_NAME_OPT=Some(\"alternative\")
+CARGO_REGISTRY_INDEX_URL=Some([..])
+[DOWNLOADING] crates ...
+[DOWNLOADED] bar v0.0.1 (registry `alternative`)
+[CHECKING] bar v0.0.1 (registry `alternative`)
+[CHECKING] foo v0.0.1 ([..])
+[FINISHED] [..]
",
)
.run();
diff --git a/src/tools/cargo/tests/testsuite/freshness.rs b/src/tools/cargo/tests/testsuite/freshness.rs
index 86b186af8..f28f1ff46 100644
--- a/src/tools/cargo/tests/testsuite/freshness.rs
+++ b/src/tools/cargo/tests/testsuite/freshness.rs
@@ -14,7 +14,8 @@ use super::death;
use cargo_test_support::paths::{self, CargoPathExt};
use cargo_test_support::registry::Package;
use cargo_test_support::{
- basic_manifest, is_coarse_mtime, project, rustc_host, rustc_host_env, sleep_ms,
+ basic_lib_manifest, basic_manifest, is_coarse_mtime, project, rustc_host, rustc_host_env,
+ sleep_ms,
};
#[cargo_test]
@@ -2814,3 +2815,62 @@ directory sources are not [..]
)
.run();
}
+
+#[cargo_test]
+fn skip_mtime_check_in_selected_cargo_home_subdirs() {
+ let p = project()
+ .at("cargo_home/registry/foo")
+ .file("Cargo.toml", &basic_lib_manifest("foo"))
+ .file("src/lib.rs", "")
+ .build();
+ let project_root = p.root();
+ let cargo_home = project_root.parent().unwrap().parent().unwrap();
+ p.cargo("check -v")
+ .env("CARGO_HOME", &cargo_home)
+ .with_stderr(
+ "\
+[CHECKING] foo v0.5.0 ([CWD])
+[RUNNING] `rustc --crate-name foo src/lib.rs [..]
+[FINISHED] dev [..]",
+ )
+ .run();
+ p.change_file("src/lib.rs", "illegal syntax");
+ p.cargo("check -v")
+ .env("CARGO_HOME", &cargo_home)
+ .with_stderr(
+ "\
+[FRESH] foo v0.5.0 ([CWD])
+[FINISHED] dev [..]",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn use_mtime_cache_in_cargo_home() {
+ let p = project()
+ .at("cargo_home/foo")
+ .file("Cargo.toml", &basic_lib_manifest("foo"))
+ .file("src/lib.rs", "")
+ .build();
+ let project_root = p.root();
+ let cargo_home = project_root.parent().unwrap();
+ p.cargo("check -v")
+ .env("CARGO_HOME", &cargo_home)
+ .with_stderr(
+ "\
+[CHECKING] foo v0.5.0 ([CWD])
+[RUNNING] `rustc --crate-name foo src/lib.rs [..]
+[FINISHED] dev [..]",
+ )
+ .run();
+ p.change_file("src/lib.rs", "illegal syntax");
+ p.cargo("check -v")
+ .env("CARGO_HOME", &cargo_home)
+ .with_stderr(
+ "\
+[DIRTY] foo v0.5.0 ([CWD]): [..]
+[CHECKING] foo v0.5.0 ([CWD])
+[RUNNING] `rustc --crate-name foo src/lib.rs [..]",
+ )
+ .run_expect_error();
+}
diff --git a/src/tools/cargo/tests/testsuite/lints.rs b/src/tools/cargo/tests/testsuite/lints.rs
index fb31da30a..854de69e9 100644
--- a/src/tools/cargo/tests/testsuite/lints.rs
+++ b/src/tools/cargo/tests/testsuite/lints.rs
@@ -637,3 +637,58 @@ error: unresolved link to `bar`
)
.run();
}
+
+#[cargo_test]
+fn doctest_respects_lints() {
+ let foo = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [lints.rust]
+ confusable-idents = 'allow'
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ r#"
+/// Test
+///
+/// [`Foo`]
+///
+/// ```
+/// let s = "rust";
+/// let s_s = "rust2";
+/// ```
+pub fn f() {}
+pub const Ě: i32 = 1;
+pub const Ĕ: i32 = 2;
+"#,
+ )
+ .build();
+
+ foo.cargo("check -Zlints")
+ .masquerade_as_nightly_cargo(&["lints"])
+ .with_stderr(
+ "\
+[CHECKING] foo v0.0.1 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+",
+ )
+ .run();
+
+ foo.cargo("test --doc -Zlints")
+ .masquerade_as_nightly_cargo(&["lints"])
+ .with_stderr(
+ "\
+[COMPILING] foo v0.0.1 ([CWD])
+[FINISHED] test [unoptimized + debuginfo] target(s) in [..]s
+[DOCTEST] foo
+",
+ )
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/lockfile_compat.rs b/src/tools/cargo/tests/testsuite/lockfile_compat.rs
index 63148cc07..97dcff123 100644
--- a/src/tools/cargo/tests/testsuite/lockfile_compat.rs
+++ b/src/tools/cargo/tests/testsuite/lockfile_compat.rs
@@ -966,3 +966,198 @@ version = "0.0.1"
let lock = p.read_lockfile();
assert_match_exact(lockfile, &lock);
}
+
+fn create_branch(repo: &git2::Repository, branch: &str, head_id: git2::Oid) {
+ repo.branch(branch, &repo.find_commit(head_id).unwrap(), true)
+ .unwrap();
+}
+
+fn create_tag(repo: &git2::Repository, tag: &str, head_id: git2::Oid) {
+ repo.tag(
+ tag,
+ &repo.find_object(head_id, None).unwrap(),
+ &repo.signature().unwrap(),
+ "make a new tag",
+ false,
+ )
+ .unwrap();
+}
+
+fn v3_and_git_url_encoded(ref_kind: &str, f: impl FnOnce(&git2::Repository, &str, git2::Oid)) {
+ let (git_project, repo) = git::new_repo("dep1", |project| {
+ project
+ .file("Cargo.toml", &basic_lib_manifest("dep1"))
+ .file("src/lib.rs", "")
+ });
+ let url = git_project.url();
+ let head_id = repo.head().unwrap().target().unwrap();
+ // Ref name with special characters
+ let git_ref = "a-_+#$)";
+ f(&repo, git_ref, head_id);
+
+ let lockfile = format!(
+ r#"# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "dep1"
+version = "0.5.0"
+source = "git+{url}?{ref_kind}={git_ref}#{head_id}"
+
+[[package]]
+name = "foo"
+version = "0.0.1"
+dependencies = [
+ "dep1",
+]
+"#,
+ );
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ dep1 = {{ git = '{url}', {ref_kind} = '{git_ref}' }}
+ "#,
+ ),
+ )
+ .file("src/lib.rs", "")
+ .file("Cargo.lock", "version = 3")
+ .build();
+
+ p.cargo("check")
+ .with_stderr(format!(
+ "\
+[UPDATING] git repository `{url}`
+[CHECKING] dep1 v0.5.0 ({url}?{ref_kind}={git_ref}#[..])
+[CHECKING] foo v0.0.1 ([CWD])
+[FINISHED] dev [..]
+"
+ ))
+ .run();
+
+ let lock = p.read_lockfile();
+ assert_match_exact(&lockfile, &lock);
+
+ // v3 doesn't URL-encode URL parameters, but `url` crate does decode as it
+ // was URL-encoded. Therefore Cargo thinks they are from different source
+ // and clones the repository again.
+ p.cargo("check")
+ .with_stderr(format!(
+ "\
+[UPDATING] git repository `{url}`
+[FINISHED] dev [..]
+"
+ ))
+ .run();
+}
+
+#[cargo_test]
+fn v3_and_git_url_encoded_branch() {
+ v3_and_git_url_encoded("branch", create_branch);
+}
+
+#[cargo_test]
+fn v3_and_git_url_encoded_tag() {
+ v3_and_git_url_encoded("tag", create_tag);
+}
+
+#[cargo_test]
+fn v3_and_git_url_encoded_rev() {
+ v3_and_git_url_encoded("rev", create_tag);
+}
+
+fn v4_and_git_url_encoded(ref_kind: &str, f: impl FnOnce(&git2::Repository, &str, git2::Oid)) {
+ let (git_project, repo) = git::new_repo("dep1", |project| {
+ project
+ .file("Cargo.toml", &basic_lib_manifest("dep1"))
+ .file("src/lib.rs", "")
+ });
+ let url = git_project.url();
+ let head_id = repo.head().unwrap().target().unwrap();
+ // Ref name with special characters
+ let git_ref = "a-_+#$)";
+ let encoded_ref = "a-_%2B%23%24%29";
+ f(&repo, git_ref, head_id);
+
+ let lockfile = format!(
+ r#"# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "dep1"
+version = "0.5.0"
+source = "git+{url}?{ref_kind}={encoded_ref}#{head_id}"
+
+[[package]]
+name = "foo"
+version = "0.0.1"
+dependencies = [
+ "dep1",
+]
+"#,
+ );
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ dep1 = {{ git = '{url}', {ref_kind} = '{git_ref}' }}
+ "#,
+ ),
+ )
+ .file("src/lib.rs", "")
+ .file("Cargo.lock", "version = 4")
+ .build();
+
+ p.cargo("check -Znext-lockfile-bump")
+ .masquerade_as_nightly_cargo(&["-Znext-lockfile-bump"])
+ .with_stderr(format!(
+ "\
+[UPDATING] git repository `{url}`
+[CHECKING] dep1 v0.5.0 ({url}?{ref_kind}={git_ref}#[..])
+[CHECKING] foo v0.0.1 ([CWD])
+[FINISHED] dev [..]
+"
+ ))
+ .run();
+
+ let lock = p.read_lockfile();
+ assert_match_exact(&lockfile, &lock);
+
+ // Unlike v3_and_git_url_encoded, v4 encodes URL parameters so no git
+ // repository re-clone happen.
+ p.cargo("check -Znext-lockfile-bump")
+ .masquerade_as_nightly_cargo(&["-Znext-lockfile-bump"])
+ .with_stderr("[FINISHED] dev [..]")
+ .run();
+}
+
+#[cargo_test]
+fn v4_and_git_url_encoded_branch() {
+ v4_and_git_url_encoded("branch", create_branch);
+}
+
+#[cargo_test]
+fn v4_and_git_url_encoded_tag() {
+ v4_and_git_url_encoded("tag", create_tag);
+}
+
+#[cargo_test]
+fn v4_and_git_url_encoded_rev() {
+ v4_and_git_url_encoded("rev", create_tag)
+}
diff --git a/src/tools/cargo/tests/testsuite/login.rs b/src/tools/cargo/tests/testsuite/login.rs
index 85b299f28..16bd29dce 100644
--- a/src/tools/cargo/tests/testsuite/login.rs
+++ b/src/tools/cargo/tests/testsuite/login.rs
@@ -109,12 +109,14 @@ fn empty_login_token() {
cargo_process("login")
.replace_crates_io(registry.index_url())
- .with_stdout("please paste the token found on [..]/me below")
.with_stdin("\t\n")
.with_stderr(
"\
[UPDATING] crates.io index
-[ERROR] please provide a non-empty token
+[ERROR] credential provider `cargo:token` failed action `login`
+
+Caused by:
+ please provide a non-empty token
",
)
.with_status(101)
@@ -125,7 +127,10 @@ fn empty_login_token() {
.arg("")
.with_stderr(
"\
-[ERROR] please provide a non-empty token
+[ERROR] credential provider `cargo:token` failed action `login`
+
+Caused by:
+ please provide a non-empty token
",
)
.with_status(101)
@@ -143,7 +148,6 @@ fn invalid_login_token() {
let check = |stdin: &str, stderr: &str, status: i32| {
cargo_process("login")
.replace_crates_io(registry.index_url())
- .with_stdout("please paste the token found on [..]/me below")
.with_stdin(stdin)
.with_stderr(stderr)
.with_status(status)
@@ -153,12 +157,15 @@ fn invalid_login_token() {
let invalid = |stdin: &str| {
check(
stdin,
- "[ERROR] token contains invalid characters.
-Only printable ISO-8859-1 characters are allowed as it is sent in a HTTPS header.",
+ "[ERROR] credential provider `cargo:token` failed action `login`
+
+Caused by:
+ token contains invalid characters.
+ Only printable ISO-8859-1 characters are allowed as it is sent in a HTTPS header.",
101,
)
};
- let valid = |stdin: &str| check(stdin, "[LOGIN] token for `crates.io` saved", 0);
+ let valid = |stdin: &str| check(stdin, "[LOGIN] token for `crates-io` saved", 0);
// Update config.json so that the rest of the tests don't need to care
// whether or not `Updating` is printed.
@@ -166,7 +173,7 @@ Only printable ISO-8859-1 characters are allowed as it is sent in a HTTPS header
"test",
"\
[UPDATING] crates.io index
-[LOGIN] token for `crates.io` saved
+[LOGIN] token for `crates-io` saved
",
0,
);
@@ -184,54 +191,19 @@ Only printable ISO-8859-1 characters are allowed as it is sent in a HTTPS header
#[cargo_test]
fn bad_asymmetric_token_args() {
- // These cases are kept brief as the implementation is covered by clap, so this is only smoke testing that we have clap configured correctly.
- cargo_process("login --key-subject=foo tok")
- .with_stderr_contains(
- "error: the argument '--key-subject <SUBJECT>' cannot be used with '[token]'",
- )
- .with_status(1)
- .run();
-
- cargo_process("login --generate-keypair tok")
- .with_stderr_contains(
- "error: the argument '--generate-keypair' cannot be used with '[token]'",
- )
- .with_status(1)
- .run();
-
- cargo_process("login --secret-key tok")
- .with_stderr_contains("error: the argument '--secret-key' cannot be used with '[token]'")
- .with_status(1)
- .run();
+ let registry = RegistryBuilder::new()
+ .credential_provider(&["cargo:paseto"])
+ .no_configure_token()
+ .build();
- cargo_process("login --generate-keypair --secret-key")
+ // These cases are kept brief as the implementation is covered by clap, so this is only smoke testing that we have clap configured correctly.
+ cargo_process("login -Zcredential-process -- --key-subject")
+ .masquerade_as_nightly_cargo(&["credential-process"])
+ .replace_crates_io(registry.index_url())
.with_stderr_contains(
- "error: the argument '--generate-keypair' cannot be used with '--secret-key'",
+ " error: a value is required for '--key-subject <SUBJECT>' but none was supplied",
)
- .with_status(1)
- .run();
-}
-
-#[cargo_test]
-fn asymmetric_requires_nightly() {
- let registry = registry::init();
- cargo_process("login --key-subject=foo")
- .replace_crates_io(registry.index_url())
- .with_status(101)
- .with_stderr_contains("[ERROR] the `key-subject` flag is unstable, pass `-Z registry-auth` to enable it\n\
- See https://github.com/rust-lang/cargo/issues/10519 for more information about the `key-subject` flag.")
- .run();
- cargo_process("login --generate-keypair")
- .replace_crates_io(registry.index_url())
.with_status(101)
- .with_stderr_contains("[ERROR] the `generate-keypair` flag is unstable, pass `-Z registry-auth` to enable it\n\
- See https://github.com/rust-lang/cargo/issues/10519 for more information about the `generate-keypair` flag.")
- .run();
- cargo_process("login --secret-key")
- .replace_crates_io(registry.index_url())
- .with_status(101)
- .with_stderr_contains("[ERROR] the `secret-key` flag is unstable, pass `-Z registry-auth` to enable it\n\
- See https://github.com/rust-lang/cargo/issues/10519 for more information about the `secret-key` flag.")
.run();
}
@@ -250,6 +222,28 @@ fn login_with_no_cargo_dir() {
}
#[cargo_test]
+fn login_with_asymmetric_token_and_subject_on_stdin() {
+ let registry = RegistryBuilder::new()
+ .credential_provider(&["cargo:paseto"])
+ .no_configure_token()
+ .build();
+ let credentials = credentials_toml();
+ cargo_process("login -v -Z credential-process -- --key-subject=foo")
+ .masquerade_as_nightly_cargo(&["credential-process"])
+ .replace_crates_io(registry.index_url())
+ .with_stderr_contains(
+ "\
+k3.public.AmDwjlyf8jAV3gm5Z7Kz9xAOcsKslt_Vwp5v-emjFzBHLCtcANzTaVEghTNEMj9PkQ",
+ )
+ .with_stdin("k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36")
+ .run();
+ let credentials = fs::read_to_string(&credentials).unwrap();
+ assert!(credentials.starts_with("[registry]\n"));
+ assert!(credentials.contains("secret-key-subject = \"foo\"\n"));
+ assert!(credentials.contains("secret-key = \"k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36\"\n"));
+}
+
+#[cargo_test]
fn login_with_differently_sized_token() {
// Verify that the configuration file gets properly truncated.
let registry = registry::init();
@@ -278,7 +272,6 @@ fn login_with_token_on_stdin() {
.run();
cargo_process("login")
.replace_crates_io(registry.index_url())
- .with_stdout("please paste the token found on [..]/me below")
.with_stdin("some token")
.run();
let credentials = fs::read_to_string(&credentials).unwrap();
@@ -286,89 +279,38 @@ fn login_with_token_on_stdin() {
}
#[cargo_test]
-fn login_with_asymmetric_token_and_subject_on_stdin() {
- let registry = registry::init();
- let credentials = credentials_toml();
- fs::remove_file(&credentials).unwrap();
- cargo_process("login --key-subject=foo --secret-key -v -Z registry-auth")
- .masquerade_as_nightly_cargo(&["registry-auth"])
- .replace_crates_io(registry.index_url())
- .with_stdout(
- "\
- please paste the API secret key below
-k3.public.AmDwjlyf8jAV3gm5Z7Kz9xAOcsKslt_Vwp5v-emjFzBHLCtcANzTaVEghTNEMj9PkQ",
- )
- .with_stdin("k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36")
- .run();
- let credentials = fs::read_to_string(&credentials).unwrap();
- assert!(credentials.starts_with("[registry]\n"));
- assert!(credentials.contains("secret-key-subject = \"foo\"\n"));
- assert!(credentials.contains("secret-key = \"k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36\"\n"));
-}
-
-#[cargo_test]
fn login_with_asymmetric_token_on_stdin() {
- let registry = registry::init();
+ let _registry = RegistryBuilder::new()
+ .credential_provider(&["cargo:paseto"])
+ .alternative()
+ .no_configure_token()
+ .build();
let credentials = credentials_toml();
- fs::remove_file(&credentials).unwrap();
- cargo_process("login --secret-key -v -Z registry-auth")
- .masquerade_as_nightly_cargo(&["registry-auth"])
- .replace_crates_io(registry.index_url())
- .with_stdout(
+ cargo_process("login -vZ credential-process --registry alternative")
+ .masquerade_as_nightly_cargo(&["credential-process"])
+ .with_stderr(
"\
- please paste the API secret key below
-k3.public.AmDwjlyf8jAV3gm5Z7Kz9xAOcsKslt_Vwp5v-emjFzBHLCtcANzTaVEghTNEMj9PkQ",
- )
- .with_stdin("k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36")
- .run();
- let credentials = fs::read_to_string(&credentials).unwrap();
- assert_eq!(credentials, "[registry]\nsecret-key = \"k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36\"\n");
-}
-
-#[cargo_test]
-fn login_with_asymmetric_key_subject_without_key() {
- let registry = registry::init();
- let credentials = credentials_toml();
- fs::remove_file(&credentials).unwrap();
- cargo_process("login --key-subject=foo -Z registry-auth")
- .masquerade_as_nightly_cargo(&["registry-auth"])
- .replace_crates_io(registry.index_url())
- .with_stderr_contains("error: need a secret_key to set a key_subject")
- .with_status(101)
- .run();
-
- // ok so add a secret_key to the credentials
- cargo_process("login --secret-key -v -Z registry-auth")
- .masquerade_as_nightly_cargo(&["registry-auth"])
- .replace_crates_io(registry.index_url())
- .with_stdout(
- "please paste the API secret key below
+[UPDATING] [..]
+[CREDENTIAL] cargo:paseto login alternative
k3.public.AmDwjlyf8jAV3gm5Z7Kz9xAOcsKslt_Vwp5v-emjFzBHLCtcANzTaVEghTNEMj9PkQ",
)
.with_stdin("k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36")
.run();
-
- // and then it should work
- cargo_process("login --key-subject=foo -Z registry-auth")
- .masquerade_as_nightly_cargo(&["registry-auth"])
- .replace_crates_io(registry.index_url())
- .run();
-
let credentials = fs::read_to_string(&credentials).unwrap();
- assert!(credentials.starts_with("[registry]\n"));
- assert!(credentials.contains("secret-key-subject = \"foo\"\n"));
- assert!(credentials.contains("secret-key = \"k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36\"\n"));
+ assert_eq!(credentials, "[registries.alternative]\nsecret-key = \"k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36\"\n");
}
#[cargo_test]
fn login_with_generate_asymmetric_token() {
- let registry = registry::init();
+ let _registry = RegistryBuilder::new()
+ .credential_provider(&["cargo:paseto"])
+ .alternative()
+ .no_configure_token()
+ .build();
let credentials = credentials_toml();
- fs::remove_file(&credentials).unwrap();
- cargo_process("login --generate-keypair -Z registry-auth")
- .masquerade_as_nightly_cargo(&["registry-auth"])
- .replace_crates_io(registry.index_url())
- .with_stdout("k3.public.[..]")
+ cargo_process("login -Z credential-process --registry alternative")
+ .masquerade_as_nightly_cargo(&["credential-process"])
+ .with_stderr("[UPDATING] `alternative` index\nk3.public.[..]")
.run();
let credentials = fs::read_to_string(&credentials).unwrap();
assert!(credentials.contains("secret-key = \"k3.secret."));
diff --git a/src/tools/cargo/tests/testsuite/main.rs b/src/tools/cargo/tests/testsuite/main.rs
index 2c282c0a3..8279f5818 100644
--- a/src/tools/cargo/tests/testsuite/main.rs
+++ b/src/tools/cargo/tests/testsuite/main.rs
@@ -18,15 +18,50 @@ mod build_script;
mod build_script_env;
mod build_script_extra_link_arg;
mod cache_messages;
+mod cargo;
mod cargo_add;
mod cargo_alias_config;
+mod cargo_bench;
+mod cargo_build;
+mod cargo_check;
+mod cargo_clean;
mod cargo_command;
mod cargo_config;
+mod cargo_doc;
mod cargo_env_config;
mod cargo_features;
+mod cargo_fetch;
+mod cargo_fix;
+mod cargo_generate_lockfile;
+mod cargo_git_checkout;
+mod cargo_help;
+mod cargo_init;
+mod cargo_install;
+mod cargo_locate_project;
+mod cargo_login;
+mod cargo_logout;
+mod cargo_metadata;
mod cargo_new;
+mod cargo_owner;
+mod cargo_package;
+mod cargo_pkgid;
+mod cargo_publish;
+mod cargo_read_manifest;
mod cargo_remove;
+mod cargo_report;
+mod cargo_run;
+mod cargo_rustc;
+mod cargo_rustdoc;
+mod cargo_search;
mod cargo_targets;
+mod cargo_test;
+mod cargo_tree;
+mod cargo_uninstall;
+mod cargo_update;
+mod cargo_vendor;
+mod cargo_verify_project;
+mod cargo_version;
+mod cargo_yank;
mod cfg;
mod check;
mod check_cfg;
@@ -65,7 +100,6 @@ mod glob_targets;
mod help;
mod https;
mod inheritable_workspace_fields;
-mod init;
mod install;
mod install_upgrade;
mod jobserver;
diff --git a/src/tools/cargo/tests/testsuite/owner.rs b/src/tools/cargo/tests/testsuite/owner.rs
index 9fc960c92..7b38bcc5e 100644
--- a/src/tools/cargo/tests/testsuite/owner.rs
+++ b/src/tools/cargo/tests/testsuite/owner.rs
@@ -117,8 +117,8 @@ fn simple_add_with_asymmetric() {
// The http_api server will check that the authorization is correct.
// If the authorization was not sent then we would get an unauthorized error.
p.cargo("owner -a username")
- .arg("-Zregistry-auth")
- .masquerade_as_nightly_cargo(&["registry-auth"])
+ .arg("-Zcredential-process")
+ .masquerade_as_nightly_cargo(&["credential-process"])
.replace_crates_io(registry.index_url())
.with_status(0)
.run();
@@ -184,9 +184,9 @@ fn simple_remove_with_asymmetric() {
// The http_api server will check that the authorization is correct.
// If the authorization was not sent then we would get an unauthorized error.
p.cargo("owner -r username")
- .arg("-Zregistry-auth")
+ .arg("-Zcredential-process")
.replace_crates_io(registry.index_url())
- .masquerade_as_nightly_cargo(&["registry-auth"])
+ .masquerade_as_nightly_cargo(&["credential-process"])
.with_status(0)
.run();
}
diff --git a/src/tools/cargo/tests/testsuite/package.rs b/src/tools/cargo/tests/testsuite/package.rs
index 3b4328242..010523fda 100644
--- a/src/tools/cargo/tests/testsuite/package.rs
+++ b/src/tools/cargo/tests/testsuite/package.rs
@@ -2983,3 +2983,115 @@ src/main.rs.bak
],
);
}
+
+#[cargo_test]
+#[cfg(windows)] // windows is the platform that is most consistently configured for case insensitive filesystems
+fn normalize_case() {
+ let p = project()
+ .file("src/main.rs", r#"fn main() { println!("hello"); }"#)
+ .file("src/bar.txt", "") // should be ignored when packaging
+ .build();
+ // Workaround `project()` making a `Cargo.toml` on our behalf
+ std::fs::remove_file(p.root().join("Cargo.toml")).unwrap();
+ std::fs::write(
+ p.root().join("cargo.toml"),
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ exclude = ["*.txt"]
+ license = "MIT"
+ description = "foo"
+ "#,
+ )
+ .unwrap();
+
+ p.cargo("package")
+ .with_stderr(
+ "\
+[WARNING] manifest has no documentation[..]
+See [..]
+[PACKAGING] foo v0.0.1 ([CWD])
+[VERIFYING] foo v0.0.1 ([CWD])
+[COMPILING] foo v0.0.1 ([CWD][..])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[PACKAGED] 4 files, [..] ([..] compressed)
+",
+ )
+ .run();
+ assert!(p.root().join("target/package/foo-0.0.1.crate").is_file());
+ p.cargo("package -l")
+ .with_stdout(
+ "\
+Cargo.lock
+Cargo.toml
+Cargo.toml.orig
+src/main.rs
+",
+ )
+ .run();
+ p.cargo("package").with_stdout("").run();
+
+ let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
+ validate_crate_contents(
+ f,
+ "foo-0.0.1.crate",
+ &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"],
+ &[],
+ );
+}
+
+#[cargo_test]
+#[cfg(target_os = "linux")] // linux is generally configured to be case sensitive
+fn mixed_case() {
+ let manifest = r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ exclude = ["*.txt"]
+ license = "MIT"
+ description = "foo"
+ "#;
+ let p = project()
+ .file("Cargo.toml", manifest)
+ .file("cargo.toml", manifest)
+ .file("src/main.rs", r#"fn main() { println!("hello"); }"#)
+ .file("src/bar.txt", "") // should be ignored when packaging
+ .build();
+
+ p.cargo("package")
+ .with_stderr(
+ "\
+[WARNING] manifest has no documentation[..]
+See [..]
+[PACKAGING] foo v0.0.1 ([CWD])
+[VERIFYING] foo v0.0.1 ([CWD])
+[COMPILING] foo v0.0.1 ([CWD][..])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[PACKAGED] 4 files, [..] ([..] compressed)
+",
+ )
+ .run();
+ assert!(p.root().join("target/package/foo-0.0.1.crate").is_file());
+ p.cargo("package -l")
+ .with_stdout(
+ "\
+Cargo.lock
+Cargo.toml
+Cargo.toml.orig
+src/main.rs
+",
+ )
+ .run();
+ p.cargo("package").with_stdout("").run();
+
+ let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
+ validate_crate_contents(
+ f,
+ "foo-0.0.1.crate",
+ &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"],
+ &[],
+ );
+}
diff --git a/src/tools/cargo/tests/testsuite/profile_targets.rs b/src/tools/cargo/tests/testsuite/profile_targets.rs
index a88ca34fd..f2de169b9 100644
--- a/src/tools/cargo/tests/testsuite/profile_targets.rs
+++ b/src/tools/cargo/tests/testsuite/profile_targets.rs
@@ -328,7 +328,7 @@ fn profile_selection_test() {
[RUNNING] `[..]/deps/foo-[..]`
[RUNNING] `[..]/deps/test1-[..]`
[DOCTEST] foo
-[RUNNING] `rustdoc [..]--test [..]
+[RUNNING] `[..] rustdoc [..]--test [..]
").run();
p.cargo("test -vv")
.with_stderr_unordered(
@@ -341,7 +341,7 @@ fn profile_selection_test() {
[RUNNING] `[..]/deps/foo-[..]`
[RUNNING] `[..]/deps/test1-[..]`
[DOCTEST] foo
-[RUNNING] `rustdoc [..]--test [..]
+[RUNNING] `[..] rustdoc [..]--test [..]
",
)
.run();
@@ -395,7 +395,7 @@ fn profile_selection_test_release() {
[RUNNING] `[..]/deps/foo-[..]`
[RUNNING] `[..]/deps/test1-[..]`
[DOCTEST] foo
-[RUNNING] `rustdoc [..]--test [..]`
+[RUNNING] `[..] rustdoc [..]--test [..]`
").run();
p.cargo("test --release -vv")
.with_stderr_unordered(
@@ -408,7 +408,7 @@ fn profile_selection_test_release() {
[RUNNING] `[..]/deps/foo-[..]`
[RUNNING] `[..]/deps/test1-[..]`
[DOCTEST] foo
-[RUNNING] `rustdoc [..]--test [..]
+[RUNNING] `[..] rustdoc [..]--test [..]
",
)
.run();
diff --git a/src/tools/cargo/tests/testsuite/publish.rs b/src/tools/cargo/tests/testsuite/publish.rs
index 45b7c7da5..50ad697d5 100644
--- a/src/tools/cargo/tests/testsuite/publish.rs
+++ b/src/tools/cargo/tests/testsuite/publish.rs
@@ -194,8 +194,8 @@ fn simple_publish_with_asymmetric() {
.file("src/main.rs", "fn main() {}")
.build();
- p.cargo("publish --no-verify -Zregistry-auth --registry dummy-registry")
- .masquerade_as_nightly_cargo(&["registry-auth"])
+ p.cargo("publish --no-verify -Zcredential-process --registry dummy-registry")
+ .masquerade_as_nightly_cargo(&["credential-process"])
.with_stderr(
"\
[UPDATING] `dummy-registry` index
@@ -338,7 +338,7 @@ fn git_deps() {
.file("src/main.rs", "fn main() {}")
.build();
- p.cargo("publish -v --no-verify")
+ p.cargo("publish --no-verify")
.replace_crates_io(registry.index_url())
.with_status(101)
.with_stderr(
@@ -2023,10 +2023,10 @@ fn api_other_error() {
[ERROR] failed to publish to registry at http://127.0.0.1:[..]/
Caused by:
- invalid response from server
+ invalid response body from server
Caused by:
- response body was not valid utf-8
+ invalid utf-8 sequence of [..]
",
)
.run();
diff --git a/src/tools/cargo/tests/testsuite/registry.rs b/src/tools/cargo/tests/testsuite/registry.rs
index bd5e42b45..8982b1cb6 100644
--- a/src/tools/cargo/tests/testsuite/registry.rs
+++ b/src/tools/cargo/tests/testsuite/registry.rs
@@ -3529,3 +3529,38 @@ fn unpack_again_when_cargo_ok_is_unrecognized() {
let ok = fs::read_to_string(&cargo_ok).unwrap();
assert_eq!(&ok, r#"{"v":1}"#);
}
+
+#[cargo_test]
+fn differ_only_by_metadata() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies]
+ baz = "=0.0.1"
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ Package::new("baz", "0.0.1+b").publish();
+ Package::new("baz", "0.0.1+c").yanked(true).publish();
+
+ p.cargo("check")
+ .with_stderr(
+ "\
+[UPDATING] `dummy-registry` index
+[DOWNLOADING] crates ...
+[DOWNLOADED] [..] v0.0.1+b (registry `dummy-registry`)
+[CHECKING] baz v0.0.1+b
+[CHECKING] foo v0.0.1 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+",
+ )
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/registry_auth.rs b/src/tools/cargo/tests/testsuite/registry_auth.rs
index 97cdf6748..4422c638a 100644
--- a/src/tools/cargo/tests/testsuite/registry_auth.rs
+++ b/src/tools/cargo/tests/testsuite/registry_auth.rs
@@ -6,8 +6,9 @@ use cargo_test_support::{project, Execs, Project};
fn cargo(p: &Project, s: &str) -> Execs {
let mut e = p.cargo(s);
- e.masquerade_as_nightly_cargo(&["registry-auth"])
- .arg("-Zregistry-auth");
+ e.masquerade_as_nightly_cargo(&["registry-auth", "credential-process"])
+ .arg("-Zregistry-auth")
+ .arg("-Zcredential-process");
e
}
@@ -149,95 +150,6 @@ fn environment_token_with_asymmetric() {
}
#[cargo_test]
-fn warn_both_asymmetric_and_token() {
- let _server = RegistryBuilder::new()
- .alternative()
- .no_configure_token()
- .build();
- let p = project()
- .file(
- ".cargo/config",
- r#"
- [registries.alternative]
- token = "sekrit"
- secret-key = "k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36"
- "#,
- )
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.1.0"
- description = "foo"
- authors = []
- license = "MIT"
- homepage = "https://example.com/"
- "#,
- )
- .file("src/lib.rs", "")
- .build();
-
- p.cargo("publish --no-verify --registry alternative")
- .masquerade_as_nightly_cargo(&["credential-process", "registry-auth"])
- .arg("-Zregistry-auth")
- .with_status(101)
- .with_stderr(
- "\
-[UPDATING] [..]
-[ERROR] both `token` and `secret-key` were specified in the config for registry `alternative`.
-Only one of these values may be set, remove one or the other to proceed.
-",
- )
- .run();
-}
-
-#[cargo_test]
-fn warn_both_asymmetric_and_credential_process() {
- let _server = RegistryBuilder::new()
- .alternative()
- .no_configure_token()
- .build();
- let p = project()
- .file(
- ".cargo/config",
- r#"
- [registries.alternative]
- credential-process = "false"
- secret-key = "k3.secret.fNYVuMvBgOlljt9TDohnaYLblghqaHoQquVZwgR6X12cBFHZLFsaU3q7X3k1Zn36"
- "#,
- )
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.1.0"
- description = "foo"
- authors = []
- license = "MIT"
- homepage = "https://example.com/"
- "#,
- )
- .file("src/lib.rs", "")
- .build();
-
- p.cargo("publish --no-verify --registry alternative")
- .masquerade_as_nightly_cargo(&["credential-process", "registry-auth"])
- .arg("-Zcredential-process")
- .arg("-Zregistry-auth")
- .with_status(101)
- .with_stderr(
- "\
-[UPDATING] [..]
-[ERROR] both `credential-process` and `secret-key` were specified in the config for registry `alternative`.
-Only one of these values may be set, remove one or the other to proceed.
-",
- )
- .run();
-}
-
-#[cargo_test]
fn bad_environment_token_with_asymmetric_subject() {
let registry = RegistryBuilder::new()
.alternative()
@@ -463,7 +375,6 @@ fn login() {
let p = make_project();
cargo(&p, "login --registry alternative")
- .with_stdout("please paste the token found on https://test-registry-login/me below")
.with_stdin("sekrit")
.run();
}
@@ -478,7 +389,6 @@ fn login_existing_token() {
let p = make_project();
cargo(&p, "login --registry alternative")
- .with_stdout("please paste the token found on file://[..]/me below")
.with_stdin("sekrit")
.run();
}
diff --git a/src/tools/cargo/tests/testsuite/run.rs b/src/tools/cargo/tests/testsuite/run.rs
index 586502288..64cf4e16c 100644
--- a/src/tools/cargo/tests/testsuite/run.rs
+++ b/src/tools/cargo/tests/testsuite/run.rs
@@ -1,6 +1,8 @@
//! Tests for the `cargo run` command.
-use cargo_test_support::{basic_bin_manifest, basic_lib_manifest, project, Project};
+use cargo_test_support::{
+ basic_bin_manifest, basic_lib_manifest, basic_manifest, project, Project,
+};
use cargo_util::paths::dylib_path_envvar;
#[cargo_test]
@@ -1417,6 +1419,24 @@ fn default_run_workspace() {
}
#[cargo_test]
+fn print_env_verbose() {
+ let p = project()
+ .file("Cargo.toml", &basic_manifest("a", "0.0.1"))
+ .file("src/main.rs", r#"fn main() {println!("run-a");}"#)
+ .build();
+
+ p.cargo("run -vv")
+ .with_stderr(
+ "\
+[COMPILING] a v0.0.1 ([CWD])
+[RUNNING] `[..]CARGO_MANIFEST_DIR=[CWD][..] rustc --crate-name a[..]`
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[RUNNING] `[..]CARGO_MANIFEST_DIR=[CWD][..] target/debug/a[EXE]`",
+ )
+ .run();
+}
+
+#[cargo_test]
#[cfg(target_os = "macos")]
fn run_link_system_path_macos() {
use cargo_test_support::paths::{self, CargoPathExt};
diff --git a/src/tools/cargo/tests/testsuite/script.rs b/src/tools/cargo/tests/testsuite/script.rs
index fcf58de69..0c0441d62 100644
--- a/src/tools/cargo/tests/testsuite/script.rs
+++ b/src/tools/cargo/tests/testsuite/script.rs
@@ -35,7 +35,7 @@ args: []
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] echo v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
[RUNNING] `[..]/debug/echo[EXE]`
@@ -59,7 +59,7 @@ args: []
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] echo v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
[RUNNING] `[..]/debug/echo[EXE]`
@@ -136,7 +136,7 @@ args: []
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] echo v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
[RUNNING] `[..]/debug/echo[EXE]`
@@ -260,7 +260,7 @@ fn main() {
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] script v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
[RUNNING] `[..]/debug/script[EXE]`
@@ -289,7 +289,7 @@ fn main() {
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] script v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
[RUNNING] `[..]/debug/script[EXE]`
@@ -306,7 +306,7 @@ fn main() {
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
[RUNNING] `[..]/debug/script[EXE]`
",
@@ -323,7 +323,7 @@ fn main() {
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] script v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
[RUNNING] `[..]/debug/script[EXE]`
@@ -435,7 +435,7 @@ fn main() {
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] script v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
[RUNNING] `[..]/debug/script[EXE]`
@@ -460,7 +460,7 @@ args: ["-NotAnArg"]
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] script v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
[RUNNING] `[..]/debug/script[EXE] -NotAnArg`
@@ -485,7 +485,7 @@ args: ["-NotAnArg"]
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] script v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
[RUNNING] `[..]/debug/script[EXE] -NotAnArg`
@@ -510,7 +510,7 @@ args: ["--help"]
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] script v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
[RUNNING] `[..]/debug/script[EXE] --help`
@@ -534,7 +534,7 @@ args: []
"#,
)
.with_stderr(
- r#"[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+ r#"[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] s-h-w-c- v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
[RUNNING] `[..]/debug/s-h-w-c-[EXE]`
@@ -544,6 +544,52 @@ args: []
}
#[cargo_test]
+fn test_name_has_leading_number() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project()
+ .file("42answer.rs", script)
+ .build();
+
+ p.cargo("-Zscript -v 42answer.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"bin: [..]/debug/answer[EXE]
+args: []
+"#,
+ )
+ .with_stderr(
+ r#"[WARNING] `package.edition` is unspecified, defaulting to `2021`
+[COMPILING] answer v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/answer[EXE]`
+"#,
+ )
+ .run();
+}
+
+#[cargo_test]
+fn test_name_is_number() {
+ let script = ECHO_SCRIPT;
+ let p = cargo_test_support::project().file("42.rs", script).build();
+
+ p.cargo("-Zscript -v 42.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_stdout(
+ r#"bin: [..]/debug/package[EXE]
+args: []
+"#,
+ )
+ .with_stderr(
+ r#"[WARNING] `package.edition` is unspecified, defaulting to `2021`
+[COMPILING] package v0.0.0 ([ROOT]/foo)
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
+[RUNNING] `[..]/debug/package[EXE]`
+"#,
+ )
+ .run();
+}
+
+#[cargo_test]
fn script_like_dir() {
let p = cargo_test_support::project()
.file("script.rs/foo", "something")
@@ -600,7 +646,7 @@ fn main() {
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[UPDATING] `dummy-registry` index
[DOWNLOADING] crates ...
[DOWNLOADED] script v1.0.0 (registry `dummy-registry`)
@@ -640,7 +686,7 @@ fn main() {
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] bar v0.0.1 ([ROOT]/foo/bar)
[COMPILING] script v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
@@ -670,7 +716,7 @@ fn main() {
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] script v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
[RUNNING] `[..]/debug/script[EXE] --help`
@@ -699,7 +745,7 @@ fn main() {
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] script v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
[RUNNING] `[..]/debug/script[EXE] --help`
@@ -724,7 +770,7 @@ args: []
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] script v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
[RUNNING] `[ROOT]/home/.cargo/target/[..]/debug/script[EXE]`
@@ -752,7 +798,7 @@ args: []
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] script v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
[RUNNING] `[ROOT]/home/.cargo/target/[..]/debug/script[EXE]`
@@ -815,7 +861,7 @@ fn cmd_check_with_embedded() {
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[CHECKING] script v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
",
@@ -876,7 +922,7 @@ fn cmd_build_with_embedded() {
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] script v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
",
@@ -904,7 +950,7 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; fini
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] script v0.0.0 ([ROOT]/foo)
[FINISHED] test [unoptimized + debuginfo] target(s) in [..]s
[RUNNING] unittests script.rs ([..])
@@ -933,7 +979,7 @@ fn cmd_clean_with_embedded() {
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
",
)
.run();
@@ -954,7 +1000,7 @@ fn cmd_generate_lockfile_with_embedded() {
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
",
)
.run();
@@ -1039,7 +1085,7 @@ fn cmd_metadata_with_embedded() {
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
",
)
.run();
@@ -1095,7 +1141,7 @@ fn cmd_read_manifest_with_embedded() {
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
",
)
.run();
@@ -1116,7 +1162,7 @@ args: []
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
[COMPILING] script v0.0.0 ([ROOT]/foo)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s
[RUNNING] `[..]/debug/script[EXE]`
@@ -1140,7 +1186,7 @@ script v0.0.0 ([ROOT]/foo)
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
",
)
.run();
@@ -1160,7 +1206,7 @@ fn cmd_update_with_embedded() {
)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
",
)
.run();
@@ -1177,7 +1223,61 @@ fn cmd_verify_project_with_embedded() {
.with_json(r#"{"success":"true"}"#)
.with_stderr(
"\
-[WARNING] `package.edition` is unspecifiead, defaulting to `2021`
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn cmd_pkgid_with_embedded() {
+ let p = cargo_test_support::project()
+ .file("script.rs", ECHO_SCRIPT)
+ .build();
+
+ p.cargo("-Zscript pkgid --manifest-path script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_status(101)
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
+[ERROR] [ROOT]/foo/script.rs is unsupported by `cargo pkgid`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn cmd_package_with_embedded() {
+ let p = cargo_test_support::project()
+ .file("script.rs", ECHO_SCRIPT)
+ .build();
+
+ p.cargo("-Zscript package --manifest-path script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_status(101)
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
+[ERROR] [ROOT]/foo/script.rs is unsupported by `cargo package`
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn cmd_publish_with_embedded() {
+ let p = cargo_test_support::project()
+ .file("script.rs", ECHO_SCRIPT)
+ .build();
+
+ p.cargo("-Zscript publish --manifest-path script.rs")
+ .masquerade_as_nightly_cargo(&["script"])
+ .with_status(101)
+ .with_stderr(
+ "\
+[WARNING] `package.edition` is unspecified, defaulting to `2021`
+[ERROR] [ROOT]/foo/script.rs is unsupported by `cargo publish`
",
)
.run();
diff --git a/src/tools/cargo/tests/testsuite/test.rs b/src/tools/cargo/tests/testsuite/test.rs
index 6a062cfb6..c6ae4ce61 100644
--- a/src/tools/cargo/tests/testsuite/test.rs
+++ b/src/tools/cargo/tests/testsuite/test.rs
@@ -389,10 +389,10 @@ failures:
---- test_hello stdout ----
[..]thread '[..]' panicked at [..]",
)
- .with_stdout_contains("[..]assertion failed[..]")
- .with_stdout_contains("[..]`(left == right)`[..]")
- .with_stdout_contains("[..]left: `\"hello\"`,[..]")
- .with_stdout_contains("[..]right: `\"nope\"`[..]")
+ .with_stdout_contains("[..]assertion [..]failed[..]")
+ .with_stdout_contains("[..]left == right[..]")
+ .with_stdout_contains("[..]left: [..]\"hello\"[..]")
+ .with_stdout_contains("[..]right: [..]\"nope\"[..]")
.with_stdout_contains("[..]src/main.rs:12[..]")
.with_stdout_contains(
"\
@@ -4794,6 +4794,21 @@ error: test failed, to rerun pass `--test t2`
Caused by:
process didn't exit successfully: `[ROOT]/foo/target/debug/deps/t2[..]` (exit [..]: 4)
+note: test exited abnormally; to see the full output pass --nocapture to the harness.
+",
+ )
+ .with_status(4)
+ .run();
+
+ p.cargo("test --test t2 -- --nocapture")
+ .with_stderr(
+ "\
+[FINISHED] test [..]
+[RUNNING] tests/t2.rs (target/debug/deps/t2[..])
+error: test failed, to rerun pass `--test t2`
+
+Caused by:
+ process didn't exit successfully: `[ROOT]/foo/target/debug/deps/t2[..]` (exit [..]: 4)
",
)
.with_status(4)
@@ -4811,6 +4826,7 @@ error: test failed, to rerun pass `--test t2`
Caused by:
process didn't exit successfully: `[ROOT]/foo/target/debug/deps/t2[..]` (exit [..]: 4)
+note: test exited abnormally; to see the full output pass --nocapture to the harness.
error: 2 targets failed:
`--test t1`
`--test t2`
@@ -4818,4 +4834,51 @@ error: 2 targets failed:
)
.with_status(101)
.run();
+
+ p.cargo("test --no-fail-fast -- --nocapture")
+ .with_stderr_does_not_contain("test exited abnormally; to see the full output pass --nocapture to the harness.")
+ .with_stderr_contains("[..]thread 't' panicked [..] tests/t1[..]")
+ .with_stderr_contains("note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace")
+ .with_stderr_contains("[..]process didn't exit successfully: `[ROOT]/foo/target/debug/deps/t2[..]` (exit [..]: 4)")
+ .with_status(101)
+ .run();
+}
+
+#[cargo_test]
+fn cargo_test_no_keep_going() {
+ let p = project()
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/main.rs", "")
+ .build();
+
+ p.cargo("test --keep-going")
+ .with_stderr(
+ "\
+error: unexpected argument `--keep-going` found
+
+ tip: to run as many tests as possible without failing fast, use `--no-fail-fast`",
+ )
+ .with_status(101)
+ .run();
+}
+
+#[cargo_test]
+fn cargo_test_print_env_verbose() {
+ let p = project()
+ .file("Cargo.toml", &basic_manifest("foo", "0.0.1"))
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("test -vv")
+ .with_stderr(
+ "\
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `[..]CARGO_MANIFEST_DIR=[CWD][..] rustc --crate-name foo[..]`
+[RUNNING] `[..]CARGO_MANIFEST_DIR=[CWD][..] rustc --crate-name foo[..]`
+[FINISHED] test [unoptimized + debuginfo] target(s) in [..]
+[RUNNING] `[..]CARGO_MANIFEST_DIR=[CWD][..] [CWD]/target/debug/deps/foo-[..][EXE]`
+[DOCTEST] foo
+[RUNNING] `[..]CARGO_MANIFEST_DIR=[CWD][..] rustdoc --crate-type lib --crate-name foo[..]",
+ )
+ .run();
}
diff --git a/src/tools/cargo/tests/testsuite/update.rs b/src/tools/cargo/tests/testsuite/update.rs
index 1d3ee05b7..d42345355 100644
--- a/src/tools/cargo/tests/testsuite/update.rs
+++ b/src/tools/cargo/tests/testsuite/update.rs
@@ -464,6 +464,46 @@ fn update_aggressive() {
.run();
}
+#[cargo_test]
+fn update_aggressive_conflicts_with_precise() {
+ Package::new("log", "0.1.0").publish();
+ Package::new("serde", "0.2.1").dep("log", "0.1").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "bar"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies]
+ serde = "0.2"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("check").run();
+
+ Package::new("log", "0.1.1").publish();
+ Package::new("serde", "0.2.2").dep("log", "0.1").publish();
+
+ p.cargo("update -p serde:0.2.1 --precise 0.2.2 --aggressive")
+ .with_status(1)
+ .with_stderr(
+ "\
+error: the argument '--precise <PRECISE>' cannot be used with '--aggressive'
+
+Usage: cargo[EXE] update --package [<SPEC>] --precise <PRECISE>
+
+For more information, try '--help'.
+",
+ )
+ .run();
+}
+
// cargo update should respect its arguments even without a lockfile.
// See issue "Running cargo update without a Cargo.lock ignores arguments"
// at <https://github.com/rust-lang/cargo/issues/6872>.
diff --git a/src/tools/cargo/tests/testsuite/yank.rs b/src/tools/cargo/tests/testsuite/yank.rs
index 684a04508..c0bd24776 100644
--- a/src/tools/cargo/tests/testsuite/yank.rs
+++ b/src/tools/cargo/tests/testsuite/yank.rs
@@ -76,14 +76,14 @@ fn explicit_version_with_asymmetric() {
// The http_api server will check that the authorization is correct.
// If the authorization was not sent then we would get an unauthorized error.
p.cargo("yank --version 0.0.1")
- .arg("-Zregistry-auth")
- .masquerade_as_nightly_cargo(&["registry-auth"])
+ .arg("-Zcredential-process")
+ .masquerade_as_nightly_cargo(&["credential-process"])
.replace_crates_io(registry.index_url())
.run();
p.cargo("yank --undo --version 0.0.1")
- .arg("-Zregistry-auth")
- .masquerade_as_nightly_cargo(&["registry-auth"])
+ .arg("-Zcredential-process")
+ .masquerade_as_nightly_cargo(&["credential-process"])
.replace_crates_io(registry.index_url())
.run();
}