summaryrefslogtreecommitdiffstats
path: root/src/tools/cargo
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 05:48:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-07 05:48:48 +0000
commitef24de24a82fe681581cc130f342363c47c0969a (patch)
tree0d494f7e1a38b95c92426f58fe6eaa877303a86c /src/tools/cargo
parentReleasing progress-linux version 1.74.1+dfsg1-1~progress7.99u1. (diff)
downloadrustc-ef24de24a82fe681581cc130f342363c47c0969a.tar.xz
rustc-ef24de24a82fe681581cc130f342363c47c0969a.zip
Merging upstream version 1.75.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/cargo')
-rw-r--r--src/tools/cargo/.github/renovate.json540
-rw-r--r--src/tools/cargo/.github/workflows/audit.yml2
-rw-r--r--src/tools/cargo/.github/workflows/contrib.yml26
-rw-r--r--src/tools/cargo/.github/workflows/main.yml34
-rw-r--r--src/tools/cargo/CHANGELOG.md219
-rw-r--r--src/tools/cargo/CONTRIBUTING.md10
-rw-r--r--src/tools/cargo/Cargo.lock470
-rw-r--r--src/tools/cargo/Cargo.toml87
-rw-r--r--src/tools/cargo/ci/generate.py49
-rw-r--r--src/tools/cargo/crates/cargo-platform/Cargo.toml3
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/compare.rs2
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/diff.rs2
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/lib.rs48
-rw-r--r--src/tools/cargo/crates/cargo-util/Cargo.toml4
-rw-r--r--src/tools/cargo/crates/cargo-util/src/paths.rs39
-rw-r--r--src/tools/cargo/crates/crates-io/Cargo.toml2
-rw-r--r--src/tools/cargo/crates/crates-io/lib.rs4
-rw-r--r--src/tools/cargo/crates/home/Cargo.toml3
-rw-r--r--src/tools/cargo/crates/home/src/lib.rs1
-rw-r--r--src/tools/cargo/crates/resolver-tests/src/lib.rs21
-rw-r--r--src/tools/cargo/crates/xtask-bump-check/Cargo.toml3
-rw-r--r--src/tools/cargo/crates/xtask-bump-check/src/xtask.rs24
-rw-r--r--src/tools/cargo/credential/cargo-credential-1password/Cargo.toml3
l---------src/tools/cargo/credential/cargo-credential-1password/LICENSE-APACHE1
l---------src/tools/cargo/credential/cargo-credential-1password/LICENSE-MIT1
-rw-r--r--src/tools/cargo/credential/cargo-credential-1password/README.md38
-rw-r--r--src/tools/cargo/credential/cargo-credential-1password/src/main.rs4
-rw-r--r--src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml3
l---------src/tools/cargo/credential/cargo-credential-libsecret/LICENSE-APACHE1
l---------src/tools/cargo/credential/cargo-credential-libsecret/LICENSE-MIT1
-rw-r--r--src/tools/cargo/credential/cargo-credential-macos-keychain/Cargo.toml3
l---------src/tools/cargo/credential/cargo-credential-macos-keychain/LICENSE-APACHE1
l---------src/tools/cargo/credential/cargo-credential-macos-keychain/LICENSE-MIT1
-rw-r--r--src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml3
l---------src/tools/cargo/credential/cargo-credential-wincred/LICENSE-APACHE1
l---------src/tools/cargo/credential/cargo-credential-wincred/LICENSE-MIT1
-rw-r--r--src/tools/cargo/credential/cargo-credential/Cargo.toml4
l---------src/tools/cargo/credential/cargo-credential/LICENSE-APACHE1
l---------src/tools/cargo/credential/cargo-credential/LICENSE-MIT1
-rw-r--r--src/tools/cargo/src/bin/cargo/cli.rs32
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/add.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/bench.rs5
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/build.rs13
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/check.rs4
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/fix.rs4
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/init.rs7
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/install.rs17
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/login.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/new.rs7
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/owner.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/pkgid.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/remove.rs13
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/run.rs1
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/rustc.rs5
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/rustdoc.rs5
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/search.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/test.rs5
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/uninstall.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/yank.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/main.rs9
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/context/mod.rs8
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/custom_build.rs20
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/future_incompat.rs10
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/job_queue/mod.rs5
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/layout.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/mod.rs129
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/timings.rs32
-rw-r--r--src/tools/cargo/src/cargo/core/dependency.rs4
-rw-r--r--src/tools/cargo/src/cargo/core/features.rs82
-rw-r--r--src/tools/cargo/src/cargo/core/manifest.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/mod.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/package.rs11
-rw-r--r--src/tools/cargo/src/cargo/core/package_id.rs85
-rw-r--r--src/tools/cargo/src/cargo/core/package_id_spec.rs162
-rw-r--r--src/tools/cargo/src/cargo/core/profiles.rs70
-rw-r--r--src/tools/cargo/src/cargo/core/registry.rs10
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/context.rs4
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/dep_cache.rs77
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/encode.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/errors.rs3
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/mod.rs54
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/resolve.rs11
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/version_prefs.rs126
-rw-r--r--src/tools/cargo/src/cargo/core/shell.rs137
-rw-r--r--src/tools/cargo/src/cargo/core/source_id.rs244
-rw-r--r--src/tools/cargo/src/cargo/core/summary.rs6
-rw-r--r--src/tools/cargo/src/cargo/core/workspace.rs6
-rw-r--r--src/tools/cargo/src/cargo/lib.rs3
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_add/mod.rs47
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs2
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_doc.rs24
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_generate_lockfile.rs22
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_install.rs42
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_new.rs113
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_package.rs20
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_uninstall.rs1
-rw-r--r--src/tools/cargo/src/cargo/ops/common_for_install_and_uninstall.rs70
-rw-r--r--src/tools/cargo/src/cargo/ops/fix.rs5
-rw-r--r--src/tools/cargo/src/cargo/ops/lockfile.rs23
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/mod.rs3
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/publish.rs5
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/search.rs39
-rw-r--r--src/tools/cargo/src/cargo/ops/resolve.rs24
-rw-r--r--src/tools/cargo/src/cargo/ops/vendor.rs2
-rw-r--r--src/tools/cargo/src/cargo/sources/config.rs6
-rw-r--r--src/tools/cargo/src/cargo/sources/git/source.rs17
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/download.rs7
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/http_remote.rs5
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/index.rs87
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/mod.rs71
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/remote.rs15
-rw-r--r--src/tools/cargo/src/cargo/util/auth/mod.rs6
-rw-r--r--src/tools/cargo/src/cargo/util/cache_lock.rs549
-rw-r--r--src/tools/cargo/src/cargo/util/command_prelude.rs70
-rw-r--r--src/tools/cargo/src/cargo/util/config/mod.rs135
-rw-r--r--src/tools/cargo/src/cargo/util/config/target.rs8
-rw-r--r--src/tools/cargo/src/cargo/util/flock.rs287
-rw-r--r--src/tools/cargo/src/cargo/util/hostname.rs77
-rw-r--r--src/tools/cargo/src/cargo/util/mod.rs7
-rw-r--r--src/tools/cargo/src/cargo/util/restricted_names.rs79
-rw-r--r--src/tools/cargo/src/cargo/util/rustc.rs14
-rw-r--r--src/tools/cargo/src/cargo/util/semver_ext.rs293
-rw-r--r--src/tools/cargo/src/cargo/util/to_semver.rs36
-rw-r--r--src/tools/cargo/src/cargo/util/toml/embedded.rs16
-rw-r--r--src/tools/cargo/src/cargo/util/toml/mod.rs2495
-rw-r--r--src/tools/cargo/src/cargo/util/toml/schema.rs1189
-rw-r--r--src/tools/cargo/src/cargo/util/toml/targets.rs120
-rw-r--r--src/tools/cargo/src/cargo/util/toml_mut/dependency.rs45
-rw-r--r--src/tools/cargo/src/cargo/util/toml_mut/manifest.rs100
-rw-r--r--src/tools/cargo/src/cargo/util/toml_mut/mod.rs16
-rw-r--r--src/tools/cargo/src/cargo/util/workspace.rs4
-rw-r--r--src/tools/cargo/src/cargo/util_semver.rs195
-rw-r--r--src/tools/cargo/src/doc/contrib/book.toml2
-rw-r--r--src/tools/cargo/src/doc/contrib/src/SUMMARY.md2
-rw-r--r--src/tools/cargo/src/doc/contrib/src/implementation/formatting.md17
-rw-r--r--src/tools/cargo/src/doc/contrib/src/implementation/packages.md52
-rw-r--r--src/tools/cargo/src/doc/contrib/src/process/index.md9
-rw-r--r--src/tools/cargo/src/doc/contrib/src/process/working-on-cargo.md9
-rw-r--r--src/tools/cargo/src/doc/man/cargo-add.md2
-rw-r--r--src/tools/cargo/src/doc/man/cargo-bench.md2
-rw-r--r--src/tools/cargo/src/doc/man/cargo-install.md2
-rw-r--r--src/tools/cargo/src/doc/man/cargo-login.md4
-rw-r--r--src/tools/cargo/src/doc/man/cargo-rustc.md2
-rw-r--r--src/tools/cargo/src/doc/man/cargo-vendor.md10
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-add.txt4
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-bench.txt5
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-build.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-check.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-doc.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-fix.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-init.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-install.txt4
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-login.txt5
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-new.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-run.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-rustc.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-rustdoc.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-test.txt2
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-vendor.txt11
-rw-r--r--src/tools/cargo/src/doc/man/includes/options-new.md2
-rw-r--r--src/tools/cargo/src/doc/man/includes/options-profile-legacy-check.md2
-rw-r--r--src/tools/cargo/src/doc/man/includes/options-profile.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-add.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-bench.md4
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-build.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-check.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-doc.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-fix.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-init.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-install.md4
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-login.md4
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-new.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-run.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-rustc.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-rustdoc.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-test.md2
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-vendor.md10
-rw-r--r--src/tools/cargo/src/doc/src/reference/config.md23
-rw-r--r--src/tools/cargo/src/doc/src/reference/environment-variables.md2
-rw-r--r--src/tools/cargo/src/doc/src/reference/features.md7
-rw-r--r--src/tools/cargo/src/doc/src/reference/manifest.md35
-rw-r--r--src/tools/cargo/src/doc/src/reference/profiles.md4
-rw-r--r--src/tools/cargo/src/doc/src/reference/publishing.md2
-rw-r--r--src/tools/cargo/src/doc/src/reference/registry-authentication.md2
-rw-r--r--src/tools/cargo/src/doc/src/reference/resolver.md47
-rw-r--r--src/tools/cargo/src/doc/src/reference/specifying-dependencies.md4
-rw-r--r--src/tools/cargo/src/doc/src/reference/unstable.md172
-rw-r--r--src/tools/cargo/src/etc/man/cargo-add.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-bench.14
-rw-r--r--src/tools/cargo/src/etc/man/cargo-build.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-check.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-doc.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-fix.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-init.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-install.14
-rw-r--r--src/tools/cargo/src/etc/man/cargo-login.14
-rw-r--r--src/tools/cargo/src/etc/man/cargo-new.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-run.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-rustc.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-rustdoc.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-test.12
-rw-r--r--src/tools/cargo/src/etc/man/cargo-vendor.116
-rw-r--r--src/tools/cargo/tests/testsuite/artifact_dep.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/bad_config.rs36
-rw-r--r--src/tools/cargo/tests/testsuite/build.rs137
-rw-r--r--src/tools/cargo/tests/testsuite/build_script.rs11
-rw-r--r--src/tools/cargo/tests/testsuite/build_script_env.rs22
-rw-r--r--src/tools/cargo/tests/testsuite/build_script_extra_link_arg.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/cache_lock.rs304
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/in/Cargo.toml14
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/in/src/lib.rs (renamed from src/tools/cargo/tests/testsuite/cargo_init/empty_dir/.keep)0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/mod.rs31
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/out/Cargo.toml12
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/stderr.log7
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_bench/help/stdout.log8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_bench/no_keep_going/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_build/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_check/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_command.rs12
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_features.rs8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_fix/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/help/stdout.log6
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/in/Cargo.toml2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/in/crates/foo/.keep0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/mod.rs21
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_install/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_login/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/in/Cargo.toml5
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/mod.rs22
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/Cargo.toml6
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/Cargo.toml9
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/bar/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/bar/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/qux/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/qux/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/mod.rs22
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/in/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/mod.rs23
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/in/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/mod.rs22
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/in/Cargo.toml4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/in/src/lib.rs14
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/mod.rs22
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/Cargo.toml4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/in/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/mod.rs22
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/Cargo.toml8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/src/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/help/stdout.log6
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/mod.rs6
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/out/Cargo.toml2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/stderr.log8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_owner/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_pkgid/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/help/stdout.log6
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/out/Cargo.toml2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/out/Cargo.toml2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/in/Cargo.toml42
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/in/src/lib.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/mod.rs35
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/out/Cargo.toml40
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_run/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_rustc/help/stdout.log8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stdout.log8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_search/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_test/help/stdout.log8
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_test/no_keep_going/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_uninstall/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_yank/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/check.rs23
-rw-r--r--src/tools/cargo/tests/testsuite/check_cfg.rs307
-rw-r--r--src/tools/cargo/tests/testsuite/collisions.rs6
-rw-r--r--src/tools/cargo/tests/testsuite/config.rs109
-rw-r--r--src/tools/cargo/tests/testsuite/cross_compile.rs86
-rw-r--r--src/tools/cargo/tests/testsuite/custom_target.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/death.rs153
-rw-r--r--src/tools/cargo/tests/testsuite/doc.rs46
-rw-r--r--src/tools/cargo/tests/testsuite/docscrape.rs33
-rw-r--r--src/tools/cargo/tests/testsuite/features.rs88
-rw-r--r--src/tools/cargo/tests/testsuite/features2.rs5
-rw-r--r--src/tools/cargo/tests/testsuite/glob_targets.rs5
-rw-r--r--src/tools/cargo/tests/testsuite/install.rs50
-rw-r--r--src/tools/cargo/tests/testsuite/install_upgrade.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/list_availables.rs8
-rw-r--r--src/tools/cargo/tests/testsuite/main.rs3
-rw-r--r--src/tools/cargo/tests/testsuite/metadata.rs282
-rw-r--r--src/tools/cargo/tests/testsuite/multitarget.rs28
-rw-r--r--src/tools/cargo/tests/testsuite/new.rs26
-rw-r--r--src/tools/cargo/tests/testsuite/out_dir.rs23
-rw-r--r--src/tools/cargo/tests/testsuite/package.rs41
-rw-r--r--src/tools/cargo/tests/testsuite/plugins.rs421
-rw-r--r--src/tools/cargo/tests/testsuite/proc_macro.rs46
-rw-r--r--src/tools/cargo/tests/testsuite/profile_config.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/profile_targets.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/profile_trim_paths.rs614
-rw-r--r--src/tools/cargo/tests/testsuite/pub_priv.rs49
-rw-r--r--src/tools/cargo/tests/testsuite/publish.rs35
-rw-r--r--src/tools/cargo/tests/testsuite/registry.rs51
-rw-r--r--src/tools/cargo/tests/testsuite/run.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/rustdoc.rs8
-rw-r--r--src/tools/cargo/tests/testsuite/rustdocflags.rs6
-rw-r--r--src/tools/cargo/tests/testsuite/script.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/search.rs5
-rw-r--r--src/tools/cargo/tests/testsuite/update.rs98
-rw-r--r--src/tools/cargo/tests/testsuite/version.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/warn_on_failure.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/workspaces.rs17
-rw-r--r--src/tools/cargo/triagebot.toml10
351 files changed, 8940 insertions, 4496 deletions
diff --git a/src/tools/cargo/.github/renovate.json5 b/src/tools/cargo/.github/renovate.json5
index b633fc245..03e6d8da8 100644
--- a/src/tools/cargo/.github/renovate.json5
+++ b/src/tools/cargo/.github/renovate.json5
@@ -12,29 +12,59 @@
{
customType: 'regex',
fileMatch: [
- '^Cargo.toml$',
+ 'Cargo.toml$',
],
matchStrings: [
- 'rust-version.*?(?<currentValue>\\d+\\.\\d+(\\.\\d+)?)',
+ '\bMSRV:1\b.*?(?<currentValue>\\d+\\.\\d+(\\.\\d+)?)',
+ '(?<currentValue>\\d+\\.\\d+(\\.\\d+)?).*?\bMSRV:1\b',
],
- depNameTemplate: 'latest-msrv',
+ depNameTemplate: 'MSRV:1', // Support 1 version of rustc
+ packageNameTemplate: 'rust-lang/rust',
+ datasourceTemplate: 'github-releases',
+ },
+ {
+ customType: 'regex',
+ fileMatch: [
+ 'Cargo.toml$',
+ ],
+ matchStrings: [
+ '\bMSRV:3\b.*?(?<currentValue>\\d+\\.\\d+(\\.\\d+)?)',
+ '(?<currentValue>\\d+\\.\\d+(\\.\\d+)?).*?\bMSRV:3\b',
+ ],
+ depNameTemplate: 'MSRV:3', // Support 3 versions of rustc
packageNameTemplate: 'rust-lang/rust',
datasourceTemplate: 'github-releases',
},
],
packageRules: [
{
- commitMessageTopic: 'Latest MSRV',
+ commitMessageTopic: 'MSRV (1 version)',
+ matchManagers: [
+ 'regex',
+ ],
+ matchPackageNames: [
+ 'MSRV:1',
+ ],
+ schedule: [
+ '* * * * *',
+ ],
+ groupName: 'msrv',
+ },
+ {
+ commitMessageTopic: 'MSRV (3 versions)',
matchManagers: [
'regex',
],
matchPackageNames: [
- 'latest-msrv',
+ 'MSRV:3',
],
"extractVersion": "^(?<version>\\d+\\.\\d+)", // Drop the patch version
schedule: [
'* * * * *',
],
+ minimumReleaseAge: '85 days', // 2 releases back * 6 weeks per release * 7 days per week + 1
+ internalChecksFilter: 'strict',
+ groupName: 'msrv',
},
// Goals:
// - Rollup safe upgrades to reduce CI runner load
diff --git a/src/tools/cargo/.github/workflows/audit.yml b/src/tools/cargo/.github/workflows/audit.yml
index 14e35b7b3..d903eb0d7 100644
--- a/src/tools/cargo/.github/workflows/audit.yml
+++ b/src/tools/cargo/.github/workflows/audit.yml
@@ -21,7 +21,7 @@ jobs:
- advisories
- bans licenses sources
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- uses: EmbarkStudios/cargo-deny-action@v1
# Prevent sudden announcement of a new advisory from failing ci:
continue-on-error: ${{ matrix.checks == 'advisories' }}
diff --git a/src/tools/cargo/.github/workflows/contrib.yml b/src/tools/cargo/.github/workflows/contrib.yml
index bbd4a7ef7..a4c1cb5d0 100644
--- a/src/tools/cargo/.github/workflows/contrib.yml
+++ b/src/tools/cargo/.github/workflows/contrib.yml
@@ -4,6 +4,10 @@ on:
branches:
- master
+concurrency:
+ cancel-in-progress: false
+ group: "gh-pages"
+
permissions:
contents: read
@@ -13,7 +17,7 @@ jobs:
contents: write # for Git to git push
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install mdbook
@@ -23,16 +27,26 @@ jobs:
echo `pwd`/mdbook >> $GITHUB_PATH
- name: Deploy docs
run: |
+ GENERATE_PY="$(pwd)/ci/generate.py"
+
cd src/doc/contrib
mdbook build
- git worktree add gh-pages gh-pages
+
+ # Override previous ref to avoid keeping history.
+ git worktree add --orphan -B gh-pages gh-pages
git config user.name "Deploy from CI"
git config user.email ""
cd gh-pages
- # Delete the ref to avoid keeping history.
- git update-ref -d refs/heads/gh-pages
- rm -rf contrib
mv ../book contrib
git add contrib
+
+ # Generate HTML for link redirections.
+ python3 "$GENERATE_PY"
+ git add *.html
+ # WARN: The CNAME file is for GitHub to redirect requests to the custom domain.
+ # Missing this may entail security hazard and domain takeover.
+ # See <https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#securing-your-custom-domain>
+ git add CNAME
+
git commit -m "Deploy $GITHUB_SHA to gh-pages"
- git push --force
+ git push origin +gh-pages
diff --git a/src/tools/cargo/.github/workflows/main.yml b/src/tools/cargo/.github/workflows/main.yml
index 44dd76e13..7b8055223 100644
--- a/src/tools/cargo/.github/workflows/main.yml
+++ b/src/tools/cargo/.github/workflows/main.yml
@@ -20,7 +20,7 @@ jobs:
needs:
- build_std
- clippy
- - credential_msrv
+ - msrv
- docs
- lockfile
- resolver
@@ -38,7 +38,7 @@ jobs:
needs:
- build_std
- clippy
- - credential_msrv
+ - msrv
- docs
- lockfile
- resolver
@@ -54,7 +54,7 @@ jobs:
rustfmt:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: rustup update stable && rustup default stable
- run: rustup component add rustfmt
- run: cargo fmt --all --check
@@ -63,7 +63,7 @@ jobs:
clippy:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: rustup update stable && rustup default stable
- run: rustup component add clippy
# Only check cargo lib for now
@@ -73,7 +73,7 @@ jobs:
stale-label:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: rustup update stable && rustup default stable
- run: cargo stale-label
@@ -81,7 +81,7 @@ jobs:
lockfile:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: rustup update stable && rustup default stable
- run: cargo update -p cargo --locked
@@ -91,14 +91,14 @@ jobs:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha != '' && github.event.pull_request.head.sha || github.sha }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
with:
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 \
+ curl -Lf https://github.com/obi1kenobi/cargo-semver-checks/releases/download/v0.24.0/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
@@ -145,7 +145,7 @@ jobs:
other: i686-pc-windows-gnu
name: Tests ${{ matrix.name }}
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Dump Environment
run: ci/dump-environment.sh
- run: rustup update --no-self-update ${{ matrix.rust }} && rustup default ${{ matrix.rust }}
@@ -196,14 +196,14 @@ jobs:
resolver:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: rustup update stable && rustup default stable
- run: cargo test -p resolver-tests
test_gitoxide:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: rustup update --no-self-update stable && rustup default stable
- run: rustup target add i686-unknown-linux-gnu
- run: sudo apt update -y && sudo apt install gcc-multilib libsecret-1-0 libsecret-1-dev -y
@@ -215,7 +215,7 @@ jobs:
build_std:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: rustup update nightly && rustup default nightly
- run: rustup component add rust-src
- run: cargo build
@@ -225,7 +225,7 @@ jobs:
docs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- run: rustup update nightly && rustup default nightly
- run: rustup update stable
- run: rustup component add rust-docs
@@ -249,9 +249,9 @@ jobs:
curl -sSLO https://raw.githubusercontent.com/rust-lang/rust/master/src/tools/linkchecker/linkcheck.sh
sh linkcheck.sh --all --path ../src/doc cargo
- credential_msrv:
+ msrv:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
- - run: rustup update 1.70 && rustup default 1.70
- - run: cargo test -p cargo-credential
+ - uses: actions/checkout@v4
+ - uses: taiki-e/install-action@cargo-hack
+ - run: cargo hack check --all-targets --rust-version --workspace --ignore-private
diff --git a/src/tools/cargo/CHANGELOG.md b/src/tools/cargo/CHANGELOG.md
index f2c9bd0eb..08be017a1 100644
--- a/src/tools/cargo/CHANGELOG.md
+++ b/src/tools/cargo/CHANGELOG.md
@@ -1,16 +1,150 @@
# Changelog
+## Cargo 1.75 (2023-12-28)
+[59596f0f...HEAD](https://github.com/rust-lang/cargo/compare/59596f0f...HEAD)
+
+### Added
+
+### Changed
+
+### Fixed
+
+- Fixed corruption when cargo was killed while writing to files.
+ [#12744](https://github.com/rust-lang/cargo/pull/12744)
+
+### Nightly only
+
+### Documentation
+
+- profile: add missing `strip` info.
+ [#12754](https://github.com/rust-lang/cargo/pull/12754)
+
+### Internal
+
+- Updated to `itertools` 0.11.0.
+ [#12759](https://github.com/rust-lang/cargo/pull/12759)
+- Updated to `cargo_metadata` 0.18.0.
+ [#12758](https://github.com/rust-lang/cargo/pull/12758)
+- Disabled the `custom_target::custom_bin_target` test on windows-gnu.
+ [#12763](https://github.com/rust-lang/cargo/pull/12763)
+
## Cargo 1.74 (2023-11-16)
-[80eca0e5...HEAD](https://github.com/rust-lang/cargo/compare/80eca0e5...HEAD)
+[80eca0e5...rust-1.74.0](https://github.com/rust-lang/cargo/compare/80eca0e5...rust-1.74.0)
### Added
+- 🎉 The `[lints]` table has been stabilized, allowing you to configure reporting levels for rustc and other tool lints in `Cargo.toml`.
+ ([RFC 3389](https://github.com/rust-lang/rfcs/blob/master/text/3389-manifest-lint.md))
+ ([docs](https://doc.rust-lang.org/nightly/cargo/reference/manifest.html#the-lints-section))
+ [#12584](https://github.com/rust-lang/cargo/pull/12584)
+ [#12648](https://github.com/rust-lang/cargo/pull/12648)
+- 🎉 The unstable features `credential-process` and `registry-auth` have been stabilized.
+ These features consolidate the way to authenticate with private registries.
+ ([RFC 2730](https://github.com/rust-lang/rfcs/blob/master/text/2730-cargo-token-from-process.md))
+ ([RFC 3139](https://github.com/rust-lang/rfcs/blob/master/text/3139-cargo-alternative-registry-auth.md))
+ ([docs](https://doc.rust-lang.org/nightly/cargo/reference/registry-authentication.html))
+ [#12590](https://github.com/rust-lang/cargo/pull/12590)
+ [#12622](https://github.com/rust-lang/cargo/pull/12622)
+ [#12623](https://github.com/rust-lang/cargo/pull/12623)
+ [#12626](https://github.com/rust-lang/cargo/pull/12626)
+ [#12641](https://github.com/rust-lang/cargo/pull/12641)
+ [#12644](https://github.com/rust-lang/cargo/pull/12644)
+ [#12649](https://github.com/rust-lang/cargo/pull/12649)
+ [#12671](https://github.com/rust-lang/cargo/pull/12671)
+ [#12709](https://github.com/rust-lang/cargo/pull/12709)
+ Notable changes:
+ - Introducing a new protocol for both external and built-in providers to store and retrieve credentials for registry authentication.
+ - Adding the `auth-required` field in the registry index's `config.json`, enabling authenticated sparse index, crate downloads, and search API.
+ - For using alternative registries with authentication, a credential provider must be configured to avoid unknowingly storing unencrypted credentials on disk.
+ - These settings can be configured in `[registry]` and `[registries]` tables.
+- 🎉 `--keep-going` flag has been stabilized and is now available in each build command
+ (except `bench` and `test`, which have `--no-fail-fast` instead).
+ ([docs](https://doc.rust-lang.org/cargo/commands/cargo-build.html#option-cargo-build---keep-going))
+ [#12568](https://github.com/rust-lang/cargo/pull/12568)
+- Added `--dry-run` flag and summary line at the end for `cargo clean`.
+ [#12638](https://github.com/rust-lang/cargo/pull/12638)
+- Added a short alias `-n` for cli option `--dry-run`.
+ [#12660](https://github.com/rust-lang/cargo/pull/12660)
+- Added support for `target.'cfg(..)'.linker`.
+ [#12535](https://github.com/rust-lang/cargo/pull/12535)
+- Allowed incomplete versions when they are unambiguous for flags like `--package`.
+ [#12591](https://github.com/rust-lang/cargo/pull/12591)
+ [#12614](https://github.com/rust-lang/cargo/pull/12614)
+
### Changed
+- ❗️ Changed how arrays in configuration are merged.
+ The order was unspecified and now follows how other configuration types work for consistency.
+ [summary](https://blog.rust-lang.org/inside-rust/2023/08/24/cargo-config-merging.html)
+ [#12515](https://github.com/rust-lang/cargo/pull/12515)
+- ❗️ cargo-clean: error out if `--doc` is mixed with `-p`.
+ [#12637](https://github.com/rust-lang/cargo/pull/12637)
+- cargo-update: silently deprecate `--aggressive` in favor of the new `--recursive`.
+ [#12544](https://github.com/rust-lang/cargo/pull/12544)
+- cargo-update: `-p/--package` can be used as a positional argument.
+ [#12545](https://github.com/rust-lang/cargo/pull/12545)
+ [#12586](https://github.com/rust-lang/cargo/pull/12586)
+- cargo-install: suggest `--git` when the package name looks like a URL.
+ [#12575](https://github.com/rust-lang/cargo/pull/12575)
+- cargo-add: summarize the feature list when it's too long.
+ [#12662](https://github.com/rust-lang/cargo/pull/12662)
+ [#12702](https://github.com/rust-lang/cargo/pull/12702)
+- Shell completion for `--target` uses rustup but falls back to rustc.
+ [#12606](https://github.com/rust-lang/cargo/pull/12606)
+- Help users know possible `--target` values.
+ [#12607](https://github.com/rust-lang/cargo/pull/12607)
+- Enhanced "registry index not found" error message.
+ [#12732](https://github.com/rust-lang/cargo/pull/12732)
+- Enhanced CLI help message of `--explain`.
+ [#12592](https://github.com/rust-lang/cargo/pull/12592)
+- Enhanced deserialization errors of untagged enums with `serde-untagged`.
+ [#12574](https://github.com/rust-lang/cargo/pull/12574)
+ [#12581](https://github.com/rust-lang/cargo/pull/12581)
+- Enhanced the error when mismatching prerelease version candidates.
+ [#12659](https://github.com/rust-lang/cargo/pull/12659)
+- Enhanced the suggestion on ambiguous Package ID spec.
+ [#12685](https://github.com/rust-lang/cargo/pull/12685)
+- Enhanced TOML parse errors to show the context.
+ [#12556](https://github.com/rust-lang/cargo/pull/12556)
+- Enhanced filesystem error by adding wrappers around `std::fs::metadata`.
+ [#12636](https://github.com/rust-lang/cargo/pull/12636)
+- Enhanced resolver version mismatch warning.
+ [#12573](https://github.com/rust-lang/cargo/pull/12573)
+- Use clap to suggest alternative argument for unsupported arguments.
+ [#12529](https://github.com/rust-lang/cargo/pull/12529)
+ [#12693](https://github.com/rust-lang/cargo/pull/12693)
+ [#12723](https://github.com/rust-lang/cargo/pull/12723)
+- Removed redundant information from cargo new/init `--help` output.
+ [#12594](https://github.com/rust-lang/cargo/pull/12594)
+- Console output and styling tweaks.
+ [#12578](https://github.com/rust-lang/cargo/pull/12578)
+ [#12655](https://github.com/rust-lang/cargo/pull/12655)
+ [#12593](https://github.com/rust-lang/cargo/pull/12593)
+
### Fixed
+- Use full target spec for `cargo rustc --print --target`.
+ [#12743](https://github.com/rust-lang/cargo/pull/12743)
+- Copy PDBs also for EFI targets.
+ [#12688](https://github.com/rust-lang/cargo/pull/12688)
+- Fixed resolver behavior being independent of package order.
+ [#12602](https://github.com/rust-lang/cargo/pull/12602)
+- Fixed unnecessary clean up of `profile.release.package."*"` for `cargo remove`.
+ [#12624](https://github.com/rust-lang/cargo/pull/12624)
+
### Nightly only
+- `-Zasymmetric-token`: Created dedicated unstable flag for asymmetric-token support.
+ [#12551](https://github.com/rust-lang/cargo/pull/12551)
+- `-Zasymmetric-token`: Improved logout message for asymmetric tokens.
+ [#12587](https://github.com/rust-lang/cargo/pull/12587)
+- `-Zmsrv-policy`: **Very** preliminary MSRV resolver support.
+ [#12560](https://github.com/rust-lang/cargo/pull/12560)
+- `-Zscript`: Hack in code fence support.
+ [#12681](https://github.com/rust-lang/cargo/pull/12681)
+- `-Zbindeps`: Support dependencies from registries.
+ [#12421](https://github.com/rust-lang/cargo/pull/12421)
+
### Documentation
- ❗ Policy change: Checking `Cargo.lock` into version control is now the default choice,
@@ -19,6 +153,83 @@
[Lockfile docs](https://doc.rust-lang.org/nightly/cargo/guide/cargo-toml-vs-cargo-lock.html),
[CI docs](https://doc.rust-lang.org/nightly/cargo/guide/continuous-integration.html),
[#12382](https://github.com/rust-lang/cargo/pull/12382)
+ [#12630](https://github.com/rust-lang/cargo/pull/12630)
+- SemVer: Update documentation about removing optional dependencies.
+ [#12687](https://github.com/rust-lang/cargo/pull/12687)
+- Contrib: Add process for security responses.
+ [#12487](https://github.com/rust-lang/cargo/pull/12487)
+- cargo-publish: warn about upload timeout.
+ [#12733](https://github.com/rust-lang/cargo/pull/12733)
+- mdbook: use *AND* search when having multiple terms.
+ [#12548](https://github.com/rust-lang/cargo/pull/12548)
+- Established publish best practices
+ [#12745](https://github.com/rust-lang/cargo/pull/12745)
+- Clarify caret requirements.
+ [#12679](https://github.com/rust-lang/cargo/pull/12679)
+- Clarify how `version` works for `git` dependencies.
+ [#12270](https://github.com/rust-lang/cargo/pull/12270)
+- Clarify and differentiate defaults for split-debuginfo.
+ [#12680](https://github.com/rust-lang/cargo/pull/12680)
+- Added missing `strip` entries in `dev` and `release` profiles.
+ [#12748](https://github.com/rust-lang/cargo/pull/12748)
+
+### Internal
+
+- Updated to `curl-sys` 0.4.66, which corresponds to curl 8.3.0.
+ [#12718](https://github.com/rust-lang/cargo/pull/12718)
+- Updated to `gitoxide` 0.54.1.
+ [#12731](https://github.com/rust-lang/cargo/pull/12731)
+- Updated to `git2` 0.18.0, which corresponds to libgit2 1.7.1.
+ [#12580](https://github.com/rust-lang/cargo/pull/12580)
+- Updated to `cargo_metadata` 0.17.0.
+ [#12758](https://github.com/rust-lang/cargo/pull/12610)
+- Updated target-arch-aware crates to support mips r6 targets
+ [#12720](https://github.com/rust-lang/cargo/pull/12720)
+- publish.py: Remove obsolete `sleep()` calls.
+ [#12686](https://github.com/rust-lang/cargo/pull/12686)
+- Define `{{command}}` for use in src/doc/man/includes
+ [#12570](https://github.com/rust-lang/cargo/pull/12570)
+- Set tracing target `network` for networking messages.
+ [#12582](https://github.com/rust-lang/cargo/pull/12582)
+- cargo-test-support: Add `with_stdout_unordered`.
+ [#12635](https://github.com/rust-lang/cargo/pull/12635)
+- dep: Switch from `termcolor` to `anstream`.
+ [#12751](https://github.com/rust-lang/cargo/pull/12751)
+- Put `Source` trait under `cargo::sources`.
+ [#12527](https://github.com/rust-lang/cargo/pull/12527)
+- SourceId: merge `name` and `alt_registry_key` into one enum.
+ [#12675](https://github.com/rust-lang/cargo/pull/12675)
+- TomlManifest: fail when package_root is not a directory.
+ [#12722](https://github.com/rust-lang/cargo/pull/12722)
+- util: enhanced doc of `network::retry` doc.
+ [#12583](https://github.com/rust-lang/cargo/pull/12583)
+- refactor: Pull out cargo-add MSRV code for reuse
+ [#12553](https://github.com/rust-lang/cargo/pull/12553)
+- refactor(install): Move value parsing to clap
+ [#12547](https://github.com/rust-lang/cargo/pull/12547)
+- Fixed spurious errors with networking tests.
+ [#12726](https://github.com/rust-lang/cargo/pull/12726)
+- Use a more compact relative-time format for `CARGO_LOG` internal logging.
+ [#12542](https://github.com/rust-lang/cargo/pull/12542)
+- Use newer std API for cleaner code.
+ [#12559](https://github.com/rust-lang/cargo/pull/12559)
+ [#12604](https://github.com/rust-lang/cargo/pull/12604)
+ [#12615](https://github.com/rust-lang/cargo/pull/12615)
+ [#12631](https://github.com/rust-lang/cargo/pull/12631)
+- Buffer console status messages.
+ [#12727](https://github.com/rust-lang/cargo/pull/12727)
+- Use enum to describe index summaries to provide a richer information when summaries are not available for resolution.
+ [#12643](https://github.com/rust-lang/cargo/pull/12643)
+- Use shortest path for resolving the path from the given dependency up to the root.
+ [#12678](https://github.com/rust-lang/cargo/pull/12678)
+- Read/write the encoded `cargo update --precise` in the same place
+ [#12629](https://github.com/rust-lang/cargo/pull/12629)
+- Set MSRV for internal packages.
+ [#12381](https://github.com/rust-lang/cargo/pull/12381)
+- ci: Update Renovate schema
+ [#12741](https://github.com/rust-lang/cargo/pull/12741)
+- ci: Ignore patch version in MSRV
+ [#12716](https://github.com/rust-lang/cargo/pull/12716)
## Cargo 1.73 (2023-10-05)
[45782b6b...rust-1.73.0](https://github.com/rust-lang/cargo/compare/45782b6b...rust-1.73.0)
@@ -32,9 +243,13 @@
### Changed
-- Cargo now bails out when using `cargo::` in custom build scripts. This is
+- ❗️ Cargo now bails out when using `cargo::` in custom build scripts. This is
a preparation for an upcoming change in build script invocations.
[#12332](https://github.com/rust-lang/cargo/pull/12332)
+- ❗️ `cargo login` no longer accept any token after the `--` syntax.
+ Arguments after `--` are now reserved in the preparation of the new credential provider feature.
+ This introduces a regression that overlooks the `cargo login -- <token>` support in preivous versions.
+ [#12499](https://github.com/rust-lang/cargo/pull/12499)
- Make Cargo `--help` easier to browse.
[#11905](https://github.com/rust-lang/cargo/pull/11905)
- Prompt the use of `--nocapture` flag if `cargo test` process is terminated via a signal.
diff --git a/src/tools/cargo/CONTRIBUTING.md b/src/tools/cargo/CONTRIBUTING.md
index 88ffbd3d0..6046d5a35 100644
--- a/src/tools/cargo/CONTRIBUTING.md
+++ b/src/tools/cargo/CONTRIBUTING.md
@@ -8,14 +8,12 @@ Contributing documentation has moved to the **[Cargo Contributor Guide]**.
We encourage people to discuss their design before hacking on code. Typically,
you [file an issue] or start a thread on the [internals forum] before submitting
-a pull request. Please read [the process] of how features and bugs are managed
-in Cargo.
+a pull request.
-**NOTICE: Due to limited review capacity, the Cargo team is not accepting new
-features or major changes at this time. Please consult with the team before
-opening a new PR. Only issues that have been explicitly marked as accepted
-will be reviewed.**
+Please read [the process] of how features and bugs are managed in Cargo.
+**Only issues that have been explicitly marked as [accepted] will be reviewed.**
[internals forum]: https://internals.rust-lang.org/c/tools-and-infrastructure/cargo
[file an issue]: https://github.com/rust-lang/cargo/issues
[the process]: https://doc.crates.io/contrib/process/index.html
+[accepted]: https://github.com/rust-lang/cargo/issues?q=is%3Aissue+is%3Aopen+label%3AS-accepted
diff --git a/src/tools/cargo/Cargo.lock b/src/tools/cargo/Cargo.lock
index cc0cb9a88..a2d339b0c 100644
--- a/src/tools/cargo/Cargo.lock
+++ b/src/tools/cargo/Cargo.lock
@@ -25,9 +25,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstream"
-version = "0.6.3"
+version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "83d7b3983a025adeb201ef26a5564ebd1641ea9851f6282aee4940f745a3c07c"
+checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -97,9 +97,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
[[package]]
name = "base64"
-version = "0.21.3"
+version = "0.21.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "414dcefbc63d77c526a76b3afcf6fbb9b5e2791c19c3aa2297733208750c6e53"
+checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
[[package]]
name = "base64ct"
@@ -141,9 +141,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.3.3"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42"
+checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
[[package]]
name = "bitmaps"
@@ -190,12 +190,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
[[package]]
-name = "byteorder"
-version = "1.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
-
-[[package]]
name = "bytes"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -208,12 +202,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc"
[[package]]
-name = "byteyarn"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7534301c0ea17abb4db06d75efc7b4b0fa360fce8e175a4330d721c71c942ff"
-
-[[package]]
name = "camino"
version = "1.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -234,7 +222,7 @@ dependencies = [
[[package]]
name = "cargo"
-version = "0.75.0"
+version = "0.76.0"
dependencies = [
"anstream",
"anstyle",
@@ -245,7 +233,7 @@ dependencies = [
"cargo-credential-libsecret",
"cargo-credential-macos-keychain",
"cargo-credential-wincred",
- "cargo-platform 0.1.5",
+ "cargo-platform 0.1.6",
"cargo-test-macro",
"cargo-test-support",
"cargo-util",
@@ -259,7 +247,7 @@ dependencies = [
"git2",
"git2-curl",
"gix",
- "gix-features",
+ "gix-features 0.35.0",
"glob",
"hex",
"hmac",
@@ -269,7 +257,7 @@ dependencies = [
"ignore",
"im-rc",
"indexmap",
- "itertools",
+ "itertools 0.11.0",
"jobserver",
"lazycell",
"libc",
@@ -293,7 +281,8 @@ dependencies = [
"sha1",
"shell-escape",
"snapbox",
- "syn 2.0.29",
+ "supports-hyperlinks",
+ "syn 2.0.38",
"tar",
"tempfile",
"time",
@@ -311,7 +300,7 @@ dependencies = [
[[package]]
name = "cargo-credential"
-version = "0.4.0"
+version = "0.4.1"
dependencies = [
"anyhow",
"libc",
@@ -325,7 +314,7 @@ dependencies = [
[[package]]
name = "cargo-credential-1password"
-version = "0.4.0"
+version = "0.4.2"
dependencies = [
"cargo-credential",
"serde",
@@ -334,7 +323,7 @@ dependencies = [
[[package]]
name = "cargo-credential-libsecret"
-version = "0.3.2"
+version = "0.4.1"
dependencies = [
"anyhow",
"cargo-credential",
@@ -343,7 +332,7 @@ dependencies = [
[[package]]
name = "cargo-credential-macos-keychain"
-version = "0.3.1"
+version = "0.4.1"
dependencies = [
"cargo-credential",
"security-framework",
@@ -351,7 +340,7 @@ dependencies = [
[[package]]
name = "cargo-credential-wincred"
-version = "0.3.1"
+version = "0.4.1"
dependencies = [
"cargo-credential",
"windows-sys",
@@ -368,7 +357,7 @@ dependencies = [
[[package]]
name = "cargo-platform"
-version = "0.1.5"
+version = "0.1.6"
dependencies = [
"serde",
]
@@ -391,7 +380,7 @@ dependencies = [
"flate2",
"git2",
"glob",
- "itertools",
+ "itertools 0.11.0",
"pasetors",
"serde",
"serde_json",
@@ -405,7 +394,7 @@ dependencies = [
[[package]]
name = "cargo-util"
-version = "0.2.7"
+version = "0.2.8"
dependencies = [
"anyhow",
"core-foundation",
@@ -425,9 +414,9 @@ dependencies = [
[[package]]
name = "cargo_metadata"
-version = "0.17.0"
+version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7daec1a2a2129eeba1644b220b4647ec537b0b5d4bfd6876fcc5a540056b592"
+checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037"
dependencies = [
"camino",
"cargo-platform 0.1.2",
@@ -487,18 +476,18 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.4.6"
+version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956"
+checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
-version = "4.4.6"
+version = "4.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45"
+checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"
dependencies = [
"anstream",
"anstyle",
@@ -509,9 +498,9 @@ dependencies = [
[[package]]
name = "clap_lex"
-version = "0.5.0"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
+checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1"
[[package]]
name = "clru"
@@ -521,18 +510,18 @@ checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807"
[[package]]
name = "color-print"
-version = "0.3.4"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2a5e6504ed8648554968650feecea00557a3476bc040d0ffc33080e66b646d0"
+checksum = "7a858372ff14bab9b1b30ea504f2a4bc534582aee3e42ba2d41d2a7baba63d5d"
dependencies = [
"color-print-proc-macro",
]
[[package]]
name = "color-print-proc-macro"
-version = "0.3.4"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d51beaa537d73d2d1ff34ee70bc095f170420ab2ec5d687ecd3ec2b0d092514b"
+checksum = "57e37866456a721d0a404439a1adae37a31be4e0055590d053dfe6981e05003f"
dependencies = [
"nom",
"proc-macro2",
@@ -588,7 +577,7 @@ dependencies = [
[[package]]
name = "crates-io"
-version = "0.39.0"
+version = "0.39.1"
dependencies = [
"curl",
"percent-encoding",
@@ -619,7 +608,7 @@ dependencies = [
"clap",
"criterion-plot",
"is-terminal",
- "itertools",
+ "itertools 0.10.5",
"num-traits",
"once_cell",
"oorandom",
@@ -640,7 +629,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
- "itertools",
+ "itertools 0.10.5",
]
[[package]]
@@ -935,15 +924,15 @@ checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall",
+ "redox_syscall 0.3.5",
"windows-sys",
]
[[package]]
name = "flate2"
-version = "1.0.27"
+version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010"
+checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e"
dependencies = [
"crc32fast",
"libz-sys",
@@ -1006,11 +995,11 @@ dependencies = [
[[package]]
name = "git2"
-version = "0.18.0"
+version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "12ef350ba88a33b4d524b1d1c79096c9ade5ef8c59395df0e60d1e1889414c0e"
+checksum = "fbf97ba92db08df386e10c8ede66a2a0369bd277090afd8710e19e38de9ec0cd"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"libc",
"libgit2-sys",
"log",
@@ -1033,9 +1022,9 @@ dependencies = [
[[package]]
name = "gix"
-version = "0.54.1"
+version = "0.55.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ad6d32e74454459690d57d18ea4ebec1629936e6b130b51d12cb4a81630ac953"
+checksum = "002667cd1ebb789313d0d0afe3d23b2821cf3b0e91605095f0e6d8751f0ceeea"
dependencies = [
"gix-actor",
"gix-attributes",
@@ -1045,7 +1034,7 @@ dependencies = [
"gix-date",
"gix-diff",
"gix-discover",
- "gix-features",
+ "gix-features 0.36.0",
"gix-filter",
"gix-fs",
"gix-glob",
@@ -1087,9 +1076,9 @@ dependencies = [
[[package]]
name = "gix-actor"
-version = "0.27.0"
+version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08c60e982c5290897122d4e2622447f014a2dadd5a18cb73d50bb91b31645e27"
+checksum = "948a5f9e43559d16faf583694f1c742eb401ce24ce8e6f2238caedea7486433c"
dependencies = [
"bstr",
"btoi",
@@ -1101,16 +1090,16 @@ dependencies = [
[[package]]
name = "gix-attributes"
-version = "0.19.0"
+version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2451665e70709ba4753b623ef97511ee98c4a73816b2c5b5df25678d607ed820"
+checksum = "dca120f0c6562d2d7cae467f2466e576d9f7f189beec2af2e026145107c729e2"
dependencies = [
"bstr",
- "byteyarn",
"gix-glob",
"gix-path",
"gix-quote",
"gix-trace",
+ "kstring",
"smallvec",
"thiserror",
"unicode-bom",
@@ -1136,22 +1125,22 @@ dependencies = [
[[package]]
name = "gix-command"
-version = "0.2.9"
+version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f28f654184b5f725c5737c7e4f466cbd8f0102ac352d5257eeab19647ee4256"
+checksum = "3c576cfbf577f72c097b5f88aedea502cd62952bdc1fb3adcab4531d5525a4c7"
dependencies = [
"bstr",
]
[[package]]
name = "gix-commitgraph"
-version = "0.21.0"
+version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e75a975ee22cf0a002bfe9b5d5cb3d2a88e263a8a178cd7509133cff10f4df8a"
+checksum = "7e8bc78b1a6328fa6d8b3a53b6c73997af37fd6bfc1d6c49f149e63bda5cbb36"
dependencies = [
"bstr",
"gix-chunk",
- "gix-features",
+ "gix-features 0.36.0",
"gix-hash",
"memmap2",
"thiserror",
@@ -1159,13 +1148,13 @@ dependencies = [
[[package]]
name = "gix-config"
-version = "0.30.0"
+version = "0.31.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c171514b40487d3f677ae37efc0f45ac980e3169f23c27eb30a70b47fdf88ab5"
+checksum = "5cae98c6b4c66c09379bc35274b172587d6b0ac369a416c39128ad8c6454f9bb"
dependencies = [
"bstr",
"gix-config-value",
- "gix-features",
+ "gix-features 0.36.0",
"gix-glob",
"gix-path",
"gix-ref",
@@ -1184,7 +1173,7 @@ version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea7505b97f4d8e7933e29735a568ba2f86d8de466669d9f0e8321384f9972f47"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"bstr",
"gix-path",
"libc",
@@ -1193,9 +1182,9 @@ dependencies = [
[[package]]
name = "gix-credentials"
-version = "0.20.0"
+version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "46900b884cc5af6a6c141ee741607c0c651a4e1d33614b8d888a1ba81cc0bc8a"
+checksum = "1c5c5d74069b842a1861e581027ac6b7ad9ff66f5911c89b9f45484d7ebda6a4"
dependencies = [
"bstr",
"gix-command",
@@ -1221,9 +1210,9 @@ dependencies = [
[[package]]
name = "gix-diff"
-version = "0.36.0"
+version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "788ddb152c388206e81f36bcbb574e7ed7827c27d8fa62227b34edc333d8928c"
+checksum = "931394f69fb8c9ed6afc0aae3487bd869e936339bcc13ed8884472af072e0554"
dependencies = [
"gix-hash",
"gix-object",
@@ -1232,9 +1221,9 @@ dependencies = [
[[package]]
name = "gix-discover"
-version = "0.25.0"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69507643d75a0ea9a402fcf73ced517d2b95cc95385904ac09d03e0b952fde33"
+checksum = "a45d5cf0321178883e38705ab2b098f625d609a7d4c391b33ac952eff2c490f2"
dependencies = [
"bstr",
"dunce",
@@ -1251,15 +1240,26 @@ version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b9ff423ae4983f762659040d13dd7a5defbd54b6a04ac3cc7347741cec828cd"
dependencies = [
+ "crossbeam-channel",
+ "gix-hash",
+ "gix-trace",
+ "libc",
+ "parking_lot",
+]
+
+[[package]]
+name = "gix-features"
+version = "0.36.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "51f4365ba17c4f218d7fd9ec102b8d2d3cb0ca200a835e81151ace7778aec827"
+dependencies = [
"bytes",
"crc32fast",
- "crossbeam-channel",
"flate2",
"gix-hash",
"gix-trace",
"libc",
"once_cell",
- "parking_lot",
"prodash",
"sha1_smol",
"thiserror",
@@ -1268,9 +1268,9 @@ dependencies = [
[[package]]
name = "gix-filter"
-version = "0.5.0"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1be40d28cd41445bb6cd52c4d847d915900e5466f7433eaee6a9e0a3d1d88b08"
+checksum = "92f674d3fdb6b1987b04521ec9a5b7be8650671f2c4bbd17c3c81e2a364242ff"
dependencies = [
"bstr",
"encoding_rs",
@@ -1288,30 +1288,30 @@ dependencies = [
[[package]]
name = "gix-fs"
-version = "0.7.0"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09815faba62fe9b32d918b75a554686c98e43f7d48c43a80df58eb718e5c6635"
+checksum = "8cd171c0cae97cd0dc57e7b4601cb1ebf596450e263ef3c02be9107272c877bd"
dependencies = [
- "gix-features",
+ "gix-features 0.36.0",
]
[[package]]
name = "gix-glob"
-version = "0.13.0"
+version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9d76e85f11251dcf751d2c5e918a14f562db5be6f727fd24775245653e9b19d"
+checksum = "8fac08925dbc14d414bd02eb45ffb4cecd912d1fce3883f867bd0103c192d3e4"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"bstr",
- "gix-features",
+ "gix-features 0.36.0",
"gix-path",
]
[[package]]
name = "gix-hash"
-version = "0.13.0"
+version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ccf425543779cddaa4a7c62aba3fa9d90ea135b160be0a72dd93c063121ad4a"
+checksum = "1884c7b41ea0875217c1be9ce91322f90bde433e91d374d0e1276073a51ccc60"
dependencies = [
"faster-hex",
"thiserror",
@@ -1330,9 +1330,9 @@ dependencies = [
[[package]]
name = "gix-ignore"
-version = "0.8.0"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b048f443a1f6b02da4205c34d2e287e3fd45d75e8e2f06cfb216630ea9bff5e3"
+checksum = "1e73c07763a8005ae02cb5cf83040729cea9bb70c7cef68ec6c24159904c499a"
dependencies = [
"bstr",
"gix-glob",
@@ -1342,16 +1342,16 @@ dependencies = [
[[package]]
name = "gix-index"
-version = "0.25.0"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f54d63a9d13c13088f41f5a3accbec284e492ac8f4f707fcc307c139622e17b7"
+checksum = "c83a4fcc121b2f2e109088f677f89f85e7a8ebf39e8e6659c0ae54d4283b1650"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"bstr",
"btoi",
"filetime",
"gix-bitmap",
- "gix-features",
+ "gix-features 0.36.0",
"gix-fs",
"gix-hash",
"gix-lock",
@@ -1365,9 +1365,9 @@ dependencies = [
[[package]]
name = "gix-lock"
-version = "10.0.0"
+version = "11.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47fc96fa8b6b6d33555021907c81eb3b27635daecf6e630630bdad44f8feaa95"
+checksum = "f4feb1dcd304fe384ddc22edba9dd56a42b0800032de6537728cea2f033a4f37"
dependencies = [
"gix-tempfile",
"gix-utils",
@@ -1382,16 +1382,16 @@ checksum = "9d8acb5ee668d55f0f2d19a320a3f9ef67a6999ad483e11135abcc2464ed18b6"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.29",
+ "syn 2.0.38",
]
[[package]]
name = "gix-negotiate"
-version = "0.8.0"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f1697bf9911c6d1b8d709b9e6ef718cb5ea5821a1b7991520125a8134448004"
+checksum = "2a5cdcf491ecc9ce39dcc227216c540355fe0024ae7c38e94557752ca5ebb67f"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"gix-commitgraph",
"gix-date",
"gix-hash",
@@ -1403,15 +1403,15 @@ dependencies = [
[[package]]
name = "gix-object"
-version = "0.37.0"
+version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e7e19616c67967374137bae83e950e9b518a9ea8a605069bd6716ada357fd6f"
+checksum = "740f2a44267f58770a1cb3a3d01d14e67b089c7136c48d4bddbb3cfd2bf86a51"
dependencies = [
"bstr",
"btoi",
"gix-actor",
"gix-date",
- "gix-features",
+ "gix-features 0.36.0",
"gix-hash",
"gix-validate",
"itoa 1.0.6",
@@ -1422,13 +1422,13 @@ dependencies = [
[[package]]
name = "gix-odb"
-version = "0.53.0"
+version = "0.54.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d6a392c6ba3a2f133cdc63120e9bc7aec81eef763db372c817de31febfe64bf"
+checksum = "8630b56cb80d8fa684d383dad006a66401ee8314e12fbf0e566ddad8c115143b"
dependencies = [
"arc-swap",
"gix-date",
- "gix-features",
+ "gix-features 0.36.0",
"gix-hash",
"gix-object",
"gix-pack",
@@ -1441,13 +1441,13 @@ dependencies = [
[[package]]
name = "gix-pack"
-version = "0.43.0"
+version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7536203a45b31e1bc5694bbf90ba8da1b736c77040dd6a520db369f371eb1ab3"
+checksum = "1431ba2e30deff1405920693d54ab231c88d7c240dd6ccc936ee223d8f8697c3"
dependencies = [
"clru",
"gix-chunk",
- "gix-features",
+ "gix-features 0.36.0",
"gix-hash",
"gix-hashtable",
"gix-object",
@@ -1461,9 +1461,9 @@ dependencies = [
[[package]]
name = "gix-packetline"
-version = "0.16.6"
+version = "0.16.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6df0b75361353e7c0a6d72d49617a37379a7a22cba4569ae33a7720a4c8755a"
+checksum = "8a8384b1e964151aff0d5632dd9b191059d07dff358b96bd940f1b452600d7ab"
dependencies = [
"bstr",
"faster-hex",
@@ -1496,11 +1496,11 @@ dependencies = [
[[package]]
name = "gix-pathspec"
-version = "0.3.0"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3e26c9b47c51be73f98d38c84494bd5fb99334c5d6fda14ef5d036d50a9e5fd"
+checksum = "e9cc7194fdcf43b4a1ccfa13ffae1d79f83beb4becff7761d88dd99faeafe625"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"bstr",
"gix-attributes",
"gix-config-value",
@@ -1524,15 +1524,15 @@ dependencies = [
[[package]]
name = "gix-protocol"
-version = "0.40.0"
+version = "0.41.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cc7b700dc20cc9be8a5130a1fd7e10c34117ffa7068431c8c24d963f0a2e0c9b"
+checksum = "391e3feabdfa5f90dad6673ce59e3291ac28901b2ff248d86c5a7fbde0391e0e"
dependencies = [
"bstr",
"btoi",
"gix-credentials",
"gix-date",
- "gix-features",
+ "gix-features 0.36.0",
"gix-hash",
"gix-transport",
"maybe-async",
@@ -1553,13 +1553,13 @@ dependencies = [
[[package]]
name = "gix-ref"
-version = "0.37.0"
+version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22e6b749660b613641769edc1954132eb8071a13c32224891686091bef078de4"
+checksum = "0ec2f6d07ac88d2fb8007ee3fa3e801856fb9d82e7366ec0ca332eb2c9d74a52"
dependencies = [
"gix-actor",
"gix-date",
- "gix-features",
+ "gix-features 0.36.0",
"gix-fs",
"gix-hash",
"gix-lock",
@@ -1574,9 +1574,9 @@ dependencies = [
[[package]]
name = "gix-refspec"
-version = "0.18.0"
+version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0895cb7b1e70f3c3bd4550c329e9f5caf2975f97fcd4238e05754e72208ef61e"
+checksum = "ccb0974cc41dbdb43a180c7f67aa481e1c1e160fcfa8f4a55291fd1126c1a6e7"
dependencies = [
"bstr",
"gix-hash",
@@ -1588,9 +1588,9 @@ dependencies = [
[[package]]
name = "gix-revision"
-version = "0.22.0"
+version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8c4b15cf2ab7a35f5bcb3ef146187c8d36df0177e171ca061913cbaaa890e89"
+checksum = "2ca97ac73459a7f3766aa4a5638a6e37d56d4c7962bc1986fbaf4883d0772588"
dependencies = [
"bstr",
"gix-date",
@@ -1604,9 +1604,9 @@ dependencies = [
[[package]]
name = "gix-revwalk"
-version = "0.8.0"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9870c6b1032f2084567710c3b2106ac603377f8d25766b8a6b7c33e6e3ca279"
+checksum = "a16d8c892e4cd676d86f0265bf9d40cefd73d8d94f86b213b8b77d50e77efae0"
dependencies = [
"gix-commitgraph",
"gix-date",
@@ -1623,7 +1623,7 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92b9542ac025a8c02ed5d17b3fc031a111a384e859d0be3532ec4d58c40a0f28"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"gix-path",
"libc",
"windows",
@@ -1631,9 +1631,9 @@ dependencies = [
[[package]]
name = "gix-submodule"
-version = "0.4.0"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dd0150e82e9282d3f2ab2dd57a22f9f6c3447b9d9856e5321ac92d38e3e0e2b7"
+checksum = "bba78c8d12aa24370178453ec3a472ff08dfaa657d116229f57f2c9cd469a1c2"
dependencies = [
"bstr",
"gix-config",
@@ -1646,9 +1646,9 @@ dependencies = [
[[package]]
name = "gix-tempfile"
-version = "10.0.0"
+version = "11.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ae0978f3e11dc57290ee75ac2477c815bca1ce2fa7ed5dc5f16db067410ac4d"
+checksum = "05cc2205cf10d99f70b96e04e16c55d4c7cf33efc151df1f793e29fd12a931f8"
dependencies = [
"gix-fs",
"libc",
@@ -1665,16 +1665,16 @@ checksum = "96b6d623a1152c3facb79067d6e2ecdae48130030cf27d6eb21109f13bd7b836"
[[package]]
name = "gix-transport"
-version = "0.37.0"
+version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9ec726e6a245e68ace59a34126a1d679de60360676612985e70b0d3b102fb4e"
+checksum = "2f209a93364e24f20319751bc11092272e2f3fe82bb72592b2822679cf5be752"
dependencies = [
"base64",
"bstr",
"curl",
"gix-command",
"gix-credentials",
- "gix-features",
+ "gix-features 0.36.0",
"gix-packetline",
"gix-quote",
"gix-sec",
@@ -1684,9 +1684,9 @@ dependencies = [
[[package]]
name = "gix-traverse"
-version = "0.33.0"
+version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22ef04ab3643acba289b5cedd25d6f53c0430770b1d689d1d654511e6fb81ba0"
+checksum = "14d050ec7d4e1bb76abf0636cf4104fb915b70e54e3ced9a4427c999100ff38a"
dependencies = [
"gix-commitgraph",
"gix-date",
@@ -1700,12 +1700,12 @@ dependencies = [
[[package]]
name = "gix-url"
-version = "0.24.0"
+version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6125ecf46e8c68bf7202da6cad239831daebf0247ffbab30210d72f3856e420f"
+checksum = "b1b9ac8ed32ad45f9fc6c5f8c0be2ed911e544a5a19afd62d95d524ebaa95671"
dependencies = [
"bstr",
- "gix-features",
+ "gix-features 0.36.0",
"gix-path",
"home 0.5.5",
"thiserror",
@@ -1733,13 +1733,13 @@ dependencies = [
[[package]]
name = "gix-worktree"
-version = "0.26.0"
+version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f5e32972801bd82d56609e6fc84efc358fa1f11f25c5e83b7807ee2280f14fe"
+checksum = "ddaf79e721dba64fe726a42f297a3c8ed42e55cdc0d81ca68452f2def3c2d7fd"
dependencies = [
"bstr",
"gix-attributes",
- "gix-features",
+ "gix-features 0.36.0",
"gix-fs",
"gix-glob",
"gix-hash",
@@ -1856,7 +1856,7 @@ dependencies = [
[[package]]
name = "home"
-version = "0.5.7"
+version = "0.5.8"
dependencies = [
"windows-sys",
]
@@ -1948,6 +1948,15 @@ dependencies = [
]
[[package]]
+name = "itertools"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
+dependencies = [
+ "either",
+]
+
+[[package]]
name = "itoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1961,9 +1970,9 @@ checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "jobserver"
-version = "0.1.26"
+version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2"
+checksum = "8c37f63953c4c63420ed5fd3d6d398c719489b9f872b9fa683262f8edd363c7d"
dependencies = [
"libc",
]
@@ -1978,6 +1987,15 @@ dependencies = [
]
[[package]]
+name = "kstring"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747"
+dependencies = [
+ "static_assertions",
+]
+
+[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1997,9 +2015,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
-version = "0.2.148"
+version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b"
+checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]]
name = "libgit2-sys"
@@ -2017,9 +2035,9 @@ dependencies = [
[[package]]
name = "libloading"
-version = "0.8.0"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb"
+checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
dependencies = [
"cfg-if",
"windows-sys",
@@ -2069,9 +2087,9 @@ dependencies = [
[[package]]
name = "linux-raw-sys"
-version = "0.4.5"
+version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
+checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
[[package]]
name = "lock_api"
@@ -2124,9 +2142,9 @@ dependencies = [
[[package]]
name = "memchr"
-version = "2.6.2"
+version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5486aed0026218e61b8a01d5fbd5a0a134649abb71a0e53b7bc088529dced86e"
+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "memmap2"
@@ -2263,7 +2281,7 @@ version = "0.10.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"cfg-if",
"foreign-types",
"libc",
@@ -2280,7 +2298,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.29",
+ "syn 2.0.38",
]
[[package]]
@@ -2378,7 +2396,7 @@ checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall",
+ "redox_syscall 0.3.5",
"smallvec",
"windows-targets",
]
@@ -2475,7 +2493,7 @@ dependencies = [
"pest_meta",
"proc-macro2",
"quote",
- "syn 2.0.29",
+ "syn 2.0.38",
]
[[package]]
@@ -2566,9 +2584,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.66"
+version = "1.0.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9"
+checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
dependencies = [
"unicode-ident",
]
@@ -2584,19 +2602,19 @@ dependencies = [
[[package]]
name = "proptest"
-version = "1.2.0"
+version = "1.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65"
+checksum = "7c003ac8c77cb07bb74f5f198bce836a689bcd5a42574612bf14d17bfd08c20e"
dependencies = [
"bit-set",
- "bitflags 1.3.2",
- "byteorder",
+ "bit-vec",
+ "bitflags 2.4.0",
"lazy_static",
"num-traits",
"rand",
"rand_chacha",
"rand_xorshift",
- "regex-syntax 0.6.29",
+ "regex-syntax 0.7.2",
"rusty-fork",
"tempfile",
"unarray",
@@ -2714,6 +2732,15 @@ dependencies = [
]
[[package]]
+name = "redox_syscall"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
name = "regex"
version = "1.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2791,11 +2818,11 @@ dependencies = [
[[package]]
name = "rustix"
-version = "0.38.6"
+version = "0.38.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ee020b1716f0a80e2ace9b03441a749e402e86712f15f16fe8a8f75afac732f"
+checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
dependencies = [
- "bitflags 2.3.3",
+ "bitflags 2.4.0",
"errno",
"libc",
"linux-raw-sys",
@@ -2883,9 +2910,9 @@ dependencies = [
[[package]]
name = "semver"
-version = "1.0.18"
+version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918"
+checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
dependencies = [
"serde",
]
@@ -2899,9 +2926,9 @@ dependencies = [
[[package]]
name = "serde"
-version = "1.0.188"
+version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
+checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
dependencies = [
"serde_derive",
]
@@ -2928,13 +2955,13 @@ dependencies = [
[[package]]
name = "serde_derive"
-version = "1.0.188"
+version = "1.0.190"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
+checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.29",
+ "syn 2.0.38",
]
[[package]]
@@ -2948,9 +2975,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.105"
+version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
+checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"itoa 1.0.6",
"ryu",
@@ -2959,18 +2986,18 @@ dependencies = [
[[package]]
name = "serde_spanned"
-version = "0.6.3"
+version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186"
+checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80"
dependencies = [
"serde",
]
[[package]]
name = "sha1"
-version = "0.10.5"
+version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
+checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -2985,9 +3012,9 @@ checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
[[package]]
name = "sha2"
-version = "0.10.7"
+version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
+checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -3043,9 +3070,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "snapbox"
-version = "0.4.13"
+version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b439536a42c43be148b610c7f7f968fb79a457254910a9cb20900da73cd3271"
+checksum = "4b377c0b6e4715c116473d8e40d51e3fa5b0a2297ca9b2a931ba800667b259ed"
dependencies = [
"anstream",
"anstyle",
@@ -3090,6 +3117,12 @@ dependencies = [
]
[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
+[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3102,6 +3135,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
+name = "supports-hyperlinks"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f84231692eb0d4d41e4cdd0cabfdd2e6cd9e255e65f80c9aa7c98dd502b4233d"
+dependencies = [
+ "is-terminal",
+]
+
+[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3114,9 +3156,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.29"
+version = "2.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a"
+checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
dependencies = [
"proc-macro2",
"quote",
@@ -3147,13 +3189,13 @@ dependencies = [
[[package]]
name = "tempfile"
-version = "3.8.0"
+version = "3.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef"
+checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
dependencies = [
"cfg-if",
"fastrand",
- "redox_syscall",
+ "redox_syscall 0.4.1",
"rustix",
"windows-sys",
]
@@ -3170,22 +3212,22 @@ dependencies = [
[[package]]
name = "thiserror"
-version = "1.0.47"
+version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f"
+checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
-version = "1.0.47"
+version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b"
+checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.29",
+ "syn 2.0.38",
]
[[package]]
@@ -3255,9 +3297,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
-version = "0.7.6"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542"
+checksum = "8ff9e3abce27ee2c9a37f9ad37238c1bdd4e789c84ba37df76aa4d528f5072cc"
dependencies = [
"serde",
"serde_spanned",
@@ -3267,18 +3309,18 @@ dependencies = [
[[package]]
name = "toml_datetime"
-version = "0.6.3"
+version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b"
+checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
-version = "0.19.14"
+version = "0.20.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a"
+checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81"
dependencies = [
"indexmap",
"serde",
@@ -3289,11 +3331,10 @@ dependencies = [
[[package]]
name = "tracing"
-version = "0.1.37"
+version = "0.1.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef"
dependencies = [
- "cfg-if",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
@@ -3301,20 +3342,20 @@ dependencies = [
[[package]]
name = "tracing-attributes"
-version = "0.1.26"
+version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab"
+checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.29",
+ "syn 2.0.38",
]
[[package]]
name = "tracing-core"
-version = "0.1.31"
+version = "0.1.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0955b8137a1df6f1a2e9a37d8a6656291ff0297c1a97c24e0d8425fe2312f79a"
+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54"
dependencies = [
"once_cell",
"valuable",
@@ -3405,9 +3446,9 @@ dependencies = [
[[package]]
name = "unicode-width"
-version = "0.1.10"
+version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
+checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "unicode-xid"
@@ -3548,9 +3589,9 @@ dependencies = [
[[package]]
name = "walkdir"
-version = "2.3.3"
+version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
+checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee"
dependencies = [
"same-file",
"winapi-util",
@@ -3583,7 +3624,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
- "syn 2.0.29",
+ "syn 2.0.38",
"wasm-bindgen-shared",
]
@@ -3605,7 +3646,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.29",
+ "syn 2.0.38",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@@ -3754,6 +3795,7 @@ dependencies = [
"cargo-util",
"clap",
"git2",
+ "semver",
"tracing",
"tracing-subscriber",
]
diff --git a/src/tools/cargo/Cargo.toml b/src/tools/cargo/Cargo.toml
index 440304416..3fb36b44e 100644
--- a/src/tools/cargo/Cargo.toml
+++ b/src/tools/cargo/Cargo.toml
@@ -11,38 +11,38 @@ exclude = [
]
[workspace.package]
-rust-version = "1.72.0"
+rust-version = "1.73" # MSRV:1
edition = "2021"
license = "MIT OR Apache-2.0"
[workspace.dependencies]
-anstream = "0.6.3"
+anstream = "0.6.4"
anstyle = "1.0.4"
anyhow = "1.0.75"
-base64 = "0.21.3"
+base64 = "0.21.5"
bytesize = "1.3"
cargo = { path = "" }
-cargo-credential = { version = "0.4.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-credential = { version = "0.4.1", path = "credential/cargo-credential" }
+cargo-credential-libsecret = { version = "0.4.1", path = "credential/cargo-credential-libsecret" }
+cargo-credential-macos-keychain = { version = "0.4.1", path = "credential/cargo-credential-macos-keychain" }
+cargo-credential-wincred = { version = "0.4.1", path = "credential/cargo-credential-wincred" }
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.6", path = "crates/cargo-util" }
-cargo_metadata = "0.17.0"
-clap = "4.4.6"
-color-print = "0.3.4"
+cargo_metadata = "0.18.1"
+clap = "4.4.7"
+color-print = "0.3.5"
core-foundation = { version = "0.9.3", features = ["mac_os_10_7_support"] }
crates-io = { version = "0.39.0", path = "crates/crates-io" }
criterion = { version = "0.5.1", features = ["html_reports"] }
curl = "0.4.44"
curl-sys = "0.4.68"
filetime = "0.2.22"
-flate2 = { version = "1.0.27", default-features = false, features = ["zlib"] }
-git2 = "0.18.0"
+flate2 = { version = "1.0.28", default-features = false, features = ["zlib"] }
+git2 = "0.18.1"
git2-curl = "0.19.0"
-gix = { version = "0.54.1", default-features = false, features = ["blocking-http-transport-curl", "progress-tree", "revision"] }
+gix = { version = "0.55.2", default-features = false, features = ["blocking-http-transport-curl", "progress-tree", "revision"] }
gix-features-for-configuration-only = { version = "0.35.0", package = "gix-features", features = [ "parallel" ] }
glob = "0.3.1"
handlebars = { version = "3.5.5", features = ["dir_source"] }
@@ -54,13 +54,13 @@ humantime = "2.1.0"
ignore = "0.4.20"
im-rc = "15.1.0"
indexmap = "2"
-itertools = "0.10.0"
-jobserver = "0.1.26"
+itertools = "0.11.0"
+jobserver = "0.1.27"
lazycell = "1.3.0"
-libc = "0.2.148"
+libc = "0.2.149"
libgit2-sys = "0.16.1"
-libloading = "0.8.0"
-memchr = "2.6.2"
+libloading = "0.8.1"
+memchr = "2.6.4"
miow = "0.6.0"
opener = "0.6.1"
openssl ="0.10.57"
@@ -70,44 +70,46 @@ pathdiff = "0.2"
percent-encoding = "2.3"
pkg-config = "0.3.27"
pretty_assertions = "1.4.0"
-proptest = "1.2.0"
+proptest = "1.3.1"
pulldown-cmark = { version = "0.9.3", default-features = false }
rand = "0.8.5"
rustfix = "0.6.1"
same-file = "1.0.6"
security-framework = "2.9.2"
-semver = { version = "1.0.18", features = ["serde"] }
-serde = "1.0.188"
+semver = { version = "1.0.20", features = ["serde"] }
+serde = "1.0.190"
serde-untagged = "0.1.1"
serde-value = "0.7.0"
serde_ignored = "0.1.9"
-serde_json = "1.0.105"
-sha1 = "0.10.5"
-sha2 = "0.10.7"
+serde_json = "1.0.108"
+sha1 = "0.10.6"
+sha2 = "0.10.8"
shell-escape = "0.1.5"
-snapbox = { version = "0.4.13", features = ["diff", "path"] }
-syn = { version = "2.0.29", features = ["extra-traits", "full"] }
+supports-hyperlinks = "2.1.0"
+snapbox = { version = "0.4.14", features = ["diff", "path"] }
+syn = { version = "2.0.38", features = ["extra-traits", "full"] }
tar = { version = "0.4.40", default-features = false }
-tempfile = "3.8.0"
-thiserror = "1.0.47"
+tempfile = "3.8.1"
+thiserror = "1.0.50"
time = { version = "0.3", features = ["parsing", "formatting", "serde"] }
-toml = "0.7.6"
-toml_edit = "0.19.14"
-tracing = "0.1.37"
+toml = "0.8.6"
+toml_edit = { version = "0.20.7", features = ["serde"] }
+tracing = "0.1.40"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
unicase = "2.7.0"
-unicode-width = "0.1.10"
+unicode-width = "0.1.11"
unicode-xid = "0.2.4"
url = "2.4.1"
varisat = "0.2.2"
-walkdir = "2.3.3"
+walkdir = "2.4.0"
windows-sys = "0.48"
[package]
name = "cargo"
-version = "0.75.0"
+version = "0.76.0"
edition.workspace = true
license.workspace = true
+rust-version.workspace = true
homepage = "https://crates.io"
repository = "https://github.com/rust-lang/cargo"
documentation = "https://docs.rs/cargo"
@@ -125,14 +127,11 @@ anstyle.workspace = true
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-platform.workspace = true
cargo-util.workspace = true
-color-print.workspace = true
clap = { workspace = true, features = ["wrap_help"] }
+color-print.workspace = true
crates-io.workspace = true
curl = { workspace = true, features = ["http2"] }
curl-sys.workspace = true
@@ -172,6 +171,7 @@ serde_ignored.workspace = true
serde_json = { workspace = true, features = ["raw_value"] }
sha1.workspace = true
shell-escape.workspace = true
+supports-hyperlinks.workspace = true
syn.workspace = true
tar.workspace = true
tempfile.workspace = true
@@ -186,9 +186,18 @@ unicode-xid.workspace = true
url.workspace = true
walkdir.workspace = true
+[target.'cfg(target_os = "linux")'.dependencies]
+cargo-credential-libsecret.workspace = true
+
+[target.'cfg(target_os = "macos")'.dependencies]
+cargo-credential-macos-keychain.workspace = true
+
[target.'cfg(not(windows))'.dependencies]
openssl = { workspace = true, optional = true }
+[target.'cfg(windows)'.dependencies]
+cargo-credential-wincred.workspace = true
+
[target.'cfg(windows)'.dependencies.windows-sys]
workspace = true
features = [
diff --git a/src/tools/cargo/ci/generate.py b/src/tools/cargo/ci/generate.py
new file mode 100644
index 000000000..b750729dc
--- /dev/null
+++ b/src/tools/cargo/ci/generate.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python3
+
+MAPPING = {
+ "build-script.html": "https://doc.rust-lang.org/cargo/reference/build-scripts.html",
+ "config.html": None,
+ "crates-io.html": "https://doc.rust-lang.org/cargo/reference/publishing.html",
+ "environment-variables.html": None,
+ "external-tools.html": None,
+ "faq.html": "https://doc.rust-lang.org/cargo/faq.html",
+ "guide.html": "https://doc.rust-lang.org/cargo/guide/",
+ "index.html": "https://doc.rust-lang.org/cargo/",
+ "manifest.html": None,
+ "pkgid-spec.html": None,
+ "policies.html": "https://crates.io/policies",
+ "source-replacement.html": None,
+ "specifying-dependencies.html": None,
+}
+
+TEMPLATE = """\
+<html>
+<head>
+<meta http-equiv="refresh" content="0; url={mapped}" />
+<script>
+window.location.replace("{mapped}" + window.location.hash);
+</script>
+<title>Page Moved</title>
+</head>
+<body>
+This page has moved. Click <a href="{mapped}">here</a> to go to the new page.
+</body>
+</html>
+"""
+
+def main():
+ for name in sorted(MAPPING):
+ with open(name, 'w') as f:
+ mapped = MAPPING[name]
+ if mapped is None:
+ mapped = "https://doc.rust-lang.org/cargo/reference/{}".format(name)
+ f.write(TEMPLATE.format(name=name, mapped=mapped))
+
+ # WARN: The CNAME file is for GitHub to redirect requests to the custom domain.
+ # Missing this may entail security hazard and domain takeover.
+ # See <https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site#securing-your-custom-domain>
+ with open('CNAME', 'w') as f:
+ f.write('doc.crates.io')
+
+if __name__ == '__main__':
+ main()
diff --git a/src/tools/cargo/crates/cargo-platform/Cargo.toml b/src/tools/cargo/crates/cargo-platform/Cargo.toml
index 016ead686..786948ff3 100644
--- a/src/tools/cargo/crates/cargo-platform/Cargo.toml
+++ b/src/tools/cargo/crates/cargo-platform/Cargo.toml
@@ -1,8 +1,9 @@
[package]
name = "cargo-platform"
-version = "0.1.5"
+version = "0.1.6"
edition.workspace = true
license.workspace = true
+rust-version = "1.70.0" # MSRV:3
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-support/src/compare.rs b/src/tools/cargo/crates/cargo-test-support/src/compare.rs
index 09e3a5a0c..d9e8d5454 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/compare.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/compare.rs
@@ -236,6 +236,8 @@ fn substitute_macros(input: &str) -> String {
("[SKIPPING]", " Skipping"),
("[WAITING]", " Waiting"),
("[PUBLISHED]", " Published"),
+ ("[BLOCKING]", " Blocking"),
+ ("[GENERATED]", " Generated"),
];
let mut result = input.to_owned();
for &(pat, subst) in &macros {
diff --git a/src/tools/cargo/crates/cargo-test-support/src/diff.rs b/src/tools/cargo/crates/cargo-test-support/src/diff.rs
index 3fedc839b..cd0c97385 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/diff.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/diff.rs
@@ -132,7 +132,7 @@ pub fn render_colored_changes<T: fmt::Display>(changes: &[Change<T>]) -> String
Change::Remove(i, s) => (format!("{:<4} ", i), '-', red, s),
Change::Keep(x, y, s) => (format!("{:<4}{:<4} ", x, y), ' ', dim, s),
};
- write!(
+ writeln!(
buffer,
"{dim}{nums}{reset}{bold}{sign}{reset}{color}{text}{reset}"
)
diff --git a/src/tools/cargo/crates/cargo-test-support/src/lib.rs b/src/tools/cargo/crates/cargo-test-support/src/lib.rs
index 1a8742720..ec74ce0b2 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/lib.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/lib.rs
@@ -13,6 +13,7 @@ use std::path::{Path, PathBuf};
use std::process::{Command, Output};
use std::str;
use std::sync::OnceLock;
+use std::thread::JoinHandle;
use std::time::{self, Duration};
use anyhow::{bail, Result};
@@ -1470,3 +1471,50 @@ pub fn symlink_supported() -> bool {
pub fn no_such_file_err_msg() -> String {
std::io::Error::from_raw_os_error(2).to_string()
}
+
+/// Helper to retry a function `n` times.
+///
+/// The function should return `Some` when it is ready.
+pub fn retry<F, R>(n: u32, mut f: F) -> R
+where
+ F: FnMut() -> Option<R>,
+{
+ let mut count = 0;
+ let start = std::time::Instant::now();
+ loop {
+ if let Some(r) = f() {
+ return r;
+ }
+ count += 1;
+ if count > n {
+ panic!(
+ "test did not finish within {n} attempts ({:?} total)",
+ start.elapsed()
+ );
+ }
+ sleep_ms(100);
+ }
+}
+
+#[test]
+#[should_panic(expected = "test did not finish")]
+fn retry_fails() {
+ retry(2, || None::<()>);
+}
+
+/// Helper that waits for a thread to finish, up to `n` tenths of a second.
+pub fn thread_wait_timeout<T>(n: u32, thread: JoinHandle<T>) -> T {
+ retry(n, || thread.is_finished().then_some(()));
+ thread.join().unwrap()
+}
+
+/// Helper that runs some function, and waits up to `n` tenths of a second for
+/// it to finish.
+pub fn threaded_timeout<F, R>(n: u32, f: F) -> R
+where
+ F: FnOnce() -> R + Send + 'static,
+ R: Send + 'static,
+{
+ let thread = std::thread::spawn(|| f());
+ thread_wait_timeout(n, thread)
+}
diff --git a/src/tools/cargo/crates/cargo-util/Cargo.toml b/src/tools/cargo/crates/cargo-util/Cargo.toml
index cba00f917..616a79c5e 100644
--- a/src/tools/cargo/crates/cargo-util/Cargo.toml
+++ b/src/tools/cargo/crates/cargo-util/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "cargo-util"
-version = "0.2.7"
+version = "0.2.8"
rust-version.workspace = true
edition.workspace = true
license.workspace = true
@@ -10,12 +10,12 @@ description = "Miscellaneous support code used by Cargo."
[dependencies]
anyhow.workspace = true
-sha2.workspace = true
filetime.workspace = true
hex.workspace = true
jobserver.workspace = true
libc.workspace = true
same-file.workspace = true
+sha2.workspace = true
shell-escape.workspace = true
tempfile.workspace = true
tracing.workspace = true
diff --git a/src/tools/cargo/crates/cargo-util/src/paths.rs b/src/tools/cargo/crates/cargo-util/src/paths.rs
index 888ca1af5..f405c8f97 100644
--- a/src/tools/cargo/crates/cargo-util/src/paths.rs
+++ b/src/tools/cargo/crates/cargo-util/src/paths.rs
@@ -180,6 +180,19 @@ pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()>
.with_context(|| format!("failed to write `{}`", path.display()))
}
+/// Writes a file to disk atomically.
+///
+/// write_atomic uses tempfile::persist to accomplish atomic writes.
+pub fn write_atomic<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()> {
+ let path = path.as_ref();
+ let mut tmp = TempFileBuilder::new()
+ .prefix(path.file_name().unwrap())
+ .tempfile_in(path.parent().unwrap())?;
+ tmp.write_all(contents.as_ref())?;
+ tmp.persist(path)?;
+ Ok(())
+}
+
/// Equivalent to [`write()`], but does not write anything if the file contents
/// are identical to the given contents.
pub fn write_if_changed<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<()> {
@@ -681,7 +694,8 @@ pub fn create_dir_all_excluded_from_backups_atomic(p: impl AsRef<Path>) -> Resul
// we can infer from it's another cargo process doing work.
if let Err(e) = fs::rename(tempdir.path(), path) {
if !path.exists() {
- return Err(anyhow::Error::from(e));
+ return Err(anyhow::Error::from(e))
+ .with_context(|| format!("failed to create directory `{}`", path.display()));
}
}
Ok(())
@@ -775,6 +789,29 @@ fn exclude_from_time_machine(path: &Path) {
#[cfg(test)]
mod tests {
use super::join_paths;
+ use super::write;
+ use super::write_atomic;
+
+ #[test]
+ fn write_works() {
+ let original_contents = "[dependencies]\nfoo = 0.1.0";
+
+ let tmpdir = tempfile::tempdir().unwrap();
+ let path = tmpdir.path().join("Cargo.toml");
+ write(&path, original_contents).unwrap();
+ let contents = std::fs::read_to_string(&path).unwrap();
+ assert_eq!(contents, original_contents);
+ }
+ #[test]
+ fn write_atomic_works() {
+ let original_contents = "[dependencies]\nfoo = 0.1.0";
+
+ let tmpdir = tempfile::tempdir().unwrap();
+ let path = tmpdir.path().join("Cargo.toml");
+ write_atomic(&path, original_contents).unwrap();
+ let contents = std::fs::read_to_string(&path).unwrap();
+ assert_eq!(contents, original_contents);
+ }
#[test]
fn join_paths_lists_paths_on_error() {
diff --git a/src/tools/cargo/crates/crates-io/Cargo.toml b/src/tools/cargo/crates/crates-io/Cargo.toml
index d06dacdfa..f1b92602e 100644
--- a/src/tools/cargo/crates/crates-io/Cargo.toml
+++ b/src/tools/cargo/crates/crates-io/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "crates-io"
-version = "0.39.0"
+version = "0.39.1"
rust-version.workspace = true
edition.workspace = true
license.workspace = true
diff --git a/src/tools/cargo/crates/crates-io/lib.rs b/src/tools/cargo/crates/crates-io/lib.rs
index 757241fd3..1764ce527 100644
--- a/src/tools/cargo/crates/crates-io/lib.rs
+++ b/src/tools/cargo/crates/crates-io/lib.rs
@@ -38,6 +38,10 @@ pub struct Crate {
pub max_version: String,
}
+/// This struct is serialized as JSON and sent as metadata ahead of the crate
+/// tarball when publishing crates to a crate registry like crates.io.
+///
+/// see <https://doc.rust-lang.org/cargo/reference/registry-web-api.html#publish>
#[derive(Serialize, Deserialize)]
pub struct NewCrate {
pub name: String,
diff --git a/src/tools/cargo/crates/home/Cargo.toml b/src/tools/cargo/crates/home/Cargo.toml
index 03bd555a2..702a14e55 100644
--- a/src/tools/cargo/crates/home/Cargo.toml
+++ b/src/tools/cargo/crates/home/Cargo.toml
@@ -1,7 +1,8 @@
[package]
name = "home"
-version = "0.5.7" # also update `html_root_url` in `src/lib.rs`
+version = "0.5.8"
authors = ["Brian Anderson <andersrb@gmail.com>"]
+rust-version = "1.70.0" # MSRV:3
documentation = "https://docs.rs/home"
edition.workspace = true
include = [
diff --git a/src/tools/cargo/crates/home/src/lib.rs b/src/tools/cargo/crates/home/src/lib.rs
index 0e1e975e4..4aee7383b 100644
--- a/src/tools/cargo/crates/home/src/lib.rs
+++ b/src/tools/cargo/crates/home/src/lib.rs
@@ -18,7 +18,6 @@
//!
//! [discussion]: https://github.com/rust-lang/rust/pull/46799#issuecomment-361156935
-#![doc(html_root_url = "https://docs.rs/home/0.5.6")]
#![deny(rust_2018_idioms)]
pub mod env;
diff --git a/src/tools/cargo/crates/resolver-tests/src/lib.rs b/src/tools/cargo/crates/resolver-tests/src/lib.rs
index 9bdeb8674..e2cbcee62 100644
--- a/src/tools/cargo/crates/resolver-tests/src/lib.rs
+++ b/src/tools/cargo/crates/resolver-tests/src/lib.rs
@@ -12,7 +12,7 @@ use std::task::Poll;
use std::time::Instant;
use cargo::core::dependency::DepKind;
-use cargo::core::resolver::{self, ResolveOpts, VersionPreferences};
+use cargo::core::resolver::{self, ResolveOpts, VersionOrdering, VersionPreferences};
use cargo::core::Resolve;
use cargo::core::{Dependency, PackageId, Registry, Summary};
use cargo::core::{GitReference, SourceId};
@@ -190,15 +190,17 @@ pub fn resolve_with_config_raw(
.unwrap();
let opts = ResolveOpts::everything();
let start = Instant::now();
- let max_rust_version = None;
+ let mut version_prefs = VersionPreferences::default();
+ if config.cli_unstable().minimal_versions {
+ version_prefs.version_ordering(VersionOrdering::MinimumVersionsFirst)
+ }
let resolve = resolver::resolve(
&[(summary, opts)],
&[],
&mut registry,
- &VersionPreferences::default(),
+ &version_prefs,
Some(config),
true,
- max_rust_version,
);
// The largest test in our suite takes less then 30 sec.
@@ -982,14 +984,17 @@ fn meta_test_multiple_versions_strategy() {
/// Assert `xs` contains `elems`
#[track_caller]
-pub fn assert_contains<A: PartialEq>(xs: &[A], elems: &[A]) {
+pub fn assert_contains<A: PartialEq + std::fmt::Debug>(xs: &[A], elems: &[A]) {
for elem in elems {
- assert!(xs.contains(elem));
+ assert!(
+ xs.contains(elem),
+ "missing element\nset: {xs:?}\nmissing: {elem:?}"
+ );
}
}
#[track_caller]
-pub fn assert_same<A: PartialEq>(a: &[A], b: &[A]) {
- assert_eq!(a.len(), b.len());
+pub fn assert_same<A: PartialEq + std::fmt::Debug>(a: &[A], b: &[A]) {
+ assert_eq!(a.len(), b.len(), "not equal\n{a:?}\n{b:?}");
assert_contains(b, a);
}
diff --git a/src/tools/cargo/crates/xtask-bump-check/Cargo.toml b/src/tools/cargo/crates/xtask-bump-check/Cargo.toml
index e878f7dda..c8a472adc 100644
--- a/src/tools/cargo/crates/xtask-bump-check/Cargo.toml
+++ b/src/tools/cargo/crates/xtask-bump-check/Cargo.toml
@@ -11,5 +11,6 @@ cargo.workspace = true
cargo-util.workspace = true
clap.workspace = true
git2.workspace = true
-tracing.workspace = true
+semver.workspace = true
tracing-subscriber.workspace = true
+tracing.workspace = true
diff --git a/src/tools/cargo/crates/xtask-bump-check/src/xtask.rs b/src/tools/cargo/crates/xtask-bump-check/src/xtask.rs
index 4bf3f03d5..b99ac8b32 100644
--- a/src/tools/cargo/crates/xtask-bump-check/src/xtask.rs
+++ b/src/tools/cargo/crates/xtask-bump-check/src/xtask.rs
@@ -22,8 +22,8 @@ use cargo::core::Registry;
use cargo::core::SourceId;
use cargo::core::Workspace;
use cargo::sources::source::QueryKind;
+use cargo::util::cache_lock::CacheLockMode;
use cargo::util::command_prelude::*;
-use cargo::util::ToSemver;
use cargo::CargoResult;
use cargo_util::ProcessBuilder;
@@ -148,26 +148,13 @@ fn bump_check(args: &clap::ArgMatches, config: &cargo::util::Config) -> CargoRes
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 against 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);
+ .arg("--workspace");
config.shell().status("Running", &cmd)?;
cmd.exec()?;
@@ -176,8 +163,7 @@ fn bump_check(args: &clap::ArgMatches, config: &cargo::util::Config) -> CargoRes
cmd.arg("semver-checks")
.arg("--workspace")
.arg("--baseline-rev")
- .arg(referenced_commit.id().to_string())
- .args(&exclude_args);
+ .arg(referenced_commit.id().to_string());
config.shell().status("Running", &cmd)?;
cmd.exec()?;
}
@@ -290,7 +276,7 @@ fn beta_and_stable_branch(repo: &git2::Repository) -> CargoResult<[git2::Branch<
tracing::trace!("branch `{name}` is not in the format of `<remote>/rust-<semver>`");
continue;
};
- let Ok(version) = version.to_semver() else {
+ let Ok(version) = version.parse::<semver::Version>() else {
tracing::trace!("branch `{name}` is not a valid semver: `{version}`");
continue;
};
@@ -361,7 +347,7 @@ fn check_crates_io<'a>(
) -> CargoResult<()> {
let source_id = SourceId::crates_io(config)?;
let mut registry = PackageRegistry::new(config)?;
- let _lock = config.acquire_package_cache_lock()?;
+ let _lock = config.acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
registry.lock_patches();
config.shell().status(
STATUS,
diff --git a/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml b/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml
index d7bd949d1..9e5b1e635 100644
--- a/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml
@@ -1,8 +1,9 @@
[package]
name = "cargo-credential-1password"
-version = "0.4.0"
+version = "0.4.2"
edition.workspace = true
license.workspace = true
+rust-version = "1.70.0" # MSRV:3
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/LICENSE-APACHE b/src/tools/cargo/credential/cargo-credential-1password/LICENSE-APACHE
new file mode 120000
index 000000000..1cd601d0a
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-1password/LICENSE-APACHE
@@ -0,0 +1 @@
+../../LICENSE-APACHE \ No newline at end of file
diff --git a/src/tools/cargo/credential/cargo-credential-1password/LICENSE-MIT b/src/tools/cargo/credential/cargo-credential-1password/LICENSE-MIT
new file mode 120000
index 000000000..b2cfbdc7b
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-1password/LICENSE-MIT
@@ -0,0 +1 @@
+../../LICENSE-MIT \ No newline at end of file
diff --git a/src/tools/cargo/credential/cargo-credential-1password/README.md b/src/tools/cargo/credential/cargo-credential-1password/README.md
index 3648efe4b..fc3c9460a 100644
--- a/src/tools/cargo/credential/cargo-credential-1password/README.md
+++ b/src/tools/cargo/credential/cargo-credential-1password/README.md
@@ -2,17 +2,31 @@
A Cargo [credential provider] for [1password].
-`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.
+## Usage
+
+`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/).
+
+Afterward you need to configure `cargo` to use `cargo-credential-1password` as
+the credential provider. You can do this by adding something like the following
+to your [cargo config file][credential provider]:
+
+```toml
+[registry]
+global-credential-providers = ["cargo-credential-1password --account my.1password.com"]
+```
+
+Finally, run `cargo login` to save your registry token in 1password.
+
+## CLI Arguments
+
+`cargo-credential-1password` supports the following command-line arguments:
+
+* `--account`: The account name to use. For a list of available accounts,
+ run `op account list`.
+* `--vault`: The vault name to use. For a list of available vaults,
+ run `op vault list`.
[1password]: https://1password.com/
-[credential provider]: https://doc.rust-lang.org/nightly/cargo/reference/registry-authentication.html
+[credential provider]: https://doc.rust-lang.org/stable/cargo/reference/registry-authentication.html
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 921b52145..321a99c51 100644
--- a/src/tools/cargo/credential/cargo-credential-1password/src/main.rs
+++ b/src/tools/cargo/credential/cargo-credential-1password/src/main.rs
@@ -79,6 +79,10 @@ impl OnePasswordKeychain {
}
let mut cmd = Command::new("op");
cmd.args(["signin", "--raw"]);
+ if let Some(account) = &self.account {
+ cmd.arg("--account");
+ cmd.arg(account);
+ }
cmd.stdout(Stdio::piped());
let mut child = cmd
.spawn()
diff --git a/src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml b/src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml
index 5bedad3b9..19ef33a34 100644
--- a/src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml
@@ -1,8 +1,9 @@
[package]
name = "cargo-credential-libsecret"
-version = "0.3.2"
+version = "0.4.1"
edition.workspace = true
license.workspace = true
+rust-version.workspace = true
repository = "https://github.com/rust-lang/cargo"
description = "A Cargo credential process that stores tokens with GNOME libsecret."
diff --git a/src/tools/cargo/credential/cargo-credential-libsecret/LICENSE-APACHE b/src/tools/cargo/credential/cargo-credential-libsecret/LICENSE-APACHE
new file mode 120000
index 000000000..1cd601d0a
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-libsecret/LICENSE-APACHE
@@ -0,0 +1 @@
+../../LICENSE-APACHE \ No newline at end of file
diff --git a/src/tools/cargo/credential/cargo-credential-libsecret/LICENSE-MIT b/src/tools/cargo/credential/cargo-credential-libsecret/LICENSE-MIT
new file mode 120000
index 000000000..b2cfbdc7b
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-libsecret/LICENSE-MIT
@@ -0,0 +1 @@
+../../LICENSE-MIT \ No newline at end of file
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 172e9c10b..4dec8def6 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,9 @@
[package]
name = "cargo-credential-macos-keychain"
-version = "0.3.1"
+version = "0.4.1"
edition.workspace = true
license.workspace = true
+rust-version.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/LICENSE-APACHE b/src/tools/cargo/credential/cargo-credential-macos-keychain/LICENSE-APACHE
new file mode 120000
index 000000000..1cd601d0a
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-macos-keychain/LICENSE-APACHE
@@ -0,0 +1 @@
+../../LICENSE-APACHE \ No newline at end of file
diff --git a/src/tools/cargo/credential/cargo-credential-macos-keychain/LICENSE-MIT b/src/tools/cargo/credential/cargo-credential-macos-keychain/LICENSE-MIT
new file mode 120000
index 000000000..b2cfbdc7b
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-macos-keychain/LICENSE-MIT
@@ -0,0 +1 @@
+../../LICENSE-MIT \ No newline at end of file
diff --git a/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml b/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml
index 6da6578a5..c904075bb 100644
--- a/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml
@@ -1,8 +1,9 @@
[package]
name = "cargo-credential-wincred"
-version = "0.3.1"
+version = "0.4.1"
edition.workspace = true
license.workspace = true
+rust-version.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/LICENSE-APACHE b/src/tools/cargo/credential/cargo-credential-wincred/LICENSE-APACHE
new file mode 120000
index 000000000..1cd601d0a
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-wincred/LICENSE-APACHE
@@ -0,0 +1 @@
+../../LICENSE-APACHE \ No newline at end of file
diff --git a/src/tools/cargo/credential/cargo-credential-wincred/LICENSE-MIT b/src/tools/cargo/credential/cargo-credential-wincred/LICENSE-MIT
new file mode 120000
index 000000000..b2cfbdc7b
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential-wincred/LICENSE-MIT
@@ -0,0 +1 @@
+../../LICENSE-MIT \ No newline at end of file
diff --git a/src/tools/cargo/credential/cargo-credential/Cargo.toml b/src/tools/cargo/credential/cargo-credential/Cargo.toml
index c8db996bf..8ba65b8b9 100644
--- a/src/tools/cargo/credential/cargo-credential/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential/Cargo.toml
@@ -1,9 +1,9 @@
[package]
name = "cargo-credential"
-version = "0.4.0"
+version = "0.4.1"
edition.workspace = true
license.workspace = true
-rust-version = "1.70.0"
+rust-version = "1.70.0" # MSRV:3
repository = "https://github.com/rust-lang/cargo"
description = "A library to assist writing Cargo credential helpers."
diff --git a/src/tools/cargo/credential/cargo-credential/LICENSE-APACHE b/src/tools/cargo/credential/cargo-credential/LICENSE-APACHE
new file mode 120000
index 000000000..1cd601d0a
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential/LICENSE-APACHE
@@ -0,0 +1 @@
+../../LICENSE-APACHE \ No newline at end of file
diff --git a/src/tools/cargo/credential/cargo-credential/LICENSE-MIT b/src/tools/cargo/credential/cargo-credential/LICENSE-MIT
new file mode 120000
index 000000000..b2cfbdc7b
--- /dev/null
+++ b/src/tools/cargo/credential/cargo-credential/LICENSE-MIT
@@ -0,0 +1 @@
+../../LICENSE-MIT \ No newline at end of file
diff --git a/src/tools/cargo/src/bin/cargo/cli.rs b/src/tools/cargo/src/bin/cargo/cli.rs
index 06b4a20e2..a21030f01 100644
--- a/src/tools/cargo/src/bin/cargo/cli.rs
+++ b/src/tools/cargo/src/bin/cargo/cli.rs
@@ -2,7 +2,7 @@ use anyhow::{anyhow, Context as _};
use cargo::core::shell::Shell;
use cargo::core::{features, CliUnstable};
use cargo::{self, drop_print, drop_println, CargoResult, CliResult, Config};
-use clap::{Arg, ArgMatches};
+use clap::{builder::UnknownArgumentValueParser, Arg, ArgMatches};
use itertools::Itertools;
use std::collections::HashMap;
use std::ffi::OsStr;
@@ -618,15 +618,29 @@ See '<cyan,bold>cargo help</> <cyan><<command>></>' for more information on a sp
.help_heading(heading::MANIFEST_OPTIONS)
.global(true),
)
+ // Better suggestion for the unsupported short config flag.
+ .arg( Arg::new("unsupported-short-config-flag")
+ .help("")
+ .short('c')
+ .value_parser(UnknownArgumentValueParser::suggest_arg("--config"))
+ .action(ArgAction::SetTrue)
+ .global(true)
+ .hide(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),
- )
+ // Better suggestion for the unsupported lowercase unstable feature flag.
+ .arg( Arg::new("unsupported-lowercase-unstable-feature-flag")
+ .help("")
+ .short('z')
+ .value_parser(UnknownArgumentValueParser::suggest_arg("-Z"))
+ .action(ArgAction::SetTrue)
+ .global(true)
+ .hide(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))
.subcommands(commands::builtin())
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/add.rs b/src/tools/cargo/src/bin/cargo/commands/add.rs
index 344cd2af3..e1ece14b8 100644
--- a/src/tools/cargo/src/bin/cargo/commands/add.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/add.rs
@@ -77,7 +77,7 @@ Example uses:
"Ignore `rust-version` specification in packages (unstable)"
),
])
- .arg_manifest_path()
+ .arg_manifest_path_without_unsupported_path_tip()
.arg_package("Package to modify")
.arg_dry_run("Don't actually write the manifest")
.arg_quiet()
diff --git a/src/tools/cargo/src/bin/cargo/commands/bench.rs b/src/tools/cargo/src/bin/cargo/commands/bench.rs
index 1a97d84a4..85c975a6c 100644
--- a/src/tools/cargo/src/bin/cargo/commands/bench.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/bench.rs
@@ -12,6 +12,7 @@ pub fn cli() -> Command {
)
.arg(
Arg::new("args")
+ .value_name("ARGS")
.help("Arguments for the bench binary")
.num_args(0..)
.last(true),
@@ -36,9 +37,9 @@ pub fn cli() -> Command {
"Benchmark only the specified example",
"Benchmark all examples",
"Benchmark only the specified test target",
- "Benchmark all tests",
+ "Benchmark all test targets",
"Benchmark only the specified bench target",
- "Benchmark all benches",
+ "Benchmark all bench targets",
"Benchmark all targets",
)
.arg_features()
diff --git a/src/tools/cargo/src/bin/cargo/commands/build.rs b/src/tools/cargo/src/bin/cargo/commands/build.rs
index d503f43dd..e2ed87d1b 100644
--- a/src/tools/cargo/src/bin/cargo/commands/build.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/build.rs
@@ -23,9 +23,9 @@ pub fn cli() -> Command {
"Build only the specified example",
"Build all examples",
"Build only the specified test target",
- "Build all tests",
+ "Build all test targets",
"Build only the specified bench target",
- "Build all benches",
+ "Build all bench targets",
"Build all targets",
)
.arg_features()
@@ -35,14 +35,7 @@ pub fn cli() -> Command {
.arg_parallel()
.arg_target_triple("Build for the target triple")
.arg_target_dir()
- .arg(
- opt(
- "out-dir",
- "Copy final artifacts to this directory (unstable)",
- )
- .value_name("PATH")
- .help_heading(heading::COMPILATION_OPTIONS),
- )
+ .arg_out_dir()
.arg_build_plan()
.arg_unit_graph()
.arg_timings()
diff --git a/src/tools/cargo/src/bin/cargo/commands/check.rs b/src/tools/cargo/src/bin/cargo/commands/check.rs
index a54e84cdc..77e2b9280 100644
--- a/src/tools/cargo/src/bin/cargo/commands/check.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/check.rs
@@ -23,9 +23,9 @@ pub fn cli() -> Command {
"Check only the specified example",
"Check all examples",
"Check only the specified test target",
- "Check all tests",
+ "Check all test targets",
"Check only the specified bench target",
- "Check all benches",
+ "Check all bench targets",
"Check all targets",
)
.arg_features()
diff --git a/src/tools/cargo/src/bin/cargo/commands/fix.rs b/src/tools/cargo/src/bin/cargo/commands/fix.rs
index 0ecf47450..bd938dbc7 100644
--- a/src/tools/cargo/src/bin/cargo/commands/fix.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/fix.rs
@@ -41,9 +41,9 @@ pub fn cli() -> Command {
"Fix only the specified example",
"Fix all examples",
"Fix only the specified test target",
- "Fix all tests",
+ "Fix all test targets",
"Fix only the specified bench target",
- "Fix all benches",
+ "Fix all bench targets",
"Fix all targets (default)",
)
.arg_features()
diff --git a/src/tools/cargo/src/bin/cargo/commands/init.rs b/src/tools/cargo/src/bin/cargo/commands/init.rs
index 48409d827..04dd7ae45 100644
--- a/src/tools/cargo/src/bin/cargo/commands/init.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/init.rs
@@ -5,7 +5,12 @@ use cargo::ops;
pub fn cli() -> Command {
subcommand("init")
.about("Create a new cargo package in an existing directory")
- .arg(Arg::new("path").action(ArgAction::Set).default_value("."))
+ .arg(
+ Arg::new("path")
+ .value_name("PATH")
+ .action(ArgAction::Set)
+ .default_value("."),
+ )
.arg_new_opts()
.arg_registry("Registry to use")
.arg_quiet()
diff --git a/src/tools/cargo/src/bin/cargo/commands/install.rs b/src/tools/cargo/src/bin/cargo/commands/install.rs
index 2e5163bdc..cb66ba100 100644
--- a/src/tools/cargo/src/bin/cargo/commands/install.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/install.rs
@@ -6,9 +6,9 @@ use anyhow::format_err;
use cargo::core::{GitReference, SourceId, Workspace};
use cargo::ops;
use cargo::util::IntoUrl;
-use cargo::util::ToSemver;
-use cargo::util::VersionReqExt;
+use cargo::util_semver::VersionExt;
use cargo::CargoResult;
+use itertools::Itertools;
use semver::VersionReq;
use cargo_util::paths;
@@ -16,7 +16,13 @@ use cargo_util::paths;
pub fn cli() -> Command {
subcommand("install")
.about("Install a Rust binary. Default location is $HOME/.cargo/bin")
- .arg(Arg::new("crate").value_parser(parse_crate).num_args(0..))
+ .arg(
+ Arg::new("crate")
+ .value_name("CRATE[@<VER>]")
+ .help("Select the package from the given source")
+ .value_parser(parse_crate)
+ .num_args(0..),
+ )
.arg(
opt("version", "Specify a version to install")
.alias("vers")
@@ -113,6 +119,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
.get_many::<CrateVersion>("crate")
.unwrap_or_default()
.cloned()
+ .dedup_by(|x, y| x == y)
.map(|(krate, local_version)| resolve_crate(krate, local_version, version))
.collect::<crate::CargoResult<Vec<_>>>()?;
@@ -256,8 +263,8 @@ fn parse_semver_flag(v: &str) -> CargoResult<VersionReq> {
),
}
} else {
- match v.to_semver() {
- Ok(v) => Ok(VersionReq::exact(&v)),
+ match v.trim().parse::<semver::Version>() {
+ Ok(v) => Ok(v.to_exact_req()),
Err(e) => {
let mut msg = e.to_string();
diff --git a/src/tools/cargo/src/bin/cargo/commands/login.rs b/src/tools/cargo/src/bin/cargo/commands/login.rs
index 118b03310..877ec6aeb 100644
--- a/src/tools/cargo/src/bin/cargo/commands/login.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/login.rs
@@ -6,7 +6,7 @@ use crate::command_prelude::*;
pub fn cli() -> Command {
subcommand("login")
.about("Log in to a registry.")
- .arg(Arg::new("token").action(ArgAction::Set))
+ .arg(Arg::new("token").value_name("TOKEN").action(ArgAction::Set))
.arg_registry("Registry to use")
.arg(
Arg::new("args")
diff --git a/src/tools/cargo/src/bin/cargo/commands/new.rs b/src/tools/cargo/src/bin/cargo/commands/new.rs
index c8d19218f..0ab093012 100644
--- a/src/tools/cargo/src/bin/cargo/commands/new.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/new.rs
@@ -5,7 +5,12 @@ use cargo::ops;
pub fn cli() -> Command {
subcommand("new")
.about("Create a new cargo package at <path>")
- .arg(Arg::new("path").action(ArgAction::Set).required(true))
+ .arg(
+ Arg::new("path")
+ .value_name("PATH")
+ .action(ArgAction::Set)
+ .required(true),
+ )
.arg_new_opts()
.arg_registry("Registry to use")
.arg_quiet()
diff --git a/src/tools/cargo/src/bin/cargo/commands/owner.rs b/src/tools/cargo/src/bin/cargo/commands/owner.rs
index 00080b829..b787d094c 100644
--- a/src/tools/cargo/src/bin/cargo/commands/owner.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/owner.rs
@@ -6,7 +6,7 @@ use cargo_credential::Secret;
pub fn cli() -> Command {
subcommand("owner")
.about("Manage the owners of a crate on the registry")
- .arg(Arg::new("crate").action(ArgAction::Set))
+ .arg(Arg::new("crate").value_name("CRATE").action(ArgAction::Set))
.arg(
multi_opt(
"add",
diff --git a/src/tools/cargo/src/bin/cargo/commands/pkgid.rs b/src/tools/cargo/src/bin/cargo/commands/pkgid.rs
index 9d7a6c712..2d1d41325 100644
--- a/src/tools/cargo/src/bin/cargo/commands/pkgid.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/pkgid.rs
@@ -6,7 +6,7 @@ use cargo::util::print_available_packages;
pub fn cli() -> Command {
subcommand("pkgid")
.about("Print a fully qualified package specification")
- .arg(Arg::new("spec").action(ArgAction::Set))
+ .arg(Arg::new("spec").value_name("SPEC").action(ArgAction::Set))
.arg_quiet()
.arg_package("Argument to get the package ID specifier for")
.arg_manifest_path()
diff --git a/src/tools/cargo/src/bin/cargo/commands/remove.rs b/src/tools/cargo/src/bin/cargo/commands/remove.rs
index c6508a6b2..c115291cb 100644
--- a/src/tools/cargo/src/bin/cargo/commands/remove.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/remove.rs
@@ -34,19 +34,19 @@ pub fn cli() -> clap::Command {
.conflicts_with("build")
.action(clap::ArgAction::SetTrue)
.group("section")
- .help("Remove as development dependency"),
+ .help("Remove from dev-dependencies"),
clap::Arg::new("build")
.long("build")
.conflicts_with("dev")
.action(clap::ArgAction::SetTrue)
.group("section")
- .help("Remove as build dependency"),
+ .help("Remove from build-dependencies"),
clap::Arg::new("target")
.long("target")
.num_args(1)
.value_name("TARGET")
.value_parser(clap::builder::NonEmptyStringValueParser::new())
- .help("Remove as dependency from the given target platform"),
+ .help("Remove from target-dependencies"),
])
.arg_package("Package to remove from")
.arg_manifest_path()
@@ -270,7 +270,10 @@ fn gc_workspace(workspace: &Workspace<'_>) -> CargoResult<()> {
}
if is_modified {
- cargo_util::paths::write(workspace.root_manifest(), manifest.to_string().as_bytes())?;
+ cargo_util::paths::write_atomic(
+ workspace.root_manifest(),
+ manifest.to_string().as_bytes(),
+ )?;
}
Ok(())
@@ -283,7 +286,7 @@ fn spec_has_match(
config: &Config,
) -> CargoResult<bool> {
for dep in dependencies {
- if spec.name().as_str() != &dep.name {
+ if spec.name() != &dep.name {
continue;
}
diff --git a/src/tools/cargo/src/bin/cargo/commands/run.rs b/src/tools/cargo/src/bin/cargo/commands/run.rs
index 029c9ee56..94396e63f 100644
--- a/src/tools/cargo/src/bin/cargo/commands/run.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/run.rs
@@ -16,6 +16,7 @@ pub fn cli() -> Command {
.about("Run a binary or example of the local package")
.arg(
Arg::new("args")
+ .value_name("ARGS")
.help("Arguments for the binary or example to run")
.value_parser(value_parser!(OsString))
.num_args(0..)
diff --git a/src/tools/cargo/src/bin/cargo/commands/rustc.rs b/src/tools/cargo/src/bin/cargo/commands/rustc.rs
index 60f0b9d60..9b6a57577 100644
--- a/src/tools/cargo/src/bin/cargo/commands/rustc.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/rustc.rs
@@ -10,6 +10,7 @@ pub fn cli() -> Command {
.about("Compile a package, and pass extra options to the compiler")
.arg(
Arg::new("args")
+ .value_name("ARGS")
.num_args(0..)
.help("Extra rustc flags")
.trailing_var_arg(true),
@@ -38,9 +39,9 @@ pub fn cli() -> Command {
"Build only the specified example",
"Build all examples",
"Build only the specified test target",
- "Build all tests",
+ "Build all test targets",
"Build only the specified bench target",
- "Build all benches",
+ "Build all bench targets",
"Build all targets",
)
.arg_features()
diff --git a/src/tools/cargo/src/bin/cargo/commands/rustdoc.rs b/src/tools/cargo/src/bin/cargo/commands/rustdoc.rs
index 8cb2f10de..72de57ad0 100644
--- a/src/tools/cargo/src/bin/cargo/commands/rustdoc.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/rustdoc.rs
@@ -7,6 +7,7 @@ pub fn cli() -> Command {
.about("Build a package's documentation, using specified custom flags.")
.arg(
Arg::new("args")
+ .value_name("ARGS")
.help("Extra rustdoc flags")
.num_args(0..)
.trailing_var_arg(true),
@@ -26,9 +27,9 @@ pub fn cli() -> Command {
"Build only the specified example",
"Build all examples",
"Build only the specified test target",
- "Build all tests",
+ "Build all test targets",
"Build only the specified bench target",
- "Build all benches",
+ "Build all bench targets",
"Build all targets",
)
.arg_features()
diff --git a/src/tools/cargo/src/bin/cargo/commands/search.rs b/src/tools/cargo/src/bin/cargo/commands/search.rs
index 9cacfc7e8..377aa84e1 100644
--- a/src/tools/cargo/src/bin/cargo/commands/search.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/search.rs
@@ -7,7 +7,7 @@ use cargo::ops;
pub fn cli() -> Command {
subcommand("search")
.about("Search packages in crates.io")
- .arg(Arg::new("query").num_args(0..))
+ .arg(Arg::new("query").value_name("QUERY").num_args(0..))
.arg(
opt(
"limit",
diff --git a/src/tools/cargo/src/bin/cargo/commands/test.rs b/src/tools/cargo/src/bin/cargo/commands/test.rs
index 3c7af506d..6e8aff043 100644
--- a/src/tools/cargo/src/bin/cargo/commands/test.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/test.rs
@@ -13,6 +13,7 @@ pub fn cli() -> Command {
)
.arg(
Arg::new("args")
+ .value_name("ARGS")
.help("Arguments for the test binary")
.num_args(0..)
.last(true),
@@ -42,9 +43,9 @@ pub fn cli() -> Command {
"Test only the specified example",
"Test all examples",
"Test only the specified test target",
- "Test all tests",
+ "Test all test targets",
"Test only the specified bench target",
- "Test all benches",
+ "Test all bench targets",
"Test all targets (does not include doctests)",
)
.arg_features()
diff --git a/src/tools/cargo/src/bin/cargo/commands/uninstall.rs b/src/tools/cargo/src/bin/cargo/commands/uninstall.rs
index 9585d290b..30833f292 100644
--- a/src/tools/cargo/src/bin/cargo/commands/uninstall.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/uninstall.rs
@@ -5,7 +5,7 @@ use cargo::ops;
pub fn cli() -> Command {
subcommand("uninstall")
.about("Remove a Rust binary")
- .arg(Arg::new("spec").num_args(0..))
+ .arg(Arg::new("spec").value_name("SPEC").num_args(0..))
.arg(opt("root", "Directory to uninstall packages from").value_name("DIR"))
.arg_quiet()
.arg_package_spec_simple("Package to uninstall")
diff --git a/src/tools/cargo/src/bin/cargo/commands/yank.rs b/src/tools/cargo/src/bin/cargo/commands/yank.rs
index 62d1821c3..75a1772ca 100644
--- a/src/tools/cargo/src/bin/cargo/commands/yank.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/yank.rs
@@ -6,7 +6,7 @@ use cargo_credential::Secret;
pub fn cli() -> Command {
subcommand("yank")
.about("Remove a pushed crate from the index")
- .arg(Arg::new("crate").action(ArgAction::Set))
+ .arg(Arg::new("crate").value_name("CRATE").action(ArgAction::Set))
.arg(
opt("version", "The version to yank or un-yank")
.alias("vers")
diff --git a/src/tools/cargo/src/bin/cargo/main.rs b/src/tools/cargo/src/bin/cargo/main.rs
index 16e4f24f3..245622b6c 100644
--- a/src/tools/cargo/src/bin/cargo/main.rs
+++ b/src/tools/cargo/src/bin/cargo/main.rs
@@ -4,7 +4,7 @@
use cargo::util::network::http::http_handle;
use cargo::util::network::http::needs_custom_http_transport;
-use cargo::util::toml::StringOrVec;
+use cargo::util::toml::schema::StringOrVec;
use cargo::util::CliError;
use cargo::util::{self, closest_msg, command_prelude, CargoResult, CliResult, Config};
use cargo_util::{ProcessBuilder, ProcessError};
@@ -192,10 +192,9 @@ fn execute_external_subcommand(config: &Config, cmd: &str, args: &[&OsStr]) -> C
let did_you_mean = closest_msg(cmd, suggestions.keys(), |c| c);
anyhow::format_err!(
- "no such command: `{}`{}\n\n\t\
- View all installed commands with `cargo --list`",
- cmd,
- did_you_mean
+ "no such command: `{cmd}`{did_you_mean}\n\n\t\
+ View all installed commands with `cargo --list`\n\t\
+ Find a package to install `{cmd}` with `cargo search cargo-{cmd}`",
)
};
diff --git a/src/tools/cargo/src/cargo/core/compiler/context/mod.rs b/src/tools/cargo/src/cargo/core/compiler/context/mod.rs
index 010fe2793..cfbfccb30 100644
--- a/src/tools/cargo/src/cargo/core/compiler/context/mod.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/context/mod.rs
@@ -7,6 +7,7 @@ use std::sync::{Arc, Mutex};
use crate::core::compiler::compilation::{self, UnitOutput};
use crate::core::compiler::{self, artifact, Unit};
use crate::core::PackageId;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::errors::CargoResult;
use crate::util::profile;
use anyhow::{bail, Context as _};
@@ -132,6 +133,13 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
///
/// [`ops::cargo_compile`]: ../../../ops/cargo_compile/index.html
pub fn compile(mut self, exec: &Arc<dyn Executor>) -> CargoResult<Compilation<'cfg>> {
+ // A shared lock is held during the duration of the build since rustc
+ // needs to read from the `src` cache, and we don't want other
+ // commands modifying the `src` cache while it is running.
+ let _lock = self
+ .bcx
+ .config
+ .acquire_package_cache_lock(CacheLockMode::Shared)?;
let mut queue = JobQueue::new(self.bcx);
let mut plan = BuildPlan::new();
let build_plan = self.bcx.build_config.build_plan;
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 3eeeaa0ee..c921986a8 100644
--- a/src/tools/cargo/src/cargo/core/compiler/custom_build.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/custom_build.rs
@@ -307,6 +307,10 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
cmd.env("CARGO_MANIFEST_LINKS", links);
}
+ if let Some(trim_paths) = unit.profile.trim_paths.as_ref() {
+ cmd.env("CARGO_TRIM_PATHS", trim_paths.to_string());
+ }
+
// Be sure to pass along all enabled features for this package, this is the
// last piece of statically known information that we have.
for feat in &unit.features {
@@ -353,6 +357,10 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
);
cmd.env_remove("RUSTFLAGS");
+ if cx.bcx.ws.config().extra_verbose() {
+ cmd.display_env_vars();
+ }
+
// Gather the set of native dependencies that this package has along with
// some other variables to close over.
//
@@ -399,10 +407,7 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
paths::create_dir_all(&script_out_dir)?;
let nightly_features_allowed = cx.bcx.config.nightly_features_allowed;
- let extra_check_cfg = match cx.bcx.config.cli_unstable().check_cfg {
- Some((_, _, _, output)) => output,
- None => false,
- };
+ let extra_check_cfg = cx.bcx.config.cli_unstable().check_cfg;
let targets: Vec<Target> = unit.pkg.targets().to_vec();
// Need a separate copy for the fresh closure.
let targets_fresh = targets.clone();
@@ -802,7 +807,7 @@ impl BuildOutput {
if extra_check_cfg {
check_cfgs.push(value.to_string());
} else {
- warnings.push(format!("cargo:{} requires -Zcheck-cfg=output flag", key));
+ warnings.push(format!("cargo:{} requires -Zcheck-cfg flag", key));
}
}
"rustc-env" => {
@@ -1122,10 +1127,7 @@ fn prev_build_output(cx: &mut Context<'_, '_>, unit: &Unit) -> (Option<BuildOutp
&unit.pkg.to_string(),
&prev_script_out_dir,
&script_out_dir,
- match cx.bcx.config.cli_unstable().check_cfg {
- Some((_, _, _, output)) => output,
- None => false,
- },
+ cx.bcx.config.cli_unstable().check_cfg,
cx.bcx.config.nightly_features_allowed,
unit.pkg.targets(),
)
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 907a0f97d..af44940bd 100644
--- a/src/tools/cargo/src/cargo/core/compiler/future_incompat.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/future_incompat.rs
@@ -37,6 +37,7 @@ use crate::core::compiler::BuildContext;
use crate::core::{Dependency, PackageId, Workspace};
use crate::sources::source::QueryKind;
use crate::sources::SourceConfigMap;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::{iter_join, CargoResult};
use anyhow::{bail, format_err, Context};
use serde::{Deserialize, Serialize};
@@ -166,7 +167,7 @@ impl OnDiskReports {
let on_disk = serde_json::to_vec(&self).unwrap();
if let Err(e) = ws
.target_dir()
- .open_rw(
+ .open_rw_exclusive_create(
FUTURE_INCOMPAT_FILE,
ws.config(),
"Future incompatibility report",
@@ -190,7 +191,7 @@ impl OnDiskReports {
/// Loads the on-disk reports.
pub fn load(ws: &Workspace<'_>) -> CargoResult<OnDiskReports> {
- let report_file = match ws.target_dir().open_ro(
+ let report_file = match ws.target_dir().open_ro_shared(
FUTURE_INCOMPAT_FILE,
ws.config(),
"Future incompatible report",
@@ -297,7 +298,10 @@ fn render_report(per_package_reports: &[FutureIncompatReportPackage]) -> BTreeMa
/// This is best-effort - if an error occurs, `None` will be returned.
fn get_updates(ws: &Workspace<'_>, package_ids: &BTreeSet<PackageId>) -> Option<String> {
// This in general ignores all errors since this is opportunistic.
- let _lock = ws.config().acquire_package_cache_lock().ok()?;
+ let _lock = ws
+ .config()
+ .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)
+ .ok()?;
// Create a set of updated registry sources.
let map = SourceConfigMap::new(ws.config()).ok()?;
let mut package_ids: BTreeSet<_> = package_ids
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 738c8c267..e39fe184d 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
@@ -952,7 +952,10 @@ impl<'cfg> DrainState<'cfg> {
}
for warning in output.warnings.iter() {
- bcx.config.shell().warn(warning)?;
+ let warning_with_package =
+ format!("{}@{}: {}", unit.pkg.name(), unit.pkg.version(), warning);
+
+ bcx.config.shell().warn(warning_with_package)?;
}
if msg.is_some() {
diff --git a/src/tools/cargo/src/cargo/core/compiler/layout.rs b/src/tools/cargo/src/cargo/core/compiler/layout.rs
index d92adffeb..57b65907c 100644
--- a/src/tools/cargo/src/cargo/core/compiler/layout.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/layout.rs
@@ -166,7 +166,7 @@ impl Layout {
// For now we don't do any more finer-grained locking on the artifact
// directory, so just lock the entire thing for the duration of this
// compile.
- let lock = dest.open_rw(".cargo-lock", ws.config(), "build directory")?;
+ let lock = dest.open_rw_exclusive_create(".cargo-lock", ws.config(), "build directory")?;
let root = root.into_path_unlocked();
let dest = dest.into_path_unlocked();
let deps = dest.join("deps");
diff --git a/src/tools/cargo/src/cargo/core/compiler/mod.rs b/src/tools/cargo/src/cargo/core/compiler/mod.rs
index b0f15bd61..ab43e9979 100644
--- a/src/tools/cargo/src/cargo/core/compiler/mod.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/mod.rs
@@ -93,7 +93,8 @@ use crate::core::{Feature, PackageId, Target, Verbosity};
use crate::util::errors::{CargoResult, VerboseError};
use crate::util::interning::InternedString;
use crate::util::machine_message::{self, Message};
-use crate::util::toml::TomlDebugInfo;
+use crate::util::toml::schema::TomlDebugInfo;
+use crate::util::toml::schema::TomlTrimPaths;
use crate::util::{add_path_args, internal, iter_join_onto, profile};
use cargo_util::{paths, ProcessBuilder, ProcessError};
use rustfix::diagnostics::Applicability;
@@ -950,6 +951,7 @@ fn build_base_args(cx: &Context<'_, '_>, cmd: &mut ProcessBuilder, unit: &Unit)
incremental,
strip,
rustflags: profile_rustflags,
+ trim_paths,
..
} = unit.profile.clone();
let test = unit.mode.is_any_test();
@@ -1028,6 +1030,10 @@ fn build_base_args(cx: &Context<'_, '_>, cmd: &mut ProcessBuilder, unit: &Unit)
}
}
+ if let Some(trim_paths) = trim_paths {
+ trim_paths_args(cmd, cx, unit, &trim_paths)?;
+ }
+
cmd.args(unit.pkg.manifest().lint_rustflags());
cmd.args(&profile_rustflags);
if let Some(args) = cx.bcx.extra_args_for(unit) {
@@ -1162,44 +1168,105 @@ fn features_args(unit: &Unit) -> Vec<OsString> {
args
}
+/// Generates the `--remap-path-scope` and `--remap-path-prefix` for [RFC 3127].
+/// See also unstable feature [`-Ztrim-paths`].
+///
+/// [RFC 3127]: https://rust-lang.github.io/rfcs/3127-trim-paths.html
+/// [`-Ztrim-paths`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-trim-paths-option
+fn trim_paths_args(
+ cmd: &mut ProcessBuilder,
+ cx: &Context<'_, '_>,
+ unit: &Unit,
+ trim_paths: &TomlTrimPaths,
+) -> CargoResult<()> {
+ if trim_paths.is_none() {
+ return Ok(());
+ }
+
+ // feature gate was checked during mainfest/config parsing.
+ cmd.arg("-Zunstable-options");
+ cmd.arg(format!("-Zremap-path-scope={trim_paths}"));
+
+ let sysroot_remap = {
+ let sysroot = &cx.bcx.target_data.info(unit.kind).sysroot;
+ let mut remap = OsString::from("--remap-path-prefix=");
+ remap.push(sysroot);
+ remap.push("/lib/rustlib/src/rust"); // See also `detect_sysroot_src_path()`.
+ remap.push("=");
+ remap.push("/rustc/");
+ // This remap logic aligns with rustc:
+ // <https://github.com/rust-lang/rust/blob/c2ef3516/src/bootstrap/src/lib.rs#L1113-L1116>
+ if let Some(commit_hash) = cx.bcx.rustc().commit_hash.as_ref() {
+ remap.push(commit_hash);
+ } else {
+ remap.push(cx.bcx.rustc().version.to_string());
+ }
+ remap
+ };
+ cmd.arg(sysroot_remap);
+
+ let package_remap = {
+ let pkg_root = unit.pkg.root();
+ let ws_root = cx.bcx.ws.root();
+ let is_local = unit.pkg.package_id().source_id().is_path();
+ let mut remap = OsString::from("--remap-path-prefix=");
+ // Remapped to path relative to workspace root:
+ //
+ // * path dependencies under workspace root directory
+ //
+ // Remapped to `<pkg>-<version>`
+ //
+ // * registry dependencies
+ // * git dependencies
+ // * path dependencies outside workspace root directory
+ if is_local && pkg_root.strip_prefix(ws_root).is_ok() {
+ remap.push(ws_root);
+ remap.push("="); // empty to remap to relative paths.
+ } else {
+ remap.push(pkg_root);
+ remap.push("=");
+ remap.push(unit.pkg.name());
+ remap.push("-");
+ remap.push(unit.pkg.version().to_string());
+ }
+ remap
+ };
+ cmd.arg(package_remap);
+
+ Ok(())
+}
+
/// Generates the `--check-cfg` arguments for the `unit`.
/// See unstable feature [`check-cfg`].
///
/// [`check-cfg`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#check-cfg
fn check_cfg_args(cx: &Context<'_, '_>, unit: &Unit) -> Vec<OsString> {
- if let Some((features, well_known_names, well_known_values, _output)) =
- cx.bcx.config.cli_unstable().check_cfg
- {
- let mut args = Vec::with_capacity(unit.pkg.summary().features().len() * 2 + 4);
- args.push(OsString::from("-Zunstable-options"));
-
- if features {
- // This generate something like this:
- // - values(feature)
- // - values(feature, "foo", "bar")
- let mut arg = OsString::from("values(feature");
- for (&feat, _) in unit.pkg.summary().features() {
- arg.push(", \"");
- arg.push(&feat);
- arg.push("\"");
+ if cx.bcx.config.cli_unstable().check_cfg {
+ // This generate something like this:
+ // - cfg(feature, values())
+ // - cfg(feature, values("foo", "bar"))
+ //
+ // NOTE: Despite only explicitly specifying `feature`, well known names and values
+ // are implicitly enabled when one or more `--check-cfg` argument is passed.
+
+ let gross_cap_estimation = unit.pkg.summary().features().len() * 7 + 25;
+ let mut arg_feature = OsString::with_capacity(gross_cap_estimation);
+ arg_feature.push("cfg(feature, values(");
+ for (i, feature) in unit.pkg.summary().features().keys().enumerate() {
+ if i != 0 {
+ arg_feature.push(", ");
}
- arg.push(")");
-
- args.push(OsString::from("--check-cfg"));
- args.push(arg);
- }
-
- if well_known_names {
- args.push(OsString::from("--check-cfg"));
- args.push(OsString::from("names()"));
- }
-
- if well_known_values {
- args.push(OsString::from("--check-cfg"));
- args.push(OsString::from("values()"));
+ arg_feature.push("\"");
+ arg_feature.push(feature);
+ arg_feature.push("\"");
}
+ arg_feature.push("))");
- args
+ vec![
+ OsString::from("-Zunstable-options"),
+ OsString::from("--check-cfg"),
+ arg_feature,
+ ]
} else {
Vec::new()
}
diff --git a/src/tools/cargo/src/cargo/core/compiler/timings.rs b/src/tools/cargo/src/cargo/core/compiler/timings.rs
index 7c388bd10..98c36cfdc 100644
--- a/src/tools/cargo/src/cargo/core/compiler/timings.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/timings.rs
@@ -339,18 +339,21 @@ impl<'cfg> Timings<'cfg> {
include_str!("timings.js")
)?;
drop(f);
- let msg = format!(
- "report saved to {}",
- std::env::current_dir()
- .unwrap_or_default()
- .join(&filename)
- .display()
- );
+
let unstamped_filename = timings_path.join("cargo-timing.html");
paths::link_or_copy(&filename, &unstamped_filename)?;
- self.config
- .shell()
- .status_with_color("Timing", msg, &style::NOTE)?;
+
+ let mut shell = self.config.shell();
+ let timing_path = std::env::current_dir().unwrap_or_default().join(&filename);
+ let link = shell.err_file_hyperlink(&timing_path);
+ let msg = format!(
+ "report saved to {}{}{}",
+ link.open(),
+ timing_path.display(),
+ link.close()
+ );
+ shell.status_with_color("Timing", msg, &style::NOTE)?;
+
Ok(())
}
@@ -380,14 +383,7 @@ impl<'cfg> Timings<'cfg> {
.unwrap_or_else(|_| "n/a".into());
let rustc_info = render_rustc_info(bcx);
let error_msg = match error {
- Some(e) => format!(
- r#"\
- <tr>
- <td class="error-text">Error:</td><td>{}</td>
- </tr>
-"#,
- e
- ),
+ Some(e) => format!(r#"<tr><td class="error-text">Error:</td><td>{e}</td></tr>"#),
None => "".to_string(),
};
write!(
diff --git a/src/tools/cargo/src/cargo/core/dependency.rs b/src/tools/cargo/src/cargo/core/dependency.rs
index f00bb0590..fe102842a 100644
--- a/src/tools/cargo/src/cargo/core/dependency.rs
+++ b/src/tools/cargo/src/cargo/core/dependency.rs
@@ -352,9 +352,7 @@ impl Dependency {
// Only update the `precise` of this source to preserve other
// information about dependency's source which may not otherwise be
// tested during equality/hashing.
- me.source_id = me
- .source_id
- .with_precise(id.source_id().precise().map(|s| s.to_string()));
+ me.source_id = me.source_id.with_precise_from(id.source_id());
self
}
diff --git a/src/tools/cargo/src/cargo/core/features.rs b/src/tools/cargo/src/cargo/core/features.rs
index 5faa2087e..72a267f04 100644
--- a/src/tools/cargo/src/cargo/core/features.rs
+++ b/src/tools/cargo/src/cargo/core/features.rs
@@ -195,7 +195,7 @@ pub const SEE_CHANNELS: &str =
/// [`LATEST_STABLE`]: Edition::LATEST_STABLE
/// [this example]: https://github.com/rust-lang/cargo/blob/3ebb5f15a940810f250b68821149387af583a79e/src/doc/src/reference/unstable.md?plain=1#L1238-L1264
/// [`is_stable`]: Edition::is_stable
-/// [`TomlManifest::to_real_manifest`]: crate::util::toml::TomlManifest::to_real_manifest
+/// [`TomlManifest::to_real_manifest`]: crate::util::toml::schema::TomlManifest::to_real_manifest
/// [`features!`]: macro.features.html
#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, Eq, PartialEq, Serialize, Deserialize)]
pub enum Edition {
@@ -205,20 +205,22 @@ pub enum Edition {
Edition2018,
/// The 2021 edition
Edition2021,
+ /// The 2024 edition
+ Edition2024,
}
impl Edition {
/// The latest edition that is unstable.
///
/// This is `None` if there is no next unstable edition.
- pub const LATEST_UNSTABLE: Option<Edition> = None;
+ pub const LATEST_UNSTABLE: Option<Edition> = Some(Edition::Edition2024);
/// The latest stable edition.
pub const LATEST_STABLE: Edition = Edition::Edition2021;
/// Possible values allowed for the `--edition` CLI flag.
///
/// This requires a static value due to the way clap works, otherwise I
/// would have built this dynamically.
- pub const CLI_VALUES: [&'static str; 3] = ["2015", "2018", "2021"];
+ pub const CLI_VALUES: [&'static str; 4] = ["2015", "2018", "2021", "2024"];
/// Returns the first version that a particular edition was released on
/// stable.
@@ -228,6 +230,7 @@ impl Edition {
Edition2015 => None,
Edition2018 => Some(semver::Version::new(1, 31, 0)),
Edition2021 => Some(semver::Version::new(1, 56, 0)),
+ Edition2024 => None,
}
}
@@ -238,6 +241,7 @@ impl Edition {
Edition2015 => true,
Edition2018 => true,
Edition2021 => true,
+ Edition2024 => false,
}
}
@@ -250,6 +254,7 @@ impl Edition {
Edition2015 => None,
Edition2018 => Some(Edition2015),
Edition2021 => Some(Edition2018),
+ Edition2024 => Some(Edition2021),
}
}
@@ -260,7 +265,8 @@ impl Edition {
match self {
Edition2015 => Edition2018,
Edition2018 => Edition2021,
- Edition2021 => Edition2021,
+ Edition2021 => Edition2024,
+ Edition2024 => Edition2024,
}
}
@@ -286,6 +292,7 @@ impl Edition {
Edition2015 => false,
Edition2018 => true,
Edition2021 => true,
+ Edition2024 => false,
}
}
@@ -298,6 +305,7 @@ impl Edition {
Edition2015 => false,
Edition2018 => true,
Edition2021 => false,
+ Edition2024 => false,
}
}
@@ -316,6 +324,7 @@ impl fmt::Display for Edition {
Edition::Edition2015 => f.write_str("2015"),
Edition::Edition2018 => f.write_str("2018"),
Edition::Edition2021 => f.write_str("2021"),
+ Edition::Edition2024 => f.write_str("2024"),
}
}
}
@@ -326,13 +335,14 @@ impl FromStr for Edition {
"2015" => Ok(Edition::Edition2015),
"2018" => Ok(Edition::Edition2018),
"2021" => Ok(Edition::Edition2021),
- s if s.parse().map_or(false, |y: u16| y > 2021 && y < 2050) => bail!(
+ "2024" => Ok(Edition::Edition2024),
+ s if s.parse().map_or(false, |y: u16| y > 2024 && y < 2050) => bail!(
"this version of Cargo is older than the `{}` edition, \
- and only supports `2015`, `2018`, and `2021` editions.",
+ and only supports `2015`, `2018`, `2021`, and `2024` editions.",
s
),
s => bail!(
- "supported edition values are `2015`, `2018`, or `2021`, \
+ "supported edition values are `2015`, `2018`, `2021`, or `2024`, \
but `{}` is unknown",
s
),
@@ -483,6 +493,12 @@ features! {
// Allow specifying rustflags directly in a profile
(stable, workspace_inheritance, "1.64", "reference/unstable.html#workspace-inheritance"),
+
+ // Support for 2024 edition.
+ (unstable, edition2024, "", "reference/unstable.html#edition-2024"),
+
+ // Allow setting trim-paths in a profile to control the sanitisation of file paths in build outputs.
+ (unstable, trim_paths, "", "reference/unstable.html#profile-trim-paths-option"),
}
pub struct Feature {
@@ -718,8 +734,7 @@ unstable_cli_options!(
#[serde(deserialize_with = "deserialize_build_std")]
build_std: Option<Vec<String>> = ("Enable Cargo to compile the standard library itself as part of a crate graph compilation"),
build_std_features: Option<Vec<String>> = ("Configure features enabled for the standard library itself when building the standard library"),
- #[serde(deserialize_with = "deserialize_check_cfg")]
- check_cfg: Option<(/*features:*/ bool, /*well_known_names:*/ bool, /*well_known_values:*/ bool, /*output:*/ bool)> = ("Specify scope of compile-time checking of `cfg` names/values"),
+ check_cfg: bool = ("Enable compile-time checking of `cfg` names/values/features"),
codegen_backend: bool = ("Enable the `codegen-backend` option in profiles in .cargo/config.toml file"),
config_include: bool = ("Enable the `include` key in config files"),
direct_minimal_versions: bool = ("Resolve minimal dependency versions instead of maximum (direct dependencies only)"),
@@ -743,6 +758,7 @@ unstable_cli_options!(
separate_nightlies: bool = (HIDDEN),
skip_rustdoc_fingerprint: bool = (HIDDEN),
target_applies_to_host: bool = ("Enable the `target-applies-to-host` key in the .cargo/config.toml file"),
+ trim_paths: bool = ("Enable the `trim-paths` option in profiles"),
unstable_options: bool = ("Allow the usage of unstable options"),
);
@@ -829,20 +845,6 @@ where
))
}
-fn deserialize_check_cfg<'de, D>(
- deserializer: D,
-) -> Result<Option<(bool, bool, bool, bool)>, D::Error>
-where
- D: serde::Deserializer<'de>,
-{
- use serde::de::Error;
- let Some(crates) = <Option<Vec<String>>>::deserialize(deserializer)? else {
- return Ok(None);
- };
-
- parse_check_cfg(crates.into_iter()).map_err(D::Error::custom)
-}
-
#[derive(Debug, Copy, Clone, Default, Deserialize)]
pub struct GitoxideFeatures {
/// All fetches are done with `gitoxide`, which includes git dependencies as well as the crates index.
@@ -911,32 +913,6 @@ fn parse_gitoxide(
Ok(Some(out))
}
-fn parse_check_cfg(
- it: impl Iterator<Item = impl AsRef<str>>,
-) -> CargoResult<Option<(bool, bool, bool, bool)>> {
- let mut features = false;
- let mut well_known_names = false;
- let mut well_known_values = false;
- let mut output = false;
-
- for e in it {
- match e.as_ref() {
- "features" => features = true,
- "names" => well_known_names = true,
- "values" => well_known_values = true,
- "output" => output = true,
- _ => bail!("unstable check-cfg only takes `features`, `names`, `values` or `output` as valid inputs"),
- }
- }
-
- Ok(Some((
- features,
- well_known_names,
- well_known_values,
- output,
- )))
-}
-
impl CliUnstable {
pub fn parse(
&mut self,
@@ -1094,7 +1070,7 @@ impl CliUnstable {
}
"build-std-features" => self.build_std_features = Some(parse_features(v)),
"check-cfg" => {
- self.check_cfg = v.map_or(Ok(None), |v| parse_check_cfg(v.split(',')))?
+ self.check_cfg = parse_empty(k, v)?;
}
"codegen-backend" => self.codegen_backend = parse_empty(k, v)?,
"config-include" => self.config_include = parse_empty(k, v)?,
@@ -1117,6 +1093,7 @@ impl CliUnstable {
"no-index-update" => self.no_index_update = parse_empty(k, v)?,
"panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?,
"profile-rustflags" => self.profile_rustflags = parse_empty(k, v)?,
+ "trim-paths" => self.trim_paths = parse_empty(k, v)?,
"publish-timeout" => self.publish_timeout = parse_empty(k, v)?,
"rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?,
"rustdoc-scrape-examples" => self.rustdoc_scrape_examples = parse_empty(k, v)?,
@@ -1125,7 +1102,10 @@ impl CliUnstable {
"script" => self.script = parse_empty(k, v)?,
"target-applies-to-host" => self.target_applies_to_host = parse_empty(k, v)?,
"unstable-options" => self.unstable_options = parse_empty(k, v)?,
- _ => bail!("unknown `-Z` flag specified: {}", k),
+ _ => bail!("\
+ unknown `-Z` flag specified: {k}\n\n\
+ For available unstable features, see https://doc.rust-lang.org/nightly/cargo/reference/unstable.html\n\
+ If you intended to use an unstable rustc feature, try setting `RUSTFLAGS=\"-Z{k}\"`"),
}
Ok(())
diff --git a/src/tools/cargo/src/cargo/core/manifest.rs b/src/tools/cargo/src/cargo/core/manifest.rs
index 7886abec3..66af40c10 100644
--- a/src/tools/cargo/src/cargo/core/manifest.rs
+++ b/src/tools/cargo/src/cargo/core/manifest.rs
@@ -18,7 +18,7 @@ use crate::core::{Dependency, PackageId, PackageIdSpec, SourceId, Summary};
use crate::core::{Edition, Feature, Features, WorkspaceConfig};
use crate::util::errors::*;
use crate::util::interning::InternedString;
-use crate::util::toml::{TomlManifest, TomlProfiles};
+use crate::util::toml::schema::{TomlManifest, TomlProfiles};
use crate::util::{short_hash, Config, Filesystem, RustVersion};
pub enum EitherManifest {
diff --git a/src/tools/cargo/src/cargo/core/mod.rs b/src/tools/cargo/src/cargo/core/mod.rs
index 9b56564a7..2add52d5c 100644
--- a/src/tools/cargo/src/cargo/core/mod.rs
+++ b/src/tools/cargo/src/cargo/core/mod.rs
@@ -14,7 +14,7 @@ pub use self::workspace::{
find_workspace_root, resolve_relative_path, MaybePackage, Workspace, WorkspaceConfig,
WorkspaceRootConfig,
};
-pub use crate::util::toml::InheritableFields;
+pub use crate::util::toml::schema::InheritableFields;
pub mod compiler;
pub mod dependency;
diff --git a/src/tools/cargo/src/cargo/core/package.rs b/src/tools/cargo/src/cargo/core/package.rs
index 76f6c405b..274798474 100644
--- a/src/tools/cargo/src/cargo/core/package.rs
+++ b/src/tools/cargo/src/cargo/core/package.rs
@@ -24,7 +24,7 @@ use crate::core::resolver::{HasDevUnits, Resolve};
use crate::core::{Dependency, Manifest, PackageId, SourceId, Target};
use crate::core::{Summary, Workspace};
use crate::sources::source::{MaybePackage, SourceMap};
-use crate::util::config::PackageCacheLock;
+use crate::util::cache_lock::{CacheLock, CacheLockMode};
use crate::util::errors::{CargoResult, HttpNotSuccessful};
use crate::util::interning::InternedString;
use crate::util::network::http::http_handle_and_timeout;
@@ -367,7 +367,7 @@ pub struct Downloads<'a, 'cfg> {
next_speed_check_bytes_threshold: Cell<u64>,
/// Global filesystem lock to ensure only one Cargo is downloading at a
/// time.
- _lock: PackageCacheLock<'cfg>,
+ _lock: CacheLock<'cfg>,
}
struct Download<'cfg> {
@@ -465,7 +465,9 @@ impl<'cfg> PackageSet<'cfg> {
timeout,
next_speed_check: Cell::new(Instant::now()),
next_speed_check_bytes_threshold: Cell::new(0),
- _lock: self.config.acquire_package_cache_lock()?,
+ _lock: self
+ .config
+ .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?,
})
}
@@ -478,6 +480,9 @@ impl<'cfg> PackageSet<'cfg> {
pub fn get_many(&self, ids: impl IntoIterator<Item = PackageId>) -> CargoResult<Vec<&Package>> {
let mut pkgs = Vec::new();
+ let _lock = self
+ .config
+ .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
let mut downloads = self.enable_download()?;
for id in ids {
pkgs.extend(downloads.start(id)?);
diff --git a/src/tools/cargo/src/cargo/core/package_id.rs b/src/tools/cargo/src/cargo/core/package_id.rs
index ca126172c..3e9c03a47 100644
--- a/src/tools/cargo/src/cargo/core/package_id.rs
+++ b/src/tools/cargo/src/cargo/core/package_id.rs
@@ -12,7 +12,7 @@ use serde::ser;
use crate::core::SourceId;
use crate::util::interning::InternedString;
-use crate::util::{CargoResult, ToSemver};
+use crate::util::CargoResult;
static PACKAGE_ID_CACHE: OnceLock<Mutex<HashSet<&'static PackageIdInner>>> = OnceLock::new();
@@ -29,8 +29,7 @@ struct PackageIdInner {
source_id: SourceId,
}
-// Custom equality that uses full equality of SourceId, rather than its custom equality,
-// and Version, which usually ignores `build` metadata.
+// Custom equality that uses full equality of SourceId, rather than its custom equality.
//
// The `build` part of the version is usually ignored (like a "comment").
// However, there are some cases where it is important. The download path from
@@ -40,11 +39,7 @@ struct PackageIdInner {
impl PartialEq for PackageIdInner {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
- && self.version.major == other.version.major
- && self.version.minor == other.version.minor
- && self.version.patch == other.version.patch
- && self.version.pre == other.version.pre
- && self.version.build == other.version.build
+ && self.version == other.version
&& self.source_id.full_eq(other.source_id)
}
}
@@ -53,11 +48,7 @@ impl PartialEq for PackageIdInner {
impl Hash for PackageIdInner {
fn hash<S: hash::Hasher>(&self, into: &mut S) {
self.name.hash(into);
- self.version.major.hash(into);
- self.version.minor.hash(into);
- self.version.patch.hash(into);
- self.version.pre.hash(into);
- self.version.build.hash(into);
+ self.version.hash(into);
self.source_id.full_hash(into);
}
}
@@ -82,27 +73,31 @@ impl<'de> de::Deserialize<'de> for PackageId {
D: de::Deserializer<'de>,
{
let string = String::deserialize(d)?;
- let mut s = string.splitn(3, ' ');
- let name = s.next().unwrap();
- let name = InternedString::new(name);
- let Some(version) = s.next() else {
- return Err(de::Error::custom("invalid serialized PackageId"));
- };
- let version = version.to_semver().map_err(de::Error::custom)?;
- let Some(url) = s.next() else {
- return Err(de::Error::custom("invalid serialized PackageId"));
- };
- let url = if url.starts_with('(') && url.ends_with(')') {
- &url[1..url.len() - 1]
- } else {
- return Err(de::Error::custom("invalid serialized PackageId"));
- };
+
+ let (field, rest) = string
+ .split_once(' ')
+ .ok_or_else(|| de::Error::custom("invalid serialized PackageId"))?;
+ let name = InternedString::new(field);
+
+ let (field, rest) = rest
+ .split_once(' ')
+ .ok_or_else(|| de::Error::custom("invalid serialized PackageId"))?;
+ let version = field.parse().map_err(de::Error::custom)?;
+
+ let url =
+ strip_parens(rest).ok_or_else(|| de::Error::custom("invalid serialized PackageId"))?;
let source_id = SourceId::from_url(url).map_err(de::Error::custom)?;
Ok(PackageId::pure(name, version, source_id))
}
}
+fn strip_parens(value: &str) -> Option<&str> {
+ let value = value.strip_prefix('(')?;
+ let value = value.strip_suffix(')')?;
+ Some(value)
+}
+
impl PartialEq for PackageId {
fn eq(&self, other: &PackageId) -> bool {
if ptr::eq(self.inner, other.inner) {
@@ -128,12 +123,12 @@ impl Hash for PackageId {
}
impl PackageId {
- pub fn new<T: ToSemver>(
+ pub fn new(
name: impl Into<InternedString>,
- version: T,
+ version: &str,
sid: SourceId,
) -> CargoResult<PackageId> {
- let v = version.to_semver()?;
+ let v = version.parse()?;
Ok(PackageId::pure(name.into(), v, sid))
}
@@ -165,14 +160,6 @@ impl PackageId {
self.inner.source_id
}
- pub fn with_precise(self, precise: Option<String>) -> PackageId {
- PackageId::pure(
- self.inner.name,
- self.inner.version.clone(),
- self.inner.source_id.with_precise(precise),
- )
- }
-
pub fn with_source_id(self, source: SourceId) -> PackageId {
PackageId::pure(self.inner.name, self.inner.version.clone(), source)
}
@@ -233,6 +220,16 @@ impl fmt::Debug for PackageId {
}
}
+impl fmt::Debug for PackageIdInner {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("PackageIdInner")
+ .field("name", &self.name)
+ .field("version", &self.version.to_string())
+ .field("source", &self.source_id.to_string())
+ .finish()
+ }
+}
+
#[cfg(test)]
mod tests {
use super::PackageId;
@@ -257,4 +254,14 @@ mod tests {
let pkg_id = PackageId::new("foo", "1.0.0", SourceId::for_registry(&loc).unwrap()).unwrap();
assert_eq!("foo v1.0.0", pkg_id.to_string());
}
+
+ #[test]
+ fn unequal_build_metadata() {
+ let loc = CRATES_IO_INDEX.into_url().unwrap();
+ let repo = SourceId::for_registry(&loc).unwrap();
+ let first = PackageId::new("foo", "0.0.1+first", repo).unwrap();
+ let second = PackageId::new("foo", "0.0.1+second", repo).unwrap();
+ assert_ne!(first, second);
+ assert_ne!(first.inner, second.inner);
+ }
}
diff --git a/src/tools/cargo/src/cargo/core/package_id_spec.rs b/src/tools/cargo/src/cargo/core/package_id_spec.rs
index 53d99b84b..c617c1f7a 100644
--- a/src/tools/cargo/src/cargo/core/package_id_spec.rs
+++ b/src/tools/cargo/src/cargo/core/package_id_spec.rs
@@ -9,9 +9,8 @@ use url::Url;
use crate::core::PackageId;
use crate::util::edit_distance;
use crate::util::errors::CargoResult;
-use crate::util::interning::InternedString;
-use crate::util::PartialVersion;
use crate::util::{validate_package_name, IntoUrl};
+use crate::util_semver::PartialVersion;
/// Some or all of the data required to identify a package:
///
@@ -24,7 +23,7 @@ use crate::util::{validate_package_name, IntoUrl};
/// sufficient to uniquely define a package ID.
#[derive(Clone, PartialEq, Eq, Debug, Hash, Ord, PartialOrd)]
pub struct PackageIdSpec {
- name: InternedString,
+ name: String,
version: Option<PartialVersion>,
url: Option<Url>,
}
@@ -76,7 +75,7 @@ impl PackageIdSpec {
};
validate_package_name(name, "pkgid", "")?;
Ok(PackageIdSpec {
- name: InternedString::new(name),
+ name: String::from(name),
version,
url: None,
})
@@ -99,7 +98,7 @@ impl PackageIdSpec {
/// fields filled in.
pub fn from_package_id(package_id: PackageId) -> PackageIdSpec {
PackageIdSpec {
- name: package_id.name(),
+ name: String::from(package_id.name().as_str()),
version: Some(package_id.version().clone().into()),
url: Some(package_id.source_id().url().clone()),
}
@@ -127,18 +126,18 @@ impl PackageIdSpec {
Some(fragment) => match fragment.split_once([':', '@']) {
Some((name, part)) => {
let version = part.parse::<PartialVersion>()?;
- (InternedString::new(name), Some(version))
+ (String::from(name), Some(version))
}
None => {
if fragment.chars().next().unwrap().is_alphabetic() {
- (InternedString::new(&fragment), None)
+ (String::from(fragment.as_str()), None)
} else {
let version = fragment.parse::<PartialVersion>()?;
- (InternedString::new(path_name), Some(version))
+ (String::from(path_name), Some(version))
}
}
},
- None => (InternedString::new(path_name), None),
+ None => (String::from(path_name), None),
}
};
Ok(PackageIdSpec {
@@ -148,13 +147,13 @@ impl PackageIdSpec {
})
}
- pub fn name(&self) -> InternedString {
- self.name
+ pub fn name(&self) -> &str {
+ self.name.as_str()
}
/// Full `semver::Version`, if present
pub fn version(&self) -> Option<Version> {
- self.version.as_ref().and_then(|v| v.version())
+ self.version.as_ref().and_then(|v| v.to_version())
}
pub fn partial_version(&self) -> Option<&PartialVersion> {
@@ -171,7 +170,7 @@ impl PackageIdSpec {
/// Checks whether the given `PackageId` matches the `PackageIdSpec`.
pub fn matches(&self, package_id: PackageId) -> bool {
- if self.name() != package_id.name() {
+ if self.name() != package_id.name().as_str() {
return false;
}
@@ -181,10 +180,13 @@ impl PackageIdSpec {
}
}
- match self.url {
- Some(ref u) => u == package_id.source_id().url(),
- None => true,
+ if let Some(u) = &self.url {
+ if u != package_id.source_id().url() {
+ return false;
+ }
}
+
+ true
}
/// Checks a list of `PackageId`s to find 1 that matches this `PackageIdSpec`. If 0, 2, or
@@ -211,7 +213,7 @@ impl PackageIdSpec {
if self.url.is_some() {
try_spec(
PackageIdSpec {
- name: self.name,
+ name: self.name.clone(),
version: self.version.clone(),
url: None,
},
@@ -221,7 +223,7 @@ impl PackageIdSpec {
if suggestion.is_empty() && self.version.is_some() {
try_spec(
PackageIdSpec {
- name: self.name,
+ name: self.name.clone(),
version: None,
url: None,
},
@@ -324,7 +326,6 @@ impl<'de> de::Deserialize<'de> for PackageIdSpec {
mod tests {
use super::PackageIdSpec;
use crate::core::{PackageId, SourceId};
- use crate::util::interning::InternedString;
use url::Url;
#[test]
@@ -333,13 +334,16 @@ mod tests {
fn ok(spec: &str, expected: PackageIdSpec, expected_rendered: &str) {
let parsed = PackageIdSpec::parse(spec).unwrap();
assert_eq!(parsed, expected);
- assert_eq!(parsed.to_string(), expected_rendered);
+ let rendered = parsed.to_string();
+ assert_eq!(rendered, expected_rendered);
+ let reparsed = PackageIdSpec::parse(&rendered).unwrap();
+ assert_eq!(reparsed, expected);
}
ok(
"https://crates.io/foo",
PackageIdSpec {
- name: InternedString::new("foo"),
+ name: String::from("foo"),
version: None,
url: Some(Url::parse("https://crates.io/foo").unwrap()),
},
@@ -348,7 +352,7 @@ mod tests {
ok(
"https://crates.io/foo#1.2.3",
PackageIdSpec {
- name: InternedString::new("foo"),
+ name: String::from("foo"),
version: Some("1.2.3".parse().unwrap()),
url: Some(Url::parse("https://crates.io/foo").unwrap()),
},
@@ -357,7 +361,7 @@ mod tests {
ok(
"https://crates.io/foo#1.2",
PackageIdSpec {
- name: InternedString::new("foo"),
+ name: String::from("foo"),
version: Some("1.2".parse().unwrap()),
url: Some(Url::parse("https://crates.io/foo").unwrap()),
},
@@ -366,7 +370,7 @@ mod tests {
ok(
"https://crates.io/foo#bar:1.2.3",
PackageIdSpec {
- name: InternedString::new("bar"),
+ name: String::from("bar"),
version: Some("1.2.3".parse().unwrap()),
url: Some(Url::parse("https://crates.io/foo").unwrap()),
},
@@ -375,7 +379,7 @@ mod tests {
ok(
"https://crates.io/foo#bar@1.2.3",
PackageIdSpec {
- name: InternedString::new("bar"),
+ name: String::from("bar"),
version: Some("1.2.3".parse().unwrap()),
url: Some(Url::parse("https://crates.io/foo").unwrap()),
},
@@ -384,7 +388,7 @@ mod tests {
ok(
"https://crates.io/foo#bar@1.2",
PackageIdSpec {
- name: InternedString::new("bar"),
+ name: String::from("bar"),
version: Some("1.2".parse().unwrap()),
url: Some(Url::parse("https://crates.io/foo").unwrap()),
},
@@ -393,7 +397,7 @@ mod tests {
ok(
"foo",
PackageIdSpec {
- name: InternedString::new("foo"),
+ name: String::from("foo"),
version: None,
url: None,
},
@@ -402,7 +406,7 @@ mod tests {
ok(
"foo:1.2.3",
PackageIdSpec {
- name: InternedString::new("foo"),
+ name: String::from("foo"),
version: Some("1.2.3".parse().unwrap()),
url: None,
},
@@ -411,7 +415,7 @@ mod tests {
ok(
"foo@1.2.3",
PackageIdSpec {
- name: InternedString::new("foo"),
+ name: String::from("foo"),
version: Some("1.2.3".parse().unwrap()),
url: None,
},
@@ -420,12 +424,104 @@ mod tests {
ok(
"foo@1.2",
PackageIdSpec {
- name: InternedString::new("foo"),
+ name: String::from("foo"),
version: Some("1.2".parse().unwrap()),
url: None,
},
"foo@1.2",
);
+
+ // pkgid-spec.md
+ ok(
+ "regex",
+ PackageIdSpec {
+ name: String::from("regex"),
+ version: None,
+ url: None,
+ },
+ "regex",
+ );
+ ok(
+ "regex@1.4",
+ PackageIdSpec {
+ name: String::from("regex"),
+ version: Some("1.4".parse().unwrap()),
+ url: None,
+ },
+ "regex@1.4",
+ );
+ ok(
+ "regex@1.4.3",
+ PackageIdSpec {
+ name: String::from("regex"),
+ version: Some("1.4.3".parse().unwrap()),
+ url: None,
+ },
+ "regex@1.4.3",
+ );
+ ok(
+ "https://github.com/rust-lang/crates.io-index#regex",
+ PackageIdSpec {
+ name: String::from("regex"),
+ version: None,
+ url: Some(Url::parse("https://github.com/rust-lang/crates.io-index").unwrap()),
+ },
+ "https://github.com/rust-lang/crates.io-index#regex",
+ );
+ ok(
+ "https://github.com/rust-lang/crates.io-index#regex@1.4.3",
+ PackageIdSpec {
+ name: String::from("regex"),
+ version: Some("1.4.3".parse().unwrap()),
+ url: Some(Url::parse("https://github.com/rust-lang/crates.io-index").unwrap()),
+ },
+ "https://github.com/rust-lang/crates.io-index#regex@1.4.3",
+ );
+ ok(
+ "https://github.com/rust-lang/cargo#0.52.0",
+ PackageIdSpec {
+ name: String::from("cargo"),
+ version: Some("0.52.0".parse().unwrap()),
+ url: Some(Url::parse("https://github.com/rust-lang/cargo").unwrap()),
+ },
+ "https://github.com/rust-lang/cargo#0.52.0",
+ );
+ ok(
+ "https://github.com/rust-lang/cargo#cargo-platform@0.1.2",
+ PackageIdSpec {
+ name: String::from("cargo-platform"),
+ version: Some("0.1.2".parse().unwrap()),
+ url: Some(Url::parse("https://github.com/rust-lang/cargo").unwrap()),
+ },
+ "https://github.com/rust-lang/cargo#cargo-platform@0.1.2",
+ );
+ ok(
+ "ssh://git@github.com/rust-lang/regex.git#regex@1.4.3",
+ PackageIdSpec {
+ name: String::from("regex"),
+ version: Some("1.4.3".parse().unwrap()),
+ url: Some(Url::parse("ssh://git@github.com/rust-lang/regex.git").unwrap()),
+ },
+ "ssh://git@github.com/rust-lang/regex.git#regex@1.4.3",
+ );
+ ok(
+ "file:///path/to/my/project/foo",
+ PackageIdSpec {
+ name: String::from("foo"),
+ version: None,
+ url: Some(Url::parse("file:///path/to/my/project/foo").unwrap()),
+ },
+ "file:///path/to/my/project/foo",
+ );
+ ok(
+ "file:///path/to/my/project/foo#1.1.8",
+ PackageIdSpec {
+ name: String::from("foo"),
+ version: Some("1.1.8".parse().unwrap()),
+ url: Some(Url::parse("file:///path/to/my/project/foo").unwrap()),
+ },
+ "file:///path/to/my/project/foo#1.1.8",
+ );
}
#[test]
@@ -452,6 +548,12 @@ mod tests {
assert!(PackageIdSpec::parse("foo@1.2.3").unwrap().matches(foo));
assert!(!PackageIdSpec::parse("foo@1.2.2").unwrap().matches(foo));
assert!(PackageIdSpec::parse("foo@1.2").unwrap().matches(foo));
+ assert!(PackageIdSpec::parse("https://example.com#foo@1.2")
+ .unwrap()
+ .matches(foo));
+ assert!(!PackageIdSpec::parse("https://bob.com#foo@1.2")
+ .unwrap()
+ .matches(foo));
let meta = PackageId::new("meta", "1.2.3+hello", sid).unwrap();
assert!(PackageIdSpec::parse("meta").unwrap().matches(meta));
diff --git a/src/tools/cargo/src/cargo/core/profiles.rs b/src/tools/cargo/src/cargo/core/profiles.rs
index 1ad9ed5f7..ec53dbae5 100644
--- a/src/tools/cargo/src/cargo/core/profiles.rs
+++ b/src/tools/cargo/src/cargo/core/profiles.rs
@@ -24,9 +24,12 @@
use crate::core::compiler::{CompileKind, CompileTarget, Unit};
use crate::core::dependency::Artifact;
use crate::core::resolver::features::FeaturesFor;
+use crate::core::Feature;
use crate::core::{PackageId, PackageIdSpec, Resolve, Shell, Target, Workspace};
use crate::util::interning::InternedString;
-use crate::util::toml::{
+use crate::util::toml::schema::TomlTrimPaths;
+use crate::util::toml::schema::TomlTrimPathsValue;
+use crate::util::toml::schema::{
ProfilePackageSpec, StringOrBool, TomlDebugInfo, TomlProfile, TomlProfiles,
};
use crate::util::{closest_msg, config, CargoResult, Config};
@@ -80,7 +83,9 @@ impl Profiles {
rustc_host,
};
- Self::add_root_profiles(&mut profile_makers, &profiles);
+ let trim_paths_enabled = ws.unstable_features().is_enabled(Feature::trim_paths())
+ || config.cli_unstable().trim_paths;
+ Self::add_root_profiles(&mut profile_makers, &profiles, trim_paths_enabled);
// Merge with predefined profiles.
use std::collections::btree_map::Entry;
@@ -104,7 +109,7 @@ impl Profiles {
// Verify that the requested profile is defined *somewhere*.
// This simplifies the API (no need for CargoResult), and enforces
// assumptions about how config profiles are loaded.
- profile_makers.get_profile_maker(requested_profile)?;
+ profile_makers.get_profile_maker(&requested_profile)?;
Ok(profile_makers)
}
@@ -123,6 +128,7 @@ impl Profiles {
fn add_root_profiles(
profile_makers: &mut Profiles,
profiles: &BTreeMap<InternedString, TomlProfile>,
+ trim_paths_enabled: bool,
) {
profile_makers.by_name.insert(
InternedString::new("dev"),
@@ -131,7 +137,10 @@ impl Profiles {
profile_makers.by_name.insert(
InternedString::new("release"),
- ProfileMaker::new(Profile::default_release(), profiles.get("release").cloned()),
+ ProfileMaker::new(
+ Profile::default_release(trim_paths_enabled),
+ profiles.get("release").cloned(),
+ ),
);
}
@@ -142,21 +151,21 @@ impl Profiles {
(
"bench",
TomlProfile {
- inherits: Some(InternedString::new("release")),
+ inherits: Some(String::from("release")),
..TomlProfile::default()
},
),
(
"test",
TomlProfile {
- inherits: Some(InternedString::new("dev")),
+ inherits: Some(String::from("dev")),
..TomlProfile::default()
},
),
(
"doc",
TomlProfile {
- inherits: Some(InternedString::new("dev")),
+ inherits: Some(String::from("dev")),
..TomlProfile::default()
},
),
@@ -173,7 +182,7 @@ impl Profiles {
match &profile.dir_name {
None => {}
Some(dir_name) => {
- self.dir_names.insert(name, dir_name.to_owned());
+ self.dir_names.insert(name, InternedString::new(dir_name));
}
}
@@ -212,12 +221,13 @@ impl Profiles {
set: &mut HashSet<InternedString>,
profiles: &BTreeMap<InternedString, TomlProfile>,
) -> CargoResult<ProfileMaker> {
- let mut maker = match profile.inherits {
+ let mut maker = match &profile.inherits {
Some(inherits_name) if inherits_name == "dev" || inherits_name == "release" => {
// These are the root profiles added in `add_root_profiles`.
- self.get_profile_maker(inherits_name).unwrap().clone()
+ self.get_profile_maker(&inherits_name).unwrap().clone()
}
Some(inherits_name) => {
+ let inherits_name = InternedString::new(&inherits_name);
if !set.insert(inherits_name) {
bail!(
"profile inheritance loop detected with profile `{}` inheriting `{}`",
@@ -263,7 +273,7 @@ impl Profiles {
unit_for: UnitFor,
kind: CompileKind,
) -> Profile {
- let maker = self.get_profile_maker(self.requested_profile).unwrap();
+ let maker = self.get_profile_maker(&self.requested_profile).unwrap();
let mut profile = maker.get_profile(Some(pkg_id), is_member, unit_for.is_for_host());
// Dealing with `panic=abort` and `panic=unwind` requires some special
@@ -317,6 +327,7 @@ impl Profiles {
result.root = for_unit_profile.root;
result.debuginfo = for_unit_profile.debuginfo;
result.opt_level = for_unit_profile.opt_level;
+ result.trim_paths = for_unit_profile.trim_paths.clone();
result
}
@@ -325,7 +336,7 @@ impl Profiles {
/// select for the package that was actually built.
pub fn base_profile(&self) -> Profile {
let profile_name = self.requested_profile;
- let maker = self.get_profile_maker(profile_name).unwrap();
+ let maker = self.get_profile_maker(&profile_name).unwrap();
maker.get_profile(None, /*is_member*/ true, /*is_for_host*/ false)
}
@@ -372,9 +383,9 @@ impl Profiles {
}
/// Returns the profile maker for the given profile name.
- fn get_profile_maker(&self, name: InternedString) -> CargoResult<&ProfileMaker> {
+ fn get_profile_maker(&self, name: &str) -> CargoResult<&ProfileMaker> {
self.by_name
- .get(&name)
+ .get(name)
.ok_or_else(|| anyhow::format_err!("profile `{}` is not defined", name))
}
}
@@ -521,7 +532,7 @@ fn merge_profile(profile: &mut Profile, toml: &TomlProfile) {
None => {}
}
if toml.codegen_backend.is_some() {
- profile.codegen_backend = toml.codegen_backend;
+ profile.codegen_backend = toml.codegen_backend.as_ref().map(InternedString::from);
}
if toml.codegen_units.is_some() {
profile.codegen_units = toml.codegen_units;
@@ -553,7 +564,10 @@ fn merge_profile(profile: &mut Profile, toml: &TomlProfile) {
profile.incremental = incremental;
}
if let Some(flags) = &toml.rustflags {
- profile.rustflags = flags.clone();
+ profile.rustflags = flags.iter().map(InternedString::from).collect();
+ }
+ if let Some(trim_paths) = &toml.trim_paths {
+ profile.trim_paths = Some(trim_paths.clone());
}
profile.strip = match toml.strip {
Some(StringOrBool::Bool(true)) => Strip::Named(InternedString::new("symbols")),
@@ -598,6 +612,9 @@ pub struct Profile {
#[serde(skip_serializing_if = "Vec::is_empty")] // remove when `rustflags` is stablized
// Note that `rustflags` is used for the cargo-feature `profile_rustflags`
pub rustflags: Vec<InternedString>,
+ // remove when `-Ztrim-paths` is stablized
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub trim_paths: Option<TomlTrimPaths>,
}
impl Default for Profile {
@@ -618,6 +635,7 @@ impl Default for Profile {
panic: PanicStrategy::Unwind,
strip: Strip::None,
rustflags: vec![],
+ trim_paths: None,
}
}
}
@@ -627,7 +645,7 @@ compact_debug! {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let (default, default_name) = match self.name.as_str() {
"dev" => (Profile::default_dev(), "default_dev()"),
- "release" => (Profile::default_release(), "default_release()"),
+ "release" => (Profile::default_release(false), "default_release()"),
_ => (Profile::default(), "default()"),
};
[debug_the_fields(
@@ -646,6 +664,7 @@ compact_debug! {
panic
strip
rustflags
+ trim_paths
)]
}
}
@@ -687,11 +706,13 @@ impl Profile {
}
/// Returns a built-in `release` profile.
- fn default_release() -> Profile {
+ fn default_release(trim_paths_enabled: bool) -> Profile {
+ let trim_paths = trim_paths_enabled.then(|| TomlTrimPathsValue::Object.into());
Profile {
name: InternedString::new("release"),
root: ProfileRoot::Release,
opt_level: InternedString::new("3"),
+ trim_paths,
..Profile::default()
}
}
@@ -712,6 +733,7 @@ impl Profile {
self.rpath,
(self.incremental, self.panic, self.strip),
&self.rustflags,
+ &self.trim_paths,
)
}
}
@@ -1162,7 +1184,11 @@ fn merge_config_profiles(
requested_profile: InternedString,
) -> CargoResult<BTreeMap<InternedString, TomlProfile>> {
let mut profiles = match ws.profiles() {
- Some(profiles) => profiles.get_all().clone(),
+ Some(profiles) => profiles
+ .get_all()
+ .iter()
+ .map(|(k, v)| (InternedString::new(k), v.clone()))
+ .collect(),
None => BTreeMap::new(),
};
// Set of profile names to check if defined in config only.
@@ -1174,7 +1200,7 @@ fn merge_config_profiles(
profile.merge(&config_profile);
}
if let Some(inherits) = &profile.inherits {
- check_to_add.insert(*inherits);
+ check_to_add.insert(InternedString::new(inherits));
}
}
// Add the built-in profiles. This is important for things like `cargo
@@ -1188,10 +1214,10 @@ fn merge_config_profiles(
while !check_to_add.is_empty() {
std::mem::swap(&mut current, &mut check_to_add);
for name in current.drain() {
- if !profiles.contains_key(&name) {
+ if !profiles.contains_key(name.as_str()) {
if let Some(config_profile) = get_config_profile(ws, &name)? {
if let Some(inherits) = &config_profile.inherits {
- check_to_add.insert(*inherits);
+ check_to_add.insert(InternedString::new(inherits));
}
profiles.insert(name, config_profile);
}
diff --git a/src/tools/cargo/src/cargo/core/registry.rs b/src/tools/cargo/src/cargo/core/registry.rs
index 9a6a5a035..a91f2986a 100644
--- a/src/tools/cargo/src/cargo/core/registry.rs
+++ b/src/tools/cargo/src/cargo/core/registry.rs
@@ -116,7 +116,7 @@ enum Kind {
/// directive that we found in a lockfile, if present.
pub struct LockedPatchDependency {
/// The original `Dependency` directive, except "locked" so it's version
- /// requirement is `=foo` and its `SourceId` has a "precise" listed.
+ /// requirement is Locked to `foo` and its `SourceId` has a "precise" listed.
pub dependency: Dependency,
/// The `PackageId` that was previously found in a lock file which
/// `dependency` matches.
@@ -161,7 +161,7 @@ impl<'cfg> PackageRegistry<'cfg> {
// If the previous source was not a precise source, then we can be
// sure that it's already been updated if we've already loaded it.
- Some((previous, _)) if previous.precise().is_none() => {
+ Some((previous, _)) if !previous.has_precise() => {
debug!("load/precise {}", namespace);
return Ok(());
}
@@ -170,7 +170,7 @@ impl<'cfg> PackageRegistry<'cfg> {
// then we're done, otherwise we need to need to move forward
// updating this source.
Some((previous, _)) => {
- if previous.precise() == namespace.precise() {
+ if previous.has_same_precise_as(namespace) {
debug!("load/match {}", namespace);
return Ok(());
}
@@ -471,9 +471,9 @@ impl<'cfg> PackageRegistry<'cfg> {
//
// If we have a precise version, then we'll update lazily during the
// querying phase. Note that precise in this case is only
- // `Some("locked")` as other `Some` values indicate a `cargo update
+ // `"locked"` as other values indicate a `cargo update
// --precise` request
- if source_id.precise() != Some("locked") {
+ if !source_id.has_locked_precise() {
self.sources.get_mut(source_id).unwrap().invalidate_cache();
} else {
debug!("skipping update due to locked registry");
diff --git a/src/tools/cargo/src/cargo/core/resolver/context.rs b/src/tools/cargo/src/cargo/core/resolver/context.rs
index f19c678a6..09b16b39c 100644
--- a/src/tools/cargo/src/cargo/core/resolver/context.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/context.rs
@@ -10,10 +10,6 @@ use std::collections::HashMap;
use std::num::NonZeroU64;
use tracing::debug;
-pub use super::encode::Metadata;
-pub use super::encode::{EncodableDependency, EncodablePackageId, EncodableResolve};
-pub use super::resolve::Resolve;
-
// A `Context` is basically a bunch of local resolution information which is
// kept around for all `BacktrackFrame` instances. As a result, this runs the
// risk of being cloned *a lot* so we want to make this as cheap to clone as
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 9041c5b0f..6c904c148 100644
--- a/src/tools/cargo/src/cargo/core/resolver/dep_cache.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/dep_cache.rs
@@ -20,7 +20,6 @@ use crate::core::{Dependency, FeatureValue, PackageId, PackageIdSpec, Registry,
use crate::sources::source::QueryKind;
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
-use crate::util::RustVersion;
use anyhow::Context as _;
use std::collections::{BTreeSet, HashMap, HashSet};
@@ -32,16 +31,11 @@ pub struct RegistryQueryer<'a> {
pub registry: &'a mut (dyn Registry + 'a),
replacements: &'a [(PackageIdSpec, Dependency)],
version_prefs: &'a VersionPreferences,
- /// If set the list of dependency candidates will be sorted by minimal
- /// versions first. That allows `cargo update -Z minimal-versions` which will
- /// specify minimum dependency versions to be used.
- minimal_versions: bool,
- max_rust_version: Option<RustVersion>,
- /// a cache of `Candidate`s that fulfil a `Dependency` (and whether `first_minimal_version`)
- registry_cache: HashMap<(Dependency, bool), Poll<Rc<Vec<Summary>>>>,
+ /// a cache of `Candidate`s that fulfil a `Dependency` (and whether `first_version`)
+ registry_cache: HashMap<(Dependency, Option<VersionOrdering>), Poll<Rc<Vec<Summary>>>>,
/// a cache of `Dependency`s that are required for a `Summary`
///
- /// HACK: `first_minimal_version` is not kept in the cache key is it is 1:1 with
+ /// HACK: `first_version` is not kept in the cache key is it is 1:1 with
/// `parent.is_none()` (the first element of the cache key) as it doesn't change through
/// execution.
summary_cache: HashMap<
@@ -57,15 +51,11 @@ impl<'a> RegistryQueryer<'a> {
registry: &'a mut dyn Registry,
replacements: &'a [(PackageIdSpec, Dependency)],
version_prefs: &'a VersionPreferences,
- minimal_versions: bool,
- max_rust_version: Option<&RustVersion>,
) -> Self {
RegistryQueryer {
registry,
replacements,
version_prefs,
- minimal_versions,
- max_rust_version: max_rust_version.cloned(),
registry_cache: HashMap::new(),
summary_cache: HashMap::new(),
used_replacements: HashMap::new(),
@@ -106,23 +96,20 @@ impl<'a> RegistryQueryer<'a> {
pub fn query(
&mut self,
dep: &Dependency,
- first_minimal_version: bool,
+ first_version: Option<VersionOrdering>,
) -> Poll<CargoResult<Rc<Vec<Summary>>>> {
- let registry_cache_key = (dep.clone(), first_minimal_version);
+ let registry_cache_key = (dep.clone(), first_version);
if let Some(out) = self.registry_cache.get(&registry_cache_key).cloned() {
return out.map(Result::Ok);
}
let mut ret = Vec::new();
let ready = self.registry.query(dep, QueryKind::Exact, &mut |s| {
- if self.max_rust_version.is_none() || s.rust_version() <= self.max_rust_version.as_ref()
- {
- ret.push(s);
- }
+ ret.push(s);
})?;
if ready.is_pending() {
self.registry_cache
- .insert((dep.clone(), first_minimal_version), Poll::Pending);
+ .insert((dep.clone(), first_version), Poll::Pending);
return Poll::Pending;
}
for summary in ret.iter() {
@@ -144,7 +131,7 @@ impl<'a> RegistryQueryer<'a> {
Poll::Ready(s) => s.into_iter(),
Poll::Pending => {
self.registry_cache
- .insert((dep.clone(), first_minimal_version), Poll::Pending);
+ .insert((dep.clone(), first_version), Poll::Pending);
return Poll::Pending;
}
};
@@ -215,16 +202,8 @@ impl<'a> RegistryQueryer<'a> {
}
}
- // When we attempt versions for a package we'll want to do so in a sorted fashion to pick
- // the "best candidates" first. VersionPreferences implements this notion.
- let ordering = if first_minimal_version || self.minimal_versions {
- VersionOrdering::MinimumVersionsFirst
- } else {
- VersionOrdering::MaximumVersionsFirst
- };
- let first_version = first_minimal_version;
- self.version_prefs
- .sort_summaries(&mut ret, ordering, first_version);
+ let first_version = first_version;
+ self.version_prefs.sort_summaries(&mut ret, first_version);
let out = Poll::Ready(Rc::new(ret));
@@ -243,7 +222,7 @@ impl<'a> RegistryQueryer<'a> {
parent: Option<PackageId>,
candidate: &Summary,
opts: &ResolveOpts,
- first_minimal_version: bool,
+ first_version: Option<VersionOrdering>,
) -> ActivateResult<Rc<(HashSet<InternedString>, Rc<Vec<DepInfo>>)>> {
// if we have calculated a result before, then we can just return it,
// as it is a "pure" query of its arguments.
@@ -263,24 +242,22 @@ impl<'a> RegistryQueryer<'a> {
let mut all_ready = true;
let mut deps = deps
.into_iter()
- .filter_map(
- |(dep, features)| match self.query(&dep, first_minimal_version) {
- Poll::Ready(Ok(candidates)) => Some(Ok((dep, candidates, features))),
- Poll::Pending => {
- all_ready = false;
- // we can ignore Pending deps, resolve will be repeatedly called
- // until there are none to ignore
- None
- }
- Poll::Ready(Err(e)) => Some(Err(e).with_context(|| {
- format!(
- "failed to get `{}` as a dependency of {}",
- dep.package_name(),
- describe_path_in_context(cx, &candidate.package_id()),
- )
- })),
- },
- )
+ .filter_map(|(dep, features)| match self.query(&dep, first_version) {
+ Poll::Ready(Ok(candidates)) => Some(Ok((dep, candidates, features))),
+ Poll::Pending => {
+ all_ready = false;
+ // we can ignore Pending deps, resolve will be repeatedly called
+ // until there are none to ignore
+ None
+ }
+ Poll::Ready(Err(e)) => Some(Err(e).with_context(|| {
+ format!(
+ "failed to get `{}` as a dependency of {}",
+ dep.package_name(),
+ describe_path_in_context(cx, &candidate.package_id()),
+ )
+ })),
+ })
.collect::<CargoResult<Vec<DepInfo>>>()?;
// Attempt to resolve dependencies with fewer candidates before trying
diff --git a/src/tools/cargo/src/cargo/core/resolver/encode.rs b/src/tools/cargo/src/cargo/core/resolver/encode.rs
index 7835c2219..fcef1578a 100644
--- a/src/tools/cargo/src/cargo/core/resolver/encode.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/encode.rs
@@ -776,7 +776,7 @@ pub fn encodable_package_id(
}
}
}
- let mut source = encodable_source_id(id_to_encode.with_precise(None), resolve_version);
+ let mut source = encodable_source_id(id_to_encode.without_precise(), resolve_version);
if let Some(counts) = &state.counts {
let version_counts = &counts[&id.name()];
if version_counts[&id.version()] == 1 {
diff --git a/src/tools/cargo/src/cargo/core/resolver/errors.rs b/src/tools/cargo/src/cargo/core/resolver/errors.rs
index b57a7c3eb..15a006ffb 100644
--- a/src/tools/cargo/src/cargo/core/resolver/errors.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/errors.rs
@@ -4,7 +4,8 @@ use std::task::Poll;
use crate::core::{Dependency, PackageId, Registry, Summary};
use crate::sources::source::QueryKind;
use crate::util::edit_distance::edit_distance;
-use crate::util::{Config, OptVersionReq, VersionExt};
+use crate::util::{Config, OptVersionReq};
+use crate::util_semver::VersionExt;
use anyhow::Error;
use super::context::Context;
diff --git a/src/tools/cargo/src/cargo/core/resolver/mod.rs b/src/tools/cargo/src/cargo/core/resolver/mod.rs
index 7d8e8acd4..ecb6f36e6 100644
--- a/src/tools/cargo/src/cargo/core/resolver/mod.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/mod.rs
@@ -71,7 +71,6 @@ use crate::util::config::Config;
use crate::util::errors::CargoResult;
use crate::util::network::PollExt;
use crate::util::profile;
-use crate::util::RustVersion;
use self::context::Context;
use self::dep_cache::RegistryQueryer;
@@ -139,39 +138,18 @@ pub fn resolve(
version_prefs: &VersionPreferences,
config: Option<&Config>,
check_public_visible_dependencies: bool,
- mut max_rust_version: Option<&RustVersion>,
) -> CargoResult<Resolve> {
let _p = profile::start("resolving");
- let minimal_versions = match config {
- Some(config) => config.cli_unstable().minimal_versions,
- None => false,
- };
- let direct_minimal_versions = match config {
- Some(config) => config.cli_unstable().direct_minimal_versions,
- None => false,
+ let first_version = match config {
+ Some(config) if config.cli_unstable().direct_minimal_versions => {
+ Some(VersionOrdering::MinimumVersionsFirst)
+ }
+ _ => None,
};
- if !config
- .map(|c| c.cli_unstable().msrv_policy)
- .unwrap_or(false)
- {
- max_rust_version = None;
- }
- let mut registry = RegistryQueryer::new(
- registry,
- replacements,
- version_prefs,
- minimal_versions,
- max_rust_version,
- );
+ let mut registry = RegistryQueryer::new(registry, replacements, version_prefs);
let cx = loop {
let cx = Context::new(check_public_visible_dependencies);
- let cx = activate_deps_loop(
- cx,
- &mut registry,
- summaries,
- direct_minimal_versions,
- config,
- )?;
+ let cx = activate_deps_loop(cx, &mut registry, summaries, first_version, config)?;
if registry.reset_pending() {
break cx;
} else {
@@ -223,7 +201,7 @@ fn activate_deps_loop(
mut cx: Context,
registry: &mut RegistryQueryer<'_>,
summaries: &[(Summary, ResolveOpts)],
- direct_minimal_versions: bool,
+ first_version: Option<VersionOrdering>,
config: Option<&Config>,
) -> CargoResult<Context> {
let mut backtrack_stack = Vec::new();
@@ -241,7 +219,7 @@ fn activate_deps_loop(
registry,
None,
summary.clone(),
- direct_minimal_versions,
+ first_version,
opts,
);
match res {
@@ -441,13 +419,13 @@ fn activate_deps_loop(
dep.package_name(),
candidate.version()
);
- let direct_minimal_version = false; // this is an indirect dependency
+ let first_version = None; // this is an indirect dependency
let res = activate(
&mut cx,
registry,
Some((&parent, &dep)),
candidate,
- direct_minimal_version,
+ first_version,
&opts,
);
@@ -659,7 +637,7 @@ fn activate(
registry: &mut RegistryQueryer<'_>,
parent: Option<(&Summary, &Dependency)>,
candidate: Summary,
- first_minimal_version: bool,
+ first_version: Option<VersionOrdering>,
opts: &ResolveOpts,
) -> ActivateResult<Option<(DepsFrame, Duration)>> {
let candidate_pid = candidate.package_id();
@@ -716,7 +694,7 @@ fn activate(
parent.map(|p| p.0.package_id()),
&candidate,
opts,
- first_minimal_version,
+ first_version,
)?;
// Record what list of features is active for this package.
@@ -905,14 +883,14 @@ fn generalize_conflicting(
})
{
for critical_parents_dep in critical_parents_deps.iter() {
- // We only want `first_minimal_version=true` for direct dependencies of workspace
+ // We only want `first_version.is_some()` for direct dependencies of workspace
// members which isn't the case here as this has a `parent`
- let first_minimal_version = false;
+ let first_version = None;
// A dep is equivalent to one of the things it can resolve to.
// Thus, if all the things it can resolve to have already ben determined
// to be conflicting, then we can just say that we conflict with the parent.
if let Some(others) = registry
- .query(critical_parents_dep, first_minimal_version)
+ .query(critical_parents_dep, first_version)
.expect("an already used dep now error!?")
.expect("an already used dep now pending!?")
.iter()
diff --git a/src/tools/cargo/src/cargo/core/resolver/resolve.rs b/src/tools/cargo/src/cargo/core/resolver/resolve.rs
index 18a389773..b401e9232 100644
--- a/src/tools/cargo/src/cargo/core/resolver/resolve.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/resolve.rs
@@ -88,6 +88,17 @@ pub enum ResolveVersion {
V4,
}
+impl ResolveVersion {
+ /// The maximum version of lockfile made into the stable channel.
+ ///
+ /// Any version larger than this needs `-Znext-lockfile-bump` to enable.
+ ///
+ /// Update this when you're going to stabilize a new lockfile format.
+ pub fn max_stable() -> ResolveVersion {
+ ResolveVersion::V3
+ }
+}
+
impl Resolve {
pub fn new(
graph: Graph<PackageId, HashSet<Dependency>>,
diff --git a/src/tools/cargo/src/cargo/core/resolver/version_prefs.rs b/src/tools/cargo/src/cargo/core/resolver/version_prefs.rs
index 28de77f11..0deef5565 100644
--- a/src/tools/cargo/src/cargo/core/resolver/version_prefs.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/version_prefs.rs
@@ -6,6 +6,7 @@ use std::collections::{HashMap, HashSet};
use crate::core::{Dependency, PackageId, Summary};
use crate::util::interning::InternedString;
+use crate::util::RustVersion;
/// A collection of preferences for particular package versions.
///
@@ -18,9 +19,13 @@ use crate::util::interning::InternedString;
pub struct VersionPreferences {
try_to_use: HashSet<PackageId>,
prefer_patch_deps: HashMap<InternedString, HashSet<Dependency>>,
+ version_ordering: VersionOrdering,
+ max_rust_version: Option<RustVersion>,
}
+#[derive(Copy, Clone, Default, PartialEq, Eq, Hash, Debug)]
pub enum VersionOrdering {
+ #[default]
MaximumVersionsFirst,
MinimumVersionsFirst,
}
@@ -39,14 +44,29 @@ impl VersionPreferences {
.insert(dep);
}
- /// Sort the given vector of summaries in-place, with all summaries presumed to be for
- /// the same package. Preferred versions appear first in the result, sorted by
- /// `version_ordering`, followed by non-preferred versions sorted the same way.
+ pub fn version_ordering(&mut self, ordering: VersionOrdering) {
+ self.version_ordering = ordering;
+ }
+
+ pub fn max_rust_version(&mut self, ver: Option<RustVersion>) {
+ self.max_rust_version = ver;
+ }
+
+ /// Sort (and filter) the given vector of summaries in-place
+ ///
+ /// Note: all summaries presumed to be for the same package.
+ ///
+ /// Sort order:
+ /// 1. Preferred packages
+ /// 2. `first_version`, falling back to [`VersionPreferences::version_ordering`] when `None`
+ ///
+ /// Filtering:
+ /// - [`VersionPreferences::max_rust_version`]
+ /// - `first_version`
pub fn sort_summaries(
&self,
summaries: &mut Vec<Summary>,
- version_ordering: VersionOrdering,
- first_version: bool,
+ first_version: Option<VersionOrdering>,
) {
let should_prefer = |pkg_id: &PackageId| {
self.try_to_use.contains(pkg_id)
@@ -56,22 +76,24 @@ impl VersionPreferences {
.map(|deps| deps.iter().any(|d| d.matches_id(*pkg_id)))
.unwrap_or(false)
};
+ if self.max_rust_version.is_some() {
+ summaries.retain(|s| s.rust_version() <= self.max_rust_version.as_ref());
+ }
summaries.sort_unstable_by(|a, b| {
let prefer_a = should_prefer(&a.package_id());
let prefer_b = should_prefer(&b.package_id());
let previous_cmp = prefer_a.cmp(&prefer_b).reverse();
- match previous_cmp {
- Ordering::Equal => {
- let cmp = a.version().cmp(b.version());
- match version_ordering {
- VersionOrdering::MaximumVersionsFirst => cmp.reverse(),
- VersionOrdering::MinimumVersionsFirst => cmp,
- }
- }
- _ => previous_cmp,
+ if previous_cmp != Ordering::Equal {
+ return previous_cmp;
+ }
+
+ let cmp = a.version().cmp(b.version());
+ match first_version.unwrap_or(self.version_ordering) {
+ VersionOrdering::MaximumVersionsFirst => cmp.reverse(),
+ VersionOrdering::MinimumVersionsFirst => cmp,
}
});
- if first_version {
+ if first_version.is_some() {
let _ = summaries.split_off(1);
}
}
@@ -81,7 +103,6 @@ impl VersionPreferences {
mod test {
use super::*;
use crate::core::SourceId;
- use crate::util::RustVersion;
use std::collections::BTreeMap;
fn pkgid(name: &str, version: &str) -> PackageId {
@@ -96,7 +117,7 @@ mod test {
Dependency::parse(name, Some(version), src_id).unwrap()
}
- fn summ(name: &str, version: &str) -> Summary {
+ fn summ(name: &str, version: &str, msrv: Option<&str>) -> Summary {
let pkg_id = pkgid(name, version);
let features = BTreeMap::new();
Summary::new(
@@ -104,7 +125,7 @@ mod test {
Vec::new(),
&features,
None::<&String>,
- None::<RustVersion>,
+ msrv.map(|m| m.parse().unwrap()),
)
.unwrap()
}
@@ -123,19 +144,21 @@ mod test {
vp.prefer_package_id(pkgid("foo", "1.2.3"));
let mut summaries = vec![
- summ("foo", "1.2.4"),
- summ("foo", "1.2.3"),
- summ("foo", "1.1.0"),
- summ("foo", "1.0.9"),
+ summ("foo", "1.2.4", None),
+ summ("foo", "1.2.3", None),
+ summ("foo", "1.1.0", None),
+ summ("foo", "1.0.9", None),
];
- vp.sort_summaries(&mut summaries, VersionOrdering::MaximumVersionsFirst, false);
+ vp.version_ordering(VersionOrdering::MaximumVersionsFirst);
+ vp.sort_summaries(&mut summaries, None);
assert_eq!(
describe(&summaries),
"foo/1.2.3, foo/1.2.4, foo/1.1.0, foo/1.0.9".to_string()
);
- vp.sort_summaries(&mut summaries, VersionOrdering::MinimumVersionsFirst, false);
+ vp.version_ordering(VersionOrdering::MinimumVersionsFirst);
+ vp.sort_summaries(&mut summaries, None);
assert_eq!(
describe(&summaries),
"foo/1.2.3, foo/1.0.9, foo/1.1.0, foo/1.2.4".to_string()
@@ -148,19 +171,21 @@ mod test {
vp.prefer_dependency(dep("foo", "=1.2.3"));
let mut summaries = vec![
- summ("foo", "1.2.4"),
- summ("foo", "1.2.3"),
- summ("foo", "1.1.0"),
- summ("foo", "1.0.9"),
+ summ("foo", "1.2.4", None),
+ summ("foo", "1.2.3", None),
+ summ("foo", "1.1.0", None),
+ summ("foo", "1.0.9", None),
];
- vp.sort_summaries(&mut summaries, VersionOrdering::MaximumVersionsFirst, false);
+ vp.version_ordering(VersionOrdering::MaximumVersionsFirst);
+ vp.sort_summaries(&mut summaries, None);
assert_eq!(
describe(&summaries),
"foo/1.2.3, foo/1.2.4, foo/1.1.0, foo/1.0.9".to_string()
);
- vp.sort_summaries(&mut summaries, VersionOrdering::MinimumVersionsFirst, false);
+ vp.version_ordering(VersionOrdering::MinimumVersionsFirst);
+ vp.sort_summaries(&mut summaries, None);
assert_eq!(
describe(&summaries),
"foo/1.2.3, foo/1.0.9, foo/1.1.0, foo/1.2.4".to_string()
@@ -174,22 +199,51 @@ mod test {
vp.prefer_dependency(dep("foo", "=1.1.0"));
let mut summaries = vec![
- summ("foo", "1.2.4"),
- summ("foo", "1.2.3"),
- summ("foo", "1.1.0"),
- summ("foo", "1.0.9"),
+ summ("foo", "1.2.4", None),
+ summ("foo", "1.2.3", None),
+ summ("foo", "1.1.0", None),
+ summ("foo", "1.0.9", None),
];
- vp.sort_summaries(&mut summaries, VersionOrdering::MaximumVersionsFirst, false);
+ vp.version_ordering(VersionOrdering::MaximumVersionsFirst);
+ vp.sort_summaries(&mut summaries, None);
assert_eq!(
describe(&summaries),
"foo/1.2.3, foo/1.1.0, foo/1.2.4, foo/1.0.9".to_string()
);
- vp.sort_summaries(&mut summaries, VersionOrdering::MinimumVersionsFirst, false);
+ vp.version_ordering(VersionOrdering::MinimumVersionsFirst);
+ vp.sort_summaries(&mut summaries, None);
assert_eq!(
describe(&summaries),
"foo/1.1.0, foo/1.2.3, foo/1.0.9, foo/1.2.4".to_string()
);
}
+
+ #[test]
+ fn test_max_rust_version() {
+ let mut vp = VersionPreferences::default();
+ vp.max_rust_version(Some("1.50".parse().unwrap()));
+
+ let mut summaries = vec![
+ summ("foo", "1.2.4", Some("1.60")),
+ summ("foo", "1.2.3", Some("1.50")),
+ summ("foo", "1.1.0", Some("1.40")),
+ summ("foo", "1.0.9", None),
+ ];
+
+ vp.version_ordering(VersionOrdering::MaximumVersionsFirst);
+ vp.sort_summaries(&mut summaries, None);
+ assert_eq!(
+ describe(&summaries),
+ "foo/1.2.3, foo/1.1.0, foo/1.0.9".to_string()
+ );
+
+ vp.version_ordering(VersionOrdering::MinimumVersionsFirst);
+ vp.sort_summaries(&mut summaries, None);
+ assert_eq!(
+ describe(&summaries),
+ "foo/1.0.9, foo/1.1.0, foo/1.2.3".to_string()
+ );
+ }
}
diff --git a/src/tools/cargo/src/cargo/core/shell.rs b/src/tools/cargo/src/cargo/core/shell.rs
index a9b9e84af..3d446664f 100644
--- a/src/tools/cargo/src/cargo/core/shell.rs
+++ b/src/tools/cargo/src/cargo/core/shell.rs
@@ -6,6 +6,7 @@ use anstream::AutoStream;
use anstyle::Style;
use crate::util::errors::CargoResult;
+use crate::util::hostname;
use crate::util::style::*;
pub enum TtyWidth {
@@ -57,6 +58,7 @@ pub struct Shell {
/// Flag that indicates the current line needs to be cleared before
/// printing. Used when a progress bar is currently displayed.
needs_clear: bool,
+ hostname: Option<String>,
}
impl fmt::Debug for Shell {
@@ -85,6 +87,7 @@ enum ShellOut {
stderr: AutoStream<std::io::Stderr>,
stderr_tty: bool,
color_choice: ColorChoice,
+ hyperlinks: bool,
},
}
@@ -111,10 +114,12 @@ impl Shell {
stdout: AutoStream::new(std::io::stdout(), stdout_choice),
stderr: AutoStream::new(std::io::stderr(), stderr_choice),
color_choice: auto_clr,
+ hyperlinks: supports_hyperlinks(),
stderr_tty: std::io::stderr().is_terminal(),
},
verbosity: Verbosity::Verbose,
needs_clear: false,
+ hostname: None,
}
}
@@ -124,6 +129,7 @@ impl Shell {
output: ShellOut::Write(AutoStream::never(out)), // strip all formatting on write
verbosity: Verbosity::Verbose,
needs_clear: false,
+ hostname: None,
}
}
@@ -314,6 +320,16 @@ impl Shell {
Ok(())
}
+ pub fn set_hyperlinks(&mut self, yes: bool) -> CargoResult<()> {
+ if let ShellOut::Stream {
+ ref mut hyperlinks, ..
+ } = self.output
+ {
+ *hyperlinks = yes;
+ }
+ Ok(())
+ }
+
/// Gets the current color choice.
///
/// If we are not using a color stream, this will always return `Never`, even if the color
@@ -340,18 +356,59 @@ impl Shell {
}
}
- /// Write a styled fragment
- ///
- /// Caller is responsible for deciding whether [`Shell::verbosity`] is affects output.
- pub fn write_stdout(&mut self, fragment: impl fmt::Display, color: &Style) -> CargoResult<()> {
- self.output.write_stdout(fragment, color)
+ pub fn out_hyperlink<D: fmt::Display>(&self, url: D) -> Hyperlink<D> {
+ let supports_hyperlinks = match &self.output {
+ ShellOut::Write(_) => false,
+ ShellOut::Stream {
+ stdout, hyperlinks, ..
+ } => stdout.current_choice() == anstream::ColorChoice::AlwaysAnsi && *hyperlinks,
+ };
+ Hyperlink {
+ url: supports_hyperlinks.then_some(url),
+ }
}
- /// Write a styled fragment
- ///
- /// Caller is responsible for deciding whether [`Shell::verbosity`] is affects output.
- pub fn write_stderr(&mut self, fragment: impl fmt::Display, color: &Style) -> CargoResult<()> {
- self.output.write_stderr(fragment, color)
+ pub fn err_hyperlink<D: fmt::Display>(&self, url: D) -> Hyperlink<D> {
+ let supports_hyperlinks = match &self.output {
+ ShellOut::Write(_) => false,
+ ShellOut::Stream {
+ stderr, hyperlinks, ..
+ } => stderr.current_choice() == anstream::ColorChoice::AlwaysAnsi && *hyperlinks,
+ };
+ if supports_hyperlinks {
+ Hyperlink { url: Some(url) }
+ } else {
+ Hyperlink { url: None }
+ }
+ }
+
+ pub fn out_file_hyperlink(&mut self, path: &std::path::Path) -> Hyperlink<url::Url> {
+ let url = self.file_hyperlink(path);
+ url.map(|u| self.out_hyperlink(u)).unwrap_or_default()
+ }
+
+ pub fn err_file_hyperlink(&mut self, path: &std::path::Path) -> Hyperlink<url::Url> {
+ let url = self.file_hyperlink(path);
+ url.map(|u| self.err_hyperlink(u)).unwrap_or_default()
+ }
+
+ fn file_hyperlink(&mut self, path: &std::path::Path) -> Option<url::Url> {
+ let mut url = url::Url::from_file_path(path).ok()?;
+ // Do a best-effort of setting the host in the URL to avoid issues with opening a link
+ // scoped to the computer you've SSHed into
+ let hostname = if cfg!(windows) {
+ // Not supported correctly on windows
+ None
+ } else {
+ if let Some(hostname) = self.hostname.as_deref() {
+ Some(hostname)
+ } else {
+ self.hostname = hostname().ok().and_then(|h| h.into_string().ok());
+ self.hostname.as_deref()
+ }
+ };
+ let _ = url.set_host(hostname);
+ Some(url)
}
/// Prints a message to stderr and translates ANSI escape code into console colors.
@@ -416,28 +473,6 @@ impl ShellOut {
Ok(())
}
- /// Write a styled fragment
- fn write_stdout(&mut self, fragment: impl fmt::Display, style: &Style) -> CargoResult<()> {
- let style = style.render();
- let reset = anstyle::Reset.render();
-
- let mut buffer = Vec::new();
- write!(buffer, "{style}{}{reset}", fragment)?;
- self.stdout().write_all(&buffer)?;
- Ok(())
- }
-
- /// Write a styled fragment
- fn write_stderr(&mut self, fragment: impl fmt::Display, style: &Style) -> CargoResult<()> {
- let style = style.render();
- let reset = anstyle::Reset.render();
-
- let mut buffer = Vec::new();
- write!(buffer, "{style}{}{reset}", fragment)?;
- self.stderr().write_all(&buffer)?;
- Ok(())
- }
-
/// Gets stdout as a `io::Write`.
fn stdout(&mut self) -> &mut dyn Write {
match *self {
@@ -475,6 +510,44 @@ fn supports_color(choice: anstream::ColorChoice) -> bool {
}
}
+fn supports_hyperlinks() -> bool {
+ #[allow(clippy::disallowed_methods)] // We are reading the state of the system, not config
+ if std::env::var_os("TERM_PROGRAM").as_deref() == Some(std::ffi::OsStr::new("iTerm.app")) {
+ // Override `supports_hyperlinks` as we have an unknown incompatibility with iTerm2
+ return false;
+ }
+
+ supports_hyperlinks::supports_hyperlinks()
+}
+
+pub struct Hyperlink<D: fmt::Display> {
+ url: Option<D>,
+}
+
+impl<D: fmt::Display> Default for Hyperlink<D> {
+ fn default() -> Self {
+ Self { url: None }
+ }
+}
+
+impl<D: fmt::Display> Hyperlink<D> {
+ pub fn open(&self) -> impl fmt::Display {
+ if let Some(url) = self.url.as_ref() {
+ format!("\x1B]8;;{url}\x1B\\")
+ } else {
+ String::new()
+ }
+ }
+
+ pub fn close(&self) -> impl fmt::Display {
+ if self.url.is_some() {
+ "\x1B]8;;\x1B\\"
+ } else {
+ ""
+ }
+ }
+}
+
#[cfg(unix)]
mod imp {
use super::{Shell, TtyWidth};
diff --git a/src/tools/cargo/src/cargo/core/source_id.rs b/src/tools/cargo/src/cargo/core/source_id.rs
index d688b8739..e53b1704d 100644
--- a/src/tools/cargo/src/cargo/core/source_id.rs
+++ b/src/tools/cargo/src/cargo/core/source_id.rs
@@ -3,7 +3,8 @@ use crate::sources::registry::CRATES_IO_HTTP_INDEX;
use crate::sources::source::Source;
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, ToSemver};
+use crate::util::interning::InternedString;
+use crate::util::{config, CanonicalUrl, CargoResult, Config, IntoUrl};
use anyhow::Context;
use serde::de;
use serde::ser;
@@ -50,7 +51,7 @@ struct SourceIdInner {
/// The source kind.
kind: SourceKind,
/// For example, the exact Git revision of the specified branch for a Git Source.
- precise: Option<String>,
+ precise: Option<Precise>,
/// Name of the remote registry.
///
/// WARNING: this is not always set when the name is not known,
@@ -58,6 +59,29 @@ struct SourceIdInner {
registry_key: Option<KeyOf>,
}
+#[derive(Eq, PartialEq, Clone, Debug, Hash)]
+enum Precise {
+ Locked,
+ Updated {
+ name: InternedString,
+ from: semver::Version,
+ to: semver::Version,
+ },
+ GitUrlFragment(String),
+}
+
+impl fmt::Display for Precise {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ Precise::Locked => "locked".fmt(f),
+ Precise::Updated { name, from, to } => {
+ write!(f, "{name}={from}->{to}")
+ }
+ Precise::GitUrlFragment(s) => s.fmt(f),
+ }
+ }
+}
+
/// The possible kinds of code source.
/// Along with [`SourceIdInner`], this fully defines the source.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -164,31 +188,19 @@ impl SourceId {
match kind {
"git" => {
let mut url = url.into_url()?;
- let mut reference = GitReference::DefaultBranch;
- for (k, v) in url.query_pairs() {
- match &k[..] {
- // Map older 'ref' to branch.
- "branch" | "ref" => reference = GitReference::Branch(v.into_owned()),
-
- "rev" => reference = GitReference::Rev(v.into_owned()),
- "tag" => reference = GitReference::Tag(v.into_owned()),
- _ => {}
- }
- }
+ let reference = GitReference::from_query(url.query_pairs());
let precise = url.fragment().map(|s| s.to_owned());
url.set_fragment(None);
url.set_query(None);
- Ok(SourceId::for_git(&url, reference)?.with_precise(precise))
+ Ok(SourceId::for_git(&url, reference)?.with_git_precise(precise))
}
"registry" => {
let url = url.into_url()?;
- Ok(SourceId::new(SourceKind::Registry, url, None)?
- .with_precise(Some("locked".to_string())))
+ Ok(SourceId::new(SourceKind::Registry, url, None)?.with_locked_precise())
}
"sparse" => {
let url = string.into_url()?;
- Ok(SourceId::new(SourceKind::SparseRegistry, url, None)?
- .with_precise(Some("locked".to_string())))
+ Ok(SourceId::new(SourceKind::SparseRegistry, url, None)?.with_locked_precise())
}
"path" => {
let url = url.into_url()?;
@@ -332,10 +344,10 @@ impl SourceId {
pub fn display_registry_name(self) -> String {
if let Some(key) = self.inner.registry_key.as_ref().map(|k| k.key()) {
key.into()
- } else if self.precise().is_some() {
+ } else if self.has_precise() {
// We remove `precise` here to retrieve an permissive version of
// `SourceIdInner`, which may contain the registry name.
- self.with_precise(None).display_registry_name()
+ self.without_precise().display_registry_name()
} else {
url_display(self.url())
}
@@ -444,37 +456,81 @@ impl SourceId {
}
}
- /// Gets the value of the precise field.
- pub fn precise(self) -> Option<&'static str> {
- self.inner.precise.as_deref()
+ /// Check if the precise data field has bean set
+ pub fn has_precise(self) -> bool {
+ self.inner.precise.is_some()
+ }
+
+ /// Check if the precise data field has bean set to "locked"
+ pub fn has_locked_precise(self) -> bool {
+ self.inner.precise == Some(Precise::Locked)
+ }
+
+ /// Check if two sources have the same precise data field
+ pub fn has_same_precise_as(self, other: Self) -> bool {
+ self.inner.precise == other.inner.precise
}
/// Check if the precise data field stores information for this `name`
/// from a call to [SourceId::with_precise_registry_version].
///
/// If so return the version currently in the lock file and the version to be updated to.
- /// If specified, our own source will have a precise version listed of the form
- // `<pkg>=<p_req>-><f_req>` where `<pkg>` is the name of a crate on
- // this source, `<p_req>` is the version installed and `<f_req>` is the
- // version requested (argument to `--precise`).
pub fn precise_registry_version(
self,
- name: &str,
- ) -> Option<(semver::Version, semver::Version)> {
- self.inner
- .precise
- .as_deref()
- .and_then(|p| p.strip_prefix(name)?.strip_prefix('='))
- .map(|p| {
- let (current, requested) = p.split_once("->").unwrap();
- (current.to_semver().unwrap(), requested.to_semver().unwrap())
- })
+ pkg: &str,
+ ) -> Option<(&semver::Version, &semver::Version)> {
+ match &self.inner.precise {
+ Some(Precise::Updated { name, from, to }) if name == pkg => Some((from, to)),
+ _ => None,
+ }
+ }
+
+ pub fn precise_git_fragment(self) -> Option<&'static str> {
+ match &self.inner.precise {
+ Some(Precise::GitUrlFragment(s)) => Some(&s[..8]),
+ _ => None,
+ }
+ }
+
+ pub fn precise_git_oid(self) -> CargoResult<Option<git2::Oid>> {
+ Ok(match self.inner.precise.as_ref() {
+ Some(Precise::GitUrlFragment(s)) => {
+ Some(git2::Oid::from_str(s).with_context(|| {
+ format!("precise value for git is not a git revision: {}", s)
+ })?)
+ }
+ _ => None,
+ })
}
/// Creates a new `SourceId` from this source with the given `precise`.
- pub fn with_precise(self, v: Option<String>) -> SourceId {
+ pub fn with_git_precise(self, fragment: Option<String>) -> SourceId {
+ SourceId::wrap(SourceIdInner {
+ precise: fragment.map(|f| Precise::GitUrlFragment(f)),
+ ..(*self.inner).clone()
+ })
+ }
+
+ /// Creates a new `SourceId` from this source without a `precise`.
+ pub fn without_precise(self) -> SourceId {
SourceId::wrap(SourceIdInner {
- precise: v,
+ precise: None,
+ ..(*self.inner).clone()
+ })
+ }
+
+ /// Creates a new `SourceId` from this source without a `precise`.
+ pub fn with_locked_precise(self) -> SourceId {
+ SourceId::wrap(SourceIdInner {
+ precise: Some(Precise::Locked),
+ ..(*self.inner).clone()
+ })
+ }
+
+ /// Creates a new `SourceId` from this source with the `precise` from some other `SourceId`.
+ pub fn with_precise_from(self, v: Self) -> SourceId {
+ SourceId::wrap(SourceIdInner {
+ precise: v.inner.precise.clone(),
..(*self.inner).clone()
})
}
@@ -487,13 +543,21 @@ impl SourceId {
/// The data can be read with [SourceId::precise_registry_version]
pub fn with_precise_registry_version(
self,
- name: impl fmt::Display,
- version: &semver::Version,
+ name: InternedString,
+ version: semver::Version,
precise: &str,
) -> CargoResult<SourceId> {
- semver::Version::parse(precise)
+ let precise = semver::Version::parse(precise)
.with_context(|| format!("invalid version format for precise version `{precise}`"))?;
- Ok(self.with_precise(Some(format!("{}={}->{}", name, version, precise))))
+
+ Ok(SourceId::wrap(SourceIdInner {
+ precise: Some(Precise::Updated {
+ name,
+ from: version,
+ to: precise,
+ }),
+ ..(*self.inner).clone()
+ }))
}
/// Returns `true` if the remote registry is the standard <https://crates.io>.
@@ -625,7 +689,8 @@ impl fmt::Display for SourceId {
write!(f, "?{}", pretty)?;
}
- if let Some(ref s) = self.inner.precise {
+ if let Some(s) = &self.inner.precise {
+ let s = s.to_string();
let len = cmp::min(s.len(), 8);
write!(f, "#{}", &s[..len])?;
}
@@ -677,6 +742,20 @@ impl PartialEq for SourceIdInner {
}
}
+impl SourceKind {
+ pub(crate) fn protocol(&self) -> Option<&str> {
+ match self {
+ SourceKind::Path => Some("path"),
+ SourceKind::Git(_) => Some("git"),
+ SourceKind::Registry => Some("registry"),
+ // Sparse registry URL already includes the `sparse+` prefix
+ SourceKind::SparseRegistry => None,
+ SourceKind::LocalRegistry => Some("local-registry"),
+ SourceKind::Directory => Some("directory"),
+ }
+ }
+}
+
/// Forwards to `Ord`
impl PartialOrd for SourceKind {
fn partial_cmp(&self, other: &SourceKind) -> Option<Ordering> {
@@ -773,57 +852,46 @@ pub struct SourceIdAsUrl<'a> {
impl<'a> fmt::Display for SourceIdAsUrl<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match *self.inner {
- SourceIdInner {
- kind: SourceKind::Path,
- ref url,
- ..
- } => write!(f, "path+{}", url),
- SourceIdInner {
- kind: SourceKind::Git(ref reference),
- ref url,
- ref precise,
- ..
- } => {
- write!(f, "git+{}", url)?;
- if let Some(pretty) = reference.pretty_ref(self.encoded) {
- write!(f, "?{}", pretty)?;
- }
- if let Some(precise) = precise.as_ref() {
- write!(f, "#{}", precise)?;
- }
- Ok(())
- }
- SourceIdInner {
- kind: SourceKind::Registry,
- ref url,
- ..
- } => {
- write!(f, "registry+{url}")
+ if let Some(protocol) = self.inner.kind.protocol() {
+ write!(f, "{protocol}+")?;
+ }
+ write!(f, "{}", self.inner.url)?;
+ if let SourceIdInner {
+ kind: SourceKind::Git(ref reference),
+ ref precise,
+ ..
+ } = *self.inner
+ {
+ if let Some(pretty) = reference.pretty_ref(self.encoded) {
+ write!(f, "?{}", pretty)?;
}
- SourceIdInner {
- kind: SourceKind::SparseRegistry,
- ref url,
- ..
- } => {
- // Sparse registry URL already includes the `sparse+` prefix
- write!(f, "{url}")
+ if let Some(precise) = precise.as_ref() {
+ write!(f, "#{}", precise)?;
}
- SourceIdInner {
- kind: SourceKind::LocalRegistry,
- ref url,
- ..
- } => write!(f, "local-registry+{}", url),
- SourceIdInner {
- kind: SourceKind::Directory,
- ref url,
- ..
- } => write!(f, "directory+{}", url),
}
+ Ok(())
}
}
impl GitReference {
+ pub fn from_query(
+ query_pairs: impl Iterator<Item = (impl AsRef<str>, impl AsRef<str>)>,
+ ) -> Self {
+ let mut reference = GitReference::DefaultBranch;
+ for (k, v) in query_pairs {
+ let v = v.as_ref();
+ match k.as_ref() {
+ // Map older 'ref' to branch.
+ "branch" | "ref" => reference = GitReference::Branch(v.to_owned()),
+
+ "rev" => reference = GitReference::Rev(v.to_owned()),
+ "tag" => reference = GitReference::Tag(v.to_owned()),
+ _ => {}
+ }
+ }
+ reference
+ }
+
/// Returns a `Display`able view of this git reference, or None if using
/// the head of the default branch
pub fn pretty_ref(&self, url_encoded: bool) -> Option<PrettyRef<'_>> {
diff --git a/src/tools/cargo/src/cargo/core/summary.rs b/src/tools/cargo/src/cargo/core/summary.rs
index 128c0db9c..243f6b398 100644
--- a/src/tools/cargo/src/cargo/core/summary.rs
+++ b/src/tools/cargo/src/cargo/core/summary.rs
@@ -431,6 +431,9 @@ impl fmt::Display for FeatureValue {
pub type FeatureMap = BTreeMap<InternedString, Vec<FeatureValue>>;
fn validate_feature_name(pkg_id: PackageId, name: &str) -> CargoResult<()> {
+ if name.is_empty() {
+ bail!("feature name cannot be empty");
+ }
let mut chars = name.chars();
if let Some(ch) = chars.next() {
if !(unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' || ch.is_digit(10)) {
@@ -448,7 +451,7 @@ fn validate_feature_name(pkg_id: PackageId, name: &str) -> CargoResult<()> {
if !(unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-' || ch == '+' || ch == '.') {
bail!(
"invalid character `{}` in feature `{}` in package {}, \
- characters must be Unicode XID characters, `+`, or `.` \
+ characters must be Unicode XID characters, '-', `+`, or `.` \
(numbers, `+`, `-`, `_`, `.`, or most letters)",
ch,
name,
@@ -488,5 +491,6 @@ mod tests {
assert!(validate_feature_name(pkg_id, "?foo").is_err());
assert!(validate_feature_name(pkg_id, "ⒶⒷⒸ").is_err());
assert!(validate_feature_name(pkg_id, "a¼").is_err());
+ assert!(validate_feature_name(pkg_id, "").is_err());
}
}
diff --git a/src/tools/cargo/src/cargo/core/workspace.rs b/src/tools/cargo/src/cargo/core/workspace.rs
index db379d780..4667c8029 100644
--- a/src/tools/cargo/src/cargo/core/workspace.rs
+++ b/src/tools/cargo/src/cargo/core/workspace.rs
@@ -22,7 +22,9 @@ use crate::sources::{PathSource, CRATES_IO_INDEX, CRATES_IO_REGISTRY};
use crate::util::edit_distance;
use crate::util::errors::{CargoResult, ManifestError};
use crate::util::interning::InternedString;
-use crate::util::toml::{read_manifest, InheritableFields, TomlDependency, TomlProfiles};
+use crate::util::toml::{
+ read_manifest, schema::InheritableFields, schema::TomlDependency, schema::TomlProfiles,
+};
use crate::util::RustVersion;
use crate::util::{config::ConfigRelativePath, Config, Filesystem, IntoUrl};
use cargo_util::paths;
@@ -1491,7 +1493,7 @@ impl<'cfg> Workspace<'cfg> {
// Check if `dep_name` is member of the workspace, but isn't associated with current package.
self.current_opt() != Some(member) && member.name() == *dep_name
});
- if is_member && specs.iter().any(|spec| spec.name() == *dep_name) {
+ if is_member && specs.iter().any(|spec| spec.name() == dep_name.as_str()) {
member_specific_features
.entry(*dep_name)
.or_default()
diff --git a/src/tools/cargo/src/cargo/lib.rs b/src/tools/cargo/src/cargo/lib.rs
index 908ff4ecc..6947642c9 100644
--- a/src/tools/cargo/src/cargo/lib.rs
+++ b/src/tools/cargo/src/cargo/lib.rs
@@ -93,7 +93,7 @@
//! Files that interact with cargo include
//!
//! - Package
-//! - `Cargo.toml`: User-written project manifest, loaded with [`util::toml::TomlManifest`] and then
+//! - `Cargo.toml`: User-written project manifest, loaded with [`util::toml::schema::TomlManifest`] and then
//! translated to [`core::manifest::Manifest`] which maybe stored in a [`core::Package`].
//! - This is editable with [`util::toml_mut::manifest::LocalManifest`]
//! - `Cargo.lock`: Generally loaded with [`ops::resolve_ws`] or a variant of it into a [`core::resolver::Resolve`]
@@ -161,6 +161,7 @@ pub mod core;
pub mod ops;
pub mod sources;
pub mod util;
+pub mod util_semver;
mod version;
pub fn exit_with_error(err: CliError, shell: &mut Shell) -> ! {
diff --git a/src/tools/cargo/src/cargo/ops/cargo_add/mod.rs b/src/tools/cargo/src/cargo/ops/cargo_add/mod.rs
index 968d6068f..39e37b156 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_add/mod.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_add/mod.rs
@@ -23,6 +23,7 @@ use crate::core::Shell;
use crate::core::Summary;
use crate::core::Workspace;
use crate::sources::source::QueryKind;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::style;
use crate::util::toml_mut::dependency::Dependency;
use crate::util::toml_mut::dependency::GitSource;
@@ -30,6 +31,7 @@ use crate::util::toml_mut::dependency::MaybeWorkspace;
use crate::util::toml_mut::dependency::PathSource;
use crate::util::toml_mut::dependency::Source;
use crate::util::toml_mut::dependency::WorkspaceSource;
+use crate::util::toml_mut::is_sorted;
use crate::util::toml_mut::manifest::DepTable;
use crate::util::toml_mut::manifest::LocalManifest;
use crate::util::RustVersion;
@@ -77,7 +79,9 @@ pub fn add(workspace: &Workspace<'_>, options: &AddOptions<'_>) -> CargoResult<(
let mut registry = PackageRegistry::new(options.config)?;
let deps = {
- let _lock = options.config.acquire_package_cache_lock()?;
+ let _lock = options
+ .config
+ .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
registry.lock_patches();
options
.dependencies
@@ -944,12 +948,17 @@ fn print_dep_table_msg(shell: &mut Shell, dep: &DependencyUI) -> CargoResult<()>
return Ok(());
}
+ let stderr = shell.err();
+ let good = style::GOOD.render();
+ let error = style::ERROR.render();
+ let reset = anstyle::Reset.render();
+
let (activated, deactivated) = dep.features();
if !activated.is_empty() || !deactivated.is_empty() {
let prefix = format!("{:>13}", " ");
let suffix = format_features_version_suffix(&dep);
- shell.write_stderr(format_args!("{prefix}Features{suffix}:\n"), &style::NOP)?;
+ writeln!(stderr, "{prefix}Features{suffix}:")?;
const MAX_FEATURE_PRINTS: usize = 30;
let total_activated = activated.len();
@@ -957,28 +966,18 @@ fn print_dep_table_msg(shell: &mut Shell, dep: &DependencyUI) -> CargoResult<()>
if total_activated <= MAX_FEATURE_PRINTS {
for feat in activated {
- shell.write_stderr(&prefix, &style::NOP)?;
- shell.write_stderr('+', &style::GOOD)?;
- shell.write_stderr(format_args!(" {feat}\n"), &style::NOP)?;
+ writeln!(stderr, "{prefix}{good}+{reset} {feat}")?;
}
} else {
- shell.write_stderr(
- format_args!("{prefix}{total_activated} activated features\n"),
- &style::NOP,
- )?;
+ writeln!(stderr, "{prefix}{total_activated} activated features")?;
}
if total_activated + total_deactivated <= MAX_FEATURE_PRINTS {
for feat in deactivated {
- shell.write_stderr(&prefix, &style::NOP)?;
- shell.write_stderr('-', &style::ERROR)?;
- shell.write_stderr(format_args!(" {feat}\n"), &style::NOP)?;
+ writeln!(stderr, "{prefix}{error}-{reset} {feat}")?;
}
} else {
- shell.write_stderr(
- format_args!("{prefix}{total_deactivated} deactivated features\n"),
- &style::NOP,
- )?;
+ writeln!(stderr, "{prefix}{total_deactivated} deactivated features")?;
}
}
@@ -1006,22 +1005,6 @@ fn format_features_version_suffix(dep: &DependencyUI) -> String {
}
}
-// Based on Iterator::is_sorted from nightly std; remove in favor of that when stabilized.
-fn is_sorted(mut it: impl Iterator<Item = impl PartialOrd>) -> bool {
- let Some(mut last) = it.next() else {
- return true;
- };
-
- for curr in it {
- if curr < last {
- return false;
- }
- last = curr;
- }
-
- true
-}
-
fn find_workspace_dep(toml_key: &str, root_manifest: &Path) -> CargoResult<Dependency> {
let manifest = LocalManifest::try_new(root_manifest)?;
let manifest = manifest
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 9cf8599c4..94c6cf9de 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs
@@ -493,7 +493,7 @@ pub fn create_bcx<'a, 'cfg>(
continue;
};
- let req = version.caret_req();
+ let req = version.to_caret_req();
if req.matches(&untagged_version) {
continue;
}
diff --git a/src/tools/cargo/src/cargo/ops/cargo_doc.rs b/src/tools/cargo/src/cargo/ops/cargo_doc.rs
index afa6ac327..ecc17e9fc 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_doc.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_doc.rs
@@ -34,11 +34,31 @@ pub fn doc(ws: &Workspace<'_>, options: &DocOptions) -> CargoResult<()> {
let cfg: Option<PathAndArgs> = ws.config().get("doc.browser")?;
cfg.map(|path_args| (path_args.path.resolve_program(ws.config()), path_args.args))
};
-
let mut shell = ws.config().shell();
- shell.status("Opening", path.display())?;
+ let link = shell.err_file_hyperlink(&path);
+ shell.status(
+ "Opening",
+ format!("{}{}{}", link.open(), path.display(), link.close()),
+ )?;
open_docs(&path, &mut shell, config_browser, ws.config())?;
}
+ } else {
+ for name in &compilation.root_crate_names {
+ for kind in &options.compile_opts.build_config.requested_kinds {
+ let path = compilation.root_output[&kind]
+ .with_file_name("doc")
+ .join(&name)
+ .join("index.html");
+ if path.exists() {
+ let mut shell = ws.config().shell();
+ let link = shell.err_file_hyperlink(&path);
+ shell.status(
+ "Generated",
+ format!("{}{}{}", link.open(), path.display(), link.close()),
+ )?;
+ }
+ }
+ }
}
Ok(())
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 a83a92ccc..a16d6d403 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_generate_lockfile.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_generate_lockfile.rs
@@ -3,10 +3,12 @@ use crate::core::resolver::features::{CliFeatures, HasDevUnits};
use crate::core::{PackageId, PackageIdSpec};
use crate::core::{Resolve, SourceId, Workspace};
use crate::ops;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::config::Config;
use crate::util::style;
use crate::util::CargoResult;
use anstyle::Style;
+use std::cmp::Ordering;
use std::collections::{BTreeMap, HashSet};
use tracing::debug;
@@ -48,7 +50,9 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
// Updates often require a lot of modifications to the registry, so ensure
// that we're synchronized against other Cargos.
- let _lock = ws.config().acquire_package_cache_lock()?;
+ let _lock = ws
+ .config()
+ .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
let max_rust_version = ws.rust_version();
@@ -101,14 +105,14 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
if pid.source_id().is_registry() {
pid.source_id().with_precise_registry_version(
pid.name(),
- pid.version(),
+ pid.version().clone(),
precise,
)?
} else {
- pid.source_id().with_precise(Some(precise.to_string()))
+ pid.source_id().with_git_precise(Some(precise.to_string()))
}
}
- None => pid.source_id().with_precise(None),
+ None => pid.source_id().without_precise(),
});
}
if let Ok(unused_id) =
@@ -143,13 +147,17 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
format!(
"{} -> #{}",
removed[0],
- &added[0].source_id().precise().unwrap()[..8]
+ &added[0].source_id().precise_git_fragment().unwrap()
)
} else {
format!("{} -> v{}", removed[0], added[0].version())
};
- if removed[0].version() > added[0].version() {
+ // If versions differ only in build metadata, we call it an "update"
+ // regardless of whether the build metadata has gone up or down.
+ // This metadata is often stuff like git commit hashes, which are
+ // not meaningfully ordered.
+ if removed[0].version().cmp_precedence(added[0].version()) == Ordering::Greater {
print_change("Downgrading", msg, &style::WARN)?;
} else {
print_change("Updating", msg, &style::GOOD)?;
@@ -222,7 +230,7 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
b[i..]
.iter()
.take_while(|b| a == b)
- .all(|b| a.source_id().precise() != b.source_id().precise())
+ .all(|b| !a.source_id().has_same_precise_as(b.source_id()))
})
.cloned()
.collect()
diff --git a/src/tools/cargo/src/cargo/ops/cargo_install.rs b/src/tools/cargo/src/cargo/ops/cargo_install.rs
index 957ab43e6..16027233e 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_install.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_install.rs
@@ -68,6 +68,7 @@ impl<'cfg> InstallablePackage<'cfg> {
force: bool,
no_track: bool,
needs_update_if_source_is_index: bool,
+ current_rust_version: Option<&semver::Version>,
) -> CargoResult<Option<Self>> {
if let Some(name) = krate {
if name == "." {
@@ -105,6 +106,7 @@ impl<'cfg> InstallablePackage<'cfg> {
dep,
|git: &mut GitSource<'_>| git.read_packages(),
config,
+ current_rust_version,
)?
} else if source_id.is_path() {
let mut src = path_source(source_id, config)?;
@@ -142,6 +144,7 @@ impl<'cfg> InstallablePackage<'cfg> {
dep,
|path: &mut PathSource<'_>| path.read_packages(),
config,
+ current_rust_version,
)?
} else if let Some(dep) = dep {
let mut source = map.load(source_id, &HashSet::new())?;
@@ -161,7 +164,13 @@ impl<'cfg> InstallablePackage<'cfg> {
config.shell().status("Ignored", &msg)?;
return Ok(None);
}
- select_dep_pkg(&mut source, dep, config, needs_update_if_source_is_index)?
+ select_dep_pkg(
+ &mut source,
+ dep,
+ config,
+ needs_update_if_source_is_index,
+ current_rust_version,
+ )?
} else {
bail!(
"must specify a crate to install from \
@@ -616,6 +625,21 @@ pub fn install(
let dst = root.join("bin").into_path_unlocked();
let map = SourceConfigMap::new(config)?;
+ let current_rust_version = if opts.honor_rust_version {
+ let rustc = config.load_global_rustc(None)?;
+
+ // Remove any pre-release identifiers for easier comparison
+ let current_version = &rustc.version;
+ let untagged_version = semver::Version::new(
+ current_version.major,
+ current_version.minor,
+ current_version.patch,
+ );
+ Some(untagged_version)
+ } else {
+ None
+ };
+
let (installed_anything, scheduled_error) = if krates.len() <= 1 {
let (krate, vers) = krates
.iter()
@@ -623,7 +647,18 @@ pub fn install(
.map(|(k, v)| (Some(k.as_str()), v.as_ref()))
.unwrap_or((None, None));
let installable_pkg = InstallablePackage::new(
- config, root, map, krate, source_id, from_cwd, vers, opts, force, no_track, true,
+ config,
+ root,
+ map,
+ krate,
+ source_id,
+ from_cwd,
+ vers,
+ opts,
+ force,
+ no_track,
+ true,
+ current_rust_version.as_ref(),
)?;
let mut installed_anything = true;
if let Some(installable_pkg) = installable_pkg {
@@ -654,6 +689,7 @@ pub fn install(
force,
no_track,
!did_update,
+ current_rust_version.as_ref(),
) {
Ok(Some(installable_pkg)) => {
did_update = true;
@@ -773,7 +809,7 @@ where
// expensive network call in the case that the package is already installed.
// If this fails, the caller will possibly do an index update and try again, this is just a
// best-effort check to see if we can avoid hitting the network.
- if let Ok(pkg) = select_dep_pkg(source, dep, config, false) {
+ if let Ok(pkg) = select_dep_pkg(source, dep, config, false, None) {
let (_ws, rustc, target) =
make_ws_rustc_target(config, opts, &source.source_id(), pkg.clone())?;
if let Ok(true) = is_installed(&pkg, config, opts, &rustc, &target, root, dst, force) {
diff --git a/src/tools/cargo/src/cargo/ops/cargo_new.rs b/src/tools/cargo/src/cargo/ops/cargo_new.rs
index 78b3cc4f6..1c06b5f82 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_new.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_new.rs
@@ -1,10 +1,11 @@
use crate::core::{Edition, Shell, Workspace};
use crate::util::errors::CargoResult;
use crate::util::important_paths::find_root_manifest_for_wd;
+use crate::util::toml_mut::is_sorted;
use crate::util::{existing_vcs_repo, FossilRepo, GitRepo, HgRepo, PijulRepo};
use crate::util::{restricted_names, Config};
-use anyhow::{anyhow, Context as _};
-use cargo_util::paths;
+use anyhow::{anyhow, Context};
+use cargo_util::paths::{self, write_atomic};
use serde::de;
use serde::Deserialize;
use std::collections::BTreeMap;
@@ -13,6 +14,7 @@ use std::io::{BufRead, BufReader, ErrorKind};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{fmt, slice};
+use toml_edit::{Array, Value};
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum VersionControl {
@@ -258,6 +260,12 @@ fn check_name(
name
))?;
}
+ let name_in_lowercase = name.to_lowercase();
+ if name != name_in_lowercase {
+ shell.warn(format!(
+ "the name `{name}` is not snake_case or kebab-case which is recommended for package names, consider `{name_in_lowercase}`"
+ ))?;
+ }
Ok(())
}
@@ -803,7 +811,7 @@ fn mk(config: &Config, opts: &MkOptions<'_>) -> CargoResult<()> {
// Sometimes the root manifest is not a valid manifest, so we only try to parse it if it is.
// This should not block the creation of the new project. It is only a best effort to
// inherit the workspace package keys.
- if let Ok(workspace_document) = root_manifest.parse::<toml_edit::Document>() {
+ if let Ok(mut workspace_document) = root_manifest.parse::<toml_edit::Document>() {
if let Some(workspace_package_keys) = workspace_document
.get("workspace")
.and_then(|workspace| workspace.get("package"))
@@ -826,6 +834,13 @@ fn mk(config: &Config, opts: &MkOptions<'_>) -> CargoResult<()> {
table["workspace"] = toml_edit::value(true);
manifest["lints"] = toml_edit::Item::Table(table);
}
+
+ // Try to add the new package to the workspace members.
+ update_manifest_with_new_member(
+ &root_manifest_path,
+ &mut workspace_document,
+ opts.path,
+ )?;
}
}
@@ -925,3 +940,95 @@ fn update_manifest_with_inherited_workspace_package_keys(
try_remove_and_inherit_package_key(key, manifest);
}
}
+
+/// Adds the new package member to the [workspace.members] array.
+/// - It first checks if the name matches any element in [workspace.exclude],
+/// and it ignores the name if there is a match.
+/// - Then it check if the name matches any element already in [workspace.members],
+/// and it ignores the name if there is a match.
+/// - If [workspace.members] doesn't exist in the manifest, it will add a new section
+/// with the new package in it.
+fn update_manifest_with_new_member(
+ root_manifest_path: &Path,
+ workspace_document: &mut toml_edit::Document,
+ package_path: &Path,
+) -> CargoResult<()> {
+ // Find the relative path for the package from the workspace root directory.
+ let workspace_root = root_manifest_path.parent().with_context(|| {
+ format!(
+ "workspace root manifest doesn't have a parent directory `{}`",
+ root_manifest_path.display()
+ )
+ })?;
+ let relpath = pathdiff::diff_paths(package_path, workspace_root).with_context(|| {
+ format!(
+ "path comparison requires two absolute paths; package_path: `{}`, workspace_path: `{}`",
+ package_path.display(),
+ workspace_root.display()
+ )
+ })?;
+
+ let mut components = Vec::new();
+ for comp in relpath.iter() {
+ let comp = comp.to_str().with_context(|| {
+ format!("invalid unicode component in path `{}`", relpath.display())
+ })?;
+ components.push(comp);
+ }
+ let display_path = components.join("/");
+
+ // Don't add the new package to the workspace's members
+ // if there is an exclusion match for it.
+ if let Some(exclude) = workspace_document
+ .get("workspace")
+ .and_then(|workspace| workspace.get("exclude"))
+ .and_then(|exclude| exclude.as_array())
+ {
+ for member in exclude {
+ let pat = member
+ .as_str()
+ .with_context(|| format!("invalid non-string exclude path `{}`", member))?;
+ if pat == display_path {
+ return Ok(());
+ }
+ }
+ }
+
+ // If the members element already exist, check if one of the patterns
+ // in the array already includes the new package's relative path.
+ // - Add the relative path if the members don't match the new package's path.
+ // - Create a new members array if there are no members element in the workspace yet.
+ if let Some(members) = workspace_document
+ .get_mut("workspace")
+ .and_then(|workspace| workspace.get_mut("members"))
+ .and_then(|members| members.as_array_mut())
+ {
+ for member in members.iter() {
+ let pat = member
+ .as_str()
+ .with_context(|| format!("invalid non-string member `{}`", member))?;
+ let pattern = glob::Pattern::new(pat)
+ .with_context(|| format!("cannot build glob pattern from `{}`", pat))?;
+
+ if pattern.matches(&display_path) {
+ return Ok(());
+ }
+ }
+
+ let was_sorted = is_sorted(members.iter().map(Value::as_str));
+ members.push(&display_path);
+ if was_sorted {
+ members.sort_by(|lhs, rhs| lhs.as_str().cmp(&rhs.as_str()));
+ }
+ } else {
+ let mut array = Array::new();
+ array.push(&display_path);
+
+ workspace_document["workspace"]["members"] = toml_edit::value(array);
+ }
+
+ write_atomic(
+ &root_manifest_path,
+ workspace_document.to_string().to_string().as_bytes(),
+ )
+}
diff --git a/src/tools/cargo/src/cargo/ops/cargo_package.rs b/src/tools/cargo/src/cargo/ops/cargo_package.rs
index b6423aa96..6ac09dc77 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_package.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_package.rs
@@ -3,7 +3,6 @@ use std::fs::{self, File};
use std::io::prelude::*;
use std::io::SeekFrom;
use std::path::{Path, PathBuf};
-use std::rc::Rc;
use std::sync::Arc;
use std::task::Poll;
@@ -13,9 +12,10 @@ use crate::core::{registry::PackageRegistry, resolver::HasDevUnits};
use crate::core::{Feature, Shell, Verbosity, Workspace};
use crate::core::{Package, PackageId, PackageSet, Resolve, SourceId};
use crate::sources::PathSource;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::config::JobsConfig;
use crate::util::errors::CargoResult;
-use crate::util::toml::TomlManifest;
+use crate::util::toml::schema::TomlManifest;
use crate::util::{self, human_readable_bytes, restricted_names, Config, FileLock};
use crate::{drop_println, ops};
use anyhow::Context as _;
@@ -132,7 +132,7 @@ pub fn package_one(
let dir = ws.target_dir().join("package");
let mut dst = {
let tmp = format!(".{}", filename);
- dir.open_rw(&tmp, config, "package scratch space")?
+ dir.open_rw_exclusive_create(&tmp, config, "package scratch space")?
};
// Package up and test a temporary tarball and only move it to the final
@@ -411,16 +411,14 @@ fn build_lock(ws: &Workspace<'_>, orig_pkg: &Package) -> CargoResult<String> {
let orig_resolve = ops::load_pkg_lockfile(ws)?;
// Convert Package -> TomlManifest -> Manifest -> Package
- let toml_manifest = Rc::new(
- orig_pkg
- .manifest()
- .original()
- .prepare_for_publish(ws, orig_pkg.root())?,
- );
+ let toml_manifest = orig_pkg
+ .manifest()
+ .original()
+ .prepare_for_publish(ws, orig_pkg.root())?;
let package_root = orig_pkg.root();
let source_id = orig_pkg.package_id().source_id();
let (manifest, _nested_paths) =
- TomlManifest::to_real_manifest(&toml_manifest, false, source_id, package_root, config)?;
+ TomlManifest::to_real_manifest(toml_manifest, false, source_id, package_root, config)?;
let new_pkg = Package::new(manifest, orig_pkg.manifest_path());
let max_rust_version = new_pkg.rust_version().cloned();
@@ -806,7 +804,7 @@ pub fn check_yanked(
) -> CargoResult<()> {
// Checking the yanked status involves taking a look at the registry and
// maybe updating files, so be sure to lock it here.
- let _lock = config.acquire_package_cache_lock()?;
+ let _lock = config.acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
let mut sources = pkg_set.sources_mut();
let mut pending: Vec<PackageId> = resolve.iter().collect();
diff --git a/src/tools/cargo/src/cargo/ops/cargo_uninstall.rs b/src/tools/cargo/src/cargo/ops/cargo_uninstall.rs
index 355154418..1f22e191e 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_uninstall.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_uninstall.rs
@@ -90,6 +90,7 @@ fn uninstall_cwd(root: &Filesystem, bins: &[String], config: &Config) -> CargoRe
None,
|path: &mut PathSource<'_>| path.read_packages(),
config,
+ None,
)?;
let pkgid = pkg.package_id();
uninstall_pkgid(root, tracker, pkgid, bins, config)
diff --git a/src/tools/cargo/src/cargo/ops/common_for_install_and_uninstall.rs b/src/tools/cargo/src/cargo/ops/common_for_install_and_uninstall.rs
index 0934cbd29..d1f9152be 100644
--- a/src/tools/cargo/src/cargo/ops/common_for_install_and_uninstall.rs
+++ b/src/tools/cargo/src/cargo/ops/common_for_install_and_uninstall.rs
@@ -17,6 +17,7 @@ use crate::ops::{self, CompileFilter, CompileOptions};
use crate::sources::source::QueryKind;
use crate::sources::source::Source;
use crate::sources::PathSource;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::errors::CargoResult;
use crate::util::Config;
use crate::util::{FileLock, Filesystem};
@@ -97,8 +98,10 @@ pub struct CrateListingV1 {
impl InstallTracker {
/// Create an InstallTracker from information on disk.
pub fn load(config: &Config, root: &Filesystem) -> CargoResult<InstallTracker> {
- let v1_lock = root.open_rw(Path::new(".crates.toml"), config, "crate metadata")?;
- let v2_lock = root.open_rw(Path::new(".crates2.json"), config, "crate metadata")?;
+ let v1_lock =
+ root.open_rw_exclusive_create(Path::new(".crates.toml"), config, "crate metadata")?;
+ let v2_lock =
+ root.open_rw_exclusive_create(Path::new(".crates2.json"), config, "crate metadata")?;
let v1 = (|| -> CargoResult<_> {
let mut contents = String::new();
@@ -212,7 +215,7 @@ impl InstallTracker {
let precise_equal = if source_id.is_git() {
// Git sources must have the exact same hash to be
// considered "fresh".
- dupe_pkg_id.source_id().precise() == source_id.precise()
+ dupe_pkg_id.source_id().has_same_precise_as(source_id)
} else {
true
};
@@ -529,6 +532,7 @@ pub fn select_dep_pkg<T>(
dep: Dependency,
config: &Config,
needs_update: bool,
+ current_rust_version: Option<&semver::Version>,
) -> CargoResult<Package>
where
T: Source,
@@ -536,7 +540,7 @@ where
// This operation may involve updating some sources or making a few queries
// which may involve frobbing caches, as a result make sure we synchronize
// with other global Cargos
- let _lock = config.acquire_package_cache_lock()?;
+ let _lock = config.acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
if needs_update {
source.invalidate_cache();
@@ -548,9 +552,56 @@ where
Poll::Pending => source.block_until_ready()?,
}
};
- match deps.iter().map(|p| p.package_id()).max() {
- Some(pkgid) => {
- let pkg = Box::new(source).download_now(pkgid, config)?;
+ match deps.iter().max_by_key(|p| p.package_id()) {
+ Some(summary) => {
+ if let (Some(current), Some(msrv)) = (current_rust_version, summary.rust_version()) {
+ let msrv_req = msrv.to_caret_req();
+ if !msrv_req.matches(current) {
+ let name = summary.name();
+ let ver = summary.version();
+ let extra = if dep.source_id().is_registry() {
+ // Match any version, not just the selected
+ let msrv_dep =
+ Dependency::parse(dep.package_name(), None, dep.source_id())?;
+ let msrv_deps = loop {
+ match source.query_vec(&msrv_dep, QueryKind::Exact)? {
+ Poll::Ready(deps) => break deps,
+ Poll::Pending => source.block_until_ready()?,
+ }
+ };
+ if let Some(alt) = msrv_deps
+ .iter()
+ .filter(|summary| {
+ summary
+ .rust_version()
+ .map(|msrv| msrv.to_caret_req().matches(current))
+ .unwrap_or(true)
+ })
+ .max_by_key(|s| s.package_id())
+ {
+ if let Some(rust_version) = alt.rust_version() {
+ format!(
+ "\n`{name} {}` supports rustc {rust_version}",
+ alt.version()
+ )
+ } else {
+ format!(
+ "\n`{name} {}` has an unspecified minimum rustc version",
+ alt.version()
+ )
+ }
+ } else {
+ String::new()
+ }
+ } else {
+ String::new()
+ };
+ bail!("\
+cannot install package `{name} {ver}`, it requires rustc {msrv} or newer, while the currently active rustc version is {current}{extra}"
+)
+ }
+ }
+ let pkg = Box::new(source).download_now(summary.package_id(), config)?;
Ok(pkg)
}
None => {
@@ -596,6 +647,7 @@ pub fn select_pkg<T, F>(
dep: Option<Dependency>,
mut list_all: F,
config: &Config,
+ current_rust_version: Option<&semver::Version>,
) -> CargoResult<Package>
where
T: Source,
@@ -604,12 +656,12 @@ where
// This operation may involve updating some sources or making a few queries
// which may involve frobbing caches, as a result make sure we synchronize
// with other global Cargos
- let _lock = config.acquire_package_cache_lock()?;
+ let _lock = config.acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
source.invalidate_cache();
return if let Some(dep) = dep {
- select_dep_pkg(source, dep, config, false)
+ select_dep_pkg(source, dep, config, false, current_rust_version)
} else {
let candidates = list_all(source)?;
let binaries = candidates
diff --git a/src/tools/cargo/src/cargo/ops/fix.rs b/src/tools/cargo/src/cargo/ops/fix.rs
index 9d6459294..6630914a4 100644
--- a/src/tools/cargo/src/cargo/ops/fix.rs
+++ b/src/tools/cargo/src/cargo/ops/fix.rs
@@ -451,6 +451,11 @@ pub fn fix_exec_rustc(config: &Config, lock_addr: &str) -> CargoResult<()> {
// things like colored output to work correctly.
rustc.arg(arg);
}
+ // Removes `FD_CLOEXEC` set by `jobserver::Client` to pass jobserver
+ // as environment variables specify.
+ if let Some(client) = config.jobserver_from_env() {
+ rustc.inherit_jobserver(client);
+ }
debug!("calling rustc to display remaining diagnostics: {rustc}");
exit_with(rustc.status()?);
}
diff --git a/src/tools/cargo/src/cargo/ops/lockfile.rs b/src/tools/cargo/src/cargo/ops/lockfile.rs
index 26456e560..2160b6f01 100644
--- a/src/tools/cargo/src/cargo/ops/lockfile.rs
+++ b/src/tools/cargo/src/cargo/ops/lockfile.rs
@@ -12,7 +12,7 @@ pub fn load_pkg_lockfile(ws: &Workspace<'_>) -> CargoResult<Option<Resolve>> {
return Ok(None);
}
- let mut f = lock_root.open_ro("Cargo.lock", ws.config(), "Cargo.lock file")?;
+ let mut f = lock_root.open_ro_shared("Cargo.lock", ws.config(), "Cargo.lock file")?;
let mut s = String::new();
f.read_to_string(&mut s)
@@ -64,22 +64,21 @@ pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &mut Resolve) -> CargoRes
// out lock file updates as they're otherwise already updated, and changes
// which don't touch dependencies won't seemingly spuriously update the lock
// file.
- if resolve.version() < ResolveVersion::default() {
- resolve.set_version(ResolveVersion::default());
+ let default_version = ResolveVersion::default();
+ let current_version = resolve.version();
+ let next_lockfile_bump = ws.config().cli_unstable().next_lockfile_bump;
+
+ if current_version < default_version {
+ resolve.set_version(default_version);
out = serialize_resolve(resolve, orig.as_deref());
- } else if resolve.version() > ResolveVersion::default()
- && !ws.config().cli_unstable().next_lockfile_bump
- {
+ } else if current_version > ResolveVersion::max_stable() && !next_lockfile_bump {
// The next version hasn't yet stabilized.
- anyhow::bail!(
- "lock file version `{:?}` requires `-Znext-lockfile-bump`",
- resolve.version()
- )
+ anyhow::bail!("lock file version `{current_version:?}` requires `-Znext-lockfile-bump`")
}
// Ok, if that didn't work just write it out
lock_root
- .open_rw("Cargo.lock", ws.config(), "Cargo.lock file")
+ .open_rw_exclusive_create("Cargo.lock", ws.config(), "Cargo.lock file")
.and_then(|mut f| {
f.file().set_len(0)?;
f.write_all(out.as_bytes())?;
@@ -100,7 +99,7 @@ fn resolve_to_string_orig(
) -> (Option<String>, String, Filesystem) {
// Load the original lock file if it exists.
let lock_root = lock_root(ws);
- let orig = lock_root.open_ro("Cargo.lock", ws.config(), "Cargo.lock file");
+ let orig = lock_root.open_ro_shared("Cargo.lock", ws.config(), "Cargo.lock file");
let orig = orig.and_then(|mut f| {
let mut s = String::new();
f.read_to_string(&mut s)?;
diff --git a/src/tools/cargo/src/cargo/ops/registry/mod.rs b/src/tools/cargo/src/cargo/ops/registry/mod.rs
index c82230a16..3fa8fd291 100644
--- a/src/tools/cargo/src/cargo/ops/registry/mod.rs
+++ b/src/tools/cargo/src/cargo/ops/registry/mod.rs
@@ -22,6 +22,7 @@ use crate::core::SourceId;
use crate::sources::source::Source;
use crate::sources::{RegistrySource, SourceConfigMap};
use crate::util::auth;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::config::{Config, PathAndArgs};
use crate::util::errors::CargoResult;
use crate::util::network::http::http_handle;
@@ -131,7 +132,7 @@ fn registry(
}
let cfg = {
- let _lock = config.acquire_package_cache_lock()?;
+ let _lock = config.acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
let mut src = RegistrySource::remote(source_ids.replacement, &HashSet::new(), config)?;
// Only update the index if `force_update` is set.
if force_update {
diff --git a/src/tools/cargo/src/cargo/ops/registry/publish.rs b/src/tools/cargo/src/cargo/ops/registry/publish.rs
index a88a0a30f..201907bb2 100644
--- a/src/tools/cargo/src/cargo/ops/registry/publish.rs
+++ b/src/tools/cargo/src/cargo/ops/registry/publish.rs
@@ -30,6 +30,7 @@ use crate::sources::source::QueryKind;
use crate::sources::SourceConfigMap;
use crate::sources::CRATES_IO_REGISTRY;
use crate::util::auth;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::config::JobsConfig;
use crate::util::Progress;
use crate::util::ProgressStyle;
@@ -102,7 +103,7 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> {
if allowed_registries.is_empty() {
bail!(
"`{}` cannot be published.\n\
- `package.publish` is set to `false` or an empty list in Cargo.toml and prevents publishing.",
+ `package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish.",
pkg.name(),
);
} else if !allowed_registries.contains(&reg_name) {
@@ -233,7 +234,7 @@ fn wait_for_publish(
progress.tick_now(0, max, "")?;
let is_available = loop {
{
- let _lock = config.acquire_package_cache_lock()?;
+ let _lock = config.acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
// Force re-fetching the source
//
// As pulling from a git source is expensive, we track when we've done it within the
diff --git a/src/tools/cargo/src/cargo/ops/registry/search.rs b/src/tools/cargo/src/cargo/ops/registry/search.rs
index 10b4d600e..0f4649754 100644
--- a/src/tools/cargo/src/cargo/ops/registry/search.rs
+++ b/src/tools/cargo/src/cargo/ops/registry/search.rs
@@ -3,7 +3,6 @@
//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html#search
use std::cmp;
-use std::iter::repeat;
use anyhow::Context as _;
use url::Url;
@@ -35,7 +34,7 @@ pub fn search(
.map(|krate| format!("{} = \"{}\"", krate.name, krate.max_version))
.collect::<Vec<String>>();
- let description_margin = names.iter().map(|s| s.len() + 4).max().unwrap_or_default();
+ let description_margin = names.iter().map(|s| s.len()).max().unwrap_or_default() + 4;
let description_length = cmp::max(80, 128 - description_margin);
@@ -46,34 +45,32 @@ pub fn search(
.map(|desc| truncate_with_ellipsis(&desc.replace("\n", " "), description_length))
});
+ let mut shell = config.shell();
+ let stdout = shell.out();
+ let good = style::GOOD.render();
+ let reset = anstyle::Reset.render();
+
for (name, description) in names.into_iter().zip(descriptions) {
let line = match description {
- Some(desc) => {
- let space = repeat(' ')
- .take(description_margin - name.len())
- .collect::<String>();
- name + &space + "# " + &desc
- }
+ Some(desc) => format!("{name: <description_margin$}# {desc}"),
None => name,
};
let mut fragments = line.split(query).peekable();
while let Some(fragment) = fragments.next() {
- let _ = config.shell().write_stdout(fragment, &style::NOP);
+ let _ = write!(stdout, "{fragment}");
if fragments.peek().is_some() {
- let _ = config.shell().write_stdout(query, &style::GOOD);
+ let _ = write!(stdout, "{good}{query}{reset}");
}
}
- let _ = config.shell().write_stdout("\n", &style::NOP);
+ let _ = writeln!(stdout);
}
let search_max_limit = 100;
if total_crates > limit && limit < search_max_limit {
- let _ = config.shell().write_stdout(
- format_args!(
- "... and {} crates more (use --limit N to see more)\n",
- total_crates - limit
- ),
- &style::NOP,
+ let _ = writeln!(
+ stdout,
+ "... and {} crates more (use --limit N to see more)",
+ total_crates - limit
);
} else if total_crates > limit && limit >= search_max_limit {
let extra = if source_ids.original.is_crates_io() {
@@ -82,9 +79,11 @@ pub fn search(
} else {
String::new()
};
- let _ = config.shell().write_stdout(
- format_args!("... and {} crates more{}\n", total_crates - limit, extra),
- &style::NOP,
+ let _ = writeln!(
+ stdout,
+ "... and {} crates more{}",
+ total_crates - limit,
+ extra
);
}
diff --git a/src/tools/cargo/src/cargo/ops/resolve.rs b/src/tools/cargo/src/cargo/ops/resolve.rs
index 053098d55..8ca72f77c 100644
--- a/src/tools/cargo/src/cargo/ops/resolve.rs
+++ b/src/tools/cargo/src/cargo/ops/resolve.rs
@@ -61,13 +61,14 @@ use crate::core::resolver::features::{
CliFeatures, FeatureOpts, FeatureResolver, ForceAllTargets, RequestedFeatures, ResolvedFeatures,
};
use crate::core::resolver::{
- self, HasDevUnits, Resolve, ResolveOpts, ResolveVersion, VersionPreferences,
+ self, HasDevUnits, Resolve, ResolveOpts, ResolveVersion, VersionOrdering, VersionPreferences,
};
use crate::core::summary::Summary;
use crate::core::Feature;
use crate::core::{GitReference, PackageId, PackageIdSpec, PackageSet, SourceId, Workspace};
use crate::ops;
use crate::sources::PathSource;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::errors::CargoResult;
use crate::util::RustVersion;
use crate::util::{profile, CanonicalUrl};
@@ -289,7 +290,9 @@ pub fn resolve_with_previous<'cfg>(
) -> CargoResult<Resolve> {
// We only want one Cargo at a time resolving a crate graph since this can
// involve a lot of frobbing of the global caches.
- let _lock = ws.config().acquire_package_cache_lock()?;
+ let _lock = ws
+ .config()
+ .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)?;
// Here we place an artificial limitation that all non-registry sources
// cannot be locked at more than one revision. This means that if a Git
@@ -318,6 +321,12 @@ pub fn resolve_with_previous<'cfg>(
// While registering patches, we will record preferences for particular versions
// of various packages.
let mut version_prefs = VersionPreferences::default();
+ if ws.config().cli_unstable().minimal_versions {
+ version_prefs.version_ordering(VersionOrdering::MinimumVersionsFirst)
+ }
+ if ws.config().cli_unstable().msrv_policy {
+ version_prefs.max_rust_version(max_rust_version.cloned());
+ }
// This is a set of PackageIds of `[patch]` entries, and some related locked PackageIds, for
// which locking should be avoided (but which will be preferred when searching dependencies,
@@ -386,12 +395,8 @@ pub fn resolve_with_previous<'cfg>(
}) {
Some(id_using_default) => {
let id_using_master = id_using_default.with_source_id(
- dep.source_id().with_precise(
- id_using_default
- .source_id()
- .precise()
- .map(|s| s.to_string()),
- ),
+ dep.source_id()
+ .with_precise_from(id_using_default.source_id()),
);
let mut locked_dep = dep.clone();
@@ -510,7 +515,6 @@ pub fn resolve_with_previous<'cfg>(
ws.unstable_features()
.require(Feature::public_dependency())
.is_ok(),
- max_rust_version,
)?;
let patches: Vec<_> = registry
.patches()
@@ -793,7 +797,7 @@ fn master_branch_git_source(id: PackageId, resolve: &Resolve) -> Option<PackageI
let new_source =
SourceId::for_git(source.url(), GitReference::Branch("master".to_string()))
.unwrap()
- .with_precise(source.precise().map(|s| s.to_string()));
+ .with_precise_from(source);
return Some(id.with_source_id(new_source));
}
}
diff --git a/src/tools/cargo/src/cargo/ops/vendor.rs b/src/tools/cargo/src/cargo/ops/vendor.rs
index 3ee46db32..cad7fc5d1 100644
--- a/src/tools/cargo/src/cargo/ops/vendor.rs
+++ b/src/tools/cargo/src/cargo/ops/vendor.rs
@@ -258,7 +258,7 @@ fn sync(
} else {
// Remove `precise` since that makes the source name very long,
// and isn't needed to disambiguate multiple sources.
- source_id.with_precise(None).as_url().to_string()
+ source_id.without_precise().as_url().to_string()
};
let source = if source_id.is_crates_io() {
diff --git a/src/tools/cargo/src/cargo/sources/config.rs b/src/tools/cargo/src/cargo/sources/config.rs
index cc7aa9925..4a0332eca 100644
--- a/src/tools/cargo/src/cargo/sources/config.rs
+++ b/src/tools/cargo/src/cargo/sources/config.rs
@@ -145,7 +145,7 @@ impl<'cfg> SourceConfigMap<'cfg> {
// Attempt to interpret the source name as an alt registry name
if let Ok(alt_id) = SourceId::alt_registry(self.config, name) {
debug!("following pointer to registry {}", name);
- break alt_id.with_precise(id.precise().map(str::to_string));
+ break alt_id.with_precise_from(id);
}
bail!(
"could not find a configured source with the \
@@ -163,7 +163,7 @@ impl<'cfg> SourceConfigMap<'cfg> {
}
None if id == cfg.id => return id.load(self.config, yanked_whitelist),
None => {
- break cfg.id.with_precise(id.precise().map(|s| s.to_string()));
+ break cfg.id.with_precise_from(id);
}
}
debug!("following pointer to {}", name);
@@ -199,7 +199,7 @@ a lock file compatible with `{orig}` cannot be generated in this situation
);
}
- if old_src.requires_precise() && id.precise().is_none() {
+ if old_src.requires_precise() && !id.has_precise() {
bail!(
"\
the source {orig} requires a lock file to be present first before it can be
diff --git a/src/tools/cargo/src/cargo/sources/git/source.rs b/src/tools/cargo/src/cargo/sources/git/source.rs
index 7af342f45..a75c1ec6d 100644
--- a/src/tools/cargo/src/cargo/sources/git/source.rs
+++ b/src/tools/cargo/src/cargo/sources/git/source.rs
@@ -8,6 +8,7 @@ use crate::sources::source::MaybePackage;
use crate::sources::source::QueryKind;
use crate::sources::source::Source;
use crate::sources::PathSource;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::errors::CargoResult;
use crate::util::hex::short_hash;
use crate::util::Config;
@@ -88,13 +89,7 @@ impl<'cfg> GitSource<'cfg> {
let remote = GitRemote::new(source_id.url());
let manifest_reference = source_id.git_reference().unwrap().clone();
- let locked_rev =
- match source_id.precise() {
- Some(s) => Some(git2::Oid::from_str(s).with_context(|| {
- format!("precise value for git is not a git revision: {}", s)
- })?),
- None => None,
- };
+ let locked_rev = source_id.precise_git_oid()?;
let ident = ident_shallow(
&source_id,
config
@@ -212,7 +207,9 @@ impl<'cfg> Source for GitSource<'cfg> {
// Ignore errors creating it, in case this is a read-only filesystem:
// perhaps the later operations can succeed anyhow.
let _ = git_fs.create_dir();
- let git_path = self.config.assert_package_cache_locked(&git_fs);
+ let git_path = self
+ .config
+ .assert_package_cache_locked(CacheLockMode::DownloadExclusive, &git_fs);
// Before getting a checkout, make sure that `<cargo_home>/git` is
// marked as excluded from indexing and backups. Older versions of Cargo
@@ -287,7 +284,9 @@ impl<'cfg> Source for GitSource<'cfg> {
.join(short_id.as_str());
db.copy_to(actual_rev, &checkout_path, self.config)?;
- let source_id = self.source_id.with_precise(Some(actual_rev.to_string()));
+ let source_id = self
+ .source_id
+ .with_git_precise(Some(actual_rev.to_string()));
let path_source = PathSource::new_recursive(&checkout_path, source_id, self.config);
self.path_source = Some(path_source);
diff --git a/src/tools/cargo/src/cargo/sources/registry/download.rs b/src/tools/cargo/src/cargo/sources/registry/download.rs
index 9bf838625..786432835 100644
--- a/src/tools/cargo/src/cargo/sources/registry/download.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/download.rs
@@ -12,6 +12,7 @@ use crate::core::PackageId;
use crate::sources::registry::MaybeLock;
use crate::sources::registry::RegistryConfig;
use crate::util::auth;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::errors::CargoResult;
use crate::util::{Config, Filesystem};
use std::fmt::Write as FmtWrite;
@@ -38,7 +39,7 @@ pub(super) fn download(
registry_config: RegistryConfig,
) -> CargoResult<MaybeLock> {
let path = cache_path.join(&pkg.tarball_name());
- let path = config.assert_package_cache_locked(&path);
+ let path = config.assert_package_cache_locked(CacheLockMode::DownloadExclusive, &path);
// Attempt to open a read-only copy first to avoid an exclusive write
// lock and also work with read-only filesystems. Note that we check the
@@ -117,7 +118,7 @@ pub(super) fn finish_download(
cache_path.create_dir()?;
let path = cache_path.join(&pkg.tarball_name());
- let path = config.assert_package_cache_locked(&path);
+ let path = config.assert_package_cache_locked(CacheLockMode::DownloadExclusive, &path);
let mut dst = OpenOptions::new()
.create(true)
.read(true)
@@ -144,7 +145,7 @@ pub(super) fn is_crate_downloaded(
pkg: PackageId,
) -> bool {
let path = cache_path.join(pkg.tarball_name());
- let path = config.assert_package_cache_locked(&path);
+ let path = config.assert_package_cache_locked(CacheLockMode::DownloadExclusive, &path);
if let Ok(meta) = fs::metadata(path) {
return meta.len() > 0;
}
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 9fe76333b..3d31110c3 100644
--- a/src/tools/cargo/src/cargo/sources/registry/http_remote.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/http_remote.rs
@@ -4,6 +4,7 @@ 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::cache_lock::CacheLockMode;
use crate::util::errors::{CargoResult, HttpNotSuccessful};
use crate::util::network::http::http_handle;
use crate::util::network::retry::{Retry, RetryResult};
@@ -461,7 +462,8 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
}
fn assert_index_locked<'a>(&self, path: &'a Filesystem) -> &'a Path {
- self.config.assert_package_cache_locked(path)
+ self.config
+ .assert_package_cache_locked(CacheLockMode::DownloadExclusive, path)
}
fn is_updated(&self) -> bool {
@@ -744,6 +746,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
Poll::Ready(cfg) => break cfg.to_owned(),
}
};
+
download::download(
&self.cache_path,
&self.config,
diff --git a/src/tools/cargo/src/cargo/sources/registry/index.rs b/src/tools/cargo/src/cargo/sources/registry/index.rs
index ca1cf4069..00f21d669 100644
--- a/src/tools/cargo/src/cargo/sources/registry/index.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/index.rs
@@ -89,6 +89,7 @@ use crate::core::dependency::{Artifact, DepKind};
use crate::core::Dependency;
use crate::core::{PackageId, SourceId, Summary};
use crate::sources::registry::{LoadResponse, RegistryData};
+use crate::util::cache_lock::CacheLockMode;
use crate::util::interning::InternedString;
use crate::util::IntoUrl;
use crate::util::{internal, CargoResult, Config, Filesystem, OptVersionReq, RustVersion};
@@ -98,7 +99,7 @@ use semver::Version;
use serde::Deserialize;
use std::borrow::Cow;
use std::collections::BTreeMap;
-use std::collections::{HashMap, HashSet};
+use std::collections::HashMap;
use std::fs;
use std::io::ErrorKind;
use std::path::Path;
@@ -437,11 +438,9 @@ impl<'cfg> RegistryIndex<'cfg> {
/// checking the integrity of a downloaded package matching the checksum in
/// the index file, aka [`IndexSummary`].
pub fn hash(&mut self, pkg: PackageId, load: &mut dyn RegistryData) -> Poll<CargoResult<&str>> {
- let req = OptVersionReq::exact(pkg.version());
+ let req = OptVersionReq::lock_to_exact(pkg.version());
let summary = self.summaries(pkg.name(), &req, load)?;
- let summary = ready!(summary)
- .filter(|s| s.package_id().version() == pkg.version())
- .next();
+ let summary = ready!(summary).next();
Poll::Ready(Ok(summary
.ok_or_else(|| internal(format!("no hash listed for {}", pkg)))?
.as_summary()
@@ -574,8 +573,7 @@ impl<'cfg> RegistryIndex<'cfg> {
name: InternedString,
req: &OptVersionReq,
load: &mut dyn RegistryData,
- yanked_whitelist: &HashSet<PackageId>,
- f: &mut dyn FnMut(Summary),
+ f: &mut dyn FnMut(IndexSummary),
) -> Poll<CargoResult<()>> {
if self.config.offline() {
// This should only return `Poll::Ready(Ok(()))` if there is at least 1 match.
@@ -592,31 +590,15 @@ impl<'cfg> RegistryIndex<'cfg> {
let callback = &mut |s: IndexSummary| {
if !s.is_offline() {
called = true;
- f(s.into_summary());
+ f(s);
}
};
- ready!(self.query_inner_with_online(
- name,
- req,
- load,
- yanked_whitelist,
- callback,
- false
- )?);
+ ready!(self.query_inner_with_online(name, req, load, callback, false)?);
if called {
return Poll::Ready(Ok(()));
}
}
- self.query_inner_with_online(
- name,
- req,
- load,
- yanked_whitelist,
- &mut |s| {
- f(s.into_summary());
- },
- true,
- )
+ self.query_inner_with_online(name, req, load, f, true)
}
/// Inner implementation of [`Self::query_inner`]. Returns the number of
@@ -628,15 +610,10 @@ impl<'cfg> RegistryIndex<'cfg> {
name: InternedString,
req: &OptVersionReq,
load: &mut dyn RegistryData,
- yanked_whitelist: &HashSet<PackageId>,
f: &mut dyn FnMut(IndexSummary),
online: bool,
) -> Poll<CargoResult<()>> {
- let source_id = self.source_id;
-
- let summaries = ready!(self.summaries(name, req, load))?;
-
- let summaries = summaries
+ ready!(self.summaries(name, &req, load))?
// First filter summaries for `--offline`. If we're online then
// everything is a candidate, otherwise if we're offline we're only
// going to consider candidates which are actually present on disk.
@@ -654,41 +631,7 @@ impl<'cfg> RegistryIndex<'cfg> {
IndexSummary::Offline(s.as_summary().clone())
}
})
- // Next filter out all yanked packages. Some yanked packages may
- // leak through if they're in a whitelist (aka if they were
- // previously in `Cargo.lock`
- .filter(|s| !s.is_yanked() || yanked_whitelist.contains(&s.package_id()));
-
- // Handle `cargo update --precise` here.
- let precise = source_id.precise_registry_version(name.as_str());
- let summaries = summaries.filter(|s| match &precise {
- Some((current, requested)) => {
- if req.matches(current) {
- // Unfortunately crates.io allows versions to differ only
- // by build metadata. This shouldn't be allowed, but since
- // it is, this will honor it if requested. However, if not
- // specified, then ignore it.
- let s_vers = s.package_id().version();
- match (s_vers.build.is_empty(), requested.build.is_empty()) {
- (true, true) => s_vers == requested,
- (true, false) => false,
- (false, true) => {
- // Strip out the metadata.
- s_vers.major == requested.major
- && s_vers.minor == requested.minor
- && s_vers.patch == requested.patch
- && s_vers.pre == requested.pre
- }
- (false, false) => s_vers == requested,
- }
- } else {
- true
- }
- }
- None => true,
- });
-
- summaries.for_each(f);
+ .for_each(f);
Poll::Ready(Ok(()))
}
@@ -698,10 +641,8 @@ impl<'cfg> RegistryIndex<'cfg> {
pkg: PackageId,
load: &mut dyn RegistryData,
) -> Poll<CargoResult<bool>> {
- let req = OptVersionReq::exact(pkg.version());
- let found = ready!(self.summaries(pkg.name(), &req, load))?
- .filter(|s| s.package_id().version() == pkg.version())
- .any(|s| s.is_yanked());
+ let req = OptVersionReq::lock_to_exact(pkg.version());
+ let found = ready!(self.summaries(pkg.name(), &req, load))?.any(|s| s.is_yanked());
Poll::Ready(Ok(found))
}
}
@@ -823,7 +764,7 @@ impl Summaries {
// something in case of error.
if paths::create_dir_all(cache_path.parent().unwrap()).is_ok() {
let path = Filesystem::new(cache_path.clone());
- config.assert_package_cache_locked(&path);
+ config.assert_package_cache_locked(CacheLockMode::DownloadExclusive, &path);
if let Err(e) = fs::write(cache_path, &cache_bytes) {
tracing::info!("failed to write cache: {}", e);
}
@@ -994,7 +935,7 @@ impl IndexSummary {
} = serde_json::from_slice(line)?;
let v = v.unwrap_or(1);
tracing::trace!("json parsed registry {}/{}", name, vers);
- let pkgid = PackageId::new(name, &vers, source_id)?;
+ let pkgid = PackageId::pure(name.into(), vers.clone(), source_id);
let deps = deps
.into_iter()
.map(|dep| dep.into_dep(source_id))
diff --git a/src/tools/cargo/src/cargo/sources/registry/mod.rs b/src/tools/cargo/src/cargo/sources/registry/mod.rs
index fb8f79817..7ee461edd 100644
--- a/src/tools/cargo/src/cargo/sources/registry/mod.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/mod.rs
@@ -206,6 +206,7 @@ use crate::sources::source::MaybePackage;
use crate::sources::source::QueryKind;
use crate::sources::source::Source;
use crate::sources::PathSource;
+use crate::util::cache_lock::CacheLockMode;
use crate::util::hex;
use crate::util::interning::InternedString;
use crate::util::network::PollExt;
@@ -581,7 +582,9 @@ impl<'cfg> RegistrySource<'cfg> {
let package_dir = format!("{}-{}", pkg.name(), pkg.version());
let dst = self.src_path.join(&package_dir);
let path = dst.join(PACKAGE_SOURCE_LOCK);
- let path = self.config.assert_package_cache_locked(&path);
+ let path = self
+ .config
+ .assert_package_cache_locked(CacheLockMode::DownloadExclusive, &path);
let unpack_dir = path.parent().unwrap();
match fs::read_to_string(path) {
Ok(ok) => match serde_json::from_str::<LockMetadata>(&ok) {
@@ -709,26 +712,33 @@ impl<'cfg> Source for RegistrySource<'cfg> {
kind: QueryKind,
f: &mut dyn FnMut(Summary),
) -> Poll<CargoResult<()>> {
- // If this is a precise dependency, then it came from a lock file and in
+ let mut req = dep.version_req().clone();
+
+ // Handle `cargo update --precise` here.
+ if let Some((_, requested)) = self
+ .source_id
+ .precise_registry_version(dep.package_name().as_str())
+ .filter(|(c, _)| req.matches(c))
+ {
+ req.update_precise(&requested);
+ }
+
+ // If this is a locked dependency, then it came from a lock file and in
// theory the registry is known to contain this version. If, however, we
// come back with no summaries, then our registry may need to be
// updated, so we fall back to performing a lazy update.
- if kind == QueryKind::Exact && dep.source_id().precise().is_some() && !self.ops.is_updated()
- {
+ if kind == QueryKind::Exact && req.is_locked() && !self.ops.is_updated() {
debug!("attempting query without update");
let mut called = false;
- ready!(self.index.query_inner(
- dep.package_name(),
- dep.version_req(),
- &mut *self.ops,
- &self.yanked_whitelist,
- &mut |s| {
- if dep.matches(&s) {
+ ready!(self
+ .index
+ .query_inner(dep.package_name(), &req, &mut *self.ops, &mut |s| {
+ if dep.matches(s.as_summary()) {
+ // We are looking for a package from a lock file so we do not care about yank
called = true;
- f(s);
+ f(s.into_summary());
}
- },
- ))?;
+ },))?;
if called {
Poll::Ready(Ok(()))
} else {
@@ -738,22 +748,23 @@ impl<'cfg> Source for RegistrySource<'cfg> {
}
} else {
let mut called = false;
- ready!(self.index.query_inner(
- dep.package_name(),
- dep.version_req(),
- &mut *self.ops,
- &self.yanked_whitelist,
- &mut |s| {
+ ready!(self
+ .index
+ .query_inner(dep.package_name(), &req, &mut *self.ops, &mut |s| {
let matched = match kind {
- QueryKind::Exact => dep.matches(&s),
+ QueryKind::Exact => dep.matches(s.as_summary()),
QueryKind::Fuzzy => true,
};
- if matched {
- f(s);
+ // Next filter out all yanked packages. Some yanked packages may
+ // leak through if they're in a whitelist (aka if they were
+ // previously in `Cargo.lock`
+ if matched
+ && (!s.is_yanked() || self.yanked_whitelist.contains(&s.package_id()))
+ {
+ f(s.into_summary());
called = true;
}
- }
- ))?;
+ }))?;
if called {
return Poll::Ready(Ok(()));
}
@@ -775,13 +786,9 @@ impl<'cfg> Source for RegistrySource<'cfg> {
}
any_pending |= self
.index
- .query_inner(
- name_permutation,
- dep.version_req(),
- &mut *self.ops,
- &self.yanked_whitelist,
- f,
- )?
+ .query_inner(name_permutation, &req, &mut *self.ops, &mut |s| {
+ f(s.into_summary());
+ })?
.is_pending();
}
}
diff --git a/src/tools/cargo/src/cargo/sources/registry/remote.rs b/src/tools/cargo/src/cargo/sources/registry/remote.rs
index 39eb14dc0..ba171eac3 100644
--- a/src/tools/cargo/src/cargo/sources/registry/remote.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/remote.rs
@@ -6,6 +6,7 @@ use crate::sources::git::fetch::RemoteKind;
use crate::sources::registry::download;
use crate::sources::registry::MaybeLock;
use crate::sources::registry::{LoadResponse, RegistryConfig, RegistryData};
+use crate::util::cache_lock::CacheLockMode;
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::{Config, Filesystem};
@@ -104,7 +105,9 @@ impl<'cfg> RemoteRegistry<'cfg> {
fn repo(&self) -> CargoResult<&git2::Repository> {
self.repo.try_borrow_with(|| {
trace!("acquiring registry index lock");
- let path = self.config.assert_package_cache_locked(&self.index_path);
+ let path = self
+ .config
+ .assert_package_cache_locked(CacheLockMode::DownloadExclusive, &self.index_path);
match git2::Repository::open(&path) {
Ok(repo) => Ok(repo),
@@ -216,7 +219,8 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
}
fn assert_index_locked<'a>(&self, path: &'a Filesystem) -> &'a Path {
- self.config.assert_package_cache_locked(path)
+ self.config
+ .assert_package_cache_locked(CacheLockMode::DownloadExclusive, path)
}
/// Read the general concept for `load()` on [`RegistryData::load`].
@@ -302,7 +306,8 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
fn config(&mut self) -> Poll<CargoResult<Option<RegistryConfig>>> {
debug!("loading config");
self.prepare()?;
- self.config.assert_package_cache_locked(&self.index_path);
+ self.config
+ .assert_package_cache_locked(CacheLockMode::DownloadExclusive, &self.index_path);
match ready!(self.load(Path::new(""), Path::new(RegistryConfig::NAME), None)?) {
LoadResponse::Data { raw_data, .. } => {
trace!("config loaded");
@@ -346,7 +351,9 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
self.head.set(None);
*self.tree.borrow_mut() = None;
self.current_sha.set(None);
- let _path = self.config.assert_package_cache_locked(&self.index_path);
+ let _path = self
+ .config
+ .assert_package_cache_locked(CacheLockMode::DownloadExclusive, &self.index_path);
if !self.quiet {
self.config
.shell()
diff --git a/src/tools/cargo/src/cargo/util/auth/mod.rs b/src/tools/cargo/src/cargo/util/auth/mod.rs
index ea82dce0c..c2f818645 100644
--- a/src/tools/cargo/src/cargo/util/auth/mod.rs
+++ b/src/tools/cargo/src/cargo/util/auth/mod.rs
@@ -529,9 +529,15 @@ fn credential_action(
}
"cargo:paseto" => bail!("cargo:paseto requires -Zasymmetric-token"),
"cargo:token-from-stdout" => Box::new(BasicProcessCredential {}),
+ #[cfg(windows)]
"cargo:wincred" => Box::new(cargo_credential_wincred::WindowsCredential {}),
+ #[cfg(target_os = "macos")]
"cargo:macos-keychain" => Box::new(cargo_credential_macos_keychain::MacKeychain {}),
+ #[cfg(target_os = "linux")]
"cargo:libsecret" => Box::new(cargo_credential_libsecret::LibSecretCredential {}),
+ name if BUILT_IN_PROVIDERS.contains(&name) => {
+ Box::new(cargo_credential::UnsupportedCredential {})
+ }
process => Box::new(CredentialProcessCredential::new(process)),
};
config.shell().verbose(|c| {
diff --git a/src/tools/cargo/src/cargo/util/cache_lock.rs b/src/tools/cargo/src/cargo/util/cache_lock.rs
new file mode 100644
index 000000000..ca9e8d1b0
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util/cache_lock.rs
@@ -0,0 +1,549 @@
+//! Support for locking the package and index caches.
+//!
+//! This implements locking on the package and index caches (source files,
+//! `.crate` files, and index caches) to coordinate when multiple cargos are
+//! running at the same time.
+//!
+//! ## Usage
+//!
+//! There is a global [`CacheLocker`] held inside cargo's venerable
+//! [`Config`]. The `CacheLocker` manages creating and tracking the locks
+//! being held. There are methods on `Config` for managing the locks:
+//!
+//! - [`Config::acquire_package_cache_lock`] --- Acquires a lock. May block if
+//! another process holds a lock.
+//! - [`Config::try_acquire_package_cache_lock`] --- Acquires a lock, returning
+//! immediately if it would block.
+//! - [`Config::assert_package_cache_locked`] --- This is used to ensure the
+//! proper lock is being held.
+//!
+//! Lower-level code that accesses the package cache typically just use
+//! `assert_package_cache_locked` to ensure that the correct lock is being
+//! held. Higher-level code is responsible for acquiring the appropriate lock,
+//! and holding it during the duration that it is performing its operation.
+//!
+//! ## Types of locking
+//!
+//! There are three styles of locks:
+//!
+//! * [`CacheLockMode::DownloadExclusive`] -- This is an exclusive lock
+//! acquired while downloading packages and doing resolution.
+//! * [`CacheLockMode::Shared`] -- This is a shared lock acquired while a
+//! build is running. In other words, whenever cargo just needs to read from
+//! the cache, it should hold this lock. This is here to ensure that no
+//! cargos are trying to read the source caches when cache garbage
+//! collection runs.
+//! * [`CacheLockMode::MutateExclusive`] -- This is an exclusive lock acquired
+//! whenever needing to modify existing source files (for example, with
+//! cache garbage collection). This is acquired to make sure that no other
+//! cargo is reading from the cache.
+//!
+//! Importantly, a `DownloadExclusive` lock does *not* interfere with a
+//! `Shared` lock. The download process generally does not modify source files
+//! (it only adds new ones), so other cargos should be able to safely proceed
+//! in reading source files[^1].
+//!
+//! See the [`CacheLockMode`] enum docs for more details on when the different
+//! modes should be used.
+//!
+//! ## Locking implementation details
+//!
+//! This is implemented by two separate lock files, the "download" one and the
+//! "mutate" one. The `MutateExclusive` lock acquired both the "mutate" and
+//! "download" locks. The `Shared` lock acquires the "mutate" lock in share
+//! mode.
+//!
+//! An important rule is that `MutateExclusive` acquires the locks in the
+//! order "mutate" first and then the "download". That helps prevent
+//! deadlocks. It is not allowed for a cargo to first acquire a
+//! `DownloadExclusive` lock and then a `Shared` lock because that would open
+//! it up for deadlock.
+//!
+//! Another rule is that there should be only one [`CacheLocker`] per process
+//! to uphold the ordering rules. You could in theory have multiple if you
+//! could ensure that other threads would make progress and drop a lock, but
+//! cargo is not architected that way.
+//!
+//! It is safe to recursively acquire a lock as many times as you want.
+//!
+//! ## Interaction with older cargos
+//!
+//! Before version 1.74, cargo only acquired the `DownloadExclusive` lock when
+//! downloading and doing resolution. Newer cargos that acquire
+//! `MutateExclusive` should still correctly block when an old cargo is
+//! downloading (because it also acquires `DownloadExclusive`), but they do
+//! not properly coordinate when an old cargo is in the build phase (because
+//! it holds no locks). This isn't expected to be much of a problem because
+//! the intended use of mutating the cache is only to delete old contents
+//! which aren't currently being used. It is possible for there to be a
+//! conflict, particularly if the user manually deletes the entire cache, but
+//! it is not expected for this scenario to happen too often, and the only
+//! consequence is that one side or the other encounters an error and needs to
+//! retry.
+//!
+//! [^1]: A minor caveat is that downloads will delete an existing `src`
+//! directory if it was extracted via an old cargo. See
+//! [`crate::sources::registry::RegistrySource::unpack_package`]. This
+//! should probably be fixed, but is unlikely to be a problem if the user is
+//! only using versions of cargo with the same deletion logic.
+
+use super::FileLock;
+use crate::CargoResult;
+use crate::Config;
+use anyhow::Context;
+use std::cell::RefCell;
+use std::io;
+
+/// The style of lock to acquire.
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum CacheLockMode {
+ /// A `DownloadExclusive` lock ensures that only one cargo is doing
+ /// resolution and downloading new packages.
+ ///
+ /// You should use this when downloading new packages or doing resolution.
+ ///
+ /// If another cargo has a `MutateExclusive` lock, then an attempt to get
+ /// a `DownloadExclusive` lock will block.
+ ///
+ /// If another cargo has a `Shared` lock, then both can operate
+ /// concurrently.
+ DownloadExclusive,
+ /// A `Shared` lock allows multiple cargos to read from the source files.
+ ///
+ /// You should use this when cargo is reading source files from the
+ /// package cache. This is typically done during the build phase, since
+ /// cargo only needs to read files during that time. This allows multiple
+ /// cargo processes to build concurrently without interfering with one
+ /// another, while guarding against other cargos using `MutateExclusive`.
+ ///
+ /// If another cargo has a `MutateExclusive` lock, then an attempt to get
+ /// a `Shared` will block.
+ ///
+ /// If another cargo has a `DownloadExclusive` lock, then they both can
+ /// operate concurrently under the assumption that downloading does not
+ /// modify existing source files.
+ Shared,
+ /// A `MutateExclusive` lock ensures no other cargo is reading or writing
+ /// from the package caches.
+ ///
+ /// You should use this when modifying existing files in the package
+ /// cache. For example, things like garbage collection want to avoid
+ /// deleting files while other cargos are trying to read (`Shared`) or
+ /// resolve or download (`DownloadExclusive`).
+ ///
+ /// If another cargo has a `DownloadExclusive` or `Shared` lock, then this
+ /// will block until they all release their locks.
+ MutateExclusive,
+}
+
+/// Whether or not a lock attempt should block.
+#[derive(Copy, Clone)]
+enum BlockingMode {
+ Blocking,
+ NonBlocking,
+}
+
+use BlockingMode::*;
+
+/// Whether or not a lock attempt blocked or succeeded.
+#[derive(PartialEq, Copy, Clone)]
+#[must_use]
+enum LockingResult {
+ LockAcquired,
+ WouldBlock,
+}
+
+use LockingResult::*;
+
+/// A file lock, with a counter to assist with recursive locking.
+#[derive(Debug)]
+struct RecursiveLock {
+ /// The file lock.
+ ///
+ /// An important note is that locks can be `None` even when they are held.
+ /// This can happen on things like old NFS mounts where locking isn't
+ /// supported. We otherwise pretend we have a lock via the lock count. See
+ /// [`FileLock`] for more detail on that.
+ lock: Option<FileLock>,
+ /// Number locks held, to support recursive locking.
+ count: u32,
+ /// If this is `true`, it is an exclusive lock, otherwise it is shared.
+ is_exclusive: bool,
+ /// The filename of the lock.
+ filename: &'static str,
+}
+
+impl RecursiveLock {
+ fn new(filename: &'static str) -> RecursiveLock {
+ RecursiveLock {
+ lock: None,
+ count: 0,
+ is_exclusive: false,
+ filename,
+ }
+ }
+
+ /// Low-level lock count increment routine.
+ fn increment(&mut self) {
+ self.count = self.count.checked_add(1).unwrap();
+ }
+
+ /// Unlocks a previously acquired lock.
+ fn decrement(&mut self) {
+ let new_cnt = self.count.checked_sub(1).unwrap();
+ self.count = new_cnt;
+ if new_cnt == 0 {
+ // This will drop, releasing the lock.
+ self.lock = None;
+ }
+ }
+
+ /// Acquires a shared lock.
+ fn lock_shared(
+ &mut self,
+ config: &Config,
+ description: &'static str,
+ blocking: BlockingMode,
+ ) -> LockingResult {
+ match blocking {
+ Blocking => {
+ self.lock_shared_blocking(config, description);
+ LockAcquired
+ }
+ NonBlocking => self.lock_shared_nonblocking(config),
+ }
+ }
+
+ /// Acquires a shared lock, blocking if held by another locker.
+ fn lock_shared_blocking(&mut self, config: &Config, description: &'static str) {
+ if self.count == 0 {
+ self.is_exclusive = false;
+ self.lock =
+ match config
+ .home()
+ .open_ro_shared_create(self.filename, config, description)
+ {
+ Ok(lock) => Some(lock),
+ Err(e) => {
+ // There is no error here because locking is mostly a
+ // best-effort attempt. If cargo home is read-only, we don't
+ // want to fail just because we couldn't create the lock file.
+ tracing::warn!("failed to acquire cache lock {}: {e:?}", self.filename);
+ None
+ }
+ };
+ }
+ self.increment();
+ }
+
+ /// Acquires a shared lock, returns [`WouldBlock`] if held by another locker.
+ fn lock_shared_nonblocking(&mut self, config: &Config) -> LockingResult {
+ if self.count == 0 {
+ self.is_exclusive = false;
+ self.lock = match config.home().try_open_ro_shared_create(self.filename) {
+ Ok(Some(lock)) => Some(lock),
+ Ok(None) => {
+ return WouldBlock;
+ }
+ Err(e) => {
+ // Pretend that the lock was acquired (see lock_shared_blocking).
+ tracing::warn!("failed to acquire cache lock {}: {e:?}", self.filename);
+ None
+ }
+ };
+ }
+ self.increment();
+ LockAcquired
+ }
+
+ /// Acquires an exclusive lock.
+ fn lock_exclusive(
+ &mut self,
+ config: &Config,
+ description: &'static str,
+ blocking: BlockingMode,
+ ) -> CargoResult<LockingResult> {
+ if self.count > 0 && !self.is_exclusive {
+ // Lock upgrades are dicey. It might be possible to support
+ // this but would take a bit of work, and so far it isn't
+ // needed.
+ panic!("lock upgrade from shared to exclusive not supported");
+ }
+ match blocking {
+ Blocking => {
+ self.lock_exclusive_blocking(config, description)?;
+ Ok(LockAcquired)
+ }
+ NonBlocking => self.lock_exclusive_nonblocking(config),
+ }
+ }
+
+ /// Acquires an exclusive lock, blocking if held by another locker.
+ fn lock_exclusive_blocking(
+ &mut self,
+ config: &Config,
+ description: &'static str,
+ ) -> CargoResult<()> {
+ if self.count == 0 {
+ self.is_exclusive = true;
+ match config
+ .home()
+ .open_rw_exclusive_create(self.filename, config, description)
+ {
+ Ok(lock) => self.lock = Some(lock),
+ Err(e) => {
+ if maybe_readonly(&e) {
+ // This is a best-effort attempt to at least try to
+ // acquire some sort of lock. This can help in the
+ // situation where this cargo only has read-only access,
+ // but maybe some other cargo has read-write. This will at
+ // least attempt to coordinate with it.
+ //
+ // We don't want to fail on a read-only mount because
+ // cargo grabs an exclusive lock in situations where it
+ // may only be reading from the package cache. In that
+ // case, cargo isn't writing anything, and we don't want
+ // to fail on that.
+ self.lock_shared_blocking(config, description);
+ // This has to pretend it is exclusive for recursive locks to work.
+ self.is_exclusive = true;
+ return Ok(());
+ } else {
+ return Err(e).with_context(|| "failed to acquire package cache lock");
+ }
+ }
+ }
+ }
+ self.increment();
+ Ok(())
+ }
+
+ /// Acquires an exclusive lock, returns [`WouldBlock`] if held by another locker.
+ fn lock_exclusive_nonblocking(&mut self, config: &Config) -> CargoResult<LockingResult> {
+ if self.count == 0 {
+ self.is_exclusive = true;
+ match config.home().try_open_rw_exclusive_create(self.filename) {
+ Ok(Some(lock)) => self.lock = Some(lock),
+ Ok(None) => return Ok(WouldBlock),
+ Err(e) => {
+ if maybe_readonly(&e) {
+ let result = self.lock_shared_nonblocking(config);
+ // This has to pretend it is exclusive for recursive locks to work.
+ self.is_exclusive = true;
+ return Ok(result);
+ } else {
+ return Err(e).with_context(|| "failed to acquire package cache lock");
+ }
+ }
+ }
+ }
+ self.increment();
+ Ok(LockAcquired)
+ }
+}
+
+/// The state of the [`CacheLocker`].
+#[derive(Debug)]
+struct CacheState {
+ /// The cache lock guards the package cache used for download and
+ /// resolution (append operations that should not interfere with reading
+ /// from existing src files).
+ cache_lock: RecursiveLock,
+ /// The mutate lock is used to either guard the entire package cache for
+ /// destructive modifications (in exclusive mode), or for reading the
+ /// package cache src files (in shared mode).
+ ///
+ /// Note that [`CacheLockMode::MutateExclusive`] holds both
+ /// [`CacheState::mutate_lock`] and [`CacheState::cache_lock`].
+ mutate_lock: RecursiveLock,
+}
+
+impl CacheState {
+ fn lock(
+ &mut self,
+ config: &Config,
+ mode: CacheLockMode,
+ blocking: BlockingMode,
+ ) -> CargoResult<LockingResult> {
+ use CacheLockMode::*;
+ if mode == Shared && self.cache_lock.count > 0 && self.mutate_lock.count == 0 {
+ // Shared lock, when a DownloadExclusive is held.
+ //
+ // This isn't supported because it could cause a deadlock. If
+ // one cargo is attempting to acquire a MutateExclusive lock,
+ // and acquires the mutate lock, but is blocked on the
+ // download lock, and the cargo that holds the download lock
+ // attempts to get a shared lock, they would end up blocking
+ // each other.
+ panic!("shared lock while holding download lock is not allowed");
+ }
+ match mode {
+ Shared => {
+ if self.mutate_lock.lock_shared(config, SHARED_DESCR, blocking) == WouldBlock {
+ return Ok(WouldBlock);
+ }
+ }
+ DownloadExclusive => {
+ if self
+ .cache_lock
+ .lock_exclusive(config, DOWNLOAD_EXCLUSIVE_DESCR, blocking)?
+ == WouldBlock
+ {
+ return Ok(WouldBlock);
+ }
+ }
+ MutateExclusive => {
+ if self
+ .mutate_lock
+ .lock_exclusive(config, MUTATE_EXCLUSIVE_DESCR, blocking)?
+ == WouldBlock
+ {
+ return Ok(WouldBlock);
+ }
+
+ // Part of the contract of MutateExclusive is that it doesn't
+ // allow any processes to have a lock on the package cache, so
+ // this acquires both locks.
+ match self
+ .cache_lock
+ .lock_exclusive(config, DOWNLOAD_EXCLUSIVE_DESCR, blocking)
+ {
+ Ok(LockAcquired) => {}
+ Ok(WouldBlock) => return Ok(WouldBlock),
+ Err(e) => {
+ self.mutate_lock.decrement();
+ return Err(e);
+ }
+ }
+ }
+ }
+ Ok(LockAcquired)
+ }
+}
+
+/// A held lock guard.
+///
+/// When this is dropped, the lock will be released.
+#[must_use]
+pub struct CacheLock<'lock> {
+ mode: CacheLockMode,
+ locker: &'lock CacheLocker,
+}
+
+impl Drop for CacheLock<'_> {
+ fn drop(&mut self) {
+ use CacheLockMode::*;
+ let mut state = self.locker.state.borrow_mut();
+ match self.mode {
+ Shared => {
+ state.mutate_lock.decrement();
+ }
+ DownloadExclusive => {
+ state.cache_lock.decrement();
+ }
+ MutateExclusive => {
+ state.cache_lock.decrement();
+ state.mutate_lock.decrement();
+ }
+ }
+ }
+}
+
+/// The filename for the [`CacheLockMode::DownloadExclusive`] lock.
+const CACHE_LOCK_NAME: &str = ".package-cache";
+/// The filename for the [`CacheLockMode::MutateExclusive`] and
+/// [`CacheLockMode::Shared`] lock.
+const MUTATE_NAME: &str = ".package-cache-mutate";
+
+// Descriptions that are displayed in the "Blocking" message shown to the user.
+const SHARED_DESCR: &str = "shared package cache";
+const DOWNLOAD_EXCLUSIVE_DESCR: &str = "package cache";
+const MUTATE_EXCLUSIVE_DESCR: &str = "package cache mutation";
+
+/// A locker that can be used to acquire locks.
+///
+/// See the [`crate::util::cache_lock`] module documentation for an overview
+/// of how cache locking works.
+#[derive(Debug)]
+pub struct CacheLocker {
+ /// The state of the locker.
+ ///
+ /// [`CacheLocker`] uses interior mutability because it is stuffed inside
+ /// the global `Config`, which does not allow mutation.
+ state: RefCell<CacheState>,
+}
+
+impl CacheLocker {
+ /// Creates a new `CacheLocker`.
+ pub fn new() -> CacheLocker {
+ CacheLocker {
+ state: RefCell::new(CacheState {
+ cache_lock: RecursiveLock::new(CACHE_LOCK_NAME),
+ mutate_lock: RecursiveLock::new(MUTATE_NAME),
+ }),
+ }
+ }
+
+ /// Acquires a lock with the given mode, possibly blocking if another
+ /// cargo is holding the lock.
+ pub fn lock(&self, config: &Config, mode: CacheLockMode) -> CargoResult<CacheLock<'_>> {
+ let mut state = self.state.borrow_mut();
+ let _ = state.lock(config, mode, Blocking)?;
+ Ok(CacheLock { mode, locker: self })
+ }
+
+ /// Acquires a lock with the given mode, returning `None` if another cargo
+ /// is holding the lock.
+ pub fn try_lock(
+ &self,
+ config: &Config,
+ mode: CacheLockMode,
+ ) -> CargoResult<Option<CacheLock<'_>>> {
+ let mut state = self.state.borrow_mut();
+ if state.lock(config, mode, NonBlocking)? == LockAcquired {
+ Ok(Some(CacheLock { mode, locker: self }))
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Returns whether or not a lock is held for the given mode in this locker.
+ ///
+ /// This does not tell you whether or not it is locked in some other
+ /// locker (such as in another process).
+ ///
+ /// Note that `Shared` will return true if a `MutateExclusive` lock is
+ /// held, since `MutateExclusive` is just an upgraded `Shared`. Likewise,
+ /// `DownlaodExclusive` will return true if a `MutateExclusive` lock is
+ /// held since they overlap.
+ pub fn is_locked(&self, mode: CacheLockMode) -> bool {
+ let state = self.state.borrow();
+ match (
+ mode,
+ state.cache_lock.count,
+ state.mutate_lock.count,
+ state.mutate_lock.is_exclusive,
+ ) {
+ (CacheLockMode::Shared, _, 1.., _) => true,
+ (CacheLockMode::MutateExclusive, _, 1.., true) => true,
+ (CacheLockMode::DownloadExclusive, 1.., _, _) => true,
+ _ => false,
+ }
+ }
+}
+
+/// Returns whether or not the error appears to be from a read-only filesystem.
+fn maybe_readonly(err: &anyhow::Error) -> bool {
+ err.chain().any(|err| {
+ if let Some(io) = err.downcast_ref::<io::Error>() {
+ if io.kind() == io::ErrorKind::PermissionDenied {
+ return true;
+ }
+
+ #[cfg(unix)]
+ return io.raw_os_error() == Some(libc::EROFS);
+ }
+
+ false
+ })
+}
diff --git a/src/tools/cargo/src/cargo/util/command_prelude.rs b/src/tools/cargo/src/cargo/util/command_prelude.rs
index bd8889bef..3888b80c4 100644
--- a/src/tools/cargo/src/cargo/util/command_prelude.rs
+++ b/src/tools/cargo/src/cargo/util/command_prelude.rs
@@ -6,9 +6,8 @@ use crate::ops::{CompileFilter, CompileOptions, NewOptions, Packages, VersionCon
use crate::util::important_paths::find_root_manifest_for_wd;
use crate::util::interning::InternedString;
use crate::util::is_rustup;
-use crate::util::restricted_names::is_glob_pattern;
-use crate::util::toml::{StringOrVec, TomlProfile};
-use crate::util::validate_package_name;
+use crate::util::restricted_names;
+use crate::util::toml::schema::StringOrVec;
use crate::util::{
print_available_benches, print_available_binaries, print_available_examples,
print_available_packages, print_available_tests,
@@ -64,9 +63,19 @@ pub trait CommandExt: Sized {
all: &'static str,
exclude: &'static str,
) -> Self {
+ let unsupported_short_arg = {
+ let value_parser = UnknownArgumentValueParser::suggest_arg("--exclude");
+ Arg::new("unsupported-short-exclude-flag")
+ .help("")
+ .short('x')
+ .value_parser(value_parser)
+ .action(ArgAction::SetTrue)
+ .hide(true)
+ };
self.arg_package_spec_simple(package)
._arg(flag("workspace", all).help_heading(heading::PACKAGE_SELECTION))
._arg(multi_opt("exclude", "SPEC", exclude).help_heading(heading::PACKAGE_SELECTION))
+ ._arg(unsupported_short_arg)
}
fn arg_package_spec_simple(self, package: &'static str) -> Self {
@@ -232,10 +241,20 @@ pub trait CommandExt: Sized {
}
fn arg_target_triple(self, target: &'static str) -> Self {
+ let unsupported_short_arg = {
+ let value_parser = UnknownArgumentValueParser::suggest_arg("--target");
+ Arg::new("unsupported-short-target-flag")
+ .help("")
+ .short('t')
+ .value_parser(value_parser)
+ .action(ArgAction::SetTrue)
+ .hide(true)
+ };
self._arg(
optional_multi_opt("target", "TRIPLE", target)
.help_heading(heading::COMPILATION_OPTIONS),
)
+ ._arg(unsupported_short_arg)
}
fn arg_target_dir(self) -> Self {
@@ -247,6 +266,20 @@ pub trait CommandExt: Sized {
}
fn arg_manifest_path(self) -> Self {
+ // We use `--manifest-path` instead of `--path`.
+ let unsupported_path_arg = {
+ let value_parser = UnknownArgumentValueParser::suggest_arg("--manifest-path");
+ flag("unsupported-path-flag", "")
+ .long("path")
+ .value_parser(value_parser)
+ .hide(true)
+ };
+ self.arg_manifest_path_without_unsupported_path_tip()
+ ._arg(unsupported_path_arg)
+ }
+
+ // `cargo add` has a `--path` flag to install a crate from a local path.
+ fn arg_manifest_path_without_unsupported_path_tip(self) -> Self {
self._arg(
opt("manifest-path", "Path to Cargo.toml")
.value_name("PATH")
@@ -338,7 +371,7 @@ pub trait CommandExt: Sized {
.value_parser(value_parser)
.hide(true)
};
- self._arg(flag("quiet", "Do not print cargo log messages").short('q'))
+ self.arg_quiet_without_unknown_silent_arg_tip()
._arg(unsupported_silent_arg)
}
@@ -357,6 +390,27 @@ pub trait CommandExt: Sized {
.help_heading(heading::COMPILATION_OPTIONS),
)
}
+
+ fn arg_out_dir(self) -> Self {
+ let unsupported_short_arg = {
+ let value_parser = UnknownArgumentValueParser::suggest_arg("--out-dir");
+ Arg::new("unsupported-short-out-dir-flag")
+ .help("")
+ .short('O')
+ .value_parser(value_parser)
+ .action(ArgAction::SetTrue)
+ .hide(true)
+ };
+ self._arg(
+ opt(
+ "out-dir",
+ "Copy final artifacts to this directory (unstable)",
+ )
+ .value_name("PATH")
+ .help_heading(heading::COMPILATION_OPTIONS),
+ )
+ ._arg(unsupported_short_arg)
+ }
}
impl CommandExt for Command {
@@ -552,7 +606,7 @@ Run `{cmd}` to see possible targets."
bail!("profile `doc` is reserved and not allowed to be explicitly specified")
}
(_, _, Some(name)) => {
- TomlProfile::validate_name(name)?;
+ restricted_names::validate_profile_name(name)?;
name
}
};
@@ -746,7 +800,7 @@ Run `{cmd}` to see possible targets."
) -> CargoResult<CompileOptions> {
let mut compile_opts = self.compile_options(config, mode, workspace, profile_checking)?;
let spec = self._values_of("package");
- if spec.iter().any(is_glob_pattern) {
+ if spec.iter().any(restricted_names::is_glob_pattern) {
anyhow::bail!("Glob patterns on package selection are not supported.")
}
compile_opts.spec = Packages::Packages(spec);
@@ -780,7 +834,7 @@ Run `{cmd}` to see possible targets."
(None, None) => config.default_registry()?.map(RegistryOrIndex::Registry),
(None, Some(i)) => Some(RegistryOrIndex::Index(i.into_url()?)),
(Some(r), None) => {
- validate_package_name(r, "registry name", "")?;
+ restricted_names::validate_package_name(r, "registry name", "")?;
Some(RegistryOrIndex::Registry(r.to_string()))
}
(Some(_), Some(_)) => {
@@ -795,7 +849,7 @@ Run `{cmd}` to see possible targets."
match self._value_of("registry").map(|s| s.to_string()) {
None => config.default_registry(),
Some(registry) => {
- validate_package_name(&registry, "registry name", "")?;
+ restricted_names::validate_package_name(&registry, "registry name", "")?;
Ok(Some(registry))
}
}
diff --git a/src/tools/cargo/src/cargo/util/config/mod.rs b/src/tools/cargo/src/cargo/util/config/mod.rs
index b87f98afd..50153466b 100644
--- a/src/tools/cargo/src/cargo/util/config/mod.rs
+++ b/src/tools/cargo/src/cargo/util/config/mod.rs
@@ -49,6 +49,7 @@
//! translate from `ConfigValue` and environment variables to the caller's
//! desired type.
+use crate::util::cache_lock::{CacheLock, CacheLockMode, CacheLocker};
use std::borrow::Cow;
use std::cell::{RefCell, RefMut};
use std::collections::hash_map::Entry::{Occupied, Vacant};
@@ -58,7 +59,7 @@ use std::ffi::{OsStr, OsString};
use std::fmt;
use std::fs::{self, File};
use std::io::prelude::*;
-use std::io::{self, SeekFrom};
+use std::io::SeekFrom;
use std::mem;
use std::path::{Path, PathBuf};
use std::str::FromStr;
@@ -75,10 +76,9 @@ use crate::sources::CRATES_IO_REGISTRY;
use crate::util::errors::CargoResult;
use crate::util::network::http::configure_http_handle;
use crate::util::network::http::http_handle;
-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 crate::util::{Filesystem, IntoUrl, IntoUrlWithBase, Rustc};
use anyhow::{anyhow, bail, format_err, Context as _};
use cargo_credential::Secret;
use cargo_util::paths;
@@ -215,9 +215,8 @@ pub struct Config {
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)>>,
+ /// Locks on the package and index caches.
+ package_cache_lock: CacheLocker,
/// Cached configuration parsed by Cargo
http_config: LazyCell<CargoHttpConfig>,
future_incompat_config: LazyCell<CargoFutureIncompatConfig>,
@@ -307,7 +306,7 @@ impl Config {
updated_sources: LazyCell::new(),
credential_cache: LazyCell::new(),
registry_config: LazyCell::new(),
- package_cache_lock: RefCell::new(None),
+ package_cache_lock: CacheLocker::new(),
http_config: LazyCell::new(),
future_incompat_config: LazyCell::new(),
net_config: LazyCell::new(),
@@ -1032,6 +1031,9 @@ impl Config {
self.shell().set_verbosity(verbosity);
self.shell().set_color_choice(color)?;
+ if let Some(hyperlinks) = term.hyperlinks {
+ self.shell().set_hyperlinks(hyperlinks)?;
+ }
self.progress_config = term.progress.unwrap_or_default();
self.extra_verbose = extra_verbose;
self.frozen = frozen;
@@ -1195,7 +1197,7 @@ impl Config {
}
let contents = fs::read_to_string(path)
.with_context(|| format!("failed to read configuration file `{}`", path.display()))?;
- let toml = cargo_toml::parse_document(&contents, path, self).with_context(|| {
+ let toml = parse_document(&contents, path, self).with_context(|| {
format!("could not parse TOML configuration in `{}`", path.display())
})?;
let def = match why_load {
@@ -1876,10 +1878,20 @@ impl Config {
T::deserialize(d).map_err(|e| e.into())
}
- pub fn assert_package_cache_locked<'a>(&self, f: &'a Filesystem) -> &'a Path {
+ /// Obtain a [`Path`] from a [`Filesystem`], verifying that the
+ /// appropriate lock is already currently held.
+ ///
+ /// Locks are usually acquired via [`Config::acquire_package_cache_lock`]
+ /// or [`Config::try_acquire_package_cache_lock`].
+ #[track_caller]
+ pub fn assert_package_cache_locked<'a>(
+ &self,
+ mode: CacheLockMode,
+ f: &'a Filesystem,
+ ) -> &'a Path {
let ret = f.as_path_unlocked();
assert!(
- self.package_cache_lock.borrow().is_some(),
+ self.package_cache_lock.is_locked(mode),
"package cache lock is not currently held, Cargo forgot to call \
`acquire_package_cache_lock` before we got to this stack frame",
);
@@ -1887,72 +1899,26 @@ impl Config {
ret
}
- /// Acquires an exclusive lock on the global "package cache"
+ /// Acquires a lock on the global "package cache", blocking if another
+ /// cargo holds the lock.
///
- /// This lock is global per-process and can be acquired recursively. An RAII
- /// structure is returned to release the lock, and if this process
- /// abnormally terminates the lock is also released.
- pub fn acquire_package_cache_lock(&self) -> CargoResult<PackageCacheLock<'_>> {
- let mut slot = self.package_cache_lock.borrow_mut();
- match *slot {
- // We've already acquired the lock in this process, so simply bump
- // the count and continue.
- Some((_, ref mut cnt)) => {
- *cnt += 1;
- }
- None => {
- let path = ".package-cache";
- let desc = "package cache";
-
- // First, attempt to open an exclusive lock which is in general
- // the purpose of this lock!
- //
- // If that fails because of a readonly filesystem or a
- // permission error, though, then we don't really want to fail
- // just because of this. All files that this lock protects are
- // in subfolders, so they're assumed by Cargo to also be
- // readonly or have invalid permissions for us to write to. If
- // that's the case, then we don't really need to grab a lock in
- // the first place here.
- //
- // Despite this we attempt to grab a readonly lock. This means
- // that if our read-only folder is shared read-write with
- // someone else on the system we should synchronize with them,
- // but if we can't even do that then we did our best and we just
- // keep on chugging elsewhere.
- match self.home_path.open_rw(path, self, desc) {
- Ok(lock) => *slot = Some((Some(lock), 1)),
- Err(e) => {
- if maybe_readonly(&e) {
- let lock = self.home_path.open_ro(path, self, desc).ok();
- *slot = Some((lock, 1));
- return Ok(PackageCacheLock(self));
- }
-
- Err(e).with_context(|| "failed to acquire package cache lock")?;
- }
- }
- }
- }
- return Ok(PackageCacheLock(self));
-
- fn maybe_readonly(err: &anyhow::Error) -> bool {
- err.chain().any(|err| {
- if let Some(io) = err.downcast_ref::<io::Error>() {
- if io.kind() == io::ErrorKind::PermissionDenied {
- return true;
- }
-
- #[cfg(unix)]
- return io.raw_os_error() == Some(libc::EROFS);
- }
-
- false
- })
- }
+ /// See [`crate::util::cache_lock`] for an in-depth discussion of locking
+ /// and lock modes.
+ pub fn acquire_package_cache_lock(&self, mode: CacheLockMode) -> CargoResult<CacheLock<'_>> {
+ self.package_cache_lock.lock(self, mode)
}
- pub fn release_package_cache_lock(&self) {}
+ /// Acquires a lock on the global "package cache", returning `None` if
+ /// another cargo holds the lock.
+ ///
+ /// See [`crate::util::cache_lock`] for an in-depth discussion of locking
+ /// and lock modes.
+ pub fn try_acquire_package_cache_lock(
+ &self,
+ mode: CacheLockMode,
+ ) -> CargoResult<Option<CacheLock<'_>>> {
+ self.package_cache_lock.try_lock(self, mode)
+ }
}
/// Internal error for serde errors.
@@ -2271,7 +2237,7 @@ pub fn save_credentials(
let mut file = {
cfg.home_path.create_dir()?;
cfg.home_path
- .open_rw(filename, cfg, "credentials' config file")?
+ .open_rw_exclusive_create(filename, cfg, "credentials' config file")?
};
let mut contents = String::new();
@@ -2282,7 +2248,7 @@ pub fn save_credentials(
)
})?;
- let mut toml = cargo_toml::parse_document(&contents, file.path(), cfg)?;
+ let mut toml = parse_document(&contents, file.path(), cfg)?;
// Move the old token location to the new one.
if let Some(token) = toml.remove("token") {
@@ -2390,19 +2356,6 @@ pub fn save_credentials(
}
}
-pub struct PackageCacheLock<'a>(&'a Config);
-
-impl Drop for PackageCacheLock<'_> {
- fn drop(&mut self) {
- let mut slot = self.0.package_cache_lock.borrow_mut();
- let (_, cnt) = slot.as_mut().unwrap();
- *cnt -= 1;
- if *cnt == 0 {
- *slot = None;
- }
- }
-}
-
#[derive(Debug, Default, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub struct CargoHttpConfig {
@@ -2609,6 +2562,7 @@ struct TermConfig {
verbose: Option<bool>,
quiet: Option<bool>,
color: Option<String>,
+ hyperlinks: Option<bool>,
#[serde(default)]
#[serde(deserialize_with = "progress_or_string")]
progress: Option<ProgressConfig>,
@@ -2761,6 +2715,11 @@ impl EnvConfigValue {
pub type EnvConfig = HashMap<String, EnvConfigValue>;
+fn parse_document(toml: &str, _file: &Path, _config: &Config) -> CargoResult<toml::Table> {
+ // At the moment, no compatibility checks are needed.
+ toml.parse().map_err(Into::into)
+}
+
/// A type to deserialize a list of strings from a toml file.
///
/// Supports deserializing either a whitespace-separated list of arguments in a
diff --git a/src/tools/cargo/src/cargo/util/config/target.rs b/src/tools/cargo/src/cargo/util/config/target.rs
index 6d6a6beff..0e2029ffc 100644
--- a/src/tools/cargo/src/cargo/util/config/target.rs
+++ b/src/tools/cargo/src/cargo/util/config/target.rs
@@ -137,10 +137,6 @@ fn parse_links_overrides(
config: &Config,
) -> CargoResult<BTreeMap<String, BuildOutput>> {
let mut links_overrides = BTreeMap::new();
- let extra_check_cfg = match config.cli_unstable().check_cfg {
- Some((_, _, _, output)) => output,
- None => false,
- };
for (lib_name, value) in links {
// Skip these keys, it shares the namespace with `TargetConfig`.
@@ -207,12 +203,12 @@ fn parse_links_overrides(
output.cfgs.extend(list.iter().map(|v| v.0.clone()));
}
"rustc-check-cfg" => {
- if extra_check_cfg {
+ if config.cli_unstable().check_cfg {
let list = value.list(key)?;
output.check_cfgs.extend(list.iter().map(|v| v.0.clone()));
} else {
config.shell().warn(format!(
- "target config `{}.{}` requires -Zcheck-cfg=output flag",
+ "target config `{}.{}` requires -Zcheck-cfg flag",
target_key, key
))?;
}
diff --git a/src/tools/cargo/src/cargo/util/flock.rs b/src/tools/cargo/src/cargo/util/flock.rs
index aa056c965..3fb2397f7 100644
--- a/src/tools/cargo/src/cargo/util/flock.rs
+++ b/src/tools/cargo/src/cargo/util/flock.rs
@@ -1,3 +1,12 @@
+//! File-locking support.
+//!
+//! This module defines the [`Filesystem`] type which is an abstraction over a
+//! filesystem, ensuring that access to the filesystem is only done through
+//! coordinated locks.
+//!
+//! The [`FileLock`] type represents a locked file, and provides access to the
+//! file.
+
use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Seek, SeekFrom, Write};
@@ -10,18 +19,22 @@ use anyhow::Context as _;
use cargo_util::paths;
use sys::*;
+/// A locked file.
+///
+/// This provides access to file while holding a lock on the file. This type
+/// implements the [`Read`], [`Write`], and [`Seek`] traits to provide access
+/// to the underlying file.
+///
+/// Locks are either shared (multiple processes can access the file) or
+/// exclusive (only one process can access the file).
+///
+/// This type is created via methods on the [`Filesystem`] type.
+///
+/// When this value is dropped, the lock will be released.
#[derive(Debug)]
pub struct FileLock {
f: Option<File>,
path: PathBuf,
- state: State,
-}
-
-#[derive(PartialEq, Debug)]
-enum State {
- Unlocked,
- Shared,
- Exclusive,
}
impl FileLock {
@@ -35,13 +48,11 @@ impl FileLock {
/// Note that special care must be taken to ensure that the path is not
/// referenced outside the lifetime of this lock.
pub fn path(&self) -> &Path {
- assert_ne!(self.state, State::Unlocked);
&self.path
}
/// Returns the parent path containing this file
pub fn parent(&self) -> &Path {
- assert_ne!(self.state, State::Unlocked);
self.path.parent().unwrap()
}
@@ -91,9 +102,9 @@ impl Write for FileLock {
impl Drop for FileLock {
fn drop(&mut self) {
- if self.state != State::Unlocked {
- if let Some(f) = self.f.take() {
- let _ = unlock(&f);
+ if let Some(f) = self.f.take() {
+ if let Err(e) = unlock(&f) {
+ tracing::warn!("failed to release lock: {e:?}");
}
}
}
@@ -105,6 +116,32 @@ impl Drop for FileLock {
/// The `Path` of a filesystem cannot be learned unless it's done in a locked
/// fashion, and otherwise functions on this structure are prepared to handle
/// concurrent invocations across multiple instances of Cargo.
+///
+/// The methods on `Filesystem` that open files return a [`FileLock`] which
+/// holds the lock, and that type provides methods for accessing the
+/// underlying file.
+///
+/// If the blocking methods (like [`Filesystem::open_ro_shared`]) detect that
+/// they will block, then they will display a message to the user letting them
+/// know it is blocked. There are non-blocking variants starting with the
+/// `try_` prefix like [`Filesystem::try_open_ro_shared_create`].
+///
+/// The behavior of locks acquired by the `Filesystem` depend on the operating
+/// system. On unix-like system, they are advisory using [`flock`], and thus
+/// not enforced against processes which do not try to acquire the lock. On
+/// Windows, they are mandatory using [`LockFileEx`], enforced against all
+/// processes.
+///
+/// This **does not** guarantee that a lock is acquired. In some cases, for
+/// example on filesystems that don't support locking, it will return a
+/// [`FileLock`] even though the filesystem lock was not acquired. This is
+/// intended to provide a graceful fallback instead of refusing to work.
+/// Usually there aren't multiple processes accessing the same resource. In
+/// that case, it is the user's responsibility to not run concurrent
+/// processes.
+///
+/// [`flock`]: https://linux.die.net/man/2/flock
+/// [`LockFileEx`]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-lockfileex
#[derive(Clone, Debug)]
pub struct Filesystem {
root: PathBuf,
@@ -157,68 +194,119 @@ impl Filesystem {
self.root.display()
}
- /// Opens exclusive access to a file, returning the locked version of a
- /// file.
+ /// Opens read-write exclusive access to a file, returning the locked
+ /// version of a file.
///
/// This function will create a file at `path` if it doesn't already exist
/// (including intermediate directories), and then it will acquire an
/// exclusive lock on `path`. If the process must block waiting for the
- /// lock, the `msg` is printed to `config`.
+ /// lock, the `msg` is printed to [`Config`].
///
/// The returned file can be accessed to look at the path and also has
/// read/write access to the underlying file.
- pub fn open_rw<P>(&self, path: P, config: &Config, msg: &str) -> CargoResult<FileLock>
+ pub fn open_rw_exclusive_create<P>(
+ &self,
+ path: P,
+ config: &Config,
+ msg: &str,
+ ) -> CargoResult<FileLock>
where
P: AsRef<Path>,
{
- self.open(
- path.as_ref(),
- OpenOptions::new().read(true).write(true).create(true),
- State::Exclusive,
- config,
- msg,
- )
+ let mut opts = OpenOptions::new();
+ opts.read(true).write(true).create(true);
+ let (path, f) = self.open(path.as_ref(), &opts, true)?;
+ acquire(config, msg, &path, &|| try_lock_exclusive(&f), &|| {
+ lock_exclusive(&f)
+ })?;
+ Ok(FileLock { f: Some(f), path })
}
- /// Opens shared access to a file, returning the locked version of a file.
+ /// A non-blocking version of [`Filesystem::open_rw_exclusive_create`].
+ ///
+ /// Returns `None` if the operation would block due to another process
+ /// holding the lock.
+ pub fn try_open_rw_exclusive_create<P: AsRef<Path>>(
+ &self,
+ path: P,
+ ) -> CargoResult<Option<FileLock>> {
+ let mut opts = OpenOptions::new();
+ opts.read(true).write(true).create(true);
+ let (path, f) = self.open(path.as_ref(), &opts, true)?;
+ if try_acquire(&path, &|| try_lock_exclusive(&f))? {
+ Ok(Some(FileLock { f: Some(f), path }))
+ } else {
+ Ok(None)
+ }
+ }
+
+ /// Opens read-only shared access to a file, returning the locked version of a file.
///
/// This function will fail if `path` doesn't already exist, but if it does
/// then it will acquire a shared lock on `path`. If the process must block
- /// waiting for the lock, the `msg` is printed to `config`.
+ /// waiting for the lock, the `msg` is printed to [`Config`].
///
/// The returned file can be accessed to look at the path and also has read
/// access to the underlying file. Any writes to the file will return an
/// error.
- pub fn open_ro<P>(&self, path: P, config: &Config, msg: &str) -> CargoResult<FileLock>
+ pub fn open_ro_shared<P>(&self, path: P, config: &Config, msg: &str) -> CargoResult<FileLock>
where
P: AsRef<Path>,
{
- self.open(
- path.as_ref(),
- OpenOptions::new().read(true),
- State::Shared,
- config,
- msg,
- )
+ let (path, f) = self.open(path.as_ref(), &OpenOptions::new().read(true), false)?;
+ acquire(config, msg, &path, &|| try_lock_shared(&f), &|| {
+ lock_shared(&f)
+ })?;
+ Ok(FileLock { f: Some(f), path })
}
- fn open(
+ /// Opens read-only shared access to a file, returning the locked version of a file.
+ ///
+ /// Compared to [`Filesystem::open_ro_shared`], this will create the file
+ /// (and any directories in the parent) if the file does not already
+ /// exist.
+ pub fn open_ro_shared_create<P: AsRef<Path>>(
&self,
- path: &Path,
- opts: &OpenOptions,
- state: State,
+ path: P,
config: &Config,
msg: &str,
) -> CargoResult<FileLock> {
- let path = self.root.join(path);
+ let mut opts = OpenOptions::new();
+ opts.read(true).write(true).create(true);
+ let (path, f) = self.open(path.as_ref(), &opts, true)?;
+ acquire(config, msg, &path, &|| try_lock_shared(&f), &|| {
+ lock_shared(&f)
+ })?;
+ Ok(FileLock { f: Some(f), path })
+ }
- // If we want an exclusive lock then if we fail because of NotFound it's
- // likely because an intermediate directory didn't exist, so try to
- // create the directory and then continue.
+ /// A non-blocking version of [`Filesystem::open_ro_shared_create`].
+ ///
+ /// Returns `None` if the operation would block due to another process
+ /// holding the lock.
+ pub fn try_open_ro_shared_create<P: AsRef<Path>>(
+ &self,
+ path: P,
+ ) -> CargoResult<Option<FileLock>> {
+ let mut opts = OpenOptions::new();
+ opts.read(true).write(true).create(true);
+ let (path, f) = self.open(path.as_ref(), &opts, true)?;
+ if try_acquire(&path, &|| try_lock_shared(&f))? {
+ Ok(Some(FileLock { f: Some(f), path }))
+ } else {
+ Ok(None)
+ }
+ }
+
+ fn open(&self, path: &Path, opts: &OpenOptions, create: bool) -> CargoResult<(PathBuf, File)> {
+ let path = self.root.join(path);
let f = opts
.open(&path)
.or_else(|e| {
- if e.kind() == io::ErrorKind::NotFound && state == State::Exclusive {
+ // If we were requested to create this file, and there was a
+ // NotFound error, then that was likely due to missing
+ // intermediate directories. Try creating them and try again.
+ if e.kind() == io::ErrorKind::NotFound && create {
paths::create_dir_all(path.parent().unwrap())?;
Ok(opts.open(&path)?)
} else {
@@ -226,24 +314,7 @@ impl Filesystem {
}
})
.with_context(|| format!("failed to open: {}", path.display()))?;
- match state {
- State::Exclusive => {
- acquire(config, msg, &path, &|| try_lock_exclusive(&f), &|| {
- lock_exclusive(&f)
- })?;
- }
- State::Shared => {
- acquire(config, msg, &path, &|| try_lock_shared(&f), &|| {
- lock_shared(&f)
- })?;
- }
- State::Unlocked => {}
- }
- Ok(FileLock {
- f: Some(f),
- path,
- state,
- })
+ Ok((path, f))
}
}
@@ -259,28 +330,7 @@ impl PartialEq<Filesystem> for Path {
}
}
-/// Acquires a lock on a file in a "nice" manner.
-///
-/// Almost all long-running blocking actions in Cargo have a status message
-/// associated with them as we're not sure how long they'll take. Whenever a
-/// conflicted file lock happens, this is the case (we're not sure when the lock
-/// will be released).
-///
-/// This function will acquire the lock on a `path`, printing out a nice message
-/// to the console if we have to wait for it. It will first attempt to use `try`
-/// to acquire a lock on the crate, and in the case of contention it will emit a
-/// status message based on `msg` to `config`'s shell, and then use `block` to
-/// block waiting to acquire a lock.
-///
-/// Returns an error if the lock could not be acquired or if any error other
-/// than a contention error happens.
-fn acquire(
- config: &Config,
- msg: &str,
- path: &Path,
- lock_try: &dyn Fn() -> io::Result<()>,
- lock_block: &dyn Fn() -> io::Result<()>,
-) -> CargoResult<()> {
+fn try_acquire(path: &Path, lock_try: &dyn Fn() -> io::Result<()>) -> CargoResult<bool> {
// File locking on Unix is currently implemented via `flock`, which is known
// to be broken on NFS. We could in theory just ignore errors that happen on
// NFS, but apparently the failure mode [1] for `flock` on NFS is **blocking
@@ -292,16 +342,17 @@ fn acquire(
//
// [1]: https://github.com/rust-lang/cargo/issues/2615
if is_on_nfs_mount(path) {
- return Ok(());
+ tracing::debug!("{path:?} appears to be an NFS mount, not trying to lock");
+ return Ok(true);
}
match lock_try() {
- Ok(()) => return Ok(()),
+ Ok(()) => return Ok(true),
// In addition to ignoring NFS which is commonly not working we also
// just ignore locking on filesystems that look like they don't
// implement file locking.
- Err(e) if error_unsupported(&e) => return Ok(()),
+ Err(e) if error_unsupported(&e) => return Ok(true),
Err(e) => {
if !error_contended(&e) {
@@ -311,36 +362,64 @@ fn acquire(
}
}
}
+ Ok(false)
+}
+
+/// Acquires a lock on a file in a "nice" manner.
+///
+/// Almost all long-running blocking actions in Cargo have a status message
+/// associated with them as we're not sure how long they'll take. Whenever a
+/// conflicted file lock happens, this is the case (we're not sure when the lock
+/// will be released).
+///
+/// This function will acquire the lock on a `path`, printing out a nice message
+/// to the console if we have to wait for it. It will first attempt to use `try`
+/// to acquire a lock on the crate, and in the case of contention it will emit a
+/// status message based on `msg` to [`Config`]'s shell, and then use `block` to
+/// block waiting to acquire a lock.
+///
+/// Returns an error if the lock could not be acquired or if any error other
+/// than a contention error happens.
+fn acquire(
+ config: &Config,
+ msg: &str,
+ path: &Path,
+ lock_try: &dyn Fn() -> io::Result<()>,
+ lock_block: &dyn Fn() -> io::Result<()>,
+) -> CargoResult<()> {
+ if try_acquire(path, lock_try)? {
+ return Ok(());
+ }
let msg = format!("waiting for file lock on {}", msg);
config
.shell()
.status_with_color("Blocking", &msg, &style::NOTE)?;
lock_block().with_context(|| format!("failed to lock file: {}", path.display()))?;
- return Ok(());
+ Ok(())
+}
- #[cfg(all(target_os = "linux", not(target_env = "musl")))]
- fn is_on_nfs_mount(path: &Path) -> bool {
- use std::ffi::CString;
- use std::mem;
- use std::os::unix::prelude::*;
+#[cfg(all(target_os = "linux", not(target_env = "musl")))]
+fn is_on_nfs_mount(path: &Path) -> bool {
+ use std::ffi::CString;
+ use std::mem;
+ use std::os::unix::prelude::*;
- let Ok(path) = CString::new(path.as_os_str().as_bytes()) else {
- return false;
- };
+ let Ok(path) = CString::new(path.as_os_str().as_bytes()) else {
+ return false;
+ };
- unsafe {
- let mut buf: libc::statfs = mem::zeroed();
- let r = libc::statfs(path.as_ptr(), &mut buf);
+ unsafe {
+ let mut buf: libc::statfs = mem::zeroed();
+ let r = libc::statfs(path.as_ptr(), &mut buf);
- r == 0 && buf.f_type as u32 == libc::NFS_SUPER_MAGIC as u32
- }
+ r == 0 && buf.f_type as u32 == libc::NFS_SUPER_MAGIC as u32
}
+}
- #[cfg(any(not(target_os = "linux"), target_env = "musl"))]
- fn is_on_nfs_mount(_path: &Path) -> bool {
- false
- }
+#[cfg(any(not(target_os = "linux"), target_env = "musl"))]
+fn is_on_nfs_mount(_path: &Path) -> bool {
+ false
}
#[cfg(unix)]
diff --git a/src/tools/cargo/src/cargo/util/hostname.rs b/src/tools/cargo/src/cargo/util/hostname.rs
new file mode 100644
index 000000000..3f53c9cf6
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util/hostname.rs
@@ -0,0 +1,77 @@
+// Copied from https://github.com/BurntSushi/ripgrep/blob/7099e174acbcbd940f57e4ab4913fee4040c826e/crates/cli/src/hostname.rs
+
+use std::{ffi::OsString, io};
+
+/// Returns the hostname of the current system.
+///
+/// It is unusual, although technically possible, for this routine to return
+/// an error. It is difficult to list out the error conditions, but one such
+/// possibility is platform support.
+///
+/// # Platform specific behavior
+///
+/// On Unix, this returns the result of the `gethostname` function from the
+/// `libc` linked into the program.
+pub fn hostname() -> io::Result<OsString> {
+ #[cfg(unix)]
+ {
+ gethostname()
+ }
+ #[cfg(not(unix))]
+ {
+ Err(io::Error::new(
+ io::ErrorKind::Other,
+ "hostname could not be found on unsupported platform",
+ ))
+ }
+}
+
+#[cfg(unix)]
+fn gethostname() -> io::Result<OsString> {
+ use std::os::unix::ffi::OsStringExt;
+
+ // SAFETY: There don't appear to be any safety requirements for calling
+ // sysconf.
+ let limit = unsafe { libc::sysconf(libc::_SC_HOST_NAME_MAX) };
+ if limit == -1 {
+ // It is in theory possible for sysconf to return -1 for a limit but
+ // *not* set errno, in which case, io::Error::last_os_error is
+ // indeterminate. But untangling that is super annoying because std
+ // doesn't expose any unix-specific APIs for inspecting the errno. (We
+ // could do it ourselves, but it just doesn't seem worth doing?)
+ return Err(io::Error::last_os_error());
+ }
+ let Ok(maxlen) = usize::try_from(limit) else {
+ let msg = format!("host name max limit ({}) overflowed usize", limit);
+ return Err(io::Error::new(io::ErrorKind::Other, msg));
+ };
+ // maxlen here includes the NUL terminator.
+ let mut buf = vec![0; maxlen];
+ // SAFETY: The pointer we give is valid as it is derived directly from a
+ // Vec. Similarly, `maxlen` is the length of our Vec, and is thus valid
+ // to write to.
+ let rc = unsafe { libc::gethostname(buf.as_mut_ptr().cast::<libc::c_char>(), maxlen) };
+ if rc == -1 {
+ return Err(io::Error::last_os_error());
+ }
+ // POSIX says that if the hostname is bigger than `maxlen`, then it may
+ // write a truncate name back that is not necessarily NUL terminated (wtf,
+ // lol). So if we can't find a NUL terminator, then just give up.
+ let Some(zeropos) = buf.iter().position(|&b| b == 0) else {
+ let msg = "could not find NUL terminator in hostname";
+ return Err(io::Error::new(io::ErrorKind::Other, msg));
+ };
+ buf.truncate(zeropos);
+ buf.shrink_to_fit();
+ Ok(OsString::from_vec(buf))
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn print_hostname() {
+ println!("{:?}", hostname());
+ }
+}
diff --git a/src/tools/cargo/src/cargo/util/mod.rs b/src/tools/cargo/src/cargo/util/mod.rs
index b4d14f038..fb4c4b39c 100644
--- a/src/tools/cargo/src/cargo/util/mod.rs
+++ b/src/tools/cargo/src/cargo/util/mod.rs
@@ -14,6 +14,7 @@ pub use self::flock::{FileLock, Filesystem};
pub use self::graph::Graph;
pub use self::hasher::StableHasher;
pub use self::hex::{hash_u64, short_hash, to_hex};
+pub use self::hostname::hostname;
pub use self::into_url::IntoUrl;
pub use self::into_url_with_base::IntoUrlWithBase;
pub(crate) use self::io::LimitErrorReader;
@@ -22,8 +23,7 @@ pub use self::progress::{Progress, ProgressStyle};
pub use self::queue::Queue;
pub use self::restricted_names::validate_package_name;
pub use self::rustc::Rustc;
-pub use self::semver_ext::{OptVersionReq, PartialVersion, RustVersion, VersionExt, VersionReqExt};
-pub use self::to_semver::ToSemver;
+pub use self::semver_ext::{OptVersionReq, RustVersion};
pub use self::vcs::{existing_vcs_repo, FossilRepo, GitRepo, HgRepo, PijulRepo};
pub use self::workspace::{
add_path_args, path_args, print_available_benches, print_available_binaries,
@@ -31,6 +31,7 @@ pub use self::workspace::{
};
pub mod auth;
+pub mod cache_lock;
mod canonical_url;
pub mod command_prelude;
pub mod config;
@@ -45,6 +46,7 @@ mod flock;
pub mod graph;
mod hasher;
pub mod hex;
+mod hostname;
pub mod important_paths;
pub mod interning;
pub mod into_url;
@@ -61,7 +63,6 @@ pub mod restricted_names;
pub mod rustc;
mod semver_ext;
pub mod style;
-pub mod to_semver;
pub mod toml;
pub mod toml_mut;
mod vcs;
diff --git a/src/tools/cargo/src/cargo/util/restricted_names.rs b/src/tools/cargo/src/cargo/util/restricted_names.rs
index 2c3eaa9e1..f61249775 100644
--- a/src/tools/cargo/src/cargo/util/restricted_names.rs
+++ b/src/tools/cargo/src/cargo/util/restricted_names.rs
@@ -120,3 +120,82 @@ pub fn is_windows_reserved_path(path: &Path) -> bool {
pub fn is_glob_pattern<T: AsRef<str>>(name: T) -> bool {
name.as_ref().contains(&['*', '?', '[', ']'][..])
}
+
+/// Validate dir-names and profile names according to RFC 2678.
+pub fn validate_profile_name(name: &str) -> CargoResult<()> {
+ if let Some(ch) = name
+ .chars()
+ .find(|ch| !ch.is_alphanumeric() && *ch != '_' && *ch != '-')
+ {
+ bail!(
+ "invalid character `{}` in profile name `{}`\n\
+ Allowed characters are letters, numbers, underscore, and hyphen.",
+ ch,
+ name
+ );
+ }
+
+ const SEE_DOCS: &str = "See https://doc.rust-lang.org/cargo/reference/profiles.html \
+ for more on configuring profiles.";
+
+ let lower_name = name.to_lowercase();
+ if lower_name == "debug" {
+ bail!(
+ "profile name `{}` is reserved\n\
+ To configure the default development profile, use the name `dev` \
+ as in [profile.dev]\n\
+ {}",
+ name,
+ SEE_DOCS
+ );
+ }
+ if lower_name == "build-override" {
+ bail!(
+ "profile name `{}` is reserved\n\
+ To configure build dependency settings, use [profile.dev.build-override] \
+ and [profile.release.build-override]\n\
+ {}",
+ name,
+ SEE_DOCS
+ );
+ }
+
+ // These are some arbitrary reservations. We have no plans to use
+ // these, but it seems safer to reserve a few just in case we want to
+ // add more built-in profiles in the future. We can also uses special
+ // syntax like cargo:foo if needed. But it is unlikely these will ever
+ // be used.
+ if matches!(
+ lower_name.as_str(),
+ "build"
+ | "check"
+ | "clean"
+ | "config"
+ | "fetch"
+ | "fix"
+ | "install"
+ | "metadata"
+ | "package"
+ | "publish"
+ | "report"
+ | "root"
+ | "run"
+ | "rust"
+ | "rustc"
+ | "rustdoc"
+ | "target"
+ | "tmp"
+ | "uninstall"
+ ) || lower_name.starts_with("cargo")
+ {
+ bail!(
+ "profile name `{}` is reserved\n\
+ Please choose a different name.\n\
+ {}",
+ name,
+ SEE_DOCS
+ );
+ }
+
+ Ok(())
+}
diff --git a/src/tools/cargo/src/cargo/util/rustc.rs b/src/tools/cargo/src/cargo/util/rustc.rs
index d1bb3981d..f51580f29 100644
--- a/src/tools/cargo/src/cargo/util/rustc.rs
+++ b/src/tools/cargo/src/cargo/util/rustc.rs
@@ -28,6 +28,8 @@ pub struct Rustc {
pub version: semver::Version,
/// The host triple (arch-platform-OS), this comes from verbose_version.
pub host: InternedString,
+ /// The rustc full commit hash, this comes from `verbose_version`.
+ pub commit_hash: Option<String>,
cache: Mutex<Cache>,
}
@@ -80,6 +82,17 @@ impl Rustc {
verbose_version
)
})?;
+ let commit_hash = extract("commit-hash: ").ok().map(|hash| {
+ debug_assert!(
+ hash.chars().all(|ch| ch.is_ascii_hexdigit()),
+ "commit hash must be a hex string"
+ );
+ debug_assert!(
+ hash.len() == 40 || hash.len() == 64,
+ "hex string must be generated from sha1 or sha256"
+ );
+ hash.to_string()
+ });
Ok(Rustc {
path,
@@ -88,6 +101,7 @@ impl Rustc {
verbose_version,
version,
host,
+ commit_hash,
cache: Mutex::new(cache),
})
}
diff --git a/src/tools/cargo/src/cargo/util/semver_ext.rs b/src/tools/cargo/src/cargo/util/semver_ext.rs
index 5839d85d2..561cf140e 100644
--- a/src/tools/cargo/src/cargo/util/semver_ext.rs
+++ b/src/tools/cargo/src/cargo/util/semver_ext.rs
@@ -1,52 +1,37 @@
-use semver::{Comparator, Op, Version, VersionReq};
-use serde_untagged::UntaggedEnumVisitor;
use std::fmt::{self, Display};
+use semver::{Op, Version, VersionReq};
+use serde_untagged::UntaggedEnumVisitor;
+
+use crate::util_semver::PartialVersion;
+use crate::util_semver::VersionExt as _;
+
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub enum OptVersionReq {
Any,
Req(VersionReq),
/// The exact locked version and the original version requirement.
Locked(Version, VersionReq),
-}
-
-pub trait VersionExt {
- fn is_prerelease(&self) -> bool;
-}
-
-pub trait VersionReqExt {
- fn exact(version: &Version) -> Self;
-}
-
-impl VersionExt for Version {
- fn is_prerelease(&self) -> bool {
- !self.pre.is_empty()
- }
-}
-
-impl VersionReqExt for VersionReq {
- fn exact(version: &Version) -> Self {
- VersionReq {
- comparators: vec![Comparator {
- op: Op::Exact,
- major: version.major,
- minor: Some(version.minor),
- patch: Some(version.patch),
- pre: version.pre.clone(),
- }],
- }
- }
+ /// The exact requested version and the original version requirement.
+ UpdatePrecise(Version, VersionReq),
}
impl OptVersionReq {
pub fn exact(version: &Version) -> Self {
- OptVersionReq::Req(VersionReq::exact(version))
+ OptVersionReq::Req(version.to_exact_req())
+ }
+
+ // Since some registries have allowed crate versions to differ only by build metadata,
+ // A query using OptVersionReq::exact return nondeterministic results.
+ // So we `lock_to` the exact version were interested in.
+ pub fn lock_to_exact(version: &Version) -> Self {
+ OptVersionReq::Locked(version.clone(), version.to_exact_req())
}
pub fn is_exact(&self) -> bool {
match self {
OptVersionReq::Any => false,
- OptVersionReq::Req(req) => {
+ OptVersionReq::Req(req) | OptVersionReq::UpdatePrecise(_, req) => {
req.comparators.len() == 1 && {
let cmp = &req.comparators[0];
cmp.op == Op::Exact && cmp.minor.is_some() && cmp.patch.is_some()
@@ -62,8 +47,18 @@ impl OptVersionReq {
let version = version.clone();
*self = match self {
Any => Locked(version, VersionReq::STAR),
- Req(req) => Locked(version, req.clone()),
- Locked(_, req) => Locked(version, req.clone()),
+ Req(req) | Locked(_, req) | UpdatePrecise(_, req) => Locked(version, req.clone()),
+ };
+ }
+
+ pub fn update_precise(&mut self, version: &Version) {
+ use OptVersionReq::*;
+ let version = version.clone();
+ *self = match self {
+ Any => UpdatePrecise(version, VersionReq::STAR),
+ Req(req) | Locked(_, req) | UpdatePrecise(_, req) => {
+ UpdatePrecise(version, req.clone())
+ }
};
}
@@ -84,10 +79,31 @@ impl OptVersionReq {
OptVersionReq::Any => true,
OptVersionReq::Req(req) => req.matches(version),
OptVersionReq::Locked(v, _) => {
+ // Generally, cargo is of the opinion that semver metadata should be ignored.
+ // If your registry has two versions that only differing metadata you get the bugs you deserve.
+ // We also believe that lock files should ensure reproducibility
+ // and protect against mutations from the registry.
+ // In this circumstance these two goals are in conflict, and we pick reproducibility.
+ // If the lock file tells us that there is a version called `1.0.0+bar` then
+ // we should not silently use `1.0.0+foo` even though they have the same version.
+ v == version
+ }
+ OptVersionReq::UpdatePrecise(v, _) => {
+ // This is used for the `--precise` field of cargo update.
+ //
+ // Unfortunately crates.io allowed versions to differ only
+ // by build metadata. This shouldn't be allowed, but since
+ // it is, this will honor it if requested.
+ //
+ // In that context we treat a requirement that does not have
+ // build metadata as allowing any metadata. But, if a requirement
+ // has build metadata, then we only allow it to match the exact
+ // metadata.
v.major == version.major
&& v.minor == version.minor
&& v.patch == version.patch
&& v.pre == version.pre
+ && (v.build == version.build || v.build.is_empty())
}
}
}
@@ -97,8 +113,9 @@ impl Display for OptVersionReq {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
OptVersionReq::Any => f.write_str("*"),
- OptVersionReq::Req(req) => Display::fmt(req, f),
- OptVersionReq::Locked(_, req) => Display::fmt(req, f),
+ OptVersionReq::Req(req)
+ | OptVersionReq::Locked(_, req)
+ | OptVersionReq::UpdatePrecise(_, req) => Display::fmt(req, f),
}
}
}
@@ -153,207 +170,3 @@ impl Display for RustVersion {
self.0.fmt(f)
}
}
-
-#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Debug)]
-pub struct PartialVersion {
- pub major: u64,
- pub minor: Option<u64>,
- pub patch: Option<u64>,
- pub pre: Option<semver::Prerelease>,
- pub build: Option<semver::BuildMetadata>,
-}
-
-impl PartialVersion {
- pub fn version(&self) -> Option<Version> {
- Some(Version {
- major: self.major,
- minor: self.minor?,
- patch: self.patch?,
- pre: self.pre.clone().unwrap_or_default(),
- build: self.build.clone().unwrap_or_default(),
- })
- }
-
- pub fn caret_req(&self) -> VersionReq {
- VersionReq {
- comparators: vec![Comparator {
- op: semver::Op::Caret,
- major: self.major,
- minor: self.minor,
- patch: self.patch,
- pre: self.pre.as_ref().cloned().unwrap_or_default(),
- }],
- }
- }
-
- /// Check if this matches a version, including build metadata
- ///
- /// Build metadata does not affect version precedence but may be necessary for uniquely
- /// identifying a package.
- pub fn matches(&self, version: &Version) -> bool {
- if !version.pre.is_empty() && self.pre.is_none() {
- // Pre-release versions must be explicitly opted into, if for no other reason than to
- // give us room to figure out and define the semantics
- return false;
- }
- self.major == version.major
- && self.minor.map(|f| f == version.minor).unwrap_or(true)
- && self.patch.map(|f| f == version.patch).unwrap_or(true)
- && self.pre.as_ref().map(|f| f == &version.pre).unwrap_or(true)
- && self
- .build
- .as_ref()
- .map(|f| f == &version.build)
- .unwrap_or(true)
- }
-}
-
-impl From<semver::Version> for PartialVersion {
- fn from(ver: semver::Version) -> Self {
- let pre = if ver.pre.is_empty() {
- None
- } else {
- Some(ver.pre)
- };
- let build = if ver.build.is_empty() {
- None
- } else {
- Some(ver.build)
- };
- Self {
- major: ver.major,
- minor: Some(ver.minor),
- patch: Some(ver.patch),
- pre,
- build,
- }
- }
-}
-
-impl std::str::FromStr for PartialVersion {
- type Err = anyhow::Error;
-
- fn from_str(value: &str) -> Result<Self, Self::Err> {
- if is_req(value) {
- anyhow::bail!("unexpected version requirement, expected a version like \"1.32\"")
- }
- match semver::Version::parse(value) {
- Ok(ver) => Ok(ver.into()),
- Err(_) => {
- // HACK: Leverage `VersionReq` for partial version parsing
- let mut version_req = match semver::VersionReq::parse(value) {
- Ok(req) => req,
- Err(_) if value.contains('-') => {
- anyhow::bail!(
- "unexpected prerelease field, expected a version like \"1.32\""
- )
- }
- Err(_) if value.contains('+') => {
- anyhow::bail!("unexpected build field, expected a version like \"1.32\"")
- }
- Err(_) => anyhow::bail!("expected a version like \"1.32\""),
- };
- assert_eq!(version_req.comparators.len(), 1, "guaranteed by is_req");
- let comp = version_req.comparators.pop().unwrap();
- assert_eq!(comp.op, semver::Op::Caret, "guaranteed by is_req");
- let pre = if comp.pre.is_empty() {
- None
- } else {
- Some(comp.pre)
- };
- Ok(Self {
- major: comp.major,
- minor: comp.minor,
- patch: comp.patch,
- pre,
- build: None,
- })
- }
- }
- }
-}
-
-impl Display for PartialVersion {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let major = self.major;
- write!(f, "{major}")?;
- if let Some(minor) = self.minor {
- write!(f, ".{minor}")?;
- }
- if let Some(patch) = self.patch {
- write!(f, ".{patch}")?;
- }
- if let Some(pre) = self.pre.as_ref() {
- write!(f, "-{pre}")?;
- }
- if let Some(build) = self.build.as_ref() {
- write!(f, "+{build}")?;
- }
- Ok(())
- }
-}
-
-impl serde::Serialize for PartialVersion {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: serde::Serializer,
- {
- serializer.collect_str(self)
- }
-}
-
-impl<'de> serde::Deserialize<'de> for PartialVersion {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: serde::Deserializer<'de>,
- {
- UntaggedEnumVisitor::new()
- .expecting("SemVer version")
- .string(|value| value.parse().map_err(serde::de::Error::custom))
- .deserialize(deserializer)
- }
-}
-
-fn is_req(value: &str) -> bool {
- let Some(first) = value.chars().next() else {
- return false;
- };
- "<>=^~".contains(first) || value.contains('*') || value.contains(',')
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn locked_has_the_same_with_exact() {
- fn test_versions(target_ver: &str, vers: &[&str]) {
- let ver = Version::parse(target_ver).unwrap();
- let exact = OptVersionReq::exact(&ver);
- let mut locked = exact.clone();
- locked.lock_to(&ver);
- for v in vers {
- let v = Version::parse(v).unwrap();
- assert_eq!(exact.matches(&v), locked.matches(&v));
- }
- }
-
- test_versions(
- "1.0.0",
- &["1.0.0", "1.0.1", "0.9.9", "0.10.0", "0.1.0", "1.0.0-pre"],
- );
- test_versions("0.9.0", &["0.9.0", "0.9.1", "1.9.0", "0.0.9", "0.9.0-pre"]);
- test_versions("0.0.2", &["0.0.2", "0.0.1", "0.0.3", "0.0.2-pre"]);
- test_versions(
- "0.1.0-beta2.a",
- &[
- "0.1.0-beta2.a",
- "0.9.1",
- "0.1.0",
- "0.1.1-beta2.a",
- "0.1.0-beta2",
- ],
- );
- test_versions("0.1.0+meta", &["0.1.0", "0.1.0+meta", "0.1.0+any"]);
- }
-}
diff --git a/src/tools/cargo/src/cargo/util/to_semver.rs b/src/tools/cargo/src/cargo/util/to_semver.rs
deleted file mode 100644
index 3cc9e5706..000000000
--- a/src/tools/cargo/src/cargo/util/to_semver.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-use crate::util::errors::CargoResult;
-use semver::Version;
-
-pub trait ToSemver {
- fn to_semver(self) -> CargoResult<Version>;
-}
-
-impl ToSemver for Version {
- fn to_semver(self) -> CargoResult<Version> {
- Ok(self)
- }
-}
-
-impl<'a> ToSemver for &'a str {
- fn to_semver(self) -> CargoResult<Version> {
- match Version::parse(self.trim()) {
- Ok(v) => Ok(v),
- Err(..) => Err(anyhow::format_err!(
- "cannot parse '{}' as a SemVer version",
- self
- )),
- }
- }
-}
-
-impl<'a> ToSemver for &'a String {
- fn to_semver(self) -> CargoResult<Version> {
- (**self).to_semver()
- }
-}
-
-impl<'a> ToSemver for &'a Version {
- fn to_semver(self) -> CargoResult<Version> {
- Ok(self.clone())
- }
-}
diff --git a/src/tools/cargo/src/cargo/util/toml/embedded.rs b/src/tools/cargo/src/cargo/util/toml/embedded.rs
index 482268923..4c57195d4 100644
--- a/src/tools/cargo/src/cargo/util/toml/embedded.rs
+++ b/src/tools/cargo/src/cargo/util/toml/embedded.rs
@@ -6,11 +6,9 @@ use crate::Config;
const DEFAULT_EDITION: crate::core::features::Edition =
crate::core::features::Edition::LATEST_STABLE;
-const DEFAULT_VERSION: &str = "0.0.0";
-const DEFAULT_PUBLISH: bool = false;
const AUTO_FIELDS: &[&str] = &["autobins", "autoexamples", "autotests", "autobenches"];
-pub fn expand_manifest(
+pub(super) fn expand_manifest(
content: &str,
path: &std::path::Path,
config: &Config,
@@ -123,9 +121,6 @@ fn expand_manifest_(
package
.entry("name".to_owned())
.or_insert(toml::Value::String(name));
- package
- .entry("version".to_owned())
- .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 unspecified, defaulting to `{}`",
@@ -136,9 +131,6 @@ fn expand_manifest_(
package
.entry("build".to_owned())
.or_insert_with(|| toml::Value::Boolean(false));
- package
- .entry("publish".to_owned())
- .or_insert_with(|| toml::Value::Boolean(DEFAULT_PUBLISH));
for field in AUTO_FIELDS {
package
.entry(field.to_owned())
@@ -337,7 +329,7 @@ impl DocFragment {
}
#[derive(Clone, Copy, PartialEq, Debug)]
-pub enum CommentKind {
+enum CommentKind {
Line,
Block,
}
@@ -621,8 +613,6 @@ autotests = false
build = false
edition = "2021"
name = "test-"
-publish = false
-version = "0.0.0"
[profile.release]
strip = true
@@ -651,8 +641,6 @@ autotests = false
build = false
edition = "2021"
name = "test-"
-publish = false
-version = "0.0.0"
[profile.release]
strip = true
diff --git a/src/tools/cargo/src/cargo/util/toml/mod.rs b/src/tools/cargo/src/cargo/util/toml/mod.rs
index 2e730b4e9..cb841476b 100644
--- a/src/tools/cargo/src/cargo/util/toml/mod.rs
+++ b/src/tools/cargo/src/cargo/util/toml/mod.rs
@@ -1,6 +1,5 @@
use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::ffi::OsStr;
-use std::fmt::{self, Display, Write};
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::str::{self, FromStr};
@@ -10,10 +9,6 @@ use cargo_platform::Platform;
use cargo_util::paths;
use itertools::Itertools;
use lazycell::LazyCell;
-use serde::de::{self, IntoDeserializer as _, Unexpected};
-use serde::ser;
-use serde::{Deserialize, Serialize};
-use serde_untagged::UntaggedEnumVisitor;
use tracing::{debug, trace};
use url::Url;
@@ -28,12 +23,14 @@ use crate::core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, Worksp
use crate::sources::{CRATES_IO_INDEX, CRATES_IO_REGISTRY};
use crate::util::errors::{CargoResult, ManifestError};
use crate::util::interning::InternedString;
+use crate::util::restricted_names;
use crate::util::{
self, config::ConfigRelativePath, validate_package_name, Config, IntoUrl, OptVersionReq,
RustVersion,
};
-pub mod embedded;
+mod embedded;
+pub mod schema;
mod targets;
use self::targets::targets;
@@ -100,7 +97,7 @@ fn read_manifest_from_str(
let mut unused = BTreeSet::new();
let deserializer = toml::de::Deserializer::new(contents);
- let manifest: TomlManifest = serde_ignored::deserialize(deserializer, |path| {
+ let manifest: schema::TomlManifest = serde_ignored::deserialize(deserializer, |path| {
let mut key = String::new();
stringify(&mut key, &path);
unused.insert(key);
@@ -114,7 +111,6 @@ fn read_manifest_from_str(
}
};
- let manifest = Rc::new(manifest);
if let Some(deps) = manifest
.workspace
.as_ref()
@@ -130,8 +126,13 @@ fn read_manifest_from_str(
}
}
return if manifest.project.is_some() || manifest.package.is_some() {
- let (mut manifest, paths) =
- TomlManifest::to_real_manifest(&manifest, embedded, source_id, package_root, config)?;
+ let (mut manifest, paths) = schema::TomlManifest::to_real_manifest(
+ manifest,
+ embedded,
+ source_id,
+ package_root,
+ config,
+ )?;
add_unused(manifest.warnings_mut());
if manifest.targets().iter().all(|t| t.is_custom_build()) {
bail!(
@@ -143,7 +144,7 @@ fn read_manifest_from_str(
Ok((EitherManifest::Real(manifest), paths))
} else {
let (mut m, paths) =
- TomlManifest::to_virtual_manifest(&manifest, source_id, package_root, config)?;
+ schema::TomlManifest::to_virtual_manifest(manifest, source_id, package_root, config)?;
add_unused(m.warnings_mut());
Ok((EitherManifest::Virtual(m), paths))
};
@@ -174,11 +175,6 @@ fn read_manifest_from_str(
}
}
-pub fn parse_document(toml: &str, _file: &Path, _config: &Config) -> CargoResult<toml::Table> {
- // At the moment, no compatibility checks are needed.
- toml.parse().map_err(Into::into)
-}
-
/// Warn about paths that have been deprecated and may conflict.
fn warn_on_deprecated(new_path: &str, name: &str, kind: &str, warnings: &mut Vec<String>) {
let old_path = new_path.replace("-", "_");
@@ -188,1421 +184,7 @@ fn warn_on_deprecated(new_path: &str, name: &str, kind: &str, warnings: &mut Vec
))
}
-type TomlLibTarget = TomlTarget;
-type TomlBinTarget = TomlTarget;
-type TomlExampleTarget = TomlTarget;
-type TomlTestTarget = TomlTarget;
-type TomlBenchTarget = TomlTarget;
-
-#[derive(Clone, Debug, Serialize)]
-#[serde(untagged)]
-pub enum TomlDependency<P: Clone = String> {
- /// In the simple format, only a version is specified, eg.
- /// `package = "<version>"`
- Simple(String),
- /// The simple format is equivalent to a detailed dependency
- /// specifying only a version, eg.
- /// `package = { version = "<version>" }`
- Detailed(DetailedTomlDependency<P>),
-}
-
-impl<'de, P: Deserialize<'de> + Clone> de::Deserialize<'de> for TomlDependency<P> {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- UntaggedEnumVisitor::new()
- .expecting(
- "a version string like \"0.9.8\" or a \
- detailed dependency like { version = \"0.9.8\" }",
- )
- .string(|value| Ok(TomlDependency::Simple(value.to_owned())))
- .map(|value| value.deserialize().map(TomlDependency::Detailed))
- .deserialize(deserializer)
- }
-}
-
-impl TomlDependency {
- fn unused_keys(&self) -> Vec<String> {
- match self {
- TomlDependency::Simple(_) => vec![],
- TomlDependency::Detailed(detailed) => detailed.other.keys().cloned().collect(),
- }
- }
-}
-
-pub trait ResolveToPath {
- fn resolve(&self, config: &Config) -> PathBuf;
-}
-
-impl ResolveToPath for String {
- fn resolve(&self, _: &Config) -> PathBuf {
- self.into()
- }
-}
-
-impl ResolveToPath for ConfigRelativePath {
- fn resolve(&self, c: &Config) -> PathBuf {
- self.resolve_path(c)
- }
-}
-
-#[derive(Deserialize, Serialize, Clone, Debug)]
-#[serde(rename_all = "kebab-case")]
-pub struct DetailedTomlDependency<P: Clone = String> {
- version: Option<String>,
- registry: Option<String>,
- /// The URL of the `registry` field.
- /// This is an internal implementation detail. When Cargo creates a
- /// package, it replaces `registry` with `registry-index` so that the
- /// manifest contains the correct URL. All users won't have the same
- /// registry names configured, so Cargo can't rely on just the name for
- /// crates published by other users.
- registry_index: Option<String>,
- // `path` is relative to the file it appears in. If that's a `Cargo.toml`, it'll be relative to
- // that TOML file, and if it's a `.cargo/config` file, it'll be relative to that file.
- path: Option<P>,
- git: Option<String>,
- branch: Option<String>,
- tag: Option<String>,
- rev: Option<String>,
- features: Option<Vec<String>>,
- optional: Option<bool>,
- default_features: Option<bool>,
- #[serde(rename = "default_features")]
- default_features2: Option<bool>,
- package: Option<String>,
- public: Option<bool>,
-
- /// One or more of `bin`, `cdylib`, `staticlib`, `bin:<name>`.
- artifact: Option<StringOrVec>,
- /// If set, the artifact should also be a dependency
- lib: Option<bool>,
- /// A platform name, like `x86_64-apple-darwin`
- target: Option<String>,
- /// This is here to provide a way to see the "unused manifest keys" when deserializing
- #[serde(skip_serializing)]
- #[serde(flatten)]
- other: BTreeMap<String, toml::Value>,
-}
-
-// Explicit implementation so we avoid pulling in P: Default
-impl<P: Clone> Default for DetailedTomlDependency<P> {
- fn default() -> Self {
- Self {
- version: Default::default(),
- registry: Default::default(),
- registry_index: Default::default(),
- path: Default::default(),
- git: Default::default(),
- branch: Default::default(),
- tag: Default::default(),
- rev: Default::default(),
- features: Default::default(),
- optional: Default::default(),
- default_features: Default::default(),
- default_features2: Default::default(),
- package: Default::default(),
- public: Default::default(),
- artifact: Default::default(),
- lib: Default::default(),
- target: Default::default(),
- other: Default::default(),
- }
- }
-}
-
-/// This type is used to deserialize `Cargo.toml` files.
-#[derive(Debug, Deserialize, Serialize)]
-#[serde(rename_all = "kebab-case")]
-pub struct TomlManifest {
- cargo_features: Option<Vec<String>>,
- package: Option<Box<TomlPackage>>,
- project: Option<Box<TomlPackage>>,
- profile: Option<TomlProfiles>,
- lib: Option<TomlLibTarget>,
- bin: Option<Vec<TomlBinTarget>>,
- example: Option<Vec<TomlExampleTarget>>,
- test: Option<Vec<TomlTestTarget>>,
- bench: Option<Vec<TomlTestTarget>>,
- dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- dev_dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- #[serde(rename = "dev_dependencies")]
- dev_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- build_dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- #[serde(rename = "build_dependencies")]
- build_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- features: Option<BTreeMap<InternedString, Vec<InternedString>>>,
- target: Option<BTreeMap<String, TomlPlatform>>,
- replace: Option<BTreeMap<String, TomlDependency>>,
- patch: Option<BTreeMap<String, BTreeMap<String, TomlDependency>>>,
- workspace: Option<TomlWorkspace>,
- badges: Option<MaybeWorkspaceBtreeMap>,
- lints: Option<MaybeWorkspaceLints>,
-}
-
-#[derive(Deserialize, Serialize, Clone, Debug, Default)]
-pub struct TomlProfiles(BTreeMap<InternedString, TomlProfile>);
-
-impl TomlProfiles {
- pub fn get_all(&self) -> &BTreeMap<InternedString, TomlProfile> {
- &self.0
- }
-
- pub fn get(&self, name: &str) -> Option<&TomlProfile> {
- self.0.get(name)
- }
-
- /// Checks syntax validity and unstable feature gate for each profile.
- ///
- /// It's a bit unfortunate both `-Z` flags and `cargo-features` are required,
- /// because profiles can now be set in either `Cargo.toml` or `config.toml`.
- pub fn validate(
- &self,
- cli_unstable: &CliUnstable,
- features: &Features,
- warnings: &mut Vec<String>,
- ) -> CargoResult<()> {
- for (name, profile) in &self.0 {
- profile.validate(name, cli_unstable, features, warnings)?;
- }
- Ok(())
- }
-}
-
-#[derive(Clone, Debug, Eq, PartialEq)]
-pub struct TomlOptLevel(pub String);
-
-impl<'de> de::Deserialize<'de> for TomlOptLevel {
- fn deserialize<D>(d: D) -> Result<TomlOptLevel, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- use serde::de::Error as _;
- UntaggedEnumVisitor::new()
- .expecting("an optimization level")
- .i64(|value| Ok(TomlOptLevel(value.to_string())))
- .string(|value| {
- if value == "s" || value == "z" {
- Ok(TomlOptLevel(value.to_string()))
- } else {
- Err(serde_untagged::de::Error::custom(format!(
- "must be `0`, `1`, `2`, `3`, `s` or `z`, \
- but found the string: \"{}\"",
- value
- )))
- }
- })
- .deserialize(d)
- }
-}
-
-impl ser::Serialize for TomlOptLevel {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: ser::Serializer,
- {
- match self.0.parse::<u32>() {
- Ok(n) => n.serialize(serializer),
- Err(_) => self.0.serialize(serializer),
- }
- }
-}
-
-#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
-pub enum TomlDebugInfo {
- None,
- LineDirectivesOnly,
- LineTablesOnly,
- Limited,
- Full,
-}
-
-impl ser::Serialize for TomlDebugInfo {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: ser::Serializer,
- {
- match self {
- Self::None => 0.serialize(serializer),
- Self::LineDirectivesOnly => "line-directives-only".serialize(serializer),
- Self::LineTablesOnly => "line-tables-only".serialize(serializer),
- Self::Limited => 1.serialize(serializer),
- Self::Full => 2.serialize(serializer),
- }
- }
-}
-
-impl<'de> de::Deserialize<'de> for TomlDebugInfo {
- fn deserialize<D>(d: D) -> Result<TomlDebugInfo, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- use serde::de::Error as _;
- let expecting = "a boolean, 0, 1, 2, \"line-tables-only\", or \"line-directives-only\"";
- UntaggedEnumVisitor::new()
- .expecting(expecting)
- .bool(|value| {
- Ok(if value {
- TomlDebugInfo::Full
- } else {
- TomlDebugInfo::None
- })
- })
- .i64(|value| {
- let debuginfo = match value {
- 0 => TomlDebugInfo::None,
- 1 => TomlDebugInfo::Limited,
- 2 => TomlDebugInfo::Full,
- _ => {
- return Err(serde_untagged::de::Error::invalid_value(
- Unexpected::Signed(value),
- &expecting,
- ))
- }
- };
- Ok(debuginfo)
- })
- .string(|value| {
- let debuginfo = match value {
- "none" => TomlDebugInfo::None,
- "limited" => TomlDebugInfo::Limited,
- "full" => TomlDebugInfo::Full,
- "line-directives-only" => TomlDebugInfo::LineDirectivesOnly,
- "line-tables-only" => TomlDebugInfo::LineTablesOnly,
- _ => {
- return Err(serde_untagged::de::Error::invalid_value(
- Unexpected::Str(value),
- &expecting,
- ))
- }
- };
- Ok(debuginfo)
- })
- .deserialize(d)
- }
-}
-
-impl Display for TomlDebugInfo {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- TomlDebugInfo::None => f.write_char('0'),
- TomlDebugInfo::Limited => f.write_char('1'),
- TomlDebugInfo::Full => f.write_char('2'),
- TomlDebugInfo::LineDirectivesOnly => f.write_str("line-directives-only"),
- TomlDebugInfo::LineTablesOnly => f.write_str("line-tables-only"),
- }
- }
-}
-
-#[derive(Deserialize, Serialize, Clone, Debug, Default, Eq, PartialEq)]
-#[serde(default, rename_all = "kebab-case")]
-pub struct TomlProfile {
- pub opt_level: Option<TomlOptLevel>,
- pub lto: Option<StringOrBool>,
- pub codegen_backend: Option<InternedString>,
- pub codegen_units: Option<u32>,
- pub debug: Option<TomlDebugInfo>,
- pub split_debuginfo: Option<String>,
- pub debug_assertions: Option<bool>,
- pub rpath: Option<bool>,
- pub panic: Option<String>,
- pub overflow_checks: Option<bool>,
- pub incremental: Option<bool>,
- pub dir_name: Option<InternedString>,
- pub inherits: Option<InternedString>,
- pub strip: Option<StringOrBool>,
- // Note that `rustflags` is used for the cargo-feature `profile_rustflags`
- pub rustflags: Option<Vec<InternedString>>,
- // These two fields must be last because they are sub-tables, and TOML
- // requires all non-tables to be listed first.
- pub package: Option<BTreeMap<ProfilePackageSpec, TomlProfile>>,
- pub build_override: Option<Box<TomlProfile>>,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
-pub enum ProfilePackageSpec {
- Spec(PackageIdSpec),
- All,
-}
-
-impl ser::Serialize for ProfilePackageSpec {
- fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
- where
- S: ser::Serializer,
- {
- self.to_string().serialize(s)
- }
-}
-
-impl<'de> de::Deserialize<'de> for ProfilePackageSpec {
- fn deserialize<D>(d: D) -> Result<ProfilePackageSpec, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- let string = String::deserialize(d)?;
- if string == "*" {
- Ok(ProfilePackageSpec::All)
- } else {
- PackageIdSpec::parse(&string)
- .map_err(de::Error::custom)
- .map(ProfilePackageSpec::Spec)
- }
- }
-}
-
-impl fmt::Display for ProfilePackageSpec {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- ProfilePackageSpec::Spec(spec) => spec.fmt(f),
- ProfilePackageSpec::All => f.write_str("*"),
- }
- }
-}
-
-impl TomlProfile {
- /// Checks stytax validity and unstable feature gate for a given profile.
- pub fn validate(
- &self,
- name: &str,
- cli_unstable: &CliUnstable,
- features: &Features,
- warnings: &mut Vec<String>,
- ) -> CargoResult<()> {
- self.validate_profile(name, cli_unstable, features)?;
- if let Some(ref profile) = self.build_override {
- profile.validate_override("build-override")?;
- profile.validate_profile(&format!("{name}.build-override"), cli_unstable, features)?;
- }
- if let Some(ref packages) = self.package {
- for (override_name, profile) in packages {
- profile.validate_override("package")?;
- profile.validate_profile(
- &format!("{name}.package.{override_name}"),
- cli_unstable,
- features,
- )?;
- }
- }
-
- // Profile name validation
- Self::validate_name(name)?;
-
- if let Some(dir_name) = self.dir_name {
- // This is disabled for now, as we would like to stabilize named
- // profiles without this, and then decide in the future if it is
- // needed. This helps simplify the UI a little.
- bail!(
- "dir-name=\"{}\" in profile `{}` is not currently allowed, \
- directory names are tied to the profile name for custom profiles",
- dir_name,
- name
- );
- }
-
- // `inherits` validation
- if matches!(self.inherits.map(|s| s.as_str()), Some("debug")) {
- bail!(
- "profile.{}.inherits=\"debug\" should be profile.{}.inherits=\"dev\"",
- name,
- name
- );
- }
-
- match name {
- "doc" => {
- warnings.push("profile `doc` is deprecated and has no effect".to_string());
- }
- "test" | "bench" => {
- if self.panic.is_some() {
- warnings.push(format!("`panic` setting is ignored for `{}` profile", name))
- }
- }
- _ => {}
- }
-
- if let Some(panic) = &self.panic {
- if panic != "unwind" && panic != "abort" {
- bail!(
- "`panic` setting of `{}` is not a valid setting, \
- must be `unwind` or `abort`",
- panic
- );
- }
- }
-
- if let Some(StringOrBool::String(arg)) = &self.lto {
- if arg == "true" || arg == "false" {
- bail!(
- "`lto` setting of string `\"{arg}\"` for `{name}` profile is not \
- a valid setting, must be a boolean (`true`/`false`) or a string \
- (`\"thin\"`/`\"fat\"`/`\"off\"`) or omitted.",
- );
- }
- }
-
- Ok(())
- }
-
- /// Validate dir-names and profile names according to RFC 2678.
- pub fn validate_name(name: &str) -> CargoResult<()> {
- if let Some(ch) = name
- .chars()
- .find(|ch| !ch.is_alphanumeric() && *ch != '_' && *ch != '-')
- {
- bail!(
- "invalid character `{}` in profile name `{}`\n\
- Allowed characters are letters, numbers, underscore, and hyphen.",
- ch,
- name
- );
- }
-
- const SEE_DOCS: &str = "See https://doc.rust-lang.org/cargo/reference/profiles.html \
- for more on configuring profiles.";
-
- let lower_name = name.to_lowercase();
- if lower_name == "debug" {
- bail!(
- "profile name `{}` is reserved\n\
- To configure the default development profile, use the name `dev` \
- as in [profile.dev]\n\
- {}",
- name,
- SEE_DOCS
- );
- }
- if lower_name == "build-override" {
- bail!(
- "profile name `{}` is reserved\n\
- To configure build dependency settings, use [profile.dev.build-override] \
- and [profile.release.build-override]\n\
- {}",
- name,
- SEE_DOCS
- );
- }
-
- // These are some arbitrary reservations. We have no plans to use
- // these, but it seems safer to reserve a few just in case we want to
- // add more built-in profiles in the future. We can also uses special
- // syntax like cargo:foo if needed. But it is unlikely these will ever
- // be used.
- if matches!(
- lower_name.as_str(),
- "build"
- | "check"
- | "clean"
- | "config"
- | "fetch"
- | "fix"
- | "install"
- | "metadata"
- | "package"
- | "publish"
- | "report"
- | "root"
- | "run"
- | "rust"
- | "rustc"
- | "rustdoc"
- | "target"
- | "tmp"
- | "uninstall"
- ) || lower_name.starts_with("cargo")
- {
- bail!(
- "profile name `{}` is reserved\n\
- Please choose a different name.\n\
- {}",
- name,
- SEE_DOCS
- );
- }
-
- Ok(())
- }
-
- /// Validates a profile.
- ///
- /// This is a shallow check, which is reused for the profile itself and any overrides.
- fn validate_profile(
- &self,
- name: &str,
- cli_unstable: &CliUnstable,
- features: &Features,
- ) -> CargoResult<()> {
- if let Some(codegen_backend) = &self.codegen_backend {
- match (
- features.require(Feature::codegen_backend()),
- cli_unstable.codegen_backend,
- ) {
- (Err(e), false) => return Err(e),
- _ => {}
- }
-
- if codegen_backend.contains(|c: char| !c.is_ascii_alphanumeric() && c != '_') {
- bail!(
- "`profile.{}.codegen-backend` setting of `{}` is not a valid backend name.",
- name,
- codegen_backend,
- );
- }
- }
- if self.rustflags.is_some() {
- match (
- features.require(Feature::profile_rustflags()),
- cli_unstable.profile_rustflags,
- ) {
- (Err(e), false) => return Err(e),
- _ => {}
- }
- }
- Ok(())
- }
-
- /// Validation that is specific to an override.
- fn validate_override(&self, which: &str) -> CargoResult<()> {
- if self.package.is_some() {
- bail!("package-specific profiles cannot be nested");
- }
- if self.build_override.is_some() {
- bail!("build-override profiles cannot be nested");
- }
- if self.panic.is_some() {
- bail!("`panic` may not be specified in a `{}` profile", which)
- }
- if self.lto.is_some() {
- bail!("`lto` may not be specified in a `{}` profile", which)
- }
- if self.rpath.is_some() {
- bail!("`rpath` may not be specified in a `{}` profile", which)
- }
- Ok(())
- }
-
- /// Overwrite self's values with the given profile.
- pub fn merge(&mut self, profile: &TomlProfile) {
- if let Some(v) = &profile.opt_level {
- self.opt_level = Some(v.clone());
- }
-
- if let Some(v) = &profile.lto {
- self.lto = Some(v.clone());
- }
-
- if let Some(v) = profile.codegen_backend {
- self.codegen_backend = Some(v);
- }
-
- if let Some(v) = profile.codegen_units {
- self.codegen_units = Some(v);
- }
-
- if let Some(v) = profile.debug {
- self.debug = Some(v);
- }
-
- if let Some(v) = profile.debug_assertions {
- self.debug_assertions = Some(v);
- }
-
- if let Some(v) = &profile.split_debuginfo {
- self.split_debuginfo = Some(v.clone());
- }
-
- if let Some(v) = profile.rpath {
- self.rpath = Some(v);
- }
-
- if let Some(v) = &profile.panic {
- self.panic = Some(v.clone());
- }
-
- if let Some(v) = profile.overflow_checks {
- self.overflow_checks = Some(v);
- }
-
- if let Some(v) = profile.incremental {
- self.incremental = Some(v);
- }
-
- if let Some(v) = &profile.rustflags {
- self.rustflags = Some(v.clone());
- }
-
- if let Some(other_package) = &profile.package {
- match &mut self.package {
- Some(self_package) => {
- for (spec, other_pkg_profile) in other_package {
- match self_package.get_mut(spec) {
- Some(p) => p.merge(other_pkg_profile),
- None => {
- self_package.insert(spec.clone(), other_pkg_profile.clone());
- }
- }
- }
- }
- None => self.package = Some(other_package.clone()),
- }
- }
-
- if let Some(other_bo) = &profile.build_override {
- match &mut self.build_override {
- Some(self_bo) => self_bo.merge(other_bo),
- None => self.build_override = Some(other_bo.clone()),
- }
- }
-
- if let Some(v) = &profile.inherits {
- self.inherits = Some(*v);
- }
-
- if let Some(v) = &profile.dir_name {
- self.dir_name = Some(*v);
- }
-
- if let Some(v) = &profile.strip {
- self.strip = Some(v.clone());
- }
- }
-}
-
-/// A StringOrVec can be parsed from either a TOML string or array,
-/// but is always stored as a vector.
-#[derive(Clone, Debug, Serialize, Eq, PartialEq, PartialOrd, Ord)]
-pub struct StringOrVec(Vec<String>);
-
-impl<'de> de::Deserialize<'de> for StringOrVec {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- UntaggedEnumVisitor::new()
- .expecting("string or list of strings")
- .string(|value| Ok(StringOrVec(vec![value.to_owned()])))
- .seq(|value| value.deserialize().map(StringOrVec))
- .deserialize(deserializer)
- }
-}
-
-impl StringOrVec {
- pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, String> {
- self.0.iter()
- }
-}
-
-#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
-#[serde(untagged)]
-pub enum StringOrBool {
- String(String),
- Bool(bool),
-}
-
-impl<'de> Deserialize<'de> for StringOrBool {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- UntaggedEnumVisitor::new()
- .bool(|b| Ok(StringOrBool::Bool(b)))
- .string(|s| Ok(StringOrBool::String(s.to_owned())))
- .deserialize(deserializer)
- }
-}
-
-#[derive(PartialEq, Clone, Debug, Serialize)]
-#[serde(untagged)]
-pub enum VecStringOrBool {
- VecString(Vec<String>),
- Bool(bool),
-}
-
-impl<'de> de::Deserialize<'de> for VecStringOrBool {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- UntaggedEnumVisitor::new()
- .expecting("a boolean or vector of strings")
- .bool(|value| Ok(VecStringOrBool::Bool(value)))
- .seq(|value| value.deserialize().map(VecStringOrBool::VecString))
- .deserialize(deserializer)
- }
-}
-
-fn version_trim_whitespace<'de, D>(deserializer: D) -> Result<MaybeWorkspaceSemverVersion, D::Error>
-where
- D: de::Deserializer<'de>,
-{
- UntaggedEnumVisitor::new()
- .expecting("SemVer version")
- .string(
- |value| match value.trim().parse().map_err(de::Error::custom) {
- Ok(parsed) => Ok(MaybeWorkspace::Defined(parsed)),
- Err(e) => Err(e),
- },
- )
- .map(|value| value.deserialize().map(MaybeWorkspace::Workspace))
- .deserialize(deserializer)
-}
-
-/// This Trait exists to make [`MaybeWorkspace::Workspace`] generic. It makes deserialization of
-/// [`MaybeWorkspace`] much easier, as well as making error messages for
-/// [`MaybeWorkspace::resolve`] much nicer
-///
-/// Implementors should have a field `workspace` with the type of `bool`. It is used to ensure
-/// `workspace` is not `false` in a `Cargo.toml`
-pub trait WorkspaceInherit {
- /// This is the workspace table that is being inherited from.
- /// For example `[workspace.dependencies]` would be the table "dependencies"
- fn inherit_toml_table(&self) -> &str;
-
- /// This is used to output the value of the implementors `workspace` field
- fn workspace(&self) -> bool;
-}
-
-/// An enum that allows for inheriting keys from a workspace in a Cargo.toml.
-#[derive(Serialize, Copy, Clone, Debug)]
-#[serde(untagged)]
-pub enum MaybeWorkspace<T, W: WorkspaceInherit> {
- /// The "defined" type, or the type that that is used when not inheriting from a workspace.
- Defined(T),
- /// The type when inheriting from a workspace.
- Workspace(W),
-}
-
-impl<T, W: WorkspaceInherit> MaybeWorkspace<T, W> {
- fn resolve<'a>(
- self,
- label: &str,
- get_ws_inheritable: impl FnOnce() -> CargoResult<T>,
- ) -> CargoResult<T> {
- match self {
- MaybeWorkspace::Defined(value) => Ok(value),
- MaybeWorkspace::Workspace(w) => get_ws_inheritable().with_context(|| {
- format!(
- "error inheriting `{label}` from workspace root manifest's `workspace.{}.{label}`",
- w.inherit_toml_table(),
- )
- }),
- }
- }
-
- fn resolve_with_self<'a>(
- self,
- label: &str,
- get_ws_inheritable: impl FnOnce(&W) -> CargoResult<T>,
- ) -> CargoResult<T> {
- match self {
- MaybeWorkspace::Defined(value) => Ok(value),
- MaybeWorkspace::Workspace(w) => get_ws_inheritable(&w).with_context(|| {
- format!(
- "error inheriting `{label}` from workspace root manifest's `workspace.{}.{label}`",
- w.inherit_toml_table(),
- )
- }),
- }
- }
-
- fn as_defined(&self) -> Option<&T> {
- match self {
- MaybeWorkspace::Workspace(_) => None,
- MaybeWorkspace::Defined(defined) => Some(defined),
- }
- }
-}
-
-type MaybeWorkspaceDependency = MaybeWorkspace<TomlDependency, TomlWorkspaceDependency>;
-
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceDependency {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- let value = serde_value::Value::deserialize(deserializer)?;
-
- if let Ok(w) = TomlWorkspaceDependency::deserialize(serde_value::ValueDeserializer::<
- D::Error,
- >::new(value.clone()))
- {
- return if w.workspace() {
- Ok(MaybeWorkspace::Workspace(w))
- } else {
- Err(de::Error::custom("`workspace` cannot be false"))
- };
- }
- TomlDependency::deserialize(serde_value::ValueDeserializer::<D::Error>::new(value))
- .map(MaybeWorkspace::Defined)
- }
-}
-
-impl MaybeWorkspaceDependency {
- fn unused_keys(&self) -> Vec<String> {
- match self {
- MaybeWorkspaceDependency::Defined(d) => d.unused_keys(),
- MaybeWorkspaceDependency::Workspace(w) => w.other.keys().cloned().collect(),
- }
- }
-}
-
-#[derive(Deserialize, Serialize, Clone, Debug)]
-#[serde(rename_all = "kebab-case")]
-pub struct TomlWorkspaceDependency {
- workspace: bool,
- features: Option<Vec<String>>,
- default_features: Option<bool>,
- #[serde(rename = "default_features")]
- default_features2: Option<bool>,
- optional: Option<bool>,
- /// This is here to provide a way to see the "unused manifest keys" when deserializing
- #[serde(skip_serializing)]
- #[serde(flatten)]
- other: BTreeMap<String, toml::Value>,
-}
-
-impl WorkspaceInherit for TomlWorkspaceDependency {
- fn inherit_toml_table(&self) -> &str {
- "dependencies"
- }
-
- fn workspace(&self) -> bool {
- self.workspace
- }
-}
-
-impl TomlWorkspaceDependency {
- fn resolve<'a>(
- &self,
- name: &str,
- inheritable: impl FnOnce() -> CargoResult<&'a InheritableFields>,
- cx: &mut Context<'_, '_>,
- ) -> CargoResult<TomlDependency> {
- fn default_features_msg(label: &str, ws_def_feat: Option<bool>, cx: &mut Context<'_, '_>) {
- let ws_def_feat = match ws_def_feat {
- Some(true) => "true",
- Some(false) => "false",
- None => "not specified",
- };
- cx.warnings.push(format!(
- "`default-features` is ignored for {label}, since `default-features` was \
- {ws_def_feat} for `workspace.dependencies.{label}`, \
- this could become a hard error in the future"
- ))
- }
- if self.default_features.is_some() && self.default_features2.is_some() {
- warn_on_deprecated("default-features", name, "dependency", cx.warnings);
- }
- inheritable()?.get_dependency(name, cx.root).map(|d| {
- match d {
- TomlDependency::Simple(s) => {
- if let Some(false) = self.default_features.or(self.default_features2) {
- default_features_msg(name, None, cx);
- }
- if self.optional.is_some() || self.features.is_some() {
- TomlDependency::Detailed(DetailedTomlDependency {
- version: Some(s),
- optional: self.optional,
- features: self.features.clone(),
- ..Default::default()
- })
- } else {
- TomlDependency::Simple(s)
- }
- }
- TomlDependency::Detailed(d) => {
- let mut d = d.clone();
- match (
- self.default_features.or(self.default_features2),
- d.default_features.or(d.default_features2),
- ) {
- // member: default-features = true and
- // workspace: default-features = false should turn on
- // default-features
- (Some(true), Some(false)) => {
- d.default_features = Some(true);
- }
- // member: default-features = false and
- // workspace: default-features = true should ignore member
- // default-features
- (Some(false), Some(true)) => {
- default_features_msg(name, Some(true), cx);
- }
- // member: default-features = false and
- // workspace: dep = "1.0" should ignore member default-features
- (Some(false), None) => {
- default_features_msg(name, None, cx);
- }
- _ => {}
- }
- d.add_features(self.features.clone());
- d.update_optional(self.optional);
- TomlDependency::Detailed(d)
- }
- }
- })
- }
-}
-
-//. This already has a `Deserialize` impl from version_trim_whitespace
-type MaybeWorkspaceSemverVersion = MaybeWorkspace<semver::Version, TomlWorkspaceField>;
-
-type MaybeWorkspaceString = MaybeWorkspace<String, TomlWorkspaceField>;
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceString {
- fn deserialize<D>(d: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- struct Visitor;
-
- impl<'de> de::Visitor<'de> for Visitor {
- type Value = MaybeWorkspaceString;
-
- fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
- f.write_str("a string or workspace")
- }
-
- fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
- where
- E: de::Error,
- {
- Ok(MaybeWorkspaceString::Defined(value))
- }
-
- fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
- where
- V: de::MapAccess<'de>,
- {
- let mvd = de::value::MapAccessDeserializer::new(map);
- TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
- }
- }
-
- d.deserialize_any(Visitor)
- }
-}
-
-type MaybeWorkspaceRustVersion = MaybeWorkspace<RustVersion, TomlWorkspaceField>;
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceRustVersion {
- fn deserialize<D>(d: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- struct Visitor;
-
- impl<'de> de::Visitor<'de> for Visitor {
- type Value = MaybeWorkspaceRustVersion;
-
- fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
- f.write_str("a semver or workspace")
- }
-
- fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
- where
- E: de::Error,
- {
- let value = value.parse::<RustVersion>().map_err(|e| E::custom(e))?;
- Ok(MaybeWorkspaceRustVersion::Defined(value))
- }
-
- fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
- where
- V: de::MapAccess<'de>,
- {
- let mvd = de::value::MapAccessDeserializer::new(map);
- TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
- }
- }
-
- d.deserialize_any(Visitor)
- }
-}
-
-type MaybeWorkspaceVecString = MaybeWorkspace<Vec<String>, TomlWorkspaceField>;
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecString {
- fn deserialize<D>(d: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- struct Visitor;
-
- impl<'de> de::Visitor<'de> for Visitor {
- type Value = MaybeWorkspaceVecString;
-
- fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
- f.write_str("a vector of strings or workspace")
- }
- fn visit_seq<A>(self, v: A) -> Result<Self::Value, A::Error>
- where
- A: de::SeqAccess<'de>,
- {
- let seq = de::value::SeqAccessDeserializer::new(v);
- Vec::deserialize(seq).map(MaybeWorkspace::Defined)
- }
-
- fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
- where
- V: de::MapAccess<'de>,
- {
- let mvd = de::value::MapAccessDeserializer::new(map);
- TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
- }
- }
-
- d.deserialize_any(Visitor)
- }
-}
-
-type MaybeWorkspaceStringOrBool = MaybeWorkspace<StringOrBool, TomlWorkspaceField>;
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceStringOrBool {
- fn deserialize<D>(d: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- struct Visitor;
-
- impl<'de> de::Visitor<'de> for Visitor {
- type Value = MaybeWorkspaceStringOrBool;
-
- fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
- f.write_str("a string, a bool, or workspace")
- }
-
- fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
- where
- E: de::Error,
- {
- let b = de::value::BoolDeserializer::new(v);
- StringOrBool::deserialize(b).map(MaybeWorkspace::Defined)
- }
-
- fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
- where
- E: de::Error,
- {
- let string = de::value::StringDeserializer::new(v);
- StringOrBool::deserialize(string).map(MaybeWorkspace::Defined)
- }
-
- fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
- where
- V: de::MapAccess<'de>,
- {
- let mvd = de::value::MapAccessDeserializer::new(map);
- TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
- }
- }
-
- d.deserialize_any(Visitor)
- }
-}
-
-type MaybeWorkspaceVecStringOrBool = MaybeWorkspace<VecStringOrBool, TomlWorkspaceField>;
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecStringOrBool {
- fn deserialize<D>(d: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- struct Visitor;
-
- impl<'de> de::Visitor<'de> for Visitor {
- type Value = MaybeWorkspaceVecStringOrBool;
-
- fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
- f.write_str("a boolean, a vector of strings, or workspace")
- }
-
- fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
- where
- E: de::Error,
- {
- let b = de::value::BoolDeserializer::new(v);
- VecStringOrBool::deserialize(b).map(MaybeWorkspace::Defined)
- }
-
- fn visit_seq<A>(self, v: A) -> Result<Self::Value, A::Error>
- where
- A: de::SeqAccess<'de>,
- {
- let seq = de::value::SeqAccessDeserializer::new(v);
- VecStringOrBool::deserialize(seq).map(MaybeWorkspace::Defined)
- }
-
- fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
- where
- V: de::MapAccess<'de>,
- {
- let mvd = de::value::MapAccessDeserializer::new(map);
- TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
- }
- }
-
- d.deserialize_any(Visitor)
- }
-}
-
-type MaybeWorkspaceBtreeMap =
- MaybeWorkspace<BTreeMap<String, BTreeMap<String, String>>, TomlWorkspaceField>;
-
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceBtreeMap {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- let value = serde_value::Value::deserialize(deserializer)?;
-
- if let Ok(w) = TomlWorkspaceField::deserialize(
- serde_value::ValueDeserializer::<D::Error>::new(value.clone()),
- ) {
- return if w.workspace() {
- Ok(MaybeWorkspace::Workspace(w))
- } else {
- Err(de::Error::custom("`workspace` cannot be false"))
- };
- }
- BTreeMap::deserialize(serde_value::ValueDeserializer::<D::Error>::new(value))
- .map(MaybeWorkspace::Defined)
- }
-}
-
-#[derive(Deserialize, Serialize, Copy, Clone, Debug)]
-pub struct TomlWorkspaceField {
- #[serde(deserialize_with = "bool_no_false")]
- workspace: bool,
-}
-
-fn bool_no_false<'de, D: de::Deserializer<'de>>(deserializer: D) -> Result<bool, D::Error> {
- let b: bool = Deserialize::deserialize(deserializer)?;
- if b {
- Ok(b)
- } else {
- Err(de::Error::custom("`workspace` cannot be false"))
- }
-}
-
-impl WorkspaceInherit for TomlWorkspaceField {
- fn inherit_toml_table(&self) -> &str {
- "package"
- }
-
- fn workspace(&self) -> bool {
- self.workspace
- }
-}
-
-/// Represents the `package`/`project` sections of a `Cargo.toml`.
-///
-/// Note that the order of the fields matters, since this is the order they
-/// are serialized to a TOML file. For example, you cannot have values after
-/// the field `metadata`, since it is a table and values cannot appear after
-/// tables.
-#[derive(Deserialize, Serialize, Clone, Debug)]
-#[serde(rename_all = "kebab-case")]
-pub struct TomlPackage {
- edition: Option<MaybeWorkspaceString>,
- rust_version: Option<MaybeWorkspaceRustVersion>,
- name: InternedString,
- #[serde(deserialize_with = "version_trim_whitespace")]
- version: MaybeWorkspaceSemverVersion,
- authors: Option<MaybeWorkspaceVecString>,
- build: Option<StringOrBool>,
- metabuild: Option<StringOrVec>,
- default_target: Option<String>,
- forced_target: Option<String>,
- links: Option<String>,
- exclude: Option<MaybeWorkspaceVecString>,
- include: Option<MaybeWorkspaceVecString>,
- publish: Option<MaybeWorkspaceVecStringOrBool>,
- workspace: Option<String>,
- im_a_teapot: Option<bool>,
- autobins: Option<bool>,
- autoexamples: Option<bool>,
- autotests: Option<bool>,
- autobenches: Option<bool>,
- default_run: Option<String>,
-
- // Package metadata.
- description: Option<MaybeWorkspaceString>,
- homepage: Option<MaybeWorkspaceString>,
- documentation: Option<MaybeWorkspaceString>,
- readme: Option<MaybeWorkspaceStringOrBool>,
- keywords: Option<MaybeWorkspaceVecString>,
- categories: Option<MaybeWorkspaceVecString>,
- license: Option<MaybeWorkspaceString>,
- license_file: Option<MaybeWorkspaceString>,
- repository: Option<MaybeWorkspaceString>,
- resolver: Option<String>,
-
- // Provide a helpful error message for a common user error.
- #[serde(rename = "cargo-features", skip_serializing)]
- _invalid_cargo_features: Option<InvalidCargoFeatures>,
-
- // Note that this field must come last due to the way toml serialization
- // works which requires tables to be emitted after all values.
- metadata: Option<toml::Value>,
-}
-
-#[derive(Debug, Deserialize, Serialize, Clone)]
-pub struct TomlWorkspace {
- members: Option<Vec<String>>,
- #[serde(rename = "default-members")]
- default_members: Option<Vec<String>>,
- exclude: Option<Vec<String>>,
- resolver: Option<String>,
-
- // Properties that can be inherited by members.
- package: Option<InheritableFields>,
- dependencies: Option<BTreeMap<String, TomlDependency>>,
- lints: Option<TomlLints>,
-
- // Note that this field must come last due to the way toml serialization
- // works which requires tables to be emitted after all values.
- metadata: Option<toml::Value>,
-}
-
-/// A group of fields that are inheritable by members of the workspace
-#[derive(Clone, Debug, Default, Deserialize, Serialize)]
-pub struct InheritableFields {
- // We use skip here since it will never be present when deserializing
- // and we don't want it present when serializing
- #[serde(skip)]
- dependencies: Option<BTreeMap<String, TomlDependency>>,
- #[serde(skip)]
- lints: Option<TomlLints>,
-
- version: Option<semver::Version>,
- authors: Option<Vec<String>>,
- description: Option<String>,
- homepage: Option<String>,
- documentation: Option<String>,
- readme: Option<StringOrBool>,
- keywords: Option<Vec<String>>,
- categories: Option<Vec<String>>,
- license: Option<String>,
- #[serde(rename = "license-file")]
- license_file: Option<String>,
- repository: Option<String>,
- publish: Option<VecStringOrBool>,
- edition: Option<String>,
- badges: Option<BTreeMap<String, BTreeMap<String, String>>>,
- exclude: Option<Vec<String>>,
- include: Option<Vec<String>>,
- #[serde(rename = "rust-version")]
- rust_version: Option<RustVersion>,
- // We use skip here since it will never be present when deserializing
- // and we don't want it present when serializing
- #[serde(skip)]
- ws_root: PathBuf,
-}
-
-/// Defines simple getter methods for inheritable fields.
-macro_rules! inheritable_field_getter {
- ( $(($key:literal, $field:ident -> $ret:ty),)* ) => (
- $(
- #[doc = concat!("Gets the field `workspace.", $key, "`.")]
- pub fn $field(&self) -> CargoResult<$ret> {
- let Some(val) = &self.$field else {
- bail!("`workspace.{}` was not defined", $key);
- };
- Ok(val.clone())
- }
- )*
- )
-}
-
-impl InheritableFields {
- inheritable_field_getter! {
- // Please keep this list lexicographically ordered.
- ("dependencies", dependencies -> BTreeMap<String, TomlDependency>),
- ("lints", lints -> TomlLints),
- ("package.authors", authors -> Vec<String>),
- ("package.badges", badges -> BTreeMap<String, BTreeMap<String, String>>),
- ("package.categories", categories -> Vec<String>),
- ("package.description", description -> String),
- ("package.documentation", documentation -> String),
- ("package.edition", edition -> String),
- ("package.exclude", exclude -> Vec<String>),
- ("package.homepage", homepage -> String),
- ("package.include", include -> Vec<String>),
- ("package.keywords", keywords -> Vec<String>),
- ("package.license", license -> String),
- ("package.publish", publish -> VecStringOrBool),
- ("package.repository", repository -> String),
- ("package.rust-version", rust_version -> RustVersion),
- ("package.version", version -> semver::Version),
- }
-
- /// Gets a workspace dependency with the `name`.
- pub fn get_dependency(&self, name: &str, package_root: &Path) -> CargoResult<TomlDependency> {
- let Some(deps) = &self.dependencies else {
- bail!("`workspace.dependencies` was not defined");
- };
- let Some(dep) = deps.get(name) else {
- bail!("`dependency.{name}` was not found in `workspace.dependencies`");
- };
- let mut dep = dep.clone();
- if let TomlDependency::Detailed(detailed) = &mut dep {
- detailed.resolve_path(name, self.ws_root(), package_root)?;
- }
- Ok(dep)
- }
-
- /// Gets the field `workspace.package.license-file`.
- pub fn license_file(&self, package_root: &Path) -> CargoResult<String> {
- let Some(license_file) = &self.license_file else {
- bail!("`workspace.package.license-file` was not defined");
- };
- resolve_relative_path("license-file", &self.ws_root, package_root, license_file)
- }
-
- /// Gets the field `workspace.package.readme`.
- pub fn readme(&self, package_root: &Path) -> CargoResult<StringOrBool> {
- let Some(readme) = readme_for_package(self.ws_root.as_path(), self.readme.as_ref()) else {
- bail!("`workspace.package.readme` was not defined");
- };
- resolve_relative_path("readme", &self.ws_root, package_root, &readme)
- .map(StringOrBool::String)
- }
-
- pub fn ws_root(&self) -> &PathBuf {
- &self.ws_root
- }
-
- pub fn update_deps(&mut self, deps: Option<BTreeMap<String, TomlDependency>>) {
- self.dependencies = deps;
- }
-
- pub fn update_lints(&mut self, lints: Option<TomlLints>) {
- self.lints = lints;
- }
-
- pub fn update_ws_path(&mut self, ws_root: PathBuf) {
- self.ws_root = ws_root;
- }
-}
-
-impl TomlPackage {
- pub fn to_package_id(
- &self,
- source_id: SourceId,
- version: semver::Version,
- ) -> CargoResult<PackageId> {
- PackageId::new(self.name, version, source_id)
- }
-}
-
-struct Context<'a, 'b> {
- deps: &'a mut Vec<Dependency>,
- source_id: SourceId,
- nested_paths: &'a mut Vec<PathBuf>,
- config: &'b Config,
- warnings: &'a mut Vec<String>,
- platform: Option<Platform>,
- root: &'a Path,
- features: &'a Features,
-}
-
-impl TomlManifest {
+impl schema::TomlManifest {
/// Prepares the manifest for publishing.
// - Path and git components of dependency specifications are removed.
// - License path is updated to point within the package.
@@ -1610,7 +192,7 @@ impl TomlManifest {
&self,
ws: &Workspace<'_>,
package_root: &Path,
- ) -> CargoResult<TomlManifest> {
+ ) -> CargoResult<schema::TomlManifest> {
let config = ws.config();
let mut package = self
.package
@@ -1648,7 +230,7 @@ impl TomlManifest {
if abs_license_path.strip_prefix(package_root).is_err() {
// This path points outside of the package root. `cargo package`
// will copy it into the root, so adjust the path to this location.
- package.license_file = Some(MaybeWorkspace::Defined(
+ package.license_file = Some(schema::MaybeWorkspace::Defined(
license_path
.file_name()
.unwrap()
@@ -1664,27 +246,29 @@ impl TomlManifest {
.as_defined()
.context("readme should have been resolved before `prepare_for_publish()`")?;
match readme {
- StringOrBool::String(readme) => {
+ schema::StringOrBool::String(readme) => {
let readme_path = Path::new(&readme);
let abs_readme_path = paths::normalize_path(&package_root.join(readme_path));
if abs_readme_path.strip_prefix(package_root).is_err() {
// This path points outside of the package root. `cargo package`
// will copy it into the root, so adjust the path to this location.
- package.readme = Some(MaybeWorkspace::Defined(StringOrBool::String(
- readme_path
- .file_name()
- .unwrap()
- .to_str()
- .unwrap()
- .to_string(),
- )));
+ package.readme = Some(schema::MaybeWorkspace::Defined(
+ schema::StringOrBool::String(
+ readme_path
+ .file_name()
+ .unwrap()
+ .to_str()
+ .unwrap()
+ .to_string(),
+ ),
+ ));
}
}
- StringOrBool::Bool(_) => {}
+ schema::StringOrBool::Bool(_) => {}
}
}
- let all = |_d: &TomlDependency| true;
- return Ok(TomlManifest {
+ let all = |_d: &schema::TomlDependency| true;
+ return Ok(schema::TomlManifest {
package: Some(package),
project: None,
profile: self.profile.clone(),
@@ -1696,19 +280,11 @@ impl TomlManifest {
dependencies: map_deps(config, self.dependencies.as_ref(), all)?,
dev_dependencies: map_deps(
config,
- self.dev_dependencies
- .as_ref()
- .or_else(|| self.dev_dependencies2.as_ref()),
- TomlDependency::is_version_specified,
+ self.dev_dependencies(),
+ schema::TomlDependency::is_version_specified,
)?,
dev_dependencies2: None,
- build_dependencies: map_deps(
- config,
- self.build_dependencies
- .as_ref()
- .or_else(|| self.build_dependencies2.as_ref()),
- all,
- )?,
+ build_dependencies: map_deps(config, self.build_dependencies(), all)?,
build_dependencies2: None,
features: self.features.clone(),
target: match self.target.as_ref().map(|target_map| {
@@ -1717,23 +293,15 @@ impl TomlManifest {
.map(|(k, v)| {
Ok((
k.clone(),
- TomlPlatform {
+ schema::TomlPlatform {
dependencies: map_deps(config, v.dependencies.as_ref(), all)?,
dev_dependencies: map_deps(
config,
- v.dev_dependencies
- .as_ref()
- .or_else(|| v.dev_dependencies2.as_ref()),
- TomlDependency::is_version_specified,
+ v.dev_dependencies(),
+ schema::TomlDependency::is_version_specified,
)?,
dev_dependencies2: None,
- build_dependencies: map_deps(
- config,
- v.build_dependencies
- .as_ref()
- .or_else(|| v.build_dependencies2.as_ref()),
- all,
- )?,
+ build_dependencies: map_deps(config, v.build_dependencies(), all)?,
build_dependencies2: None,
},
))
@@ -1754,14 +322,14 @@ impl TomlManifest {
fn map_deps(
config: &Config,
- deps: Option<&BTreeMap<String, MaybeWorkspaceDependency>>,
- filter: impl Fn(&TomlDependency) -> bool,
- ) -> CargoResult<Option<BTreeMap<String, MaybeWorkspaceDependency>>> {
+ deps: Option<&BTreeMap<String, schema::MaybeWorkspaceDependency>>,
+ filter: impl Fn(&schema::TomlDependency) -> bool,
+ ) -> CargoResult<Option<BTreeMap<String, schema::MaybeWorkspaceDependency>>> {
let Some(deps) = deps else { return Ok(None) };
let deps = deps
.iter()
.filter(|(_k, v)| {
- if let MaybeWorkspace::Defined(def) = v {
+ if let schema::MaybeWorkspace::Defined(def) = v {
filter(def)
} else {
false
@@ -1774,10 +342,10 @@ impl TomlManifest {
fn map_dependency(
config: &Config,
- dep: &MaybeWorkspaceDependency,
- ) -> CargoResult<MaybeWorkspaceDependency> {
+ dep: &schema::MaybeWorkspaceDependency,
+ ) -> CargoResult<schema::MaybeWorkspaceDependency> {
let dep = match dep {
- MaybeWorkspace::Defined(TomlDependency::Detailed(d)) => {
+ schema::MaybeWorkspace::Defined(schema::TomlDependency::Detailed(d)) => {
let mut d = d.clone();
// Path dependencies become crates.io deps.
d.path.take();
@@ -1792,19 +360,21 @@ impl TomlManifest {
}
Ok(d)
}
- MaybeWorkspace::Defined(TomlDependency::Simple(s)) => Ok(DetailedTomlDependency {
- version: Some(s.clone()),
- ..Default::default()
- }),
+ schema::MaybeWorkspace::Defined(schema::TomlDependency::Simple(s)) => {
+ Ok(schema::DetailedTomlDependency {
+ version: Some(s.clone()),
+ ..Default::default()
+ })
+ }
_ => unreachable!(),
};
- dep.map(TomlDependency::Detailed)
- .map(MaybeWorkspace::Defined)
+ dep.map(schema::TomlDependency::Detailed)
+ .map(schema::MaybeWorkspace::Defined)
}
}
pub fn to_real_manifest(
- me: &Rc<TomlManifest>,
+ me: schema::TomlManifest,
embedded: bool,
source_id: SourceId,
package_root: &Path,
@@ -1814,7 +384,7 @@ impl TomlManifest {
config: &Config,
resolved_path: &Path,
workspace_config: &WorkspaceConfig,
- ) -> CargoResult<InheritableFields> {
+ ) -> CargoResult<schema::InheritableFields> {
match workspace_config {
WorkspaceConfig::Root(root) => Ok(root.inheritable().clone()),
WorkspaceConfig::Member {
@@ -1928,25 +498,31 @@ impl TomlManifest {
let resolved_path = package_root.join("Cargo.toml");
- let inherit_cell: LazyCell<InheritableFields> = LazyCell::new();
+ let inherit_cell: LazyCell<schema::InheritableFields> = LazyCell::new();
let inherit =
|| inherit_cell.try_borrow_with(|| get_ws(config, &resolved_path, &workspace_config));
let version = package
.version
.clone()
- .resolve("version", || inherit()?.version())?;
+ .map(|version| version.resolve("version", || inherit()?.version()))
+ .transpose()?;
- package.version = MaybeWorkspace::Defined(version.clone());
+ package.version = version.clone().map(schema::MaybeWorkspace::Defined);
- let pkgid = package.to_package_id(source_id, version)?;
+ let pkgid = package.to_package_id(
+ source_id,
+ version
+ .clone()
+ .unwrap_or_else(|| semver::Version::new(0, 0, 0)),
+ );
let edition = if let Some(edition) = package.edition.clone() {
let edition: Edition = edition
.resolve("edition", || inherit()?.edition())?
.parse()
.with_context(|| "failed to parse the `edition` key")?;
- package.edition = Some(MaybeWorkspace::Defined(edition.to_string()));
+ package.edition = Some(schema::MaybeWorkspace::Defined(edition.to_string()));
edition
} else {
Edition::Edition2015
@@ -1954,10 +530,12 @@ impl TomlManifest {
// Add these lines if start a new unstable edition.
// ```
// if edition == Edition::Edition20xx {
- // features.require(Feature::edition20xx))?;
+ // features.require(Feature::edition20xx())?;
// }
// ```
- if !edition.is_stable() {
+ if edition == Edition::Edition2024 {
+ features.require(Feature::edition2024())?;
+ } else if !edition.is_stable() {
// Guard in case someone forgets to add .require()
return Err(util::errors::internal(format!(
"edition {} should be gated",
@@ -1969,7 +547,7 @@ impl TomlManifest {
let rust_version = rust_version
.clone()
.resolve("rust_version", || inherit()?.rust_version())?;
- let req = rust_version.caret_req();
+ let req = rust_version.to_caret_req();
if let Some(first_version) = edition.first_version() {
let unsupported =
semver::Version::new(first_version.major, first_version.minor - 1, 9999);
@@ -2008,7 +586,7 @@ impl TomlManifest {
// If we have a lib with no path, use the inferred lib or else the package name.
let targets = targets(
&features,
- me,
+ &me,
package_name,
package_root,
edition,
@@ -2068,11 +646,11 @@ impl TomlManifest {
fn process_dependencies(
cx: &mut Context<'_, '_>,
- new_deps: Option<&BTreeMap<String, MaybeWorkspaceDependency>>,
+ new_deps: Option<&BTreeMap<String, schema::MaybeWorkspaceDependency>>,
kind: Option<DepKind>,
workspace_config: &WorkspaceConfig,
- inherit_cell: &LazyCell<InheritableFields>,
- ) -> CargoResult<Option<BTreeMap<String, MaybeWorkspaceDependency>>> {
+ inherit_cell: &LazyCell<schema::InheritableFields>,
+ ) -> CargoResult<Option<BTreeMap<String, schema::MaybeWorkspaceDependency>>> {
let Some(dependencies) = new_deps else {
return Ok(None);
};
@@ -2083,7 +661,7 @@ impl TomlManifest {
})
};
- let mut deps: BTreeMap<String, MaybeWorkspaceDependency> = BTreeMap::new();
+ let mut deps: BTreeMap<String, schema::MaybeWorkspaceDependency> = BTreeMap::new();
for (n, v) in dependencies.iter() {
let resolved = v
.clone()
@@ -2102,7 +680,10 @@ impl TomlManifest {
};
unused_dep_keys(name_in_toml, &table_in_toml, v.unused_keys(), cx.warnings);
cx.deps.push(dep);
- deps.insert(n.to_string(), MaybeWorkspace::Defined(resolved.clone()));
+ deps.insert(
+ n.to_string(),
+ schema::MaybeWorkspace::Defined(resolved.clone()),
+ );
}
Ok(Some(deps))
}
@@ -2118,10 +699,7 @@ impl TomlManifest {
if me.dev_dependencies.is_some() && me.dev_dependencies2.is_some() {
warn_on_deprecated("dev-dependencies", package_name, "package", cx.warnings);
}
- let dev_deps = me
- .dev_dependencies
- .as_ref()
- .or_else(|| me.dev_dependencies2.as_ref());
+ let dev_deps = me.dev_dependencies();
let dev_deps = process_dependencies(
&mut cx,
dev_deps,
@@ -2132,10 +710,7 @@ impl TomlManifest {
if me.build_dependencies.is_some() && me.build_dependencies2.is_some() {
warn_on_deprecated("build-dependencies", package_name, "package", cx.warnings);
}
- let build_deps = me
- .build_dependencies
- .as_ref()
- .or_else(|| me.build_dependencies2.as_ref());
+ let build_deps = me.build_dependencies();
let build_deps = process_dependencies(
&mut cx,
build_deps,
@@ -2150,10 +725,10 @@ impl TomlManifest {
.map(|mw| mw.resolve(|| inherit()?.lints()))
.transpose()?;
let lints = verify_lints(lints)?;
- let default = TomlLints::default();
+ let default = schema::TomlLints::default();
let rustflags = lints_to_rustflags(lints.as_ref().unwrap_or(&default));
- let mut target: BTreeMap<String, TomlPlatform> = BTreeMap::new();
+ let mut target: BTreeMap<String, schema::TomlPlatform> = BTreeMap::new();
for (name, platform) in me.target.iter().flatten() {
cx.platform = {
let platform: Platform = name.parse()?;
@@ -2170,10 +745,7 @@ impl TomlManifest {
if platform.build_dependencies.is_some() && platform.build_dependencies2.is_some() {
warn_on_deprecated("build-dependencies", name, "platform target", cx.warnings);
}
- let build_deps = platform
- .build_dependencies
- .as_ref()
- .or_else(|| platform.build_dependencies2.as_ref());
+ let build_deps = platform.build_dependencies();
let build_deps = process_dependencies(
&mut cx,
build_deps,
@@ -2184,10 +756,7 @@ impl TomlManifest {
if platform.dev_dependencies.is_some() && platform.dev_dependencies2.is_some() {
warn_on_deprecated("dev-dependencies", name, "platform target", cx.warnings);
}
- let dev_deps = platform
- .dev_dependencies
- .as_ref()
- .or_else(|| platform.dev_dependencies2.as_ref());
+ let dev_deps = platform.dev_dependencies();
let dev_deps = process_dependencies(
&mut cx,
dev_deps,
@@ -2197,7 +766,7 @@ impl TomlManifest {
)?;
target.insert(
name.clone(),
- TomlPlatform {
+ schema::TomlPlatform {
dependencies: deps,
build_dependencies: build_deps,
build_dependencies2: None,
@@ -2248,7 +817,17 @@ impl TomlManifest {
let summary = Summary::new(
pkgid,
deps,
- me.features.as_ref().unwrap_or(&empty_features),
+ &me.features
+ .as_ref()
+ .unwrap_or(&empty_features)
+ .iter()
+ .map(|(k, v)| {
+ (
+ InternedString::new(k),
+ v.iter().map(InternedString::from).collect(),
+ )
+ })
+ .collect(),
package.links.as_deref(),
rust_version.clone(),
)?;
@@ -2326,52 +905,54 @@ impl TomlManifest {
package.description = metadata
.description
.clone()
- .map(|description| MaybeWorkspace::Defined(description));
+ .map(|description| schema::MaybeWorkspace::Defined(description));
package.homepage = metadata
.homepage
.clone()
- .map(|homepage| MaybeWorkspace::Defined(homepage));
+ .map(|homepage| schema::MaybeWorkspace::Defined(homepage));
package.documentation = metadata
.documentation
.clone()
- .map(|documentation| MaybeWorkspace::Defined(documentation));
+ .map(|documentation| schema::MaybeWorkspace::Defined(documentation));
package.readme = metadata
.readme
.clone()
- .map(|readme| MaybeWorkspace::Defined(StringOrBool::String(readme)));
+ .map(|readme| schema::MaybeWorkspace::Defined(schema::StringOrBool::String(readme)));
package.authors = package
.authors
.as_ref()
- .map(|_| MaybeWorkspace::Defined(metadata.authors.clone()));
+ .map(|_| schema::MaybeWorkspace::Defined(metadata.authors.clone()));
package.license = metadata
.license
.clone()
- .map(|license| MaybeWorkspace::Defined(license));
+ .map(|license| schema::MaybeWorkspace::Defined(license));
package.license_file = metadata
.license_file
.clone()
- .map(|license_file| MaybeWorkspace::Defined(license_file));
+ .map(|license_file| schema::MaybeWorkspace::Defined(license_file));
package.repository = metadata
.repository
.clone()
- .map(|repository| MaybeWorkspace::Defined(repository));
+ .map(|repository| schema::MaybeWorkspace::Defined(repository));
package.keywords = package
.keywords
.as_ref()
- .map(|_| MaybeWorkspace::Defined(metadata.keywords.clone()));
+ .map(|_| schema::MaybeWorkspace::Defined(metadata.keywords.clone()));
package.categories = package
.categories
.as_ref()
- .map(|_| MaybeWorkspace::Defined(metadata.categories.clone()));
- package.rust_version = rust_version.clone().map(|rv| MaybeWorkspace::Defined(rv));
+ .map(|_| schema::MaybeWorkspace::Defined(metadata.categories.clone()));
+ package.rust_version = rust_version
+ .clone()
+ .map(|rv| schema::MaybeWorkspace::Defined(rv));
package.exclude = package
.exclude
.as_ref()
- .map(|_| MaybeWorkspace::Defined(exclude.clone()));
+ .map(|_| schema::MaybeWorkspace::Defined(exclude.clone()));
package.include = package
.include
.as_ref()
- .map(|_| MaybeWorkspace::Defined(include.clone()));
+ .map(|_| schema::MaybeWorkspace::Defined(include.clone()));
let profiles = me.profile.clone();
if let Some(profiles) = &profiles {
@@ -2384,14 +965,19 @@ impl TomlManifest {
.clone()
.map(|publish| publish.resolve("publish", || inherit()?.publish()).unwrap());
- package.publish = publish.clone().map(|p| MaybeWorkspace::Defined(p));
+ package.publish = publish.clone().map(|p| schema::MaybeWorkspace::Defined(p));
let publish = match publish {
- Some(VecStringOrBool::VecString(ref vecstring)) => Some(vecstring.clone()),
- Some(VecStringOrBool::Bool(false)) => Some(vec![]),
- None | Some(VecStringOrBool::Bool(true)) => None,
+ Some(schema::VecStringOrBool::VecString(ref vecstring)) => Some(vecstring.clone()),
+ Some(schema::VecStringOrBool::Bool(false)) => Some(vec![]),
+ Some(schema::VecStringOrBool::Bool(true)) => None,
+ None => version.is_none().then_some(vec![]),
};
+ if version.is_none() && publish != Some(vec![]) {
+ bail!("`package.publish` requires `package.version` be specified");
+ }
+
if summary.features().contains_key("default-features") {
warnings.push(
"`default-features = [\"..\"]` was found in [features]. \
@@ -2425,7 +1011,7 @@ impl TomlManifest {
.transpose()?
.map(CompileKind::Target);
let custom_metadata = package.metadata.clone();
- let resolved_toml = TomlManifest {
+ let resolved_toml = schema::TomlManifest {
cargo_features: me.cargo_features.clone(),
package: Some(package.clone()),
project: None,
@@ -2448,8 +1034,8 @@ impl TomlManifest {
badges: me
.badges
.as_ref()
- .map(|_| MaybeWorkspace::Defined(metadata.badges.clone())),
- lints: lints.map(|lints| MaybeWorkspaceLints {
+ .map(|_| schema::MaybeWorkspace::Defined(metadata.badges.clone())),
+ lints: lints.map(|lints| schema::MaybeWorkspaceLints {
workspace: false,
lints,
}),
@@ -2504,7 +1090,7 @@ impl TomlManifest {
}
fn to_virtual_manifest(
- me: &Rc<TomlManifest>,
+ me: schema::TomlManifest,
source_id: SourceId,
root: &Path,
config: &Config,
@@ -2533,10 +1119,10 @@ impl TomlManifest {
if me.dependencies.is_some() {
bail!("this virtual manifest specifies a [dependencies] section, which is not allowed");
}
- if me.dev_dependencies.is_some() || me.dev_dependencies2.is_some() {
+ if me.dev_dependencies().is_some() {
bail!("this virtual manifest specifies a [dev-dependencies] section, which is not allowed");
}
- if me.build_dependencies.is_some() || me.build_dependencies2.is_some() {
+ if me.build_dependencies().is_some() {
bail!("this virtual manifest specifies a [build-dependencies] section, which is not allowed");
}
if me.features.is_some() {
@@ -2643,7 +1229,7 @@ impl TomlManifest {
);
}
- let mut dep = replacement.to_dependency(spec.name().as_str(), cx, None)?;
+ let mut dep = replacement.to_dependency(spec.name(), cx, None)?;
let version = spec.version().ok_or_else(|| {
anyhow!(
"replacements must specify a version \
@@ -2657,8 +1243,7 @@ impl TomlManifest {
replacement.unused_keys(),
&mut cx.warnings,
);
- dep.set_version_req(OptVersionReq::exact(&version))
- .lock_version(&version);
+ dep.set_version_req(OptVersionReq::exact(&version));
replace.push((spec, dep));
}
Ok(replace)
@@ -2697,41 +1282,20 @@ impl TomlManifest {
}
Ok(patch)
}
+}
- /// Returns the path to the build script if one exists for this crate.
- fn maybe_custom_build(
- &self,
- build: &Option<StringOrBool>,
- package_root: &Path,
- ) -> Option<PathBuf> {
- let build_rs = package_root.join("build.rs");
- match *build {
- // Explicitly no build script.
- Some(StringOrBool::Bool(false)) => None,
- Some(StringOrBool::Bool(true)) => Some(build_rs),
- Some(StringOrBool::String(ref s)) => Some(PathBuf::from(s)),
- None => {
- // If there is a `build.rs` file next to the `Cargo.toml`, assume it is
- // a build script.
- if build_rs.is_file() {
- Some(build_rs)
- } else {
- None
- }
- }
- }
- }
-
- pub fn has_profiles(&self) -> bool {
- self.profile.is_some()
- }
-
- pub fn features(&self) -> Option<&BTreeMap<InternedString, Vec<InternedString>>> {
- self.features.as_ref()
- }
+struct Context<'a, 'b> {
+ deps: &'a mut Vec<Dependency>,
+ source_id: SourceId,
+ nested_paths: &'a mut Vec<PathBuf>,
+ config: &'b Config,
+ warnings: &'a mut Vec<String>,
+ platform: Option<Platform>,
+ root: &'a Path,
+ features: &'a Features,
}
-fn verify_lints(lints: Option<TomlLints>) -> CargoResult<Option<TomlLints>> {
+fn verify_lints(lints: Option<schema::TomlLints>) -> CargoResult<Option<schema::TomlLints>> {
let Some(lints) = lints else {
return Ok(None);
};
@@ -2762,7 +1326,7 @@ fn verify_lints(lints: Option<TomlLints>) -> CargoResult<Option<TomlLints>> {
Ok(Some(lints))
}
-fn lints_to_rustflags(lints: &TomlLints) -> Vec<String> {
+fn lints_to_rustflags(lints: &schema::TomlLints) -> Vec<String> {
let mut rustflags = lints
.iter()
.flat_map(|(tool, lints)| {
@@ -2802,7 +1366,7 @@ fn unused_dep_keys(
fn inheritable_from_path(
config: &Config,
workspace_path: PathBuf,
-) -> CargoResult<InheritableFields> {
+) -> CargoResult<schema::InheritableFields> {
// Workspace path should have Cargo.toml at the end
let workspace_path_root = workspace_path.parent().unwrap();
@@ -2829,14 +1393,17 @@ fn inheritable_from_path(
}
}
-/// Returns the name of the README file for a [`TomlPackage`].
-pub fn readme_for_package(package_root: &Path, readme: Option<&StringOrBool>) -> Option<String> {
+/// Returns the name of the README file for a [`schema::TomlPackage`].
+fn readme_for_package(
+ package_root: &Path,
+ readme: Option<&schema::StringOrBool>,
+) -> Option<String> {
match &readme {
None => default_readme_from_package_root(package_root),
Some(value) => match value {
- StringOrBool::Bool(false) => None,
- StringOrBool::Bool(true) => Some("README.md".to_string()),
- StringOrBool::String(v) => Some(v.clone()),
+ schema::StringOrBool::Bool(false) => None,
+ schema::StringOrBool::Bool(true) => Some("README.md".to_string()),
+ schema::StringOrBool::String(v) => Some(v.clone()),
},
}
}
@@ -2881,7 +1448,254 @@ fn unique_build_targets(
Ok(())
}
-impl<P: ResolveToPath + Clone> TomlDependency<P> {
+/// Defines simple getter methods for inheritable fields.
+macro_rules! inheritable_field_getter {
+ ( $(($key:literal, $field:ident -> $ret:ty),)* ) => (
+ $(
+ #[doc = concat!("Gets the field `workspace.", $key, "`.")]
+ fn $field(&self) -> CargoResult<$ret> {
+ let Some(val) = &self.$field else {
+ bail!("`workspace.{}` was not defined", $key);
+ };
+ Ok(val.clone())
+ }
+ )*
+ )
+}
+
+impl schema::InheritableFields {
+ inheritable_field_getter! {
+ // Please keep this list lexicographically ordered.
+ ("lints", lints -> schema::TomlLints),
+ ("package.authors", authors -> Vec<String>),
+ ("package.badges", badges -> BTreeMap<String, BTreeMap<String, String>>),
+ ("package.categories", categories -> Vec<String>),
+ ("package.description", description -> String),
+ ("package.documentation", documentation -> String),
+ ("package.edition", edition -> String),
+ ("package.exclude", exclude -> Vec<String>),
+ ("package.homepage", homepage -> String),
+ ("package.include", include -> Vec<String>),
+ ("package.keywords", keywords -> Vec<String>),
+ ("package.license", license -> String),
+ ("package.publish", publish -> schema::VecStringOrBool),
+ ("package.repository", repository -> String),
+ ("package.rust-version", rust_version -> RustVersion),
+ ("package.version", version -> semver::Version),
+ }
+
+ /// Gets a workspace dependency with the `name`.
+ fn get_dependency(
+ &self,
+ name: &str,
+ package_root: &Path,
+ ) -> CargoResult<schema::TomlDependency> {
+ let Some(deps) = &self.dependencies else {
+ bail!("`workspace.dependencies` was not defined");
+ };
+ let Some(dep) = deps.get(name) else {
+ bail!("`dependency.{name}` was not found in `workspace.dependencies`");
+ };
+ let mut dep = dep.clone();
+ if let schema::TomlDependency::Detailed(detailed) = &mut dep {
+ detailed.resolve_path(name, self.ws_root(), package_root)?;
+ }
+ Ok(dep)
+ }
+
+ /// Gets the field `workspace.package.license-file`.
+ fn license_file(&self, package_root: &Path) -> CargoResult<String> {
+ let Some(license_file) = &self.license_file else {
+ bail!("`workspace.package.license-file` was not defined");
+ };
+ resolve_relative_path("license-file", &self.ws_root, package_root, license_file)
+ }
+
+ /// Gets the field `workspace.package.readme`.
+ fn readme(&self, package_root: &Path) -> CargoResult<schema::StringOrBool> {
+ let Some(readme) = readme_for_package(self.ws_root.as_path(), self.readme.as_ref()) else {
+ bail!("`workspace.package.readme` was not defined");
+ };
+ resolve_relative_path("readme", &self.ws_root, package_root, &readme)
+ .map(schema::StringOrBool::String)
+ }
+
+ fn ws_root(&self) -> &PathBuf {
+ &self.ws_root
+ }
+
+ fn update_deps(&mut self, deps: Option<BTreeMap<String, schema::TomlDependency>>) {
+ self.dependencies = deps;
+ }
+
+ fn update_lints(&mut self, lints: Option<schema::TomlLints>) {
+ self.lints = lints;
+ }
+
+ fn update_ws_path(&mut self, ws_root: PathBuf) {
+ self.ws_root = ws_root;
+ }
+}
+
+impl schema::TomlPackage {
+ fn to_package_id(&self, source_id: SourceId, version: semver::Version) -> PackageId {
+ PackageId::pure(self.name.as_str().into(), version, source_id)
+ }
+}
+
+/// This Trait exists to make [`schema::MaybeWorkspace::Workspace`] generic. It makes deserialization of
+/// [`schema::MaybeWorkspace`] much easier, as well as making error messages for
+/// [`schema::MaybeWorkspace::resolve`] much nicer
+///
+/// Implementors should have a field `workspace` with the type of `bool`. It is used to ensure
+/// `workspace` is not `false` in a `Cargo.toml`
+pub trait WorkspaceInherit {
+ /// This is the workspace table that is being inherited from.
+ /// For example `[workspace.dependencies]` would be the table "dependencies"
+ fn inherit_toml_table(&self) -> &str;
+
+ /// This is used to output the value of the implementors `workspace` field
+ fn workspace(&self) -> bool;
+}
+
+impl<T, W: WorkspaceInherit> schema::MaybeWorkspace<T, W> {
+ fn resolve<'a>(
+ self,
+ label: &str,
+ get_ws_inheritable: impl FnOnce() -> CargoResult<T>,
+ ) -> CargoResult<T> {
+ match self {
+ schema::MaybeWorkspace::Defined(value) => Ok(value),
+ schema::MaybeWorkspace::Workspace(w) => get_ws_inheritable().with_context(|| {
+ format!(
+ "error inheriting `{label}` from workspace root manifest's `workspace.{}.{label}`",
+ w.inherit_toml_table(),
+ )
+ }),
+ }
+ }
+
+ fn resolve_with_self<'a>(
+ self,
+ label: &str,
+ get_ws_inheritable: impl FnOnce(&W) -> CargoResult<T>,
+ ) -> CargoResult<T> {
+ match self {
+ schema::MaybeWorkspace::Defined(value) => Ok(value),
+ schema::MaybeWorkspace::Workspace(w) => get_ws_inheritable(&w).with_context(|| {
+ format!(
+ "error inheriting `{label}` from workspace root manifest's `workspace.{}.{label}`",
+ w.inherit_toml_table(),
+ )
+ }),
+ }
+ }
+
+ fn as_defined(&self) -> Option<&T> {
+ match self {
+ schema::MaybeWorkspace::Workspace(_) => None,
+ schema::MaybeWorkspace::Defined(defined) => Some(defined),
+ }
+ }
+}
+
+impl WorkspaceInherit for schema::TomlWorkspaceField {
+ fn inherit_toml_table(&self) -> &str {
+ "package"
+ }
+
+ fn workspace(&self) -> bool {
+ self.workspace
+ }
+}
+
+impl schema::TomlWorkspaceDependency {
+ fn resolve<'a>(
+ &self,
+ name: &str,
+ inheritable: impl FnOnce() -> CargoResult<&'a schema::InheritableFields>,
+ cx: &mut Context<'_, '_>,
+ ) -> CargoResult<schema::TomlDependency> {
+ fn default_features_msg(label: &str, ws_def_feat: Option<bool>, cx: &mut Context<'_, '_>) {
+ let ws_def_feat = match ws_def_feat {
+ Some(true) => "true",
+ Some(false) => "false",
+ None => "not specified",
+ };
+ cx.warnings.push(format!(
+ "`default-features` is ignored for {label}, since `default-features` was \
+ {ws_def_feat} for `workspace.dependencies.{label}`, \
+ this could become a hard error in the future"
+ ))
+ }
+ if self.default_features.is_some() && self.default_features2.is_some() {
+ warn_on_deprecated("default-features", name, "dependency", cx.warnings);
+ }
+ inheritable()?.get_dependency(name, cx.root).map(|d| {
+ match d {
+ schema::TomlDependency::Simple(s) => {
+ if let Some(false) = self.default_features() {
+ default_features_msg(name, None, cx);
+ }
+ if self.optional.is_some() || self.features.is_some() || self.public.is_some() {
+ schema::TomlDependency::Detailed(schema::DetailedTomlDependency {
+ version: Some(s),
+ optional: self.optional,
+ features: self.features.clone(),
+ public: self.public,
+ ..Default::default()
+ })
+ } else {
+ schema::TomlDependency::Simple(s)
+ }
+ }
+ schema::TomlDependency::Detailed(d) => {
+ let mut d = d.clone();
+ match (self.default_features(), d.default_features()) {
+ // member: default-features = true and
+ // workspace: default-features = false should turn on
+ // default-features
+ (Some(true), Some(false)) => {
+ d.default_features = Some(true);
+ }
+ // member: default-features = false and
+ // workspace: default-features = true should ignore member
+ // default-features
+ (Some(false), Some(true)) => {
+ default_features_msg(name, Some(true), cx);
+ }
+ // member: default-features = false and
+ // workspace: dep = "1.0" should ignore member default-features
+ (Some(false), None) => {
+ default_features_msg(name, None, cx);
+ }
+ _ => {}
+ }
+ // Inherit the workspace configuration for `public` unless
+ // it's explicitly specified for this dependency.
+ if let Some(public) = self.public {
+ d.public = Some(public);
+ }
+ d.add_features(self.features.clone());
+ d.update_optional(self.optional);
+ schema::TomlDependency::Detailed(d)
+ }
+ }
+ })
+ }
+}
+
+impl WorkspaceInherit for schema::TomlWorkspaceDependency {
+ fn inherit_toml_table(&self) -> &str {
+ "dependencies"
+ }
+
+ fn workspace(&self) -> bool {
+ self.workspace
+ }
+}
+
+impl<P: ResolveToPath + Clone> schema::TomlDependency<P> {
pub(crate) fn to_dependency_split(
&self,
name: &str,
@@ -2917,31 +1731,54 @@ impl<P: ResolveToPath + Clone> TomlDependency<P> {
kind: Option<DepKind>,
) -> CargoResult<Dependency> {
match *self {
- TomlDependency::Simple(ref version) => DetailedTomlDependency::<P> {
+ schema::TomlDependency::Simple(ref version) => schema::DetailedTomlDependency::<P> {
version: Some(version.clone()),
..Default::default()
}
.to_dependency(name, cx, kind),
- TomlDependency::Detailed(ref details) => details.to_dependency(name, cx, kind),
+ schema::TomlDependency::Detailed(ref details) => details.to_dependency(name, cx, kind),
}
}
+}
- fn is_version_specified(&self) -> bool {
- match self {
- TomlDependency::Detailed(d) => d.version.is_some(),
- TomlDependency::Simple(..) => true,
- }
+impl schema::DetailedTomlDependency {
+ fn add_features(&mut self, features: Option<Vec<String>>) {
+ self.features = match (self.features.clone(), features.clone()) {
+ (Some(dep_feat), Some(inherit_feat)) => Some(
+ dep_feat
+ .into_iter()
+ .chain(inherit_feat)
+ .collect::<Vec<String>>(),
+ ),
+ (Some(dep_fet), None) => Some(dep_fet),
+ (None, Some(inherit_feat)) => Some(inherit_feat),
+ (None, None) => None,
+ };
}
- fn is_optional(&self) -> bool {
- match self {
- TomlDependency::Detailed(d) => d.optional.unwrap_or(false),
- TomlDependency::Simple(..) => false,
+ fn update_optional(&mut self, optional: Option<bool>) {
+ self.optional = optional;
+ }
+
+ fn resolve_path(
+ &mut self,
+ name: &str,
+ root_path: &Path,
+ package_root: &Path,
+ ) -> CargoResult<()> {
+ if let Some(rel_path) = &self.path {
+ self.path = Some(resolve_relative_path(
+ name,
+ root_path,
+ package_root,
+ rel_path,
+ )?)
}
+ Ok(())
}
}
-impl<P: ResolveToPath + Clone> DetailedTomlDependency<P> {
+impl<P: ResolveToPath + Clone> schema::DetailedTomlDependency<P> {
fn to_dependency(
&self,
name_in_toml: &str,
@@ -3114,11 +1951,7 @@ impl<P: ResolveToPath + Clone> DetailedTomlDependency<P> {
warn_on_deprecated("default-features", name_in_toml, "dependency", cx.warnings);
}
dep.set_features(self.features.iter().flatten())
- .set_default_features(
- self.default_features
- .or(self.default_features2)
- .unwrap_or(true),
- )
+ .set_default_features(self.default_features().unwrap_or(true))
.set_optional(self.optional.unwrap_or(false))
.set_platform(cx.platform.clone());
if let Some(registry) = &self.registry {
@@ -3186,184 +2019,271 @@ impl<P: ResolveToPath + Clone> DetailedTomlDependency<P> {
}
}
-impl DetailedTomlDependency {
- fn add_features(&mut self, features: Option<Vec<String>>) {
- self.features = match (self.features.clone(), features.clone()) {
- (Some(dep_feat), Some(inherit_feat)) => Some(
- dep_feat
- .into_iter()
- .chain(inherit_feat)
- .collect::<Vec<String>>(),
- ),
- (Some(dep_fet), None) => Some(dep_fet),
- (None, Some(inherit_feat)) => Some(inherit_feat),
- (None, None) => None,
- };
- }
-
- fn update_optional(&mut self, optional: Option<bool>) {
- self.optional = optional;
+impl schema::TomlProfiles {
+ /// Checks syntax validity and unstable feature gate for each profile.
+ ///
+ /// It's a bit unfortunate both `-Z` flags and `cargo-features` are required,
+ /// because profiles can now be set in either `Cargo.toml` or `config.toml`.
+ fn validate(
+ &self,
+ cli_unstable: &CliUnstable,
+ features: &Features,
+ warnings: &mut Vec<String>,
+ ) -> CargoResult<()> {
+ for (name, profile) in &self.0 {
+ profile.validate(name, cli_unstable, features, warnings)?;
+ }
+ Ok(())
}
+}
- fn resolve_path(
- &mut self,
+impl schema::TomlProfile {
+ /// Checks stytax validity and unstable feature gate for a given profile.
+ pub fn validate(
+ &self,
name: &str,
- root_path: &Path,
- package_root: &Path,
+ cli_unstable: &CliUnstable,
+ features: &Features,
+ warnings: &mut Vec<String>,
) -> CargoResult<()> {
- if let Some(rel_path) = &self.path {
- self.path = Some(resolve_relative_path(
+ self.validate_profile(name, cli_unstable, features)?;
+ if let Some(ref profile) = self.build_override {
+ profile.validate_override("build-override")?;
+ profile.validate_profile(&format!("{name}.build-override"), cli_unstable, features)?;
+ }
+ if let Some(ref packages) = self.package {
+ for (override_name, profile) in packages {
+ profile.validate_override("package")?;
+ profile.validate_profile(
+ &format!("{name}.package.{override_name}"),
+ cli_unstable,
+ features,
+ )?;
+ }
+ }
+
+ // Profile name validation
+ restricted_names::validate_profile_name(name)?;
+
+ if let Some(dir_name) = &self.dir_name {
+ // This is disabled for now, as we would like to stabilize named
+ // profiles without this, and then decide in the future if it is
+ // needed. This helps simplify the UI a little.
+ bail!(
+ "dir-name=\"{}\" in profile `{}` is not currently allowed, \
+ directory names are tied to the profile name for custom profiles",
+ dir_name,
+ name
+ );
+ }
+
+ // `inherits` validation
+ if matches!(self.inherits.as_deref(), Some("debug")) {
+ bail!(
+ "profile.{}.inherits=\"debug\" should be profile.{}.inherits=\"dev\"",
name,
- root_path,
- package_root,
- rel_path,
- )?)
+ name
+ );
}
- Ok(())
- }
-}
-#[derive(Default, Serialize, Deserialize, Debug, Clone)]
-#[serde(rename_all = "kebab-case")]
-struct TomlTarget {
- name: Option<String>,
-
- // The intention was to only accept `crate-type` here but historical
- // versions of Cargo also accepted `crate_type`, so look for both.
- crate_type: Option<Vec<String>>,
- #[serde(rename = "crate_type")]
- crate_type2: Option<Vec<String>>,
-
- path: Option<PathValue>,
- // Note that `filename` is used for the cargo-feature `different_binary_name`
- filename: Option<String>,
- test: Option<bool>,
- doctest: Option<bool>,
- bench: Option<bool>,
- doc: Option<bool>,
- plugin: Option<bool>,
- doc_scrape_examples: Option<bool>,
- #[serde(rename = "proc-macro")]
- proc_macro_raw: Option<bool>,
- #[serde(rename = "proc_macro")]
- proc_macro_raw2: Option<bool>,
- harness: Option<bool>,
- required_features: Option<Vec<String>>,
- edition: Option<String>,
-}
+ match name {
+ "doc" => {
+ warnings.push("profile `doc` is deprecated and has no effect".to_string());
+ }
+ "test" | "bench" => {
+ if self.panic.is_some() {
+ warnings.push(format!("`panic` setting is ignored for `{}` profile", name))
+ }
+ }
+ _ => {}
+ }
-#[derive(Clone)]
-struct PathValue(PathBuf);
+ if let Some(panic) = &self.panic {
+ if panic != "unwind" && panic != "abort" {
+ bail!(
+ "`panic` setting of `{}` is not a valid setting, \
+ must be `unwind` or `abort`",
+ panic
+ );
+ }
+ }
-impl<'de> de::Deserialize<'de> for PathValue {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- Ok(PathValue(String::deserialize(deserializer)?.into()))
- }
-}
+ if let Some(schema::StringOrBool::String(arg)) = &self.lto {
+ if arg == "true" || arg == "false" {
+ bail!(
+ "`lto` setting of string `\"{arg}\"` for `{name}` profile is not \
+ a valid setting, must be a boolean (`true`/`false`) or a string \
+ (`\"thin\"`/`\"fat\"`/`\"off\"`) or omitted.",
+ );
+ }
+ }
-impl ser::Serialize for PathValue {
- fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
- where
- S: ser::Serializer,
- {
- self.0.serialize(serializer)
+ Ok(())
}
-}
-/// Corresponds to a `target` entry, but `TomlTarget` is already used.
-#[derive(Serialize, Deserialize, Debug, Clone)]
-struct TomlPlatform {
- dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- #[serde(rename = "build-dependencies")]
- build_dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- #[serde(rename = "build_dependencies")]
- build_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- #[serde(rename = "dev-dependencies")]
- dev_dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- #[serde(rename = "dev_dependencies")]
- dev_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
-}
+ /// Validates a profile.
+ ///
+ /// This is a shallow check, which is reused for the profile itself and any overrides.
+ fn validate_profile(
+ &self,
+ name: &str,
+ cli_unstable: &CliUnstable,
+ features: &Features,
+ ) -> CargoResult<()> {
+ if let Some(codegen_backend) = &self.codegen_backend {
+ match (
+ features.require(Feature::codegen_backend()),
+ cli_unstable.codegen_backend,
+ ) {
+ (Err(e), false) => return Err(e),
+ _ => {}
+ }
-impl TomlTarget {
- fn new() -> TomlTarget {
- TomlTarget::default()
+ if codegen_backend.contains(|c: char| !c.is_ascii_alphanumeric() && c != '_') {
+ bail!(
+ "`profile.{}.codegen-backend` setting of `{}` is not a valid backend name.",
+ name,
+ codegen_backend,
+ );
+ }
+ }
+ if self.rustflags.is_some() {
+ match (
+ features.require(Feature::profile_rustflags()),
+ cli_unstable.profile_rustflags,
+ ) {
+ (Err(e), false) => return Err(e),
+ _ => {}
+ }
+ }
+ if self.trim_paths.is_some() {
+ match (
+ features.require(Feature::trim_paths()),
+ cli_unstable.trim_paths,
+ ) {
+ (Err(e), false) => return Err(e),
+ _ => {}
+ }
+ }
+ Ok(())
}
- fn name(&self) -> String {
- match self.name {
- Some(ref name) => name.clone(),
- None => panic!("target name is required"),
+ /// Validation that is specific to an override.
+ fn validate_override(&self, which: &str) -> CargoResult<()> {
+ if self.package.is_some() {
+ bail!("package-specific profiles cannot be nested");
+ }
+ if self.build_override.is_some() {
+ bail!("build-override profiles cannot be nested");
}
+ if self.panic.is_some() {
+ bail!("`panic` may not be specified in a `{}` profile", which)
+ }
+ if self.lto.is_some() {
+ bail!("`lto` may not be specified in a `{}` profile", which)
+ }
+ if self.rpath.is_some() {
+ bail!("`rpath` may not be specified in a `{}` profile", which)
+ }
+ Ok(())
}
- fn validate_proc_macro(&self, warnings: &mut Vec<String>) {
- if self.proc_macro_raw.is_some() && self.proc_macro_raw2.is_some() {
- warn_on_deprecated(
- "proc-macro",
- self.name().as_str(),
- "library target",
- warnings,
- );
+ /// Overwrite self's values with the given profile.
+ pub fn merge(&mut self, profile: &schema::TomlProfile) {
+ if let Some(v) = &profile.opt_level {
+ self.opt_level = Some(v.clone());
}
- }
- fn proc_macro(&self) -> Option<bool> {
- self.proc_macro_raw.or(self.proc_macro_raw2).or_else(|| {
- if let Some(types) = self.crate_types() {
- if types.contains(&"proc-macro".to_string()) {
- return Some(true);
+ if let Some(v) = &profile.lto {
+ self.lto = Some(v.clone());
+ }
+
+ if let Some(v) = &profile.codegen_backend {
+ self.codegen_backend = Some(v.clone());
+ }
+
+ if let Some(v) = profile.codegen_units {
+ self.codegen_units = Some(v);
+ }
+
+ if let Some(v) = profile.debug {
+ self.debug = Some(v);
+ }
+
+ if let Some(v) = profile.debug_assertions {
+ self.debug_assertions = Some(v);
+ }
+
+ if let Some(v) = &profile.split_debuginfo {
+ self.split_debuginfo = Some(v.clone());
+ }
+
+ if let Some(v) = profile.rpath {
+ self.rpath = Some(v);
+ }
+
+ if let Some(v) = &profile.panic {
+ self.panic = Some(v.clone());
+ }
+
+ if let Some(v) = profile.overflow_checks {
+ self.overflow_checks = Some(v);
+ }
+
+ if let Some(v) = profile.incremental {
+ self.incremental = Some(v);
+ }
+
+ if let Some(v) = &profile.rustflags {
+ self.rustflags = Some(v.clone());
+ }
+
+ if let Some(other_package) = &profile.package {
+ match &mut self.package {
+ Some(self_package) => {
+ for (spec, other_pkg_profile) in other_package {
+ match self_package.get_mut(spec) {
+ Some(p) => p.merge(other_pkg_profile),
+ None => {
+ self_package.insert(spec.clone(), other_pkg_profile.clone());
+ }
+ }
+ }
}
+ None => self.package = Some(other_package.clone()),
}
- None
- })
- }
+ }
- fn validate_crate_types(&self, target_kind_human: &str, warnings: &mut Vec<String>) {
- if self.crate_type.is_some() && self.crate_type2.is_some() {
- warn_on_deprecated(
- "crate-type",
- self.name().as_str(),
- format!("{target_kind_human} target").as_str(),
- warnings,
- );
+ if let Some(other_bo) = &profile.build_override {
+ match &mut self.build_override {
+ Some(self_bo) => self_bo.merge(other_bo),
+ None => self.build_override = Some(other_bo.clone()),
+ }
}
- }
- fn crate_types(&self) -> Option<&Vec<String>> {
- self.crate_type
- .as_ref()
- .or_else(|| self.crate_type2.as_ref())
- }
-}
+ if let Some(v) = &profile.inherits {
+ self.inherits = Some(v.clone());
+ }
-impl fmt::Debug for PathValue {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
- }
-}
+ if let Some(v) = &profile.dir_name {
+ self.dir_name = Some(v.clone());
+ }
-#[derive(Deserialize, Serialize, Debug, Clone)]
-#[serde(expecting = "a lints table")]
-pub struct MaybeWorkspaceLints {
- #[serde(skip_serializing_if = "is_false")]
- #[serde(deserialize_with = "bool_no_false", default)]
- workspace: bool,
- #[serde(flatten)]
- lints: TomlLints,
-}
+ if let Some(v) = &profile.strip {
+ self.strip = Some(v.clone());
+ }
-fn is_false(b: &bool) -> bool {
- !b
+ if let Some(v) = &profile.trim_paths {
+ self.trim_paths = Some(v.clone())
+ }
+ }
}
-impl MaybeWorkspaceLints {
+impl schema::MaybeWorkspaceLints {
fn resolve<'a>(
self,
- get_ws_inheritable: impl FnOnce() -> CargoResult<TomlLints>,
- ) -> CargoResult<TomlLints> {
+ get_ws_inheritable: impl FnOnce() -> CargoResult<schema::TomlLints>,
+ ) -> CargoResult<schema::TomlLints> {
if self.workspace {
if !self.lints.is_empty() {
anyhow::bail!("cannot override `workspace.lints` in `lints`, either remove the overrides or `lints.workspace = true` and manually specify the lints");
@@ -3377,65 +2297,7 @@ impl MaybeWorkspaceLints {
}
}
-pub type TomlLints = BTreeMap<String, TomlToolLints>;
-
-pub type TomlToolLints = BTreeMap<String, TomlLint>;
-
-#[derive(Serialize, Debug, Clone)]
-#[serde(untagged)]
-pub enum TomlLint {
- Level(TomlLintLevel),
- Config(TomlLintConfig),
-}
-
-impl<'de> Deserialize<'de> for TomlLint {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- UntaggedEnumVisitor::new()
- .string(|string| {
- TomlLintLevel::deserialize(string.into_deserializer()).map(TomlLint::Level)
- })
- .map(|map| map.deserialize().map(TomlLint::Config))
- .deserialize(deserializer)
- }
-}
-
-impl TomlLint {
- fn level(&self) -> TomlLintLevel {
- match self {
- Self::Level(level) => *level,
- Self::Config(config) => config.level,
- }
- }
-
- fn priority(&self) -> i8 {
- match self {
- Self::Level(_) => 0,
- Self::Config(config) => config.priority,
- }
- }
-}
-
-#[derive(Serialize, Deserialize, Debug, Clone)]
-#[serde(rename_all = "kebab-case")]
-pub struct TomlLintConfig {
- level: TomlLintLevel,
- #[serde(default)]
- priority: i8,
-}
-
-#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
-#[serde(rename_all = "kebab-case")]
-pub enum TomlLintLevel {
- Forbid,
- Deny,
- Warn,
- Allow,
-}
-
-impl TomlLintLevel {
+impl schema::TomlLintLevel {
fn flag(&self) -> &'static str {
match self {
Self::Forbid => "--forbid",
@@ -3446,19 +2308,18 @@ impl TomlLintLevel {
}
}
-#[derive(Copy, Clone, Debug)]
-#[non_exhaustive]
-struct InvalidCargoFeatures {}
+pub trait ResolveToPath {
+ fn resolve(&self, config: &Config) -> PathBuf;
+}
-impl<'de> de::Deserialize<'de> for InvalidCargoFeatures {
- fn deserialize<D>(_d: D) -> Result<Self, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- use serde::de::Error as _;
+impl ResolveToPath for String {
+ fn resolve(&self, _: &Config) -> PathBuf {
+ self.into()
+ }
+}
- Err(D::Error::custom(
- "the field `cargo-features` should be set at the top of Cargo.toml before any tables",
- ))
+impl ResolveToPath for ConfigRelativePath {
+ fn resolve(&self, c: &Config) -> PathBuf {
+ self.resolve_path(c)
}
}
diff --git a/src/tools/cargo/src/cargo/util/toml/schema.rs b/src/tools/cargo/src/cargo/util/toml/schema.rs
new file mode 100644
index 000000000..6ea93e021
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util/toml/schema.rs
@@ -0,0 +1,1189 @@
+use std::collections::BTreeMap;
+use std::fmt::{self, Display, Write};
+use std::path::PathBuf;
+use std::str;
+
+use serde::de::{self, IntoDeserializer as _, Unexpected};
+use serde::ser;
+use serde::{Deserialize, Serialize};
+use serde_untagged::UntaggedEnumVisitor;
+
+use crate::core::PackageIdSpec;
+use crate::util::RustVersion;
+
+/// This type is used to deserialize `Cargo.toml` files.
+#[derive(Debug, Deserialize, Serialize)]
+#[serde(rename_all = "kebab-case")]
+pub struct TomlManifest {
+ pub cargo_features: Option<Vec<String>>,
+ pub package: Option<Box<TomlPackage>>,
+ pub project: Option<Box<TomlPackage>>,
+ pub profile: Option<TomlProfiles>,
+ pub lib: Option<TomlLibTarget>,
+ pub bin: Option<Vec<TomlBinTarget>>,
+ pub example: Option<Vec<TomlExampleTarget>>,
+ pub test: Option<Vec<TomlTestTarget>>,
+ pub bench: Option<Vec<TomlTestTarget>>,
+ pub dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ pub dev_dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ #[serde(rename = "dev_dependencies")]
+ pub dev_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ pub build_dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ #[serde(rename = "build_dependencies")]
+ pub build_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ pub features: Option<BTreeMap<String, Vec<String>>>,
+ pub target: Option<BTreeMap<String, TomlPlatform>>,
+ pub replace: Option<BTreeMap<String, TomlDependency>>,
+ pub patch: Option<BTreeMap<String, BTreeMap<String, TomlDependency>>>,
+ pub workspace: Option<TomlWorkspace>,
+ pub badges: Option<MaybeWorkspaceBtreeMap>,
+ pub lints: Option<MaybeWorkspaceLints>,
+}
+
+impl TomlManifest {
+ pub fn has_profiles(&self) -> bool {
+ self.profile.is_some()
+ }
+
+ pub fn dev_dependencies(&self) -> Option<&BTreeMap<String, MaybeWorkspaceDependency>> {
+ self.dev_dependencies
+ .as_ref()
+ .or(self.dev_dependencies2.as_ref())
+ }
+
+ pub fn build_dependencies(&self) -> Option<&BTreeMap<String, MaybeWorkspaceDependency>> {
+ self.build_dependencies
+ .as_ref()
+ .or(self.build_dependencies2.as_ref())
+ }
+
+ pub fn features(&self) -> Option<&BTreeMap<String, Vec<String>>> {
+ self.features.as_ref()
+ }
+}
+
+#[derive(Debug, Deserialize, Serialize, Clone)]
+#[serde(rename_all = "kebab-case")]
+pub struct TomlWorkspace {
+ pub members: Option<Vec<String>>,
+ pub exclude: Option<Vec<String>>,
+ pub default_members: Option<Vec<String>>,
+ pub resolver: Option<String>,
+ pub metadata: Option<toml::Value>,
+
+ // Properties that can be inherited by members.
+ pub package: Option<InheritableFields>,
+ pub dependencies: Option<BTreeMap<String, TomlDependency>>,
+ pub lints: Option<TomlLints>,
+}
+
+/// A group of fields that are inheritable by members of the workspace
+#[derive(Clone, Debug, Default, Deserialize, Serialize)]
+#[serde(rename_all = "kebab-case")]
+pub struct InheritableFields {
+ // We use skip here since it will never be present when deserializing
+ // and we don't want it present when serializing
+ #[serde(skip)]
+ pub dependencies: Option<BTreeMap<String, TomlDependency>>,
+ #[serde(skip)]
+ pub lints: Option<TomlLints>,
+
+ pub version: Option<semver::Version>,
+ pub authors: Option<Vec<String>>,
+ pub description: Option<String>,
+ pub homepage: Option<String>,
+ pub documentation: Option<String>,
+ pub readme: Option<StringOrBool>,
+ pub keywords: Option<Vec<String>>,
+ pub categories: Option<Vec<String>>,
+ pub license: Option<String>,
+ pub license_file: Option<String>,
+ pub repository: Option<String>,
+ pub publish: Option<VecStringOrBool>,
+ pub edition: Option<String>,
+ pub badges: Option<BTreeMap<String, BTreeMap<String, String>>>,
+ pub exclude: Option<Vec<String>>,
+ pub include: Option<Vec<String>>,
+ pub rust_version: Option<RustVersion>,
+ // We use skip here since it will never be present when deserializing
+ // and we don't want it present when serializing
+ #[serde(skip)]
+ pub ws_root: PathBuf,
+}
+
+/// Represents the `package`/`project` sections of a `Cargo.toml`.
+///
+/// Note that the order of the fields matters, since this is the order they
+/// are serialized to a TOML file. For example, you cannot have values after
+/// the field `metadata`, since it is a table and values cannot appear after
+/// tables.
+#[derive(Deserialize, Serialize, Clone, Debug)]
+#[serde(rename_all = "kebab-case")]
+pub struct TomlPackage {
+ pub edition: Option<MaybeWorkspaceString>,
+ pub rust_version: Option<MaybeWorkspaceRustVersion>,
+ pub name: String,
+ pub version: Option<MaybeWorkspaceSemverVersion>,
+ pub authors: Option<MaybeWorkspaceVecString>,
+ pub build: Option<StringOrBool>,
+ pub metabuild: Option<StringOrVec>,
+ pub default_target: Option<String>,
+ pub forced_target: Option<String>,
+ pub links: Option<String>,
+ pub exclude: Option<MaybeWorkspaceVecString>,
+ pub include: Option<MaybeWorkspaceVecString>,
+ pub publish: Option<MaybeWorkspaceVecStringOrBool>,
+ pub workspace: Option<String>,
+ pub im_a_teapot: Option<bool>,
+ pub autobins: Option<bool>,
+ pub autoexamples: Option<bool>,
+ pub autotests: Option<bool>,
+ pub autobenches: Option<bool>,
+ pub default_run: Option<String>,
+
+ // Package metadata.
+ pub description: Option<MaybeWorkspaceString>,
+ pub homepage: Option<MaybeWorkspaceString>,
+ pub documentation: Option<MaybeWorkspaceString>,
+ pub readme: Option<MaybeWorkspaceStringOrBool>,
+ pub keywords: Option<MaybeWorkspaceVecString>,
+ pub categories: Option<MaybeWorkspaceVecString>,
+ pub license: Option<MaybeWorkspaceString>,
+ pub license_file: Option<MaybeWorkspaceString>,
+ pub repository: Option<MaybeWorkspaceString>,
+ pub resolver: Option<String>,
+
+ pub metadata: Option<toml::Value>,
+
+ /// Provide a helpful error message for a common user error.
+ #[serde(rename = "cargo-features", skip_serializing)]
+ pub _invalid_cargo_features: Option<InvalidCargoFeatures>,
+}
+
+/// An enum that allows for inheriting keys from a workspace in a Cargo.toml.
+#[derive(Serialize, Copy, Clone, Debug)]
+#[serde(untagged)]
+pub enum MaybeWorkspace<T, W> {
+ /// The "defined" type, or the type that that is used when not inheriting from a workspace.
+ Defined(T),
+ /// The type when inheriting from a workspace.
+ Workspace(W),
+}
+
+//. This already has a `Deserialize` impl from version_trim_whitespace
+pub type MaybeWorkspaceSemverVersion = MaybeWorkspace<semver::Version, TomlWorkspaceField>;
+impl<'de> de::Deserialize<'de> for MaybeWorkspaceSemverVersion {
+ fn deserialize<D>(d: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ UntaggedEnumVisitor::new()
+ .expecting("SemVer version")
+ .string(
+ |value| match value.trim().parse().map_err(de::Error::custom) {
+ Ok(parsed) => Ok(MaybeWorkspace::Defined(parsed)),
+ Err(e) => Err(e),
+ },
+ )
+ .map(|value| value.deserialize().map(MaybeWorkspace::Workspace))
+ .deserialize(d)
+ }
+}
+
+pub type MaybeWorkspaceString = MaybeWorkspace<String, TomlWorkspaceField>;
+impl<'de> de::Deserialize<'de> for MaybeWorkspaceString {
+ fn deserialize<D>(d: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = MaybeWorkspaceString;
+
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+ f.write_str("a string or workspace")
+ }
+
+ fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ Ok(MaybeWorkspaceString::Defined(value))
+ }
+
+ fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
+ where
+ V: de::MapAccess<'de>,
+ {
+ let mvd = de::value::MapAccessDeserializer::new(map);
+ TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
+ }
+ }
+
+ d.deserialize_any(Visitor)
+ }
+}
+
+pub type MaybeWorkspaceRustVersion = MaybeWorkspace<RustVersion, TomlWorkspaceField>;
+impl<'de> de::Deserialize<'de> for MaybeWorkspaceRustVersion {
+ fn deserialize<D>(d: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = MaybeWorkspaceRustVersion;
+
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+ f.write_str("a semver or workspace")
+ }
+
+ fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ let value = value.parse::<RustVersion>().map_err(|e| E::custom(e))?;
+ Ok(MaybeWorkspaceRustVersion::Defined(value))
+ }
+
+ fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
+ where
+ V: de::MapAccess<'de>,
+ {
+ let mvd = de::value::MapAccessDeserializer::new(map);
+ TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
+ }
+ }
+
+ d.deserialize_any(Visitor)
+ }
+}
+
+pub type MaybeWorkspaceVecString = MaybeWorkspace<Vec<String>, TomlWorkspaceField>;
+impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecString {
+ fn deserialize<D>(d: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = MaybeWorkspaceVecString;
+
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ f.write_str("a vector of strings or workspace")
+ }
+ fn visit_seq<A>(self, v: A) -> Result<Self::Value, A::Error>
+ where
+ A: de::SeqAccess<'de>,
+ {
+ let seq = de::value::SeqAccessDeserializer::new(v);
+ Vec::deserialize(seq).map(MaybeWorkspace::Defined)
+ }
+
+ fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
+ where
+ V: de::MapAccess<'de>,
+ {
+ let mvd = de::value::MapAccessDeserializer::new(map);
+ TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
+ }
+ }
+
+ d.deserialize_any(Visitor)
+ }
+}
+
+pub type MaybeWorkspaceStringOrBool = MaybeWorkspace<StringOrBool, TomlWorkspaceField>;
+impl<'de> de::Deserialize<'de> for MaybeWorkspaceStringOrBool {
+ fn deserialize<D>(d: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = MaybeWorkspaceStringOrBool;
+
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ f.write_str("a string, a bool, or workspace")
+ }
+
+ fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ let b = de::value::BoolDeserializer::new(v);
+ StringOrBool::deserialize(b).map(MaybeWorkspace::Defined)
+ }
+
+ fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ let string = de::value::StringDeserializer::new(v);
+ StringOrBool::deserialize(string).map(MaybeWorkspace::Defined)
+ }
+
+ fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
+ where
+ V: de::MapAccess<'de>,
+ {
+ let mvd = de::value::MapAccessDeserializer::new(map);
+ TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
+ }
+ }
+
+ d.deserialize_any(Visitor)
+ }
+}
+
+pub type MaybeWorkspaceVecStringOrBool = MaybeWorkspace<VecStringOrBool, TomlWorkspaceField>;
+impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecStringOrBool {
+ fn deserialize<D>(d: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = MaybeWorkspaceVecStringOrBool;
+
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ f.write_str("a boolean, a vector of strings, or workspace")
+ }
+
+ fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ let b = de::value::BoolDeserializer::new(v);
+ VecStringOrBool::deserialize(b).map(MaybeWorkspace::Defined)
+ }
+
+ fn visit_seq<A>(self, v: A) -> Result<Self::Value, A::Error>
+ where
+ A: de::SeqAccess<'de>,
+ {
+ let seq = de::value::SeqAccessDeserializer::new(v);
+ VecStringOrBool::deserialize(seq).map(MaybeWorkspace::Defined)
+ }
+
+ fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
+ where
+ V: de::MapAccess<'de>,
+ {
+ let mvd = de::value::MapAccessDeserializer::new(map);
+ TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
+ }
+ }
+
+ d.deserialize_any(Visitor)
+ }
+}
+
+pub type MaybeWorkspaceBtreeMap =
+ MaybeWorkspace<BTreeMap<String, BTreeMap<String, String>>, TomlWorkspaceField>;
+
+impl<'de> de::Deserialize<'de> for MaybeWorkspaceBtreeMap {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ let value = serde_value::Value::deserialize(deserializer)?;
+
+ if let Ok(w) = TomlWorkspaceField::deserialize(
+ serde_value::ValueDeserializer::<D::Error>::new(value.clone()),
+ ) {
+ return if w.workspace {
+ Ok(MaybeWorkspace::Workspace(w))
+ } else {
+ Err(de::Error::custom("`workspace` cannot be false"))
+ };
+ }
+ BTreeMap::deserialize(serde_value::ValueDeserializer::<D::Error>::new(value))
+ .map(MaybeWorkspace::Defined)
+ }
+}
+
+#[derive(Deserialize, Serialize, Copy, Clone, Debug)]
+#[serde(rename_all = "kebab-case")]
+pub struct TomlWorkspaceField {
+ #[serde(deserialize_with = "bool_no_false")]
+ pub workspace: bool,
+}
+
+fn bool_no_false<'de, D: de::Deserializer<'de>>(deserializer: D) -> Result<bool, D::Error> {
+ let b: bool = Deserialize::deserialize(deserializer)?;
+ if b {
+ Ok(b)
+ } else {
+ Err(de::Error::custom("`workspace` cannot be false"))
+ }
+}
+
+pub type MaybeWorkspaceDependency = MaybeWorkspace<TomlDependency, TomlWorkspaceDependency>;
+
+impl MaybeWorkspaceDependency {
+ pub fn unused_keys(&self) -> Vec<String> {
+ match self {
+ MaybeWorkspaceDependency::Defined(d) => d.unused_keys(),
+ MaybeWorkspaceDependency::Workspace(w) => w.unused_keys.keys().cloned().collect(),
+ }
+ }
+}
+
+impl<'de> de::Deserialize<'de> for MaybeWorkspaceDependency {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ let value = serde_value::Value::deserialize(deserializer)?;
+
+ if let Ok(w) = TomlWorkspaceDependency::deserialize(serde_value::ValueDeserializer::<
+ D::Error,
+ >::new(value.clone()))
+ {
+ return if w.workspace {
+ Ok(MaybeWorkspace::Workspace(w))
+ } else {
+ Err(de::Error::custom("`workspace` cannot be false"))
+ };
+ }
+ TomlDependency::deserialize(serde_value::ValueDeserializer::<D::Error>::new(value))
+ .map(MaybeWorkspace::Defined)
+ }
+}
+
+#[derive(Deserialize, Serialize, Clone, Debug)]
+#[serde(rename_all = "kebab-case")]
+pub struct TomlWorkspaceDependency {
+ pub workspace: bool,
+ pub features: Option<Vec<String>>,
+ pub default_features: Option<bool>,
+ #[serde(rename = "default_features")]
+ pub default_features2: Option<bool>,
+ pub optional: Option<bool>,
+ pub public: Option<bool>,
+
+ /// This is here to provide a way to see the "unused manifest keys" when deserializing
+ #[serde(skip_serializing)]
+ #[serde(flatten)]
+ pub unused_keys: BTreeMap<String, toml::Value>,
+}
+
+impl TomlWorkspaceDependency {
+ pub fn default_features(&self) -> Option<bool> {
+ self.default_features.or(self.default_features2)
+ }
+}
+
+#[derive(Clone, Debug, Serialize)]
+#[serde(untagged)]
+pub enum TomlDependency<P: Clone = String> {
+ /// In the simple format, only a version is specified, eg.
+ /// `package = "<version>"`
+ Simple(String),
+ /// The simple format is equivalent to a detailed dependency
+ /// specifying only a version, eg.
+ /// `package = { version = "<version>" }`
+ Detailed(DetailedTomlDependency<P>),
+}
+
+impl TomlDependency {
+ pub fn is_version_specified(&self) -> bool {
+ match self {
+ TomlDependency::Detailed(d) => d.version.is_some(),
+ TomlDependency::Simple(..) => true,
+ }
+ }
+
+ pub fn is_optional(&self) -> bool {
+ match self {
+ TomlDependency::Detailed(d) => d.optional.unwrap_or(false),
+ TomlDependency::Simple(..) => false,
+ }
+ }
+
+ pub fn unused_keys(&self) -> Vec<String> {
+ match self {
+ TomlDependency::Simple(_) => vec![],
+ TomlDependency::Detailed(detailed) => detailed.unused_keys.keys().cloned().collect(),
+ }
+ }
+}
+
+impl<'de, P: Deserialize<'de> + Clone> de::Deserialize<'de> for TomlDependency<P> {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ UntaggedEnumVisitor::new()
+ .expecting(
+ "a version string like \"0.9.8\" or a \
+ detailed dependency like { version = \"0.9.8\" }",
+ )
+ .string(|value| Ok(TomlDependency::Simple(value.to_owned())))
+ .map(|value| value.deserialize().map(TomlDependency::Detailed))
+ .deserialize(deserializer)
+ }
+}
+
+#[derive(Deserialize, Serialize, Clone, Debug)]
+#[serde(rename_all = "kebab-case")]
+pub struct DetailedTomlDependency<P: Clone = String> {
+ pub version: Option<String>,
+ pub registry: Option<String>,
+ /// The URL of the `registry` field.
+ /// This is an internal implementation detail. When Cargo creates a
+ /// package, it replaces `registry` with `registry-index` so that the
+ /// manifest contains the correct URL. All users won't have the same
+ /// registry names configured, so Cargo can't rely on just the name for
+ /// crates published by other users.
+ pub registry_index: Option<String>,
+ // `path` is relative to the file it appears in. If that's a `Cargo.toml`, it'll be relative to
+ // that TOML file, and if it's a `.cargo/config` file, it'll be relative to that file.
+ pub path: Option<P>,
+ pub git: Option<String>,
+ pub branch: Option<String>,
+ pub tag: Option<String>,
+ pub rev: Option<String>,
+ pub features: Option<Vec<String>>,
+ pub optional: Option<bool>,
+ pub default_features: Option<bool>,
+ #[serde(rename = "default_features")]
+ pub default_features2: Option<bool>,
+ pub package: Option<String>,
+ pub public: Option<bool>,
+
+ /// One or more of `bin`, `cdylib`, `staticlib`, `bin:<name>`.
+ pub artifact: Option<StringOrVec>,
+ /// If set, the artifact should also be a dependency
+ pub lib: Option<bool>,
+ /// A platform name, like `x86_64-apple-darwin`
+ pub target: Option<String>,
+
+ /// This is here to provide a way to see the "unused manifest keys" when deserializing
+ #[serde(skip_serializing)]
+ #[serde(flatten)]
+ pub unused_keys: BTreeMap<String, toml::Value>,
+}
+
+impl<P: Clone> DetailedTomlDependency<P> {
+ pub fn default_features(&self) -> Option<bool> {
+ self.default_features.or(self.default_features2)
+ }
+}
+
+// Explicit implementation so we avoid pulling in P: Default
+impl<P: Clone> Default for DetailedTomlDependency<P> {
+ fn default() -> Self {
+ Self {
+ version: Default::default(),
+ registry: Default::default(),
+ registry_index: Default::default(),
+ path: Default::default(),
+ git: Default::default(),
+ branch: Default::default(),
+ tag: Default::default(),
+ rev: Default::default(),
+ features: Default::default(),
+ optional: Default::default(),
+ default_features: Default::default(),
+ default_features2: Default::default(),
+ package: Default::default(),
+ public: Default::default(),
+ artifact: Default::default(),
+ lib: Default::default(),
+ target: Default::default(),
+ unused_keys: Default::default(),
+ }
+ }
+}
+
+#[derive(Deserialize, Serialize, Clone, Debug, Default)]
+pub struct TomlProfiles(pub BTreeMap<String, TomlProfile>);
+
+impl TomlProfiles {
+ pub fn get_all(&self) -> &BTreeMap<String, TomlProfile> {
+ &self.0
+ }
+
+ pub fn get(&self, name: &str) -> Option<&TomlProfile> {
+ self.0.get(name)
+ }
+}
+
+#[derive(Deserialize, Serialize, Clone, Debug, Default, Eq, PartialEq)]
+#[serde(default, rename_all = "kebab-case")]
+pub struct TomlProfile {
+ pub opt_level: Option<TomlOptLevel>,
+ pub lto: Option<StringOrBool>,
+ pub codegen_backend: Option<String>,
+ pub codegen_units: Option<u32>,
+ pub debug: Option<TomlDebugInfo>,
+ pub split_debuginfo: Option<String>,
+ pub debug_assertions: Option<bool>,
+ pub rpath: Option<bool>,
+ pub panic: Option<String>,
+ pub overflow_checks: Option<bool>,
+ pub incremental: Option<bool>,
+ pub dir_name: Option<String>,
+ pub inherits: Option<String>,
+ pub strip: Option<StringOrBool>,
+ // Note that `rustflags` is used for the cargo-feature `profile_rustflags`
+ pub rustflags: Option<Vec<String>>,
+ // These two fields must be last because they are sub-tables, and TOML
+ // requires all non-tables to be listed first.
+ pub package: Option<BTreeMap<ProfilePackageSpec, TomlProfile>>,
+ pub build_override: Option<Box<TomlProfile>>,
+ /// Unstable feature `-Ztrim-paths`.
+ pub trim_paths: Option<TomlTrimPaths>,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
+pub enum ProfilePackageSpec {
+ Spec(PackageIdSpec),
+ All,
+}
+
+impl fmt::Display for ProfilePackageSpec {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ ProfilePackageSpec::Spec(spec) => spec.fmt(f),
+ ProfilePackageSpec::All => f.write_str("*"),
+ }
+ }
+}
+
+impl ser::Serialize for ProfilePackageSpec {
+ fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
+ where
+ S: ser::Serializer,
+ {
+ self.to_string().serialize(s)
+ }
+}
+
+impl<'de> de::Deserialize<'de> for ProfilePackageSpec {
+ fn deserialize<D>(d: D) -> Result<ProfilePackageSpec, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ let string = String::deserialize(d)?;
+ if string == "*" {
+ Ok(ProfilePackageSpec::All)
+ } else {
+ PackageIdSpec::parse(&string)
+ .map_err(de::Error::custom)
+ .map(ProfilePackageSpec::Spec)
+ }
+ }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct TomlOptLevel(pub String);
+
+impl ser::Serialize for TomlOptLevel {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: ser::Serializer,
+ {
+ match self.0.parse::<u32>() {
+ Ok(n) => n.serialize(serializer),
+ Err(_) => self.0.serialize(serializer),
+ }
+ }
+}
+
+impl<'de> de::Deserialize<'de> for TomlOptLevel {
+ fn deserialize<D>(d: D) -> Result<TomlOptLevel, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ use serde::de::Error as _;
+ UntaggedEnumVisitor::new()
+ .expecting("an optimization level")
+ .i64(|value| Ok(TomlOptLevel(value.to_string())))
+ .string(|value| {
+ if value == "s" || value == "z" {
+ Ok(TomlOptLevel(value.to_string()))
+ } else {
+ Err(serde_untagged::de::Error::custom(format!(
+ "must be `0`, `1`, `2`, `3`, `s` or `z`, \
+ but found the string: \"{}\"",
+ value
+ )))
+ }
+ })
+ .deserialize(d)
+ }
+}
+
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Ord)]
+pub enum TomlDebugInfo {
+ None,
+ LineDirectivesOnly,
+ LineTablesOnly,
+ Limited,
+ Full,
+}
+
+impl Display for TomlDebugInfo {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ TomlDebugInfo::None => f.write_char('0'),
+ TomlDebugInfo::Limited => f.write_char('1'),
+ TomlDebugInfo::Full => f.write_char('2'),
+ TomlDebugInfo::LineDirectivesOnly => f.write_str("line-directives-only"),
+ TomlDebugInfo::LineTablesOnly => f.write_str("line-tables-only"),
+ }
+ }
+}
+
+impl ser::Serialize for TomlDebugInfo {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: ser::Serializer,
+ {
+ match self {
+ Self::None => 0.serialize(serializer),
+ Self::LineDirectivesOnly => "line-directives-only".serialize(serializer),
+ Self::LineTablesOnly => "line-tables-only".serialize(serializer),
+ Self::Limited => 1.serialize(serializer),
+ Self::Full => 2.serialize(serializer),
+ }
+ }
+}
+
+impl<'de> de::Deserialize<'de> for TomlDebugInfo {
+ fn deserialize<D>(d: D) -> Result<TomlDebugInfo, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ use serde::de::Error as _;
+ let expecting = "a boolean, 0, 1, 2, \"line-tables-only\", or \"line-directives-only\"";
+ UntaggedEnumVisitor::new()
+ .expecting(expecting)
+ .bool(|value| {
+ Ok(if value {
+ TomlDebugInfo::Full
+ } else {
+ TomlDebugInfo::None
+ })
+ })
+ .i64(|value| {
+ let debuginfo = match value {
+ 0 => TomlDebugInfo::None,
+ 1 => TomlDebugInfo::Limited,
+ 2 => TomlDebugInfo::Full,
+ _ => {
+ return Err(serde_untagged::de::Error::invalid_value(
+ Unexpected::Signed(value),
+ &expecting,
+ ))
+ }
+ };
+ Ok(debuginfo)
+ })
+ .string(|value| {
+ let debuginfo = match value {
+ "none" => TomlDebugInfo::None,
+ "limited" => TomlDebugInfo::Limited,
+ "full" => TomlDebugInfo::Full,
+ "line-directives-only" => TomlDebugInfo::LineDirectivesOnly,
+ "line-tables-only" => TomlDebugInfo::LineTablesOnly,
+ _ => {
+ return Err(serde_untagged::de::Error::invalid_value(
+ Unexpected::Str(value),
+ &expecting,
+ ))
+ }
+ };
+ Ok(debuginfo)
+ })
+ .deserialize(d)
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize)]
+#[serde(untagged, rename_all = "kebab-case")]
+pub enum TomlTrimPaths {
+ Values(Vec<TomlTrimPathsValue>),
+ All,
+}
+
+impl TomlTrimPaths {
+ pub fn none() -> Self {
+ TomlTrimPaths::Values(Vec::new())
+ }
+
+ pub fn is_none(&self) -> bool {
+ match self {
+ TomlTrimPaths::Values(v) => v.is_empty(),
+ TomlTrimPaths::All => false,
+ }
+ }
+}
+
+impl<'de> de::Deserialize<'de> for TomlTrimPaths {
+ fn deserialize<D>(d: D) -> Result<TomlTrimPaths, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ use serde::de::Error as _;
+ let expecting = r#"a boolean, "none", "diagnostics", "macro", "object", "all", or an array with these options"#;
+ UntaggedEnumVisitor::new()
+ .expecting(expecting)
+ .bool(|value| {
+ Ok(if value {
+ TomlTrimPaths::All
+ } else {
+ TomlTrimPaths::none()
+ })
+ })
+ .string(|v| match v {
+ "none" => Ok(TomlTrimPaths::none()),
+ "all" => Ok(TomlTrimPaths::All),
+ v => {
+ let d = v.into_deserializer();
+ let err = |_: D::Error| {
+ serde_untagged::de::Error::custom(format!("expected {expecting}"))
+ };
+ TomlTrimPathsValue::deserialize(d)
+ .map_err(err)
+ .map(|v| v.into())
+ }
+ })
+ .seq(|seq| {
+ let seq: Vec<String> = seq.deserialize()?;
+ let seq: Vec<_> = seq
+ .into_iter()
+ .map(|s| TomlTrimPathsValue::deserialize(s.into_deserializer()))
+ .collect::<Result<_, _>>()?;
+ Ok(seq.into())
+ })
+ .deserialize(d)
+ }
+}
+
+impl fmt::Display for TomlTrimPaths {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ TomlTrimPaths::All => write!(f, "all"),
+ TomlTrimPaths::Values(v) if v.is_empty() => write!(f, "none"),
+ TomlTrimPaths::Values(v) => {
+ let mut iter = v.iter();
+ if let Some(value) = iter.next() {
+ write!(f, "{value}")?;
+ }
+ for value in iter {
+ write!(f, ",{value}")?;
+ }
+ Ok(())
+ }
+ }
+ }
+}
+
+impl From<TomlTrimPathsValue> for TomlTrimPaths {
+ fn from(value: TomlTrimPathsValue) -> Self {
+ TomlTrimPaths::Values(vec![value])
+ }
+}
+
+impl From<Vec<TomlTrimPathsValue>> for TomlTrimPaths {
+ fn from(value: Vec<TomlTrimPathsValue>) -> Self {
+ TomlTrimPaths::Values(value)
+ }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
+#[serde(rename_all = "kebab-case")]
+pub enum TomlTrimPathsValue {
+ Diagnostics,
+ Macro,
+ Object,
+}
+
+impl TomlTrimPathsValue {
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ TomlTrimPathsValue::Diagnostics => "diagnostics",
+ TomlTrimPathsValue::Macro => "macro",
+ TomlTrimPathsValue::Object => "object",
+ }
+ }
+}
+
+impl fmt::Display for TomlTrimPathsValue {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.as_str())
+ }
+}
+
+pub type TomlLibTarget = TomlTarget;
+pub type TomlBinTarget = TomlTarget;
+pub type TomlExampleTarget = TomlTarget;
+pub type TomlTestTarget = TomlTarget;
+pub type TomlBenchTarget = TomlTarget;
+
+#[derive(Default, Serialize, Deserialize, Debug, Clone)]
+#[serde(rename_all = "kebab-case")]
+pub struct TomlTarget {
+ pub name: Option<String>,
+
+ // The intention was to only accept `crate-type` here but historical
+ // versions of Cargo also accepted `crate_type`, so look for both.
+ pub crate_type: Option<Vec<String>>,
+ #[serde(rename = "crate_type")]
+ pub crate_type2: Option<Vec<String>>,
+
+ pub path: Option<PathValue>,
+ // Note that `filename` is used for the cargo-feature `different_binary_name`
+ pub filename: Option<String>,
+ pub test: Option<bool>,
+ pub doctest: Option<bool>,
+ pub bench: Option<bool>,
+ pub doc: Option<bool>,
+ pub plugin: Option<bool>,
+ pub doc_scrape_examples: Option<bool>,
+ #[serde(rename = "proc-macro")]
+ pub proc_macro_raw: Option<bool>,
+ #[serde(rename = "proc_macro")]
+ pub proc_macro_raw2: Option<bool>,
+ pub harness: Option<bool>,
+ pub required_features: Option<Vec<String>>,
+ pub edition: Option<String>,
+}
+
+impl TomlTarget {
+ pub fn new() -> TomlTarget {
+ TomlTarget::default()
+ }
+
+ pub fn proc_macro(&self) -> Option<bool> {
+ self.proc_macro_raw.or(self.proc_macro_raw2).or_else(|| {
+ if let Some(types) = self.crate_types() {
+ if types.contains(&"proc-macro".to_string()) {
+ return Some(true);
+ }
+ }
+ None
+ })
+ }
+
+ pub fn crate_types(&self) -> Option<&Vec<String>> {
+ self.crate_type
+ .as_ref()
+ .or_else(|| self.crate_type2.as_ref())
+ }
+}
+
+/// Corresponds to a `target` entry, but `TomlTarget` is already used.
+#[derive(Serialize, Deserialize, Debug, Clone)]
+#[serde(rename_all = "kebab-case")]
+pub struct TomlPlatform {
+ pub dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ pub build_dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ #[serde(rename = "build_dependencies")]
+ pub build_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ pub dev_dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ #[serde(rename = "dev_dependencies")]
+ pub dev_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+}
+
+impl TomlPlatform {
+ pub fn dev_dependencies(&self) -> Option<&BTreeMap<String, MaybeWorkspaceDependency>> {
+ self.dev_dependencies
+ .as_ref()
+ .or(self.dev_dependencies2.as_ref())
+ }
+
+ pub fn build_dependencies(&self) -> Option<&BTreeMap<String, MaybeWorkspaceDependency>> {
+ self.build_dependencies
+ .as_ref()
+ .or(self.build_dependencies2.as_ref())
+ }
+}
+
+#[derive(Deserialize, Serialize, Debug, Clone)]
+#[serde(expecting = "a lints table")]
+#[serde(rename_all = "kebab-case")]
+pub struct MaybeWorkspaceLints {
+ #[serde(skip_serializing_if = "is_false")]
+ #[serde(deserialize_with = "bool_no_false", default)]
+ pub workspace: bool,
+ #[serde(flatten)]
+ pub lints: TomlLints,
+}
+
+fn is_false(b: &bool) -> bool {
+ !b
+}
+
+pub type TomlLints = BTreeMap<String, TomlToolLints>;
+
+pub type TomlToolLints = BTreeMap<String, TomlLint>;
+
+#[derive(Serialize, Debug, Clone)]
+#[serde(untagged)]
+pub enum TomlLint {
+ Level(TomlLintLevel),
+ Config(TomlLintConfig),
+}
+
+impl<'de> Deserialize<'de> for TomlLint {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ UntaggedEnumVisitor::new()
+ .string(|string| {
+ TomlLintLevel::deserialize(string.into_deserializer()).map(TomlLint::Level)
+ })
+ .map(|map| map.deserialize().map(TomlLint::Config))
+ .deserialize(deserializer)
+ }
+}
+
+impl TomlLint {
+ pub fn level(&self) -> TomlLintLevel {
+ match self {
+ Self::Level(level) => *level,
+ Self::Config(config) => config.level,
+ }
+ }
+
+ pub fn priority(&self) -> i8 {
+ match self {
+ Self::Level(_) => 0,
+ Self::Config(config) => config.priority,
+ }
+ }
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+#[serde(rename_all = "kebab-case")]
+pub struct TomlLintConfig {
+ pub level: TomlLintLevel,
+ #[serde(default)]
+ pub priority: i8,
+}
+
+#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
+#[serde(rename_all = "kebab-case")]
+pub enum TomlLintLevel {
+ Forbid,
+ Deny,
+ Warn,
+ Allow,
+}
+
+#[derive(Copy, Clone, Debug)]
+pub struct InvalidCargoFeatures {}
+
+impl<'de> de::Deserialize<'de> for InvalidCargoFeatures {
+ fn deserialize<D>(_d: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ use serde::de::Error as _;
+
+ Err(D::Error::custom(
+ "the field `cargo-features` should be set at the top of Cargo.toml before any tables",
+ ))
+ }
+}
+
+/// A StringOrVec can be parsed from either a TOML string or array,
+/// but is always stored as a vector.
+#[derive(Clone, Debug, Serialize, Eq, PartialEq, PartialOrd, Ord)]
+pub struct StringOrVec(pub Vec<String>);
+
+impl StringOrVec {
+ pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, String> {
+ self.0.iter()
+ }
+}
+
+impl<'de> de::Deserialize<'de> for StringOrVec {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ UntaggedEnumVisitor::new()
+ .expecting("string or list of strings")
+ .string(|value| Ok(StringOrVec(vec![value.to_owned()])))
+ .seq(|value| value.deserialize().map(StringOrVec))
+ .deserialize(deserializer)
+ }
+}
+
+#[derive(Clone, Debug, Serialize, Eq, PartialEq)]
+#[serde(untagged)]
+pub enum StringOrBool {
+ String(String),
+ Bool(bool),
+}
+
+impl<'de> Deserialize<'de> for StringOrBool {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ UntaggedEnumVisitor::new()
+ .bool(|b| Ok(StringOrBool::Bool(b)))
+ .string(|s| Ok(StringOrBool::String(s.to_owned())))
+ .deserialize(deserializer)
+ }
+}
+
+#[derive(PartialEq, Clone, Debug, Serialize)]
+#[serde(untagged)]
+pub enum VecStringOrBool {
+ VecString(Vec<String>),
+ Bool(bool),
+}
+
+impl<'de> de::Deserialize<'de> for VecStringOrBool {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ UntaggedEnumVisitor::new()
+ .expecting("a boolean or vector of strings")
+ .bool(|value| Ok(VecStringOrBool::Bool(value)))
+ .seq(|value| value.deserialize().map(VecStringOrBool::VecString))
+ .deserialize(deserializer)
+ }
+}
+
+#[derive(Clone)]
+pub struct PathValue(pub PathBuf);
+
+impl fmt::Debug for PathValue {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl ser::Serialize for PathValue {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: ser::Serializer,
+ {
+ self.0.serialize(serializer)
+ }
+}
+
+impl<'de> de::Deserialize<'de> for PathValue {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ Ok(PathValue(String::deserialize(deserializer)?.into()))
+ }
+}
diff --git a/src/tools/cargo/src/cargo/util/toml/targets.rs b/src/tools/cargo/src/cargo/util/toml/targets.rs
index afc4f258f..9d456ffd7 100644
--- a/src/tools/cargo/src/cargo/util/toml/targets.rs
+++ b/src/tools/cargo/src/cargo/util/toml/targets.rs
@@ -14,7 +14,7 @@ use std::collections::HashSet;
use std::fs::{self, DirEntry};
use std::path::{Path, PathBuf};
-use super::{
+use super::schema::{
PathValue, StringOrBool, StringOrVec, TomlBenchTarget, TomlBinTarget, TomlExampleTarget,
TomlLibTarget, TomlManifest, TomlTarget, TomlTestTarget,
};
@@ -23,6 +23,7 @@ use crate::core::compiler::CrateType;
use crate::core::{Edition, Feature, Features, Target};
use crate::util::errors::CargoResult;
use crate::util::restricted_names;
+use crate::util::toml::warn_on_deprecated;
use anyhow::Context as _;
@@ -31,7 +32,7 @@ const DEFAULT_BENCH_DIR_NAME: &'static str = "benches";
const DEFAULT_EXAMPLE_DIR_NAME: &'static str = "examples";
const DEFAULT_BIN_DIR_NAME: &'static str = "bin";
-pub fn targets(
+pub(super) fn targets(
features: &Features,
manifest: &TomlManifest,
package_name: &str,
@@ -105,7 +106,7 @@ pub fn targets(
)?);
// processing the custom build script
- if let Some(custom_build) = manifest.maybe_custom_build(custom_build, package_root) {
+ if let Some(custom_build) = maybe_custom_build(custom_build, package_root) {
if metabuild.is_some() {
anyhow::bail!("cannot specify both `metabuild` and `build`");
}
@@ -172,8 +173,8 @@ fn clean_lib(
};
let Some(ref lib) = lib else { return Ok(None) };
- lib.validate_proc_macro(warnings);
- lib.validate_crate_types("library", warnings);
+ validate_proc_macro(lib, "library", warnings);
+ validate_crate_types(lib, "library", warnings);
validate_target_name(lib, "library", "lib", warnings)?;
@@ -181,20 +182,22 @@ fn clean_lib(
(Some(path), _) => package_root.join(&path.0),
(None, Some(path)) => path,
(None, None) => {
- let legacy_path = package_root.join("src").join(format!("{}.rs", lib.name()));
+ let legacy_path = package_root
+ .join("src")
+ .join(format!("{}.rs", name_or_panic(lib)));
if edition == Edition::Edition2015 && legacy_path.exists() {
warnings.push(format!(
"path `{}` was erroneously implicitly accepted for library `{}`,\n\
please rename the file to `src/lib.rs` or set lib.path in Cargo.toml",
legacy_path.display(),
- lib.name()
+ name_or_panic(lib)
));
legacy_path
} else {
anyhow::bail!(
"can't find library `{}`, \
rename file to `src/lib.rs` or specify lib.path",
- lib.name()
+ name_or_panic(lib)
)
}
}
@@ -216,7 +219,7 @@ fn clean_lib(
{
anyhow::bail!(format!(
"library `{}` cannot set the crate type of both `dylib` and `cdylib`",
- lib.name()
+ name_or_panic(lib)
));
}
(Some(kinds), _, _) if kinds.contains(&"proc-macro".to_string()) => {
@@ -224,12 +227,12 @@ fn clean_lib(
// This is a warning to retain backwards compatibility.
warnings.push(format!(
"proc-macro library `{}` should not specify `plugin = true`",
- lib.name()
+ name_or_panic(lib)
));
}
warnings.push(format!(
"library `{}` should only specify `proc-macro = true` instead of setting `crate-type`",
- lib.name()
+ name_or_panic(lib)
));
if kinds.len() > 1 {
anyhow::bail!("cannot mix `proc-macro` crate type with others");
@@ -245,7 +248,7 @@ fn clean_lib(
(None, _, _) => vec![CrateType::Lib],
};
- let mut target = Target::lib_target(&lib.name(), crate_types, path, edition);
+ let mut target = Target::lib_target(name_or_panic(lib), crate_types, path, edition);
configure(lib, &mut target)?;
Ok(Some(target))
}
@@ -285,7 +288,7 @@ fn clean_bins(
validate_target_name(bin, "binary", "bin", warnings)?;
- let name = bin.name();
+ let name = name_or_panic(bin).to_owned();
if let Some(crate_types) = bin.crate_types() {
if !crate_types.is_empty() {
@@ -309,7 +312,7 @@ fn clean_bins(
if restricted_names::is_conflicting_artifact_name(&name) {
anyhow::bail!(
"the binary target name `{}` is forbidden, \
- it conflicts with with cargo's build directory names",
+ it conflicts with cargo's build directory names",
name
)
}
@@ -320,12 +323,12 @@ fn clean_bins(
let mut result = Vec::new();
for bin in &bins {
let path = target_path(bin, &inferred, "bin", package_root, edition, &mut |_| {
- if let Some(legacy_path) = legacy_bin_path(package_root, &bin.name(), has_lib) {
+ if let Some(legacy_path) = legacy_bin_path(package_root, name_or_panic(bin), has_lib) {
warnings.push(format!(
"path `{}` was erroneously implicitly accepted for binary `{}`,\n\
please set bin.path in Cargo.toml",
legacy_path.display(),
- bin.name()
+ name_or_panic(bin)
));
Some(legacy_path)
} else {
@@ -338,7 +341,7 @@ fn clean_bins(
};
let mut target = Target::bin_target(
- &bin.name(),
+ name_or_panic(bin),
bin.filename.clone(),
path,
bin.required_features.clone(),
@@ -398,14 +401,14 @@ fn clean_examples(
let mut result = Vec::new();
for (path, toml) in targets {
- toml.validate_crate_types("example", warnings);
+ validate_crate_types(&toml, "example", warnings);
let crate_types = match toml.crate_types() {
Some(kinds) => kinds.iter().map(|s| s.into()).collect(),
None => Vec::new(),
};
let mut target = Target::example_target(
- &toml.name(),
+ name_or_panic(&toml),
crate_types,
path,
toml.required_features.clone(),
@@ -443,8 +446,12 @@ fn clean_tests(
let mut result = Vec::new();
for (path, toml) in targets {
- let mut target =
- Target::test_target(&toml.name(), path, toml.required_features.clone(), edition);
+ let mut target = Target::test_target(
+ name_or_panic(&toml),
+ path,
+ toml.required_features.clone(),
+ edition,
+ );
configure(&toml, &mut target)?;
result.push(target);
}
@@ -464,14 +471,14 @@ fn clean_benches(
let targets = {
let mut legacy_bench_path = |bench: &TomlTarget| {
let legacy_path = package_root.join("src").join("bench.rs");
- if !(bench.name() == "bench" && legacy_path.exists()) {
+ if !(name_or_panic(bench) == "bench" && legacy_path.exists()) {
return None;
}
legacy_warnings.push(format!(
"path `{}` was erroneously implicitly accepted for benchmark `{}`,\n\
please set bench.path in Cargo.toml",
legacy_path.display(),
- bench.name()
+ name_or_panic(bench)
));
Some(legacy_path)
};
@@ -497,8 +504,12 @@ fn clean_benches(
let mut result = Vec::new();
for (path, toml) in targets {
- let mut target =
- Target::bench_target(&toml.name(), path, toml.required_features.clone(), edition);
+ let mut target = Target::bench_target(
+ name_or_panic(&toml),
+ path,
+ toml.required_features.clone(),
+ edition,
+ );
configure(&toml, &mut target)?;
result.push(target);
}
@@ -784,8 +795,8 @@ fn validate_target_name(
/// Will check a list of toml targets, and make sure the target names are unique within a vector.
fn validate_unique_names(targets: &[TomlTarget], target_kind: &str) -> CargoResult<()> {
let mut seen = HashSet::new();
- for name in targets.iter().map(|e| e.name()) {
- if !seen.insert(name.clone()) {
+ for name in targets.iter().map(|e| name_or_panic(e)) {
+ if !seen.insert(name) {
anyhow::bail!(
"found duplicate {target_kind} name {name}, \
but all {target_kind} targets must have a unique name",
@@ -875,7 +886,7 @@ fn target_path_not_found_error_message(
return [target_path_file, target_path_subdir];
}
- let target_name = target.name();
+ let target_name = name_or_panic(target);
let commonly_wrong_paths = possible_target_paths(&target_name, target_kind, true);
let possible_paths = possible_target_paths(&target_name, target_kind, false);
let existing_wrong_path_index = match (
@@ -922,7 +933,7 @@ fn target_path(
// Should we verify that this path exists here?
return Ok(package_root.join(&path.0));
}
- let name = target.name();
+ let name = name_or_panic(target).to_owned();
let mut matching = inferred
.iter()
@@ -955,7 +966,7 @@ fn target_path(
"\
cannot infer path for `{}` {}
Cargo doesn't know which to use because multiple target files found at `{}` and `{}`.",
- target.name(),
+ name_or_panic(target),
target_kind,
p0.strip_prefix(package_root).unwrap_or(&p0).display(),
p1.strip_prefix(package_root).unwrap_or(&p1).display(),
@@ -964,3 +975,52 @@ Cargo doesn't know which to use because multiple target files found at `{}` and
(None, Some(_)) => unreachable!(),
}
}
+
+/// Returns the path to the build script if one exists for this crate.
+fn maybe_custom_build(build: &Option<StringOrBool>, package_root: &Path) -> Option<PathBuf> {
+ let build_rs = package_root.join("build.rs");
+ match *build {
+ // Explicitly no build script.
+ Some(StringOrBool::Bool(false)) => None,
+ Some(StringOrBool::Bool(true)) => Some(build_rs),
+ Some(StringOrBool::String(ref s)) => Some(PathBuf::from(s)),
+ None => {
+ // If there is a `build.rs` file next to the `Cargo.toml`, assume it is
+ // a build script.
+ if build_rs.is_file() {
+ Some(build_rs)
+ } else {
+ None
+ }
+ }
+ }
+}
+
+fn name_or_panic(target: &TomlTarget) -> &str {
+ target
+ .name
+ .as_deref()
+ .unwrap_or_else(|| panic!("target name is required"))
+}
+
+fn validate_proc_macro(target: &TomlTarget, kind: &str, warnings: &mut Vec<String>) {
+ if target.proc_macro_raw.is_some() && target.proc_macro_raw2.is_some() {
+ warn_on_deprecated(
+ "proc-macro",
+ name_or_panic(target),
+ format!("{kind} target").as_str(),
+ warnings,
+ );
+ }
+}
+
+fn validate_crate_types(target: &TomlTarget, kind: &str, warnings: &mut Vec<String>) {
+ if target.crate_type.is_some() && target.crate_type2.is_some() {
+ warn_on_deprecated(
+ "crate-type",
+ name_or_panic(target),
+ format!("{kind} target").as_str(),
+ warnings,
+ );
+ }
+}
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 2f39b7ab4..88298fa8d 100644
--- a/src/tools/cargo/src/cargo/util/toml_mut/dependency.rs
+++ b/src/tools/cargo/src/cargo/util/toml_mut/dependency.rs
@@ -464,7 +464,7 @@ impl Dependency {
} else if let Some(table) = item.as_table_like_mut() {
match &self.source {
Some(Source::Registry(src)) => {
- table.insert("version", toml_edit::value(src.version.as_str()));
+ overwrite_value(table, "version", src.version.as_str());
for key in ["path", "git", "branch", "tag", "rev", "workspace"] {
table.remove(key);
@@ -472,9 +472,9 @@ impl Dependency {
}
Some(Source::Path(src)) => {
let relpath = path_field(crate_root, &src.path);
- table.insert("path", toml_edit::value(relpath));
+ overwrite_value(table, "path", relpath);
if let Some(r) = src.version.as_deref() {
- table.insert("version", toml_edit::value(r));
+ overwrite_value(table, "version", r);
} else {
table.remove("version");
}
@@ -484,24 +484,24 @@ impl Dependency {
}
}
Some(Source::Git(src)) => {
- table.insert("git", toml_edit::value(src.git.as_str()));
+ overwrite_value(table, "git", src.git.as_str());
if let Some(branch) = src.branch.as_deref() {
- table.insert("branch", toml_edit::value(branch));
+ overwrite_value(table, "branch", branch);
} else {
table.remove("branch");
}
if let Some(tag) = src.tag.as_deref() {
- table.insert("tag", toml_edit::value(tag));
+ overwrite_value(table, "tag", tag);
} else {
table.remove("tag");
}
if let Some(rev) = src.rev.as_deref() {
- table.insert("rev", toml_edit::value(rev));
+ overwrite_value(table, "rev", rev);
} else {
table.remove("rev");
}
if let Some(r) = src.version.as_deref() {
- table.insert("version", toml_edit::value(r));
+ overwrite_value(table, "version", r);
} else {
table.remove("version");
}
@@ -511,7 +511,7 @@ impl Dependency {
}
}
Some(Source::Workspace(_)) => {
- table.insert("workspace", toml_edit::value(true));
+ overwrite_value(table, "workspace", true);
table.set_dotted(true);
key.fmt();
for key in [
@@ -533,7 +533,7 @@ impl Dependency {
}
if table.contains_key("version") {
if let Some(r) = self.registry.as_deref() {
- table.insert("registry", toml_edit::value(r));
+ overwrite_value(table, "registry", r);
} else {
table.remove("registry");
}
@@ -542,11 +542,11 @@ impl Dependency {
}
if self.rename.is_some() {
- table.insert("package", toml_edit::value(self.name.as_str()));
+ overwrite_value(table, "package", self.name.as_str());
}
match self.default_features {
Some(v) => {
- table.insert("default-features", toml_edit::value(v));
+ overwrite_value(table, "default-features", v);
}
None => {
table.remove("default-features");
@@ -564,29 +564,40 @@ impl Dependency {
})
.unwrap_or_default();
features.extend(new_features.iter().map(|s| s.as_str()));
- let features = toml_edit::value(features.into_iter().collect::<toml_edit::Value>());
+ let features = features.into_iter().collect::<toml_edit::Value>();
table.set_dotted(false);
- table.insert("features", features);
+ overwrite_value(table, "features", features);
} else {
table.remove("features");
}
match self.optional {
Some(v) => {
table.set_dotted(false);
- table.insert("optional", toml_edit::value(v));
+ overwrite_value(table, "optional", v);
}
None => {
table.remove("optional");
}
}
-
- table.fmt();
} else {
unreachable!("Invalid dependency type: {}", item.type_name());
}
}
}
+fn overwrite_value(
+ table: &mut dyn toml_edit::TableLike,
+ key: &str,
+ value: impl Into<toml_edit::Value>,
+) {
+ let mut value = value.into();
+ let existing = table.entry(key).or_insert_with(|| Default::default());
+ if let Some(existing_value) = existing.as_value() {
+ *value.decor_mut() = existing_value.decor().clone();
+ }
+ *existing = toml_edit::Item::Value(value);
+}
+
fn invalid_type(dep: &str, key: &str, actual: &str, expected: &str) -> anyhow::Error {
anyhow::format_err!("Found {actual} for {key} when {expected} was expected for {dep}")
}
diff --git a/src/tools/cargo/src/cargo/util/toml_mut/manifest.rs b/src/tools/cargo/src/cargo/util/toml_mut/manifest.rs
index 5529b8029..e859af215 100644
--- a/src/tools/cargo/src/cargo/util/toml_mut/manifest.rs
+++ b/src/tools/cargo/src/cargo/util/toml_mut/manifest.rs
@@ -296,7 +296,7 @@ impl LocalManifest {
let s = self.manifest.data.to_string();
let new_contents_bytes = s.as_bytes();
- cargo_util::paths::write(&self.path, new_contents_bytes)
+ cargo_util::paths::write_atomic(&self.path, new_contents_bytes)
}
/// Lookup a dependency.
@@ -349,13 +349,16 @@ impl LocalManifest {
.get_key_value_mut(dep_key)
{
dep.update_toml(&crate_root, &mut dep_key, dep_item);
+ if let Some(table) = dep_item.as_inline_table_mut() {
+ // So long as we don't have `Cargo.toml` auto-formatting and inline-tables can only
+ // be on one line, there isn't really much in the way of interesting formatting to
+ // include (no comments), so let's just wipe it clean
+ table.fmt();
+ }
} else {
let new_dependency = dep.to_toml(&crate_root);
table[dep_key] = new_dependency;
}
- if let Some(t) = table.as_inline_table_mut() {
- t.fmt()
- }
Ok(())
}
@@ -364,17 +367,31 @@ impl LocalManifest {
pub fn remove_from_table(&mut self, table_path: &[String], name: &str) -> CargoResult<()> {
let parent_table = self.get_table_mut(table_path)?;
- let dep = parent_table
- .get_mut(name)
- .filter(|t| !t.is_none())
- .ok_or_else(|| non_existent_dependency_err(name, table_path.join(".")))?;
+ match parent_table.get_mut(name).filter(|t| !t.is_none()) {
+ Some(dep) => {
+ // remove the dependency
+ *dep = toml_edit::Item::None;
- // remove the dependency
- *dep = toml_edit::Item::None;
+ // remove table if empty
+ if parent_table.as_table_like().unwrap().is_empty() {
+ *parent_table = toml_edit::Item::None;
+ }
+ }
+ None => {
+ // Search in other tables.
+ let sections = self.get_sections();
+ let found_table_path = sections.iter().find_map(|(t, i)| {
+ let table_path: Vec<String> =
+ t.to_table().iter().map(|s| s.to_string()).collect();
+ i.get(name).is_some().then(|| table_path.join("."))
+ });
- // remove table if empty
- if parent_table.as_table_like().unwrap().is_empty() {
- *parent_table = toml_edit::Item::None;
+ return Err(non_existent_dependency_err(
+ name,
+ table_path.join("."),
+ found_table_path,
+ ));
+ }
}
Ok(())
@@ -494,12 +511,7 @@ fn fix_feature_activations(
// Remove found idx in revers order so we don't invalidate the idx.
for idx in remove_list.iter().rev() {
- feature_values.remove(*idx);
- }
- if !remove_list.is_empty() {
- // HACK: Instead of cleaning up the users formatting from having removed a feature, we just
- // re-format the whole feature list
- feature_values.fmt();
+ remove_array_index(feature_values, *idx);
}
if status == DependencyStatus::Required {
@@ -539,7 +551,53 @@ fn non_existent_table_err(table: impl std::fmt::Display) -> anyhow::Error {
fn non_existent_dependency_err(
name: impl std::fmt::Display,
- table: impl std::fmt::Display,
+ search_table: impl std::fmt::Display,
+ found_table: Option<impl std::fmt::Display>,
) -> anyhow::Error {
- anyhow::format_err!("the dependency `{name}` could not be found in `{table}`.")
+ let mut msg = format!("the dependency `{name}` could not be found in `{search_table}`");
+ if let Some(found_table) = found_table {
+ msg.push_str(&format!("; it is present in `{found_table}`",));
+ }
+ anyhow::format_err!(msg)
+}
+
+fn remove_array_index(array: &mut toml_edit::Array, index: usize) {
+ let value = array.remove(index);
+
+ // Captures all lines before leading whitespace
+ let prefix_lines = value
+ .decor()
+ .prefix()
+ .and_then(|p| p.as_str().expect("spans removed").rsplit_once('\n'))
+ .map(|(lines, _current)| lines);
+ // Captures all lines after trailing whitespace, before the next comma
+ let suffix_lines = value
+ .decor()
+ .suffix()
+ .and_then(|p| p.as_str().expect("spans removed").split_once('\n'))
+ .map(|(_current, lines)| lines);
+ let mut merged_lines = String::new();
+ if let Some(prefix_lines) = prefix_lines {
+ merged_lines.push_str(prefix_lines);
+ merged_lines.push('\n');
+ }
+ if let Some(suffix_lines) = suffix_lines {
+ merged_lines.push_str(suffix_lines);
+ merged_lines.push('\n');
+ }
+
+ let next_index = index; // Since `index` was removed, that effectively auto-advances us
+ if let Some(next) = array.get_mut(next_index) {
+ let next_decor = next.decor_mut();
+ let next_prefix = next_decor
+ .prefix()
+ .map(|s| s.as_str().expect("spans removed"))
+ .unwrap_or_default();
+ merged_lines.push_str(next_prefix);
+ next_decor.set_prefix(merged_lines);
+ } else {
+ let trailing = array.trailing().as_str().expect("spans removed");
+ merged_lines.push_str(trailing);
+ array.set_trailing(merged_lines);
+ }
}
diff --git a/src/tools/cargo/src/cargo/util/toml_mut/mod.rs b/src/tools/cargo/src/cargo/util/toml_mut/mod.rs
index bdd70e8e6..cb5d3aaf2 100644
--- a/src/tools/cargo/src/cargo/util/toml_mut/mod.rs
+++ b/src/tools/cargo/src/cargo/util/toml_mut/mod.rs
@@ -11,3 +11,19 @@
pub mod dependency;
pub mod manifest;
+
+// Based on Iterator::is_sorted from nightly std; remove in favor of that when stabilized.
+pub fn is_sorted(mut it: impl Iterator<Item = impl PartialOrd>) -> bool {
+ let Some(mut last) = it.next() else {
+ return true;
+ };
+
+ for curr in it {
+ if curr < last {
+ return false;
+ }
+ last = curr;
+ }
+
+ true
+}
diff --git a/src/tools/cargo/src/cargo/util/workspace.rs b/src/tools/cargo/src/cargo/util/workspace.rs
index e8317f101..a2e0fff50 100644
--- a/src/tools/cargo/src/cargo/util/workspace.rs
+++ b/src/tools/cargo/src/cargo/util/workspace.rs
@@ -87,11 +87,11 @@ pub fn print_available_binaries(ws: &Workspace<'_>, options: &CompileOptions) ->
}
pub fn print_available_benches(ws: &Workspace<'_>, options: &CompileOptions) -> CargoResult<()> {
- print_available_targets(Target::is_bench, ws, options, "--bench", "benches")
+ print_available_targets(Target::is_bench, ws, options, "--bench", "bench targets")
}
pub fn print_available_tests(ws: &Workspace<'_>, options: &CompileOptions) -> CargoResult<()> {
- print_available_targets(Target::is_test, ws, options, "--test", "tests")
+ print_available_targets(Target::is_test, ws, options, "--test", "test targets")
}
/// The path that we pass to rustc is actually fairly important because it will
diff --git a/src/tools/cargo/src/cargo/util_semver.rs b/src/tools/cargo/src/cargo/util_semver.rs
new file mode 100644
index 000000000..a84c9ee58
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util_semver.rs
@@ -0,0 +1,195 @@
+use std::fmt::{self, Display};
+
+use semver::{Comparator, Op, Version, VersionReq};
+use serde_untagged::UntaggedEnumVisitor;
+
+pub trait VersionExt {
+ fn is_prerelease(&self) -> bool;
+
+ fn to_exact_req(&self) -> VersionReq;
+}
+
+impl VersionExt for Version {
+ fn is_prerelease(&self) -> bool {
+ !self.pre.is_empty()
+ }
+
+ fn to_exact_req(&self) -> VersionReq {
+ VersionReq {
+ comparators: vec![Comparator {
+ op: Op::Exact,
+ major: self.major,
+ minor: Some(self.minor),
+ patch: Some(self.patch),
+ pre: self.pre.clone(),
+ }],
+ }
+ }
+}
+
+#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Debug)]
+pub struct PartialVersion {
+ pub major: u64,
+ pub minor: Option<u64>,
+ pub patch: Option<u64>,
+ pub pre: Option<semver::Prerelease>,
+ pub build: Option<semver::BuildMetadata>,
+}
+
+impl PartialVersion {
+ pub fn to_version(&self) -> Option<Version> {
+ Some(Version {
+ major: self.major,
+ minor: self.minor?,
+ patch: self.patch?,
+ pre: self.pre.clone().unwrap_or_default(),
+ build: self.build.clone().unwrap_or_default(),
+ })
+ }
+
+ pub fn to_caret_req(&self) -> VersionReq {
+ VersionReq {
+ comparators: vec![Comparator {
+ op: semver::Op::Caret,
+ major: self.major,
+ minor: self.minor,
+ patch: self.patch,
+ pre: self.pre.as_ref().cloned().unwrap_or_default(),
+ }],
+ }
+ }
+
+ /// Check if this matches a version, including build metadata
+ ///
+ /// Build metadata does not affect version precedence but may be necessary for uniquely
+ /// identifying a package.
+ pub fn matches(&self, version: &Version) -> bool {
+ if !version.pre.is_empty() && self.pre.is_none() {
+ // Pre-release versions must be explicitly opted into, if for no other reason than to
+ // give us room to figure out and define the semantics
+ return false;
+ }
+ self.major == version.major
+ && self.minor.map(|f| f == version.minor).unwrap_or(true)
+ && self.patch.map(|f| f == version.patch).unwrap_or(true)
+ && self.pre.as_ref().map(|f| f == &version.pre).unwrap_or(true)
+ && self
+ .build
+ .as_ref()
+ .map(|f| f == &version.build)
+ .unwrap_or(true)
+ }
+}
+
+impl From<semver::Version> for PartialVersion {
+ fn from(ver: semver::Version) -> Self {
+ let pre = if ver.pre.is_empty() {
+ None
+ } else {
+ Some(ver.pre)
+ };
+ let build = if ver.build.is_empty() {
+ None
+ } else {
+ Some(ver.build)
+ };
+ Self {
+ major: ver.major,
+ minor: Some(ver.minor),
+ patch: Some(ver.patch),
+ pre,
+ build,
+ }
+ }
+}
+
+impl std::str::FromStr for PartialVersion {
+ type Err = anyhow::Error;
+
+ fn from_str(value: &str) -> Result<Self, Self::Err> {
+ if is_req(value) {
+ anyhow::bail!("unexpected version requirement, expected a version like \"1.32\"")
+ }
+ match semver::Version::parse(value) {
+ Ok(ver) => Ok(ver.into()),
+ Err(_) => {
+ // HACK: Leverage `VersionReq` for partial version parsing
+ let mut version_req = match semver::VersionReq::parse(value) {
+ Ok(req) => req,
+ Err(_) if value.contains('-') => {
+ anyhow::bail!(
+ "unexpected prerelease field, expected a version like \"1.32\""
+ )
+ }
+ Err(_) if value.contains('+') => {
+ anyhow::bail!("unexpected build field, expected a version like \"1.32\"")
+ }
+ Err(_) => anyhow::bail!("expected a version like \"1.32\""),
+ };
+ assert_eq!(version_req.comparators.len(), 1, "guaranteed by is_req");
+ let comp = version_req.comparators.pop().unwrap();
+ assert_eq!(comp.op, semver::Op::Caret, "guaranteed by is_req");
+ let pre = if comp.pre.is_empty() {
+ None
+ } else {
+ Some(comp.pre)
+ };
+ Ok(Self {
+ major: comp.major,
+ minor: comp.minor,
+ patch: comp.patch,
+ pre,
+ build: None,
+ })
+ }
+ }
+ }
+}
+
+impl Display for PartialVersion {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let major = self.major;
+ write!(f, "{major}")?;
+ if let Some(minor) = self.minor {
+ write!(f, ".{minor}")?;
+ }
+ if let Some(patch) = self.patch {
+ write!(f, ".{patch}")?;
+ }
+ if let Some(pre) = self.pre.as_ref() {
+ write!(f, "-{pre}")?;
+ }
+ if let Some(build) = self.build.as_ref() {
+ write!(f, "+{build}")?;
+ }
+ Ok(())
+ }
+}
+
+impl serde::Serialize for PartialVersion {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: serde::Serializer,
+ {
+ serializer.collect_str(self)
+ }
+}
+
+impl<'de> serde::Deserialize<'de> for PartialVersion {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ UntaggedEnumVisitor::new()
+ .expecting("SemVer version")
+ .string(|value| value.parse().map_err(serde::de::Error::custom))
+ .deserialize(deserializer)
+ }
+}
+
+fn is_req(value: &str) -> bool {
+ let Some(first) = value.chars().next() else {
+ return false;
+ };
+ "<>=^~".contains(first) || value.contains('*') || value.contains(',')
+}
diff --git a/src/tools/cargo/src/doc/contrib/book.toml b/src/tools/cargo/src/doc/contrib/book.toml
index 628179c0d..e322fd8f3 100644
--- a/src/tools/cargo/src/doc/contrib/book.toml
+++ b/src/tools/cargo/src/doc/contrib/book.toml
@@ -5,6 +5,8 @@ authors = ["Eric Huss"]
[output.html]
curly-quotes = true # Enable smart-punctuation feature for more than quotes.
git-repository-url = "https://github.com/rust-lang/cargo/tree/master/src/doc/contrib/src"
+edit-url-template = "https://github.com/rust-lang/cargo/edit/master/src/doc/contrib/{path}"
+search.use-boolean-and = true
[output.html.redirect]
"/apidoc/cargo/index.html" = "https://doc.rust-lang.org/nightly/nightly-rustc/cargo/"
diff --git a/src/tools/cargo/src/doc/contrib/src/SUMMARY.md b/src/tools/cargo/src/doc/contrib/src/SUMMARY.md
index f8796b3f3..e97028eb5 100644
--- a/src/tools/cargo/src/doc/contrib/src/SUMMARY.md
+++ b/src/tools/cargo/src/doc/contrib/src/SUMMARY.md
@@ -11,9 +11,11 @@
- [Design Principles](./design.md)
- [Implementing a Change](./implementation/index.md)
- [Architecture](./implementation/architecture.md)
+ - [New packages](./implementation/packages.md)
- [New subcommands](./implementation/subcommands.md)
- [Console Output](./implementation/console.md)
- [Filesystem](./implementation/filesystem.md)
+ - [Formatting](./implementation/formatting.md)
- [Debugging](./implementation/debugging.md)
- [Tests](./tests/index.md)
- [Running Tests](./tests/running.md)
diff --git a/src/tools/cargo/src/doc/contrib/src/implementation/formatting.md b/src/tools/cargo/src/doc/contrib/src/implementation/formatting.md
new file mode 100644
index 000000000..91321f1a5
--- /dev/null
+++ b/src/tools/cargo/src/doc/contrib/src/implementation/formatting.md
@@ -0,0 +1,17 @@
+# Formatting
+
+When modifying user files, like `Cargo.toml`, we should not change other
+sections of the file,
+preserving the general formatting.
+This includes the table, inline-table, or array that a field is being edited in.
+
+When adding new entries, they do not need to match the canonical style of the
+document but can use the default formatting.
+If the entry is already sorted, preserving the sort order is preferred.
+
+When removing entries,
+comments on the same line should be removed but comments on following lines
+should be preserved.
+
+Inconsistencies in style after making a change are left to the user and their
+preferred auto-formatter.
diff --git a/src/tools/cargo/src/doc/contrib/src/implementation/packages.md b/src/tools/cargo/src/doc/contrib/src/implementation/packages.md
new file mode 100644
index 000000000..e7d965eca
--- /dev/null
+++ b/src/tools/cargo/src/doc/contrib/src/implementation/packages.md
@@ -0,0 +1,52 @@
+# New Packages
+
+This chapter sketches out how to add a new package to the cargo workspace.
+
+## Steps
+
+Choose the relevant parent directory
+- `credential/` for credential-process related packages
+- `benches/` for benchmarking of cargo itself
+- `crates/` for everything else
+
+Run `cargo new <name>`
+- `<name>`:
+ - We tend to use `-` over `_`
+ - For internal APIs, to avoid collisions with third-party subcommands, we can use the `cargo-util-` prefix
+ - For xtasks, we use the `xtask-` prefix
+- `package.rust-version`
+ - Internal packages tend to have a policy of "latest" with a [`# MSRV:1` comment](#msrv-policy)
+ - Ecosystem packages tend to have a policy of "N-2" with a [`# MSRV:3` comment](#msrv-policy)
+ - If the right choice is inherited from the workspace, feel free to keep it that way
+- If running without [cargo new automatically adding to workspace](https://github.com/rust-lang/cargo/pull/12779), add it as a workspace member if not already captured by a glob
+
+If its an xtask,
+- Add it to `.cargo/config.toml`s `[alias]` table
+- Mark `package.publish = false`
+
+If needed to be published with `cargo`,
+add the package to `publish.py` in the repo root,
+in dependency order.
+
+Note: by adding the package to the workspace, you automatically get
+- CI running `cargo test`
+- CI verifying MSRV
+- CI checking for `cargo doc` warnings
+
+## MSRV Policy
+
+Our MSRV policies are
+- Internal packages: support latest version
+- Ecosystem packages: support latest 3 versions
+
+We proactively update the MSRV
+- So contributors don't shy away from using newer features, either assuming they
+ can't ask or feeling like they have to have a justification when asking
+- To avoid a de facto MSRV developing from staying on a version for a long
+ period of time, leaving users unhappy when their expectations aren't met
+
+To proactively update the MSRV, we use [RenovateBot](https://docs.renovatebot.com/)
+with the configuration file in `.github/renovatebot.json5`.
+To know what MSRV policy to use,
+it looks for comments of the form `# MSRV:N`,
+where `N` is the number of supported rust versions.
diff --git a/src/tools/cargo/src/doc/contrib/src/process/index.md b/src/tools/cargo/src/doc/contrib/src/process/index.md
index c9dae918c..b5cfa5e46 100644
--- a/src/tools/cargo/src/doc/contrib/src/process/index.md
+++ b/src/tools/cargo/src/doc/contrib/src/process/index.md
@@ -8,11 +8,6 @@ process.
Please read the guidelines below before working on an issue or new feature.
-**Due to limited review capacity, the Cargo team is not accepting new features
-or major changes at this time. Please consult with the team before opening a
-new PR. Only issues that have been explicitly marked as accepted will be
-reviewed.**
-
[Working on Cargo]: working-on-cargo.md
## Mentorship
@@ -30,8 +25,8 @@ an overview of the things the team is interested in and thinking about.
The [RFC Project Board] is used for tracking [RFCs].
[the 2020 roadmap]: https://blog.rust-lang.org/inside-rust/2020/01/10/cargo-in-2020.html
-[Roadmap Project Board]: https://github.com/rust-lang/cargo/projects/1
-[RFC Project Board]: https://github.com/rust-lang/cargo/projects/2
+[Roadmap Project Board]: https://github.com/orgs/rust-lang/projects/37
+[RFC Project Board]: https://github.com/orgs/rust-lang/projects/36
[RFCs]: https://github.com/rust-lang/rfcs/
## Working on issues
diff --git a/src/tools/cargo/src/doc/contrib/src/process/working-on-cargo.md b/src/tools/cargo/src/doc/contrib/src/process/working-on-cargo.md
index 1567197c4..68f20db38 100644
--- a/src/tools/cargo/src/doc/contrib/src/process/working-on-cargo.md
+++ b/src/tools/cargo/src/doc/contrib/src/process/working-on-cargo.md
@@ -17,10 +17,12 @@ We encourage people to discuss their design before hacking on code. This gives
the Cargo team a chance to know your idea more. Sometimes after a discussion,
we even find a way to solve the problem without coding! Typically, you
[file an issue] or start a thread on the [internals forum] before submitting a
-pull request. Please read [the process] of how features and bugs are managed in
-Cargo.
+pull request.
-## Checkout out the source
+Please read [the process] of how features and bugs are managed in Cargo.
+**Only issues that have been explicitly marked as [accepted] will be reviewed.**
+
+## Checkout the source
We use the "fork and pull" model [described here][development-models], where
contributors push changes to their personal fork and [create pull requests] to
@@ -170,3 +172,4 @@ more information on how Cargo releases are made.
[internals forum]: https://internals.rust-lang.org/c/tools-and-infrastructure/cargo
[file an issue]: https://github.com/rust-lang/cargo/issues
[the process]: index.md
+[accepted]: https://github.com/rust-lang/cargo/issues?q=is%3Aissue+is%3Aopen+label%3AS-accepted
diff --git a/src/tools/cargo/src/doc/man/cargo-add.md b/src/tools/cargo/src/doc/man/cargo-add.md
index 21c222a39..d3a62900a 100644
--- a/src/tools/cargo/src/doc/man/cargo-add.md
+++ b/src/tools/cargo/src/doc/man/cargo-add.md
@@ -35,7 +35,7 @@ When you add a package that is already present, the existing entry will be updat
Upon successful invocation, the enabled (`+`) and disabled (`-`) [features] of the specified
dependency will be listed in the command's output.
-[features]: ../reference/features.md
+[features]: ../reference/features.html
## OPTIONS
diff --git a/src/tools/cargo/src/doc/man/cargo-bench.md b/src/tools/cargo/src/doc/man/cargo-bench.md
index a2a602847..5b1c6ba78 100644
--- a/src/tools/cargo/src/doc/man/cargo-bench.md
+++ b/src/tools/cargo/src/doc/man/cargo-bench.md
@@ -34,7 +34,7 @@ Benchmarks are built with the `--test` option to `rustc` which creates a
special executable by linking your code with libtest. The executable
automatically runs all functions annotated with the `#[bench]` attribute.
Cargo passes the `--bench` flag to the test harness to tell it to run
-only benchmarks.
+only benchmarks, regardless of whether the harness is libtest or a custom harness.
The libtest harness may be disabled by setting `harness = false` in the target
manifest settings, in which case your code will need to provide its own `main`
diff --git a/src/tools/cargo/src/doc/man/cargo-install.md b/src/tools/cargo/src/doc/man/cargo-install.md
index 5431bdb79..8c520db0a 100644
--- a/src/tools/cargo/src/doc/man/cargo-install.md
+++ b/src/tools/cargo/src/doc/man/cargo-install.md
@@ -90,7 +90,7 @@ will be used, beginning discovery at `$PATH/.cargo/config.toml`.
{{#option "`--vers` _version_" "`--version` _version_" }}
Specify a version to install. This may be a [version
-requirement](../reference/specifying-dependencies.md), like `~1.2`, to have Cargo
+requirement](../reference/specifying-dependencies.html), like `~1.2`, to have Cargo
select the newest version from the given requirement. If the version does not
have a requirement operator (such as `^` or `~`), then it must be in the form
_MAJOR.MINOR.PATCH_, and will install exactly that version; it is *not*
diff --git a/src/tools/cargo/src/doc/man/cargo-login.md b/src/tools/cargo/src/doc/man/cargo-login.md
index 197636da8..46681f7ef 100644
--- a/src/tools/cargo/src/doc/man/cargo-login.md
+++ b/src/tools/cargo/src/doc/man/cargo-login.md
@@ -6,7 +6,7 @@ cargo-login --- Log in to a registry
## SYNOPSIS
-`cargo login` [_options_] [_token_] -- [_args_]
+`cargo login` [_options_] [_token_] [`--` _args_]
## DESCRIPTION
@@ -14,6 +14,8 @@ This command will run a credential provider to save a token so that commands
that require authentication, such as {{man "cargo-publish" 1}}, will be
automatically authenticated.
+All the arguments following the two dashes (`--`) are passed to the credential provider.
+
For the default `cargo:token` credential provider, the token is saved
in `$CARGO_HOME/credentials.toml`. `CARGO_HOME` defaults to `.cargo`
in your home directory.
diff --git a/src/tools/cargo/src/doc/man/cargo-rustc.md b/src/tools/cargo/src/doc/man/cargo-rustc.md
index 279c6dbd1..e60b12e8d 100644
--- a/src/tools/cargo/src/doc/man/cargo-rustc.md
+++ b/src/tools/cargo/src/doc/man/cargo-rustc.md
@@ -65,7 +65,7 @@ The `rustc` subcommand will treat the following named profiles with special beha
* `bench` --- Builds in the same was as the {{man "cargo-bench" 1}} command,
similar to the `test` profile.
-See the [the reference](../reference/profiles.html) for more details on profiles.
+See [the reference](../reference/profiles.html) for more details on profiles.
{{/option}}
{{> options-ignore-rust-version }}
diff --git a/src/tools/cargo/src/doc/man/cargo-vendor.md b/src/tools/cargo/src/doc/man/cargo-vendor.md
index b30d0d8dd..169e64db2 100644
--- a/src/tools/cargo/src/doc/man/cargo-vendor.md
+++ b/src/tools/cargo/src/doc/man/cargo-vendor.md
@@ -16,8 +16,10 @@ the vendor directory specified by `<path>` will contain all remote sources from
dependencies specified. Additional manifests beyond the default one can be
specified with the `-s` option.
-The `cargo vendor` command will also print out the configuration necessary
-to use the vendored sources, which you will need to add to `.cargo/config.toml`.
+The configuration necessary to use the vendored sources would be printed to
+stdout after `cargo vendor` completes the vendoring process.
+You will need to add or redirect it to your Cargo configuration file,
+which is usually `.cargo/config.toml` locally for the current package.
## OPTIONS
@@ -88,6 +90,10 @@ only a subset of the packages have changed.
cargo vendor -s ../path/to/Cargo.toml
+4. Vendor and redirect the necessary vendor configs to a config file.
+
+ cargo vendor > path/to/my/cargo/config.toml
+
## SEE ALSO
{{man "cargo" 1}}
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-add.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-add.txt
index 6447499dc..3bd4bd5aa 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-add.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-add.txt
@@ -33,8 +33,8 @@ DESCRIPTION
be updated with the flags specified.
Upon successful invocation, the enabled (+) and disabled (-) features
- <https://doc.rust-lang.org/cargo/reference/features.md> of the specified
- dependency will be listed in the command’s output.
+ <https://doc.rust-lang.org/cargo/reference/features.html> of the
+ specified dependency will be listed in the command’s output.
OPTIONS
Source options
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-bench.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-bench.txt
index 796fbd11b..a7013a077 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-bench.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-bench.txt
@@ -27,7 +27,8 @@ DESCRIPTION
special executable by linking your code with libtest. The executable
automatically runs all functions annotated with the #[bench] attribute.
Cargo passes the --bench flag to the test harness to tell it to run only
- benchmarks.
+ benchmarks, regardless of whether the harness is libtest or a custom
+ harness.
The libtest harness may be disabled by setting harness = false in the
target manifest settings, in which case your code will need to provide
@@ -235,7 +236,7 @@ OPTIONS
documentation for more details.
--profile name
- Benchmark with the given profile. See the the reference
+ Benchmark with the given profile. See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-build.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-build.txt
index 06a7a6b3c..db39e2011 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-build.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-build.txt
@@ -157,7 +157,7 @@ OPTIONS
--profile option for choosing a specific profile by name.
--profile name
- Build with the given profile. See the the reference
+ Build with the given profile. See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-check.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-check.txt
index b447455ee..5a3ea18f4 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-check.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-check.txt
@@ -161,7 +161,7 @@ OPTIONS
the test cfg option. See rustc tests
<https://doc.rust-lang.org/rustc/tests/index.html> for more detail.
- See the the reference
+ See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-doc.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-doc.txt
index 773d600c6..f34e08df9 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-doc.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-doc.txt
@@ -132,7 +132,7 @@ OPTIONS
--profile option for choosing a specific profile by name.
--profile name
- Document with the given profile. See the the reference
+ Document with the given profile. See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-fix.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-fix.txt
index 3e7910ca5..474c2beb3 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-fix.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-fix.txt
@@ -234,7 +234,7 @@ OPTIONS
the test cfg option. See rustc tests
<https://doc.rust-lang.org/rustc/tests/index.html> for more detail.
- See the the reference
+ See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-init.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-init.txt
index 678024881..5c7e386c3 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-init.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-init.txt
@@ -31,7 +31,7 @@ OPTIONS
--edition edition
Specify the Rust edition to use. Default is 2021. Possible values:
- 2015, 2018, 2021
+ 2015, 2018, 2021, 2024
--name name
Set the package name. Defaults to the directory name.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-install.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-install.txt
index 02790f1d0..0b4aece70 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-install.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-install.txt
@@ -94,7 +94,7 @@ OPTIONS
Install Options
--vers version, --version version
Specify a version to install. This may be a version requirement
- <https://doc.rust-lang.org/cargo/reference/specifying-dependencies.md>,
+ <https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html>,
like ~1.2, to have Cargo select the newest version from the given
requirement. If the version does not have a requirement operator
(such as ^ or ~), then it must be in the form MAJOR.MINOR.PATCH, and
@@ -212,7 +212,7 @@ OPTIONS
the --profile option for choosing a specific profile by name.
--profile name
- Install with the given profile. See the the reference
+ Install with the given profile. See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-login.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-login.txt
index ae8127fc9..585a7bf2e 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-login.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-login.txt
@@ -4,13 +4,16 @@ NAME
cargo-login — Log in to a registry
SYNOPSIS
- cargo login [options] [token] – [args]
+ cargo login [options] [token] [-- args]
DESCRIPTION
This command will run a credential provider to save a token so that
commands that require authentication, such as cargo-publish(1), will be
automatically authenticated.
+ All the arguments following the two dashes (--) are passed to the
+ credential provider.
+
For the default cargo:token credential provider, the token is saved in
$CARGO_HOME/credentials.toml. CARGO_HOME defaults to .cargo in your home
directory.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-new.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-new.txt
index 5d2c61b48..5f5ebbfbd 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-new.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-new.txt
@@ -26,7 +26,7 @@ OPTIONS
--edition edition
Specify the Rust edition to use. Default is 2021. Possible values:
- 2015, 2018, 2021
+ 2015, 2018, 2021, 2024
--name name
Set the package name. Defaults to the directory name.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-run.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-run.txt
index 495a08a6c..00e629337 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-run.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-run.txt
@@ -80,7 +80,7 @@ OPTIONS
--profile option for choosing a specific profile by name.
--profile name
- Run with the given profile. See the the reference
+ Run with the given profile. See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-rustc.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-rustc.txt
index af6ad9d59..866361e93 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-rustc.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-rustc.txt
@@ -165,7 +165,7 @@ OPTIONS
o bench — Builds in the same was as the cargo-bench(1) command,
similar to the test profile.
- See the the reference
+ See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-rustdoc.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-rustdoc.txt
index 5ba200644..3237c4cae 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-rustdoc.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-rustdoc.txt
@@ -148,7 +148,7 @@ OPTIONS
--profile option for choosing a specific profile by name.
--profile name
- Document with the given profile. See the the reference
+ Document with the given profile. See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
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 b992d0d2f..598c8f9af 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
@@ -262,7 +262,7 @@ OPTIONS
--profile option for choosing a specific profile by name.
--profile name
- Test with the given profile. See the the reference
+ Test with the given profile. See the reference
<https://doc.rust-lang.org/cargo/reference/profiles.html> for more
details on profiles.
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-vendor.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-vendor.txt
index c325b7534..f0314aedb 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-vendor.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-vendor.txt
@@ -13,9 +13,10 @@ DESCRIPTION
remote sources from dependencies specified. Additional manifests beyond
the default one can be specified with the -s option.
- The cargo vendor command will also print out the configuration necessary
- to use the vendored sources, which you will need to add to
- .cargo/config.toml.
+ The configuration necessary to use the vendored sources would be printed
+ to stdout after cargo vendor completes the vendoring process. You will
+ need to add or redirect it to your Cargo configuration file, which is
+ usually .cargo/config.toml locally for the current package.
OPTIONS
Vendor Options
@@ -157,6 +158,10 @@ EXAMPLES
cargo vendor -s ../path/to/Cargo.toml
+ 4. Vendor and redirect the necessary vendor configs to a config file.
+
+ cargo vendor > path/to/my/cargo/config.toml
+
SEE ALSO
cargo(1)
diff --git a/src/tools/cargo/src/doc/man/includes/options-new.md b/src/tools/cargo/src/doc/man/includes/options-new.md
index e9792f05e..0b39ae34f 100644
--- a/src/tools/cargo/src/doc/man/includes/options-new.md
+++ b/src/tools/cargo/src/doc/man/includes/options-new.md
@@ -11,7 +11,7 @@ Create a package with a library target (`src/lib.rs`).
{{#option "`--edition` _edition_" }}
Specify the Rust edition to use. Default is 2021.
-Possible values: 2015, 2018, 2021
+Possible values: 2015, 2018, 2021, 2024
{{/option}}
{{#option "`--name` _name_" }}
diff --git a/src/tools/cargo/src/doc/man/includes/options-profile-legacy-check.md b/src/tools/cargo/src/doc/man/includes/options-profile-legacy-check.md
index 0ec82e693..cb38df0f9 100644
--- a/src/tools/cargo/src/doc/man/includes/options-profile-legacy-check.md
+++ b/src/tools/cargo/src/doc/man/includes/options-profile-legacy-check.md
@@ -6,5 +6,5 @@ test mode which will enable checking tests and enable the `test` cfg option.
See [rustc tests](https://doc.rust-lang.org/rustc/tests/index.html) for more
detail.
-See the [the reference](../reference/profiles.html) for more details on profiles.
+See [the reference](../reference/profiles.html) for more details on profiles.
{{/option}}
diff --git a/src/tools/cargo/src/doc/man/includes/options-profile.md b/src/tools/cargo/src/doc/man/includes/options-profile.md
index 2452e7b14..242ca2254 100644
--- a/src/tools/cargo/src/doc/man/includes/options-profile.md
+++ b/src/tools/cargo/src/doc/man/includes/options-profile.md
@@ -1,4 +1,4 @@
{{#option "`--profile` _name_" }}
{{actionverb}} with the given profile.
-See the [the reference](../reference/profiles.html) for more details on profiles.
+See [the reference](../reference/profiles.html) for more details on profiles.
{{/option}}
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-add.md b/src/tools/cargo/src/doc/src/commands/cargo-add.md
index 03485e121..175111495 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-add.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-add.md
@@ -32,7 +32,7 @@ When you add a package that is already present, the existing entry will be updat
Upon successful invocation, the enabled (`+`) and disabled (`-`) [features] of the specified
dependency will be listed in the command's output.
-[features]: ../reference/features.md
+[features]: ../reference/features.html
## OPTIONS
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-bench.md b/src/tools/cargo/src/doc/src/commands/cargo-bench.md
index 45584023e..d50b35f11 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-bench.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-bench.md
@@ -30,7 +30,7 @@ Benchmarks are built with the `--test` option to `rustc` which creates a
special executable by linking your code with libtest. The executable
automatically runs all functions annotated with the `#[bench]` attribute.
Cargo passes the `--bench` flag to the test harness to tell it to run
-only benchmarks.
+only benchmarks, regardless of whether the harness is libtest or a custom harness.
The libtest harness may be disabled by setting `harness = false` in the target
manifest settings, in which case your code will need to provide its own `main`
@@ -278,7 +278,7 @@ target artifacts are placed in a separate directory. See the
<dt class="option-term" id="option-cargo-bench---profile"><a class="option-anchor" href="#option-cargo-bench---profile"></a><code>--profile</code> <em>name</em></dt>
<dd class="option-desc">Benchmark with the given profile.
-See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-build.md b/src/tools/cargo/src/doc/src/commands/cargo-build.md
index 8e517bd1f..70c38a05b 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-build.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-build.md
@@ -199,7 +199,7 @@ See also the <code>--profile</code> option for choosing a specific profile by na
<dt class="option-term" id="option-cargo-build---profile"><a class="option-anchor" href="#option-cargo-build---profile"></a><code>--profile</code> <em>name</em></dt>
<dd class="option-desc">Build with the given profile.
-See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-check.md b/src/tools/cargo/src/doc/src/commands/cargo-check.md
index 3d7d0a490..1bb0f85c1 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-check.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-check.md
@@ -198,7 +198,7 @@ See also the <code>--profile</code> option for choosing a specific profile by na
test mode which will enable checking tests and enable the <code>test</code> cfg option.
See <a href="https://doc.rust-lang.org/rustc/tests/index.html">rustc tests</a> for more
detail.</p>
-<p>See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+<p>See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-doc.md b/src/tools/cargo/src/doc/src/commands/cargo-doc.md
index 92843838c..aebb04c9d 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-doc.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-doc.md
@@ -172,7 +172,7 @@ See also the <code>--profile</code> option for choosing a specific profile by na
<dt class="option-term" id="option-cargo-doc---profile"><a class="option-anchor" href="#option-cargo-doc---profile"></a><code>--profile</code> <em>name</em></dt>
<dd class="option-desc">Document with the given profile.
-See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-fix.md b/src/tools/cargo/src/doc/src/commands/cargo-fix.md
index 4dde83d96..9211cf7f8 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-fix.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-fix.md
@@ -278,7 +278,7 @@ See also the <code>--profile</code> option for choosing a specific profile by na
test mode which will enable checking tests and enable the <code>test</code> cfg option.
See <a href="https://doc.rust-lang.org/rustc/tests/index.html">rustc tests</a> for more
detail.</p>
-<p>See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+<p>See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-init.md b/src/tools/cargo/src/doc/src/commands/cargo-init.md
index c0cf34b51..70b54802b 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-init.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-init.md
@@ -40,7 +40,7 @@ This is the default behavior.</dd>
<dt class="option-term" id="option-cargo-init---edition"><a class="option-anchor" href="#option-cargo-init---edition"></a><code>--edition</code> <em>edition</em></dt>
<dd class="option-desc">Specify the Rust edition to use. Default is 2021.
-Possible values: 2015, 2018, 2021</dd>
+Possible values: 2015, 2018, 2021, 2024</dd>
<dt class="option-term" id="option-cargo-init---name"><a class="option-anchor" href="#option-cargo-init---name"></a><code>--name</code> <em>name</em></dt>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-install.md b/src/tools/cargo/src/doc/src/commands/cargo-install.md
index 5640af87a..db0ff10db 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-install.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-install.md
@@ -94,7 +94,7 @@ will be used, beginning discovery at `$PATH/.cargo/config.toml`.
<dt class="option-term" id="option-cargo-install---vers"><a class="option-anchor" href="#option-cargo-install---vers"></a><code>--vers</code> <em>version</em></dt>
<dt class="option-term" id="option-cargo-install---version"><a class="option-anchor" href="#option-cargo-install---version"></a><code>--version</code> <em>version</em></dt>
-<dd class="option-desc">Specify a version to install. This may be a <a href="../reference/specifying-dependencies.md">version
+<dd class="option-desc">Specify a version to install. This may be a <a href="../reference/specifying-dependencies.html">version
requirement</a>, like <code>~1.2</code>, to have Cargo
select the newest version from the given requirement. If the version does not
have a requirement operator (such as <code>^</code> or <code>~</code>), then it must be in the form
@@ -242,7 +242,7 @@ See also the <code>--profile</code> option for choosing a specific profile by na
<dt class="option-term" id="option-cargo-install---profile"><a class="option-anchor" href="#option-cargo-install---profile"></a><code>--profile</code> <em>name</em></dt>
<dd class="option-desc">Install with the given profile.
-See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-login.md b/src/tools/cargo/src/doc/src/commands/cargo-login.md
index e2efcfe4f..ffdacfc5a 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-login.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-login.md
@@ -6,7 +6,7 @@ cargo-login --- Log in to a registry
## SYNOPSIS
-`cargo login` [_options_] [_token_] -- [_args_]
+`cargo login` [_options_] [_token_] [`--` _args_]
## DESCRIPTION
@@ -14,6 +14,8 @@ This command will run a credential provider to save a token so that commands
that require authentication, such as [cargo-publish(1)](cargo-publish.html), will be
automatically authenticated.
+All the arguments following the two dashes (`--`) are passed to the credential provider.
+
For the default `cargo:token` credential provider, the token is saved
in `$CARGO_HOME/credentials.toml`. `CARGO_HOME` defaults to `.cargo`
in your home directory.
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-new.md b/src/tools/cargo/src/doc/src/commands/cargo-new.md
index 144b6f2eb..4e9da6715 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-new.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-new.md
@@ -35,7 +35,7 @@ This is the default behavior.</dd>
<dt class="option-term" id="option-cargo-new---edition"><a class="option-anchor" href="#option-cargo-new---edition"></a><code>--edition</code> <em>edition</em></dt>
<dd class="option-desc">Specify the Rust edition to use. Default is 2021.
-Possible values: 2015, 2018, 2021</dd>
+Possible values: 2015, 2018, 2021, 2024</dd>
<dt class="option-term" id="option-cargo-new---name"><a class="option-anchor" href="#option-cargo-new---name"></a><code>--name</code> <em>name</em></dt>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-run.md b/src/tools/cargo/src/doc/src/commands/cargo-run.md
index c14ad77dc..8c24b8352 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-run.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-run.md
@@ -111,7 +111,7 @@ See also the <code>--profile</code> option for choosing a specific profile by na
<dt class="option-term" id="option-cargo-run---profile"><a class="option-anchor" href="#option-cargo-run---profile"></a><code>--profile</code> <em>name</em></dt>
<dd class="option-desc">Run with the given profile.
-See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-rustc.md b/src/tools/cargo/src/doc/src/commands/cargo-rustc.md
index 6ae52d965..f58c8fdda 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-rustc.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-rustc.md
@@ -197,7 +197,7 @@ tests</a> for more detail.</li>
<li><code>bench</code> — Builds in the same was as the <a href="cargo-bench.html">cargo-bench(1)</a> command,
similar to the <code>test</code> profile.</li>
</ul>
-<p>See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+<p>See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
<dt class="option-term" id="option-cargo-rustc---ignore-rust-version"><a class="option-anchor" href="#option-cargo-rustc---ignore-rust-version"></a><code>--ignore-rust-version</code></dt>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-rustdoc.md b/src/tools/cargo/src/doc/src/commands/cargo-rustdoc.md
index 6635cded5..2e0e67d97 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-rustdoc.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-rustdoc.md
@@ -191,7 +191,7 @@ See also the <code>--profile</code> option for choosing a specific profile by na
<dt class="option-term" id="option-cargo-rustdoc---profile"><a class="option-anchor" href="#option-cargo-rustdoc---profile"></a><code>--profile</code> <em>name</em></dt>
<dd class="option-desc">Document with the given profile.
-See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
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 6a6ae82d2..ef978570e 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-test.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-test.md
@@ -307,7 +307,7 @@ See also the <code>--profile</code> option for choosing a specific profile by na
<dt class="option-term" id="option-cargo-test---profile"><a class="option-anchor" href="#option-cargo-test---profile"></a><code>--profile</code> <em>name</em></dt>
<dd class="option-desc">Test with the given profile.
-See the <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
+See <a href="../reference/profiles.html">the reference</a> for more details on profiles.</dd>
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-vendor.md b/src/tools/cargo/src/doc/src/commands/cargo-vendor.md
index cf47fc256..84560a688 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-vendor.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-vendor.md
@@ -16,8 +16,10 @@ the vendor directory specified by `<path>` will contain all remote sources from
dependencies specified. Additional manifests beyond the default one can be
specified with the `-s` option.
-The `cargo vendor` command will also print out the configuration necessary
-to use the vendored sources, which you will need to add to `.cargo/config.toml`.
+The configuration necessary to use the vendored sources would be printed to
+stdout after `cargo vendor` completes the vendoring process.
+You will need to add or redirect it to your Cargo configuration file,
+which is usually `.cargo/config.toml` locally for the current package.
## OPTIONS
@@ -189,6 +191,10 @@ details on environment variables that Cargo reads.
cargo vendor -s ../path/to/Cargo.toml
+4. Vendor and redirect the necessary vendor configs to a config file.
+
+ cargo vendor > path/to/my/cargo/config.toml
+
## SEE ALSO
[cargo(1)](cargo.html)
diff --git a/src/tools/cargo/src/doc/src/reference/config.md b/src/tools/cargo/src/doc/src/reference/config.md
index 6a479b81b..25e17ac90 100644
--- a/src/tools/cargo/src/doc/src/reference/config.md
+++ b/src/tools/cargo/src/doc/src/reference/config.md
@@ -126,6 +126,7 @@ inherits = "dev" # Inherits settings from [profile.dev].
opt-level = 0 # Optimization level.
debug = true # Include debug info.
split-debuginfo = '...' # Debug info splitting behavior.
+strip = "none" # Removes symbols or debuginfo.
debug-assertions = true # Enables debug assertions.
overflow-checks = true # Enables runtime integer overflow checks.
lto = false # Sets link-time optimization.
@@ -133,7 +134,6 @@ 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.
@@ -180,6 +180,7 @@ metadata_key2 = "value"
quiet = false # whether cargo output is quiet
verbose = false # whether cargo provides verbose output
color = 'auto' # whether cargo colorizes output
+hyperlinks = true # whether cargo inserts links into output
progress.when = 'auto' # whether cargo shows progress bar
progress.width = 80 # width of progress bar
```
@@ -889,6 +890,13 @@ See [debug](profiles.md#debug).
See [split-debuginfo](profiles.md#split-debuginfo).
+#### `profile.<name>.strip`
+* Type: string or boolean
+* Default: See profile docs.
+* Environment: `CARGO_PROFILE_<name>_STRIP`
+
+See [strip](profiles.md#strip).
+
#### `profile.<name>.debug-assertions`
* Type: boolean
* Default: See profile docs.
@@ -926,21 +934,21 @@ See [opt-level](profiles.md#opt-level).
#### `profile.<name>.panic`
* Type: string
-* default: See profile docs.
+* Default: See profile docs.
* Environment: `CARGO_PROFILE_<name>_PANIC`
See [panic](profiles.md#panic).
#### `profile.<name>.rpath`
* Type: boolean
-* default: See profile docs.
+* Default: See profile docs.
* Environment: `CARGO_PROFILE_<name>_RPATH`
See [rpath](profiles.md#rpath).
#### `profile.<name>.strip`
* Type: string
-* default: See profile docs.
+* Default: See profile docs.
* Environment: `CARGO_PROFILE_<name>_STRIP`
See [strip](profiles.md#strip).
@@ -1264,6 +1272,13 @@ Controls whether or not colored output is used in the terminal. Possible values:
Can be overridden with the `--color` command-line option.
+#### `term.hyperlinks`
+* Type: bool
+* Default: auto-detect
+* Environment: `CARGO_TERM_HYPERLINKS`
+
+Controls whether or not hyperlinks are used in the terminal.
+
#### `term.progress.when`
* Type: string
* Default: "auto"
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 37c788f8d..3f0552613 100644
--- a/src/tools/cargo/src/doc/src/reference/environment-variables.md
+++ b/src/tools/cargo/src/doc/src/reference/environment-variables.md
@@ -396,7 +396,7 @@ let out_dir = env::var("OUT_DIR").unwrap();
* `CARGO_PKG_<var>` --- The package information variables, with the same names and values as are [provided during crate building][variables set for crates].
[`tracing`]: https://docs.rs/tracing
-[debug logging]: https://doc.crates.io/contrib/architecture/console.html#debug-logging
+[debug logging]: https://doc.crates.io/contrib/implementation/debugging.html#logging
[unix-like platforms]: ../../reference/conditional-compilation.html#unix-and-windows
[windows-like platforms]: ../../reference/conditional-compilation.html#unix-and-windows
[target family]: ../../reference/conditional-compilation.html#target_family
diff --git a/src/tools/cargo/src/doc/src/reference/features.md b/src/tools/cargo/src/doc/src/reference/features.md
index 9e521049c..e3a845d95 100644
--- a/src/tools/cargo/src/doc/src/reference/features.md
+++ b/src/tools/cargo/src/doc/src/reference/features.md
@@ -7,6 +7,13 @@ either be enabled or disabled. Features for the package being built can be
enabled on the command-line with flags such as `--features`. Features for
dependencies can be enabled in the dependency declaration in `Cargo.toml`.
+> **Note**: New crates or versions published on crates.io are now limited to
+> a maximum of 300 features. Exceptions are granted on a case-by-case basis.
+> See this [blog post] for details. Participation in solution discussions is
+> encouraged via the crates.io Zulip stream.
+
+[blog post]: https://blog.rust-lang.org/2023/10/26/broken-badges-and-23k-keywords.html
+
See also the [Features Examples] chapter for some examples of how features can
be used.
diff --git a/src/tools/cargo/src/doc/src/reference/manifest.md b/src/tools/cargo/src/doc/src/reference/manifest.md
index 5ecbe5117..e3168a47f 100644
--- a/src/tools/cargo/src/doc/src/reference/manifest.md
+++ b/src/tools/cargo/src/doc/src/reference/manifest.md
@@ -109,6 +109,8 @@ resolve dependencies, and for guidelines on setting your own version. See the
[SemVer compatibility] chapter for more details on exactly what constitutes a
breaking change.
+This field is optional and defaults to `0.0.0`. The field is required for publishing packages.
+
[Resolver]: resolver.md
[SemVer compatibility]: semver.md
@@ -258,9 +260,9 @@ The `license` field contains the name of the software license that the package
is released under. The `license-file` field contains the path to a file
containing the text of the license (relative to this `Cargo.toml`).
-[crates.io] interprets the `license` field as an [SPDX 2.1 license
-expression][spdx-2.1-license-expressions]. The name must be a known license
-from the [SPDX license list 3.11][spdx-license-list-3.11]. Parentheses are not
+[crates.io] interprets the `license` field as an [SPDX 2.3 license
+expression][spdx-2.3-license-expressions]. The name must be a known license
+from the [SPDX license list 3.20][spdx-license-list-3.20]. Parentheses are not
currently supported. See the [SPDX site] for more information.
SPDX license expressions support AND and OR operators to combine multiple
@@ -470,23 +472,22 @@ if any of those files change.
### The `publish` field
-The `publish` field can be used to prevent a package from being published to a
-package registry (like *crates.io*) by mistake, for instance to keep a package
-private in a company.
-
+The `publish` field can be used to control which registries names the package
+may be published to:
```toml
[package]
# ...
-publish = false
+publish = ["some-registry-name"]
```
-The value may also be an array of strings which are registry names that are
-allowed to be published to.
-
+To prevent a package from being published to a registry (like crates.io) by mistake,
+for instance to keep a package private in a company,
+you can omit the [`version`](#the-version-field) field.
+If you'd like to be more explicit, you can disable publishing:
```toml
[package]
# ...
-publish = ["some-registry-name"]
+publish = false
```
If publish array contains a single registry, `cargo publish` command will use
@@ -511,6 +512,10 @@ package-name = "my-awesome-android-app"
assets = "path/to/static"
```
+You'll need to look in the documentation for your tool to see how to use this field.
+For Rust Projects that use `package.metadata` tables, see:
+- [docs.rs](https://docs.rs/about/metadata)
+
There is a similar table at the workspace level at
[`workspace.metadata`][workspace-metadata]. While cargo does not specify a
format for the content of either of these tables, it is suggested that
@@ -628,9 +633,9 @@ more detail.
[docs.rs]: https://docs.rs/
[publishing]: publishing.md
[Rust Edition]: ../../edition-guide/index.html
-[spdx-2.1-license-expressions]: https://spdx.org/spdx-specification-21-web-version#h.jxpfx0ykyb60
-[spdx-license-list-3.11]: https://github.com/spdx/license-list-data/tree/v3.11
-[SPDX site]: https://spdx.org/license-list
+[spdx-2.3-license-expressions]: https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions/
+[spdx-license-list-3.20]: https://github.com/spdx/license-list-data/tree/v3.20
+[SPDX site]: https://spdx.org
[TOML]: https://toml.io/
<script>
diff --git a/src/tools/cargo/src/doc/src/reference/profiles.md b/src/tools/cargo/src/doc/src/reference/profiles.md
index 15ca8953c..165b41d60 100644
--- a/src/tools/cargo/src/doc/src/reference/profiles.md
+++ b/src/tools/cargo/src/doc/src/reference/profiles.md
@@ -270,7 +270,7 @@ The default settings for the `dev` profile are:
opt-level = 0
debug = true
split-debuginfo = '...' # Platform-specific.
-strip = false
+strip = "none"
debug-assertions = true
overflow-checks = true
lto = false
@@ -293,7 +293,7 @@ The default settings for the `release` profile are:
opt-level = 3
debug = false
split-debuginfo = '...' # Platform-specific.
-strip = false
+strip = "none"
debug-assertions = false
overflow-checks = false
lto = false
diff --git a/src/tools/cargo/src/doc/src/reference/publishing.md b/src/tools/cargo/src/doc/src/reference/publishing.md
index 54a8635fb..5dcb73d0f 100644
--- a/src/tools/cargo/src/doc/src/reference/publishing.md
+++ b/src/tools/cargo/src/doc/src/reference/publishing.md
@@ -139,7 +139,7 @@ Then run [`cargo publish`] as described above to upload the new version.
> **Recommendation:** Consider the full release process and automate what you can.
>
> Each version should include:
-> - A changelog entry, preferrably [manually curated](https://keepachangelog.com/en/1.0.0/) though a generated one is better than nothing
+> - A changelog entry, preferably [manually curated](https://keepachangelog.com/en/1.0.0/) though a generated one is better than nothing
> - A [git tag](https://git-scm.com/book/en/v2/Git-Basics-Tagging) pointing to the published commit
>
> Examples of third-party tools that are representative of different workflows include (in alphabetical order):
diff --git a/src/tools/cargo/src/doc/src/reference/registry-authentication.md b/src/tools/cargo/src/doc/src/reference/registry-authentication.md
index 9f8f7e979..f07bf7066 100644
--- a/src/tools/cargo/src/doc/src/reference/registry-authentication.md
+++ b/src/tools/cargo/src/doc/src/reference/registry-authentication.md
@@ -103,7 +103,7 @@ Install the provider with `cargo install cargo-credential-1password`
In the config, add to (or create) `registry.global-credential-providers`:
```toml
[registry]
-global-credential-providers = ["cargo:token", "cargo-credential-1password --email you@example.com"]
+global-credential-providers = ["cargo:token", "cargo-credential-1password --account my.1password.com"]
```
The values in `global-credential-providers` are split on spaces into path and command-line arguments. To
diff --git a/src/tools/cargo/src/doc/src/reference/resolver.md b/src/tools/cargo/src/doc/src/reference/resolver.md
index 7d01fb167..de9d21fbd 100644
--- a/src/tools/cargo/src/doc/src/reference/resolver.md
+++ b/src/tools/cargo/src/doc/src/reference/resolver.md
@@ -489,9 +489,43 @@ break the build.
The following illustrates some problems you may experience, and some possible
solutions.
+### Why was a dependency included?
+
+Say you see dependency `rand` in the `cargo check` output but don't think its needed and want to understand why its being pulled in.
+
+You can run
+```console
+$ cargo tree --workspace --target all --all-features --invert rand
+rand v0.8.5
+└── ...
+
+rand v0.8.5
+└── ...
+```
+
+You might identify that it was an activated feature that caused `rand` to show up. To figure out which package activated the feature, you can add the `--edges features`
+```console
+$ cargo tree --workspace --target all --all-features --edges features --invert rand
+rand v0.8.5
+└── ...
+
+rand v0.8.5
+└── ...
+```
+
### Unexpected dependency duplication
-The resolver algorithm may converge on a solution that includes two copies of a
+You see multiple instances of `rand` when you run
+```console
+$ cargo tree --workspace --target all --all-features --duplicates
+rand v0.7.3
+└── ...
+
+rand v0.8.5
+└── ...
+```
+
+The resolver algorithm has converged on a solution that includes two copies of a
dependency when one would suffice. For example:
```toml
@@ -517,6 +551,17 @@ But, if you run into this situation, the [`cargo update`] command with the
[`cargo update`]: ../commands/cargo-update.md
+### Why wasn't a newer version selected?
+
+Say you noticed that the latest version of a dependency wasn't selected when you ran:
+```console
+$ cargo update
+```
+You can enable some extra logging to see why this happened:
+```console
+$ env CARGO_LOG=cargo::core::resolver=trace cargo update
+```
+**Note:** Cargo log targets and levels may change over time.
### SemVer-breaking patch release breaks the build
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 746b5fcb2..2bdbbceee 100644
--- a/src/tools/cargo/src/doc/src/reference/specifying-dependencies.md
+++ b/src/tools/cargo/src/doc/src/reference/specifying-dependencies.md
@@ -135,7 +135,7 @@ separated with a comma, e.g., `>= 1.2, < 1.5`.
> Avoid constraining the upper bound of a version to be anything less than the
> next semver incompatible version
> (e.g. avoid `">=2.0, <2.4"`) as other packages in the dependency tree may
-> require a newer version, leading to an unresolvable error (see #6584).
+> require a newer version, leading to an unresolvable error (see [#9029]).
> Consider whether controlling the version in your [`Cargo.lock`] would be more
> appropriate.
>
@@ -152,7 +152,7 @@ separated with a comma, e.g., `>= 1.2, < 1.5`.
[`Cargo.lock`]: ../guide/cargo-toml-vs-cargo-lock.md
[#2222]: https://github.com/rust-lang/cargo/issues/2222
-[#6584]: https://github.com/rust-lang/cargo/issues/6584
+[#9029]: https://github.com/rust-lang/cargo/issues/9029
[#10599]: https://github.com/rust-lang/cargo/issues/10599
## Specifying dependencies from other registries
diff --git a/src/tools/cargo/src/doc/src/reference/unstable.md b/src/tools/cargo/src/doc/src/reference/unstable.md
index c8047de90..0683daa3c 100644
--- a/src/tools/cargo/src/doc/src/reference/unstable.md
+++ b/src/tools/cargo/src/doc/src/reference/unstable.md
@@ -93,6 +93,8 @@ For the latest nightly, see the [nightly version] of this page.
* [codegen-backend](#codegen-backend) --- Select the codegen backend used by rustc.
* [per-package-target](#per-package-target) --- Sets the `--target` to use for each individual package.
* [artifact dependencies](#artifact-dependencies) --- Allow build artifacts to be included into other build artifacts and build them for different targets.
+ * [Edition 2024](#edition-2024) — Adds support for the 2024 Edition.
+ * [Profile `trim-paths` option](#profile-trim-paths-option) --- Control the sanitization of file paths in build outputs.
* Information and metadata
* [Build-plan](#build-plan) --- Emits JSON information on which commands will be run.
* [unit-graph](#unit-graph) --- Emits JSON for Cargo's internal graph structure.
@@ -1079,34 +1081,14 @@ you are ok with dev-deps being build for `cargo doc`.
* RFC: [#3013](https://github.com/rust-lang/rfcs/pull/3013)
* Tracking Issue: [#10554](https://github.com/rust-lang/cargo/issues/10554)
-`-Z check-cfg` command line enables compile time checking of name and values in `#[cfg]`, `cfg!`,
-`#[link]` and `#[cfg_attr]` with the `rustc` and `rustdoc` unstable `--check-cfg` command line.
+`-Z check-cfg` command line enables compile time checking of Cargo features as well as `rustc`
+well known names and values in `#[cfg]`, `cfg!`, `#[link]` and `#[cfg_attr]` with the `rustc`
+and `rustdoc` unstable `--check-cfg` command line.
-It's values are:
- - `features`: enables features checking via `--check-cfg=values(feature, ...)`.
- Note than this command line options will probably become the default when stabilizing.
- - `names`: enables well known names checking via `--check-cfg=names()`.
- - `values`: enables well known values checking via `--check-cfg=values()`.
- - `output`: enable the use of `rustc-check-cfg` in build script.
+You can use the flag like this:
-For instance:
-
-```
-cargo check -Z unstable-options -Z check-cfg=features
-cargo check -Z unstable-options -Z check-cfg=names
-cargo check -Z unstable-options -Z check-cfg=values
-cargo check -Z unstable-options -Z check-cfg=features,names,values
```
-
-Or for `output`:
-
-```rust,no_run
-// build.rs
-println!("cargo:rustc-check-cfg=names(foo, bar)");
-```
-
-```
-cargo check -Z unstable-options -Z check-cfg=output
+cargo check -Z unstable-options -Z check-cfg
```
### `cargo:rustc-check-cfg=CHECK_CFG`
@@ -1115,12 +1097,23 @@ The `rustc-check-cfg` instruction tells Cargo to pass the given value to the
`--check-cfg` flag to the compiler. This may be used for compile-time
detection of unexpected conditional compilation name and/or values.
-This can only be used in combination with `-Zcheck-cfg=output` otherwise it is ignored
+This can only be used in combination with `-Zcheck-cfg` otherwise it is ignored
with a warning.
-If you want to integrate with Cargo features, use `-Zcheck-cfg=features` instead of
+If you want to integrate with Cargo features, only use `-Zcheck-cfg` instead of
trying to do it manually with this option.
+You can use the instruction like this:
+
+```rust,no_run
+// build.rs
+println!("cargo:rustc-check-cfg=cfg(foo, bar)");
+```
+
+```
+cargo check -Z unstable-options -Z check-cfg
+```
+
## codegen-backend
The `codegen-backend` feature makes it possible to select the codegen backend used by rustc using a profile.
@@ -1229,9 +1222,6 @@ at the start of the infostring at the top of the file.
Inferred / defaulted manifest fields:
- `package.name = <slugified file stem>`
-- `package.version = "0.0.0"` to [call attention to this crate being used in unexpected places](https://matklad.github.io/2021/08/22/large-rust-workspaces.html#Smaller-Tips)
-- `package.publish = false` to avoid accidental publishes, particularly if we
- later add support for including them in a workspace.
- `package.edition = <current>` to avoid always having to add an embedded
manifest at the cost of potentially breaking scripts on rust upgrades
- Warn when `edition` is unspecified to raise awareness of this
@@ -1271,6 +1261,128 @@ Differences between `cargo run --manifest-path <path>` and `cargo <path>`
### Documentation Updates
+## Edition 2024
+* Tracking Issue: (none created yet)
+* RFC: [rust-lang/rfcs#3501](https://github.com/rust-lang/rfcs/pull/3501)
+
+Support for the 2024 [edition] can be enabled by adding the `edition2024`
+unstable feature to the top of `Cargo.toml`:
+
+```toml
+cargo-features = ["edition2024"]
+
+[package]
+name = "my-package"
+version = "0.1.0"
+edition = "2024"
+```
+
+If you want to transition an existing project from a previous edition, then
+`cargo fix --edition` can be used on the nightly channel. After running `cargo
+fix`, you can switch the edition to 2024 as illustrated above.
+
+This feature is very unstable, and is only intended for early testing and
+experimentation. Future nightly releases may introduce changes for the 2024
+edition that may break your build.
+
+[edition]: ../../edition-guide/index.html
+
+## Profile `trim-paths` option
+
+* Tracking Issue: [rust-lang/cargo#12137](https://github.com/rust-lang/cargo/issues/12137)
+* Tracking Rustc Issue: [rust-lang/rust#111540](https://github.com/rust-lang/rust/issues/111540)
+
+This adds a new profile setting to control how paths are sanitized in the resulting binary.
+This can be enabled like so:
+
+```toml
+cargo-features = ["trim-paths"]
+
+[package]
+# ...
+
+[profile.release]
+trim-paths = ["diagnostics", "object"]
+```
+
+To set this in a profile in Cargo configuration,
+you need to use either `-Z trim-paths` or `[unstable]` table to enable it.
+For example,
+
+```toml
+# .cargo/config.toml
+[unstable]
+trim-paths = true
+
+[profile.release]
+trim-paths = ["diagnostics", "object"]
+```
+
+### Documentation updates
+
+#### trim-paths
+
+*as a new ["Profiles settings" entry](./profiles.html#profile-settings)*
+
+`trim-paths` is a profile setting which enables and controls the sanitization of file paths in build outputs.
+It takes the following values:
+
+- `"none"` and `false` --- disable path sanitization
+- `"macro"` --- sanitize paths in the expansion of `std::file!()` macro.
+ This is where paths in embedded panic messages come from
+- `"diagnostics"` --- sanitize paths in printed compiler diagnostics
+- `"object"` --- sanitize paths in compiled executables or libraries
+- `"all"` and `true` --- sanitize paths in all possible locations
+
+It also takes an array with the combinations of `"macro"`, `"diagnostics"`, and `"object"`.
+
+It is defaulted to `none` for the `dev` profile, and `object` for the `release` profile.
+You can manually override it by specifying this option in `Cargo.toml`:
+
+```toml
+[profile.dev]
+trim-paths = "all"
+
+[profile.release]
+trim-paths = ["object", "diagnostics"]
+```
+
+The default `release` profile setting (`object`) sanitizes only the paths in emitted executable or library files.
+It always affects paths from macros such as panic messages, and in debug information only if they will be embedded together with the binary
+(the default on platforms with ELF binaries, such as Linux and windows-gnu),
+but will not touch them if they are in separate files (the default on Windows MSVC and macOS).
+But the paths to these separate files are sanitized.
+
+If `trim-paths` is not `none` or `false`, then the following paths are sanitized if they appear in a selected scope:
+
+1. Path to the source files of the standard and core library (sysroot) will begin with `/rustc/[rustc commit hash]`,
+ e.g. `/home/username/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/result.rs` ->
+ `/rustc/fe72845f7bb6a77b9e671e6a4f32fe714962cec4/library/core/src/result.rs`
+2. Path to the current package will be stripped, relatively to the current workspace root, e.g. `/home/username/crate/src/lib.rs` -> `src/lib.rs`.
+3. Path to dependency packages will be replaced with `[package name]-[version]`. E.g. `/home/username/deps/foo/src/lib.rs` -> `foo-0.1.0/src/lib.rs`
+
+When a path to the source files of the standard and core library is *not* in scope for sanitization,
+the emitted path will depend on if `rust-src` component is present.
+If it is, then some paths will point to the copy of the source files on your file system;
+if it isn't, then they will show up as `/rustc/[rustc commit hash]/library/...`
+(just like when it is selected for sanitization).
+Paths to all other source files will not be affected.
+
+This will not affect any hard-coded paths in the source code, such as in strings.
+
+#### Environment variable
+
+*as a new entry of ["Environment variables Cargo sets for build scripts"](./environment-variables.md#environment-variables-cargo-sets-for-crates)*
+
+* `CARGO_TRIM_PATHS` --- The value of `trim-paths` profile option.
+ `false`, `"none"`, and empty arrays would be converted to `none`.
+ `true` and `"all"` become `all`.
+ Values in a non-empty array would be joined into a comma-separated list.
+ If the build script introduces absolute paths to built artifacts (such as by invoking a compiler),
+ the user may request them to be sanitized in different types of artifacts.
+ Common paths requiring sanitization include `OUT_DIR` and `CARGO_MANIFEST_DIR`,
+ plus any other introduced by the build script, such as include directories.
+
# Stabilized and removed features
## Compile progress
diff --git a/src/tools/cargo/src/etc/man/cargo-add.1 b/src/tools/cargo/src/etc/man/cargo-add.1
index f69e6d0db..4e8b68566 100644
--- a/src/tools/cargo/src/etc/man/cargo-add.1
+++ b/src/tools/cargo/src/etc/man/cargo-add.1
@@ -44,7 +44,7 @@ If no source is specified, then a best effort will be made to select one, includ
.sp
When you add a package that is already present, the existing entry will be updated with the flags specified.
.sp
-Upon successful invocation, the enabled (\fB+\fR) and disabled (\fB\-\fR) \fIfeatures\fR <https://doc.rust\-lang.org/cargo/reference/features.md> of the specified
+Upon successful invocation, the enabled (\fB+\fR) and disabled (\fB\-\fR) \fIfeatures\fR <https://doc.rust\-lang.org/cargo/reference/features.html> of the specified
dependency will be listed in the command\[cq]s output.
.SH "OPTIONS"
.SS "Source options"
diff --git a/src/tools/cargo/src/etc/man/cargo-bench.1 b/src/tools/cargo/src/etc/man/cargo-bench.1
index 64498c4d6..0f5a993e0 100644
--- a/src/tools/cargo/src/etc/man/cargo-bench.1
+++ b/src/tools/cargo/src/etc/man/cargo-bench.1
@@ -32,7 +32,7 @@ Benchmarks are built with the \fB\-\-test\fR option to \fBrustc\fR which creates
special executable by linking your code with libtest. The executable
automatically runs all functions annotated with the \fB#[bench]\fR attribute.
Cargo passes the \fB\-\-bench\fR flag to the test harness to tell it to run
-only benchmarks.
+only benchmarks, regardless of whether the harness is libtest or a custom harness.
.sp
The libtest harness may be disabled by setting \fBharness = false\fR in the target
manifest settings, in which case your code will need to provide its own \fBmain\fR
@@ -282,7 +282,7 @@ target artifacts are placed in a separate directory. See the
\fB\-\-profile\fR \fIname\fR
.RS 4
Benchmark with the given profile.
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-build.1 b/src/tools/cargo/src/etc/man/cargo-build.1
index 6bcfe8093..4194d7b77 100644
--- a/src/tools/cargo/src/etc/man/cargo-build.1
+++ b/src/tools/cargo/src/etc/man/cargo-build.1
@@ -188,7 +188,7 @@ See also the \fB\-\-profile\fR option for choosing a specific profile by name.
\fB\-\-profile\fR \fIname\fR
.RS 4
Build with the given profile.
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-check.1 b/src/tools/cargo/src/etc/man/cargo-check.1
index fdbb84647..43d570058 100644
--- a/src/tools/cargo/src/etc/man/cargo-check.1
+++ b/src/tools/cargo/src/etc/man/cargo-check.1
@@ -190,7 +190,7 @@ test mode which will enable checking tests and enable the \fBtest\fR cfg option.
See \fIrustc tests\fR <https://doc.rust\-lang.org/rustc/tests/index.html> for more
detail.
.sp
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-doc.1 b/src/tools/cargo/src/etc/man/cargo-doc.1
index 2bdd8867b..69f781de6 100644
--- a/src/tools/cargo/src/etc/man/cargo-doc.1
+++ b/src/tools/cargo/src/etc/man/cargo-doc.1
@@ -157,7 +157,7 @@ See also the \fB\-\-profile\fR option for choosing a specific profile by name.
\fB\-\-profile\fR \fIname\fR
.RS 4
Document with the given profile.
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-fix.1 b/src/tools/cargo/src/etc/man/cargo-fix.1
index 61083f214..1f10f179b 100644
--- a/src/tools/cargo/src/etc/man/cargo-fix.1
+++ b/src/tools/cargo/src/etc/man/cargo-fix.1
@@ -285,7 +285,7 @@ test mode which will enable checking tests and enable the \fBtest\fR cfg option.
See \fIrustc tests\fR <https://doc.rust\-lang.org/rustc/tests/index.html> for more
detail.
.sp
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-init.1 b/src/tools/cargo/src/etc/man/cargo-init.1
index 56d1aca9f..b37622b52 100644
--- a/src/tools/cargo/src/etc/man/cargo-init.1
+++ b/src/tools/cargo/src/etc/man/cargo-init.1
@@ -37,7 +37,7 @@ Create a package with a library target (\fBsrc/lib.rs\fR).
\fB\-\-edition\fR \fIedition\fR
.RS 4
Specify the Rust edition to use. Default is 2021.
-Possible values: 2015, 2018, 2021
+Possible values: 2015, 2018, 2021, 2024
.RE
.sp
\fB\-\-name\fR \fIname\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-install.1 b/src/tools/cargo/src/etc/man/cargo-install.1
index 5ca3180fd..d4d4d3eb6 100644
--- a/src/tools/cargo/src/etc/man/cargo-install.1
+++ b/src/tools/cargo/src/etc/man/cargo-install.1
@@ -114,7 +114,7 @@ will be used, beginning discovery at \fB$PATH/.cargo/config.toml\fR\&.
\fB\-\-version\fR \fIversion\fR
.RS 4
Specify a version to install. This may be a \fIversion
-requirement\fR <https://doc.rust\-lang.org/cargo/reference/specifying\-dependencies.md>, like \fB~1.2\fR, to have Cargo
+requirement\fR <https://doc.rust\-lang.org/cargo/reference/specifying\-dependencies.html>, like \fB~1.2\fR, to have Cargo
select the newest version from the given requirement. If the version does not
have a requirement operator (such as \fB^\fR or \fB~\fR), then it must be in the form
\fIMAJOR.MINOR.PATCH\fR, and will install exactly that version; it is \fInot\fR
@@ -270,7 +270,7 @@ See also the \fB\-\-profile\fR option for choosing a specific profile by name.
\fB\-\-profile\fR \fIname\fR
.RS 4
Install with the given profile.
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-login.1 b/src/tools/cargo/src/etc/man/cargo-login.1
index 92b3b3f3c..16f5773fa 100644
--- a/src/tools/cargo/src/etc/man/cargo-login.1
+++ b/src/tools/cargo/src/etc/man/cargo-login.1
@@ -6,12 +6,14 @@
.SH "NAME"
cargo\-login \[em] Log in to a registry
.SH "SYNOPSIS"
-\fBcargo login\fR [\fIoptions\fR] [\fItoken\fR] \[en] [\fIargs\fR]
+\fBcargo login\fR [\fIoptions\fR] [\fItoken\fR] [\fB\-\-\fR \fIargs\fR]
.SH "DESCRIPTION"
This command will run a credential provider to save a token so that commands
that require authentication, such as \fBcargo\-publish\fR(1), will be
automatically authenticated.
.sp
+All the arguments following the two dashes (\fB\-\-\fR) are passed to the credential provider.
+.sp
For the default \fBcargo:token\fR credential provider, the token is saved
in \fB$CARGO_HOME/credentials.toml\fR\&. \fBCARGO_HOME\fR defaults to \fB\&.cargo\fR
in your home directory.
diff --git a/src/tools/cargo/src/etc/man/cargo-new.1 b/src/tools/cargo/src/etc/man/cargo-new.1
index 62e0eb157..f1939a543 100644
--- a/src/tools/cargo/src/etc/man/cargo-new.1
+++ b/src/tools/cargo/src/etc/man/cargo-new.1
@@ -32,7 +32,7 @@ Create a package with a library target (\fBsrc/lib.rs\fR).
\fB\-\-edition\fR \fIedition\fR
.RS 4
Specify the Rust edition to use. Default is 2021.
-Possible values: 2015, 2018, 2021
+Possible values: 2015, 2018, 2021, 2024
.RE
.sp
\fB\-\-name\fR \fIname\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-run.1 b/src/tools/cargo/src/etc/man/cargo-run.1
index 293814674..2ecbbcf65 100644
--- a/src/tools/cargo/src/etc/man/cargo-run.1
+++ b/src/tools/cargo/src/etc/man/cargo-run.1
@@ -94,7 +94,7 @@ See also the \fB\-\-profile\fR option for choosing a specific profile by name.
\fB\-\-profile\fR \fIname\fR
.RS 4
Run with the given profile.
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-rustc.1 b/src/tools/cargo/src/etc/man/cargo-rustc.1
index 501e9208e..f3535b0f0 100644
--- a/src/tools/cargo/src/etc/man/cargo-rustc.1
+++ b/src/tools/cargo/src/etc/man/cargo-rustc.1
@@ -194,7 +194,7 @@ tests\fR <https://doc.rust\-lang.org/rustc/tests/index.html> for more detail.
similar to the \fBtest\fR profile.
.RE
.sp
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-rustdoc.1 b/src/tools/cargo/src/etc/man/cargo-rustdoc.1
index 0335d6e54..72a182de8 100644
--- a/src/tools/cargo/src/etc/man/cargo-rustdoc.1
+++ b/src/tools/cargo/src/etc/man/cargo-rustdoc.1
@@ -176,7 +176,7 @@ See also the \fB\-\-profile\fR option for choosing a specific profile by name.
\fB\-\-profile\fR \fIname\fR
.RS 4
Document with the given profile.
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-test.1 b/src/tools/cargo/src/etc/man/cargo-test.1
index 8e460e167..467000147 100644
--- a/src/tools/cargo/src/etc/man/cargo-test.1
+++ b/src/tools/cargo/src/etc/man/cargo-test.1
@@ -309,7 +309,7 @@ See also the \fB\-\-profile\fR option for choosing a specific profile by name.
\fB\-\-profile\fR \fIname\fR
.RS 4
Test with the given profile.
-See the \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
+See \fIthe reference\fR <https://doc.rust\-lang.org/cargo/reference/profiles.html> for more details on profiles.
.RE
.sp
\fB\-\-ignore\-rust\-version\fR
diff --git a/src/tools/cargo/src/etc/man/cargo-vendor.1 b/src/tools/cargo/src/etc/man/cargo-vendor.1
index cb46f67cd..67744532e 100644
--- a/src/tools/cargo/src/etc/man/cargo-vendor.1
+++ b/src/tools/cargo/src/etc/man/cargo-vendor.1
@@ -14,8 +14,10 @@ the vendor directory specified by \fB<path>\fR will contain all remote sources f
dependencies specified. Additional manifests beyond the default one can be
specified with the \fB\-s\fR option.
.sp
-The \fBcargo vendor\fR command will also print out the configuration necessary
-to use the vendored sources, which you will need to add to \fB\&.cargo/config.toml\fR\&.
+The configuration necessary to use the vendored sources would be printed to
+stdout after \fBcargo vendor\fR completes the vendoring process.
+You will need to add or redirect it to your Cargo configuration file,
+which is usually \fB\&.cargo/config.toml\fR locally for the current package.
.SH "OPTIONS"
.SS "Vendor Options"
.sp
@@ -205,5 +207,15 @@ cargo vendor \-s ../path/to/Cargo.toml
.fi
.RE
.RE
+.sp
+.RS 4
+\h'-04' 4.\h'+01'Vendor and redirect the necessary vendor configs to a config file.
+.sp
+.RS 4
+.nf
+cargo vendor > path/to/my/cargo/config.toml
+.fi
+.RE
+.RE
.SH "SEE ALSO"
\fBcargo\fR(1)
diff --git a/src/tools/cargo/tests/testsuite/artifact_dep.rs b/src/tools/cargo/tests/testsuite/artifact_dep.rs
index 64aa9d8af..c51298735 100644
--- a/src/tools/cargo/tests/testsuite/artifact_dep.rs
+++ b/src/tools/cargo/tests/testsuite/artifact_dep.rs
@@ -2152,6 +2152,7 @@ fn doc_lib_true() {
[DOCUMENTING] bar v0.0.1 ([CWD]/bar)
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -2227,6 +2228,7 @@ fn rustdoc_works_on_libs_with_artifacts_and_lib_false() {
[COMPILING] bar v0.5.0 ([CWD]/bar)
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
diff --git a/src/tools/cargo/tests/testsuite/bad_config.rs b/src/tools/cargo/tests/testsuite/bad_config.rs
index 82da880ea..1d3bb9ee1 100644
--- a/src/tools/cargo/tests/testsuite/bad_config.rs
+++ b/src/tools/cargo/tests/testsuite/bad_config.rs
@@ -373,7 +373,7 @@ Caused by:
failed to clone into: [..]
Caused by:
- URLs need to specify the path to the repository
+ URL \"git://host.xz\" does not specify a path to a repository
"
} else {
"\
@@ -1664,3 +1664,37 @@ note: Sources are not allowed to be defined multiple times.
)
.run();
}
+
+#[cargo_test]
+fn bad_trim_paths() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+
+ [profile.dev]
+ trim-paths = "split-debuginfo"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("check -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["trim-paths"])
+ .with_status(101)
+ .with_stderr(
+ r#"error: failed to parse manifest at `[..]`
+
+Caused by:
+ TOML parse error at line 7, column 30
+ |
+ 7 | trim-paths = "split-debuginfo"
+ | ^^^^^^^^^^^^^^^^^
+ expected a boolean, "none", "diagnostics", "macro", "object", "all", or an array with these options
+"#,
+ )
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/build.rs b/src/tools/cargo/tests/testsuite/build.rs
index 1afa83918..23840ad9a 100644
--- a/src/tools/cargo/tests/testsuite/build.rs
+++ b/src/tools/cargo/tests/testsuite/build.rs
@@ -159,6 +159,29 @@ For more information, try '--help'.
}
#[cargo_test]
+fn cargo_compile_with_unsupported_short_config_flag() {
+ let p = project()
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .build();
+
+ p.cargo("build -c net.git-fetch-with-cli=true")
+ .with_stderr(
+ "\
+error: unexpected argument '-c' found
+
+ tip: a similar argument exists: '--config'
+
+Usage: cargo[EXE] build [OPTIONS]
+
+For more information, try '--help'.
+",
+ )
+ .with_status(1)
+ .run();
+}
+
+#[cargo_test]
fn cargo_compile_with_workspace_excluded() {
let p = project().file("src/main.rs", "fn main() {}").build();
@@ -183,6 +206,30 @@ fn cargo_compile_manifest_path() {
}
#[cargo_test]
+fn cargo_compile_with_wrong_manifest_path_flag() {
+ let p = project()
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .build();
+
+ p.cargo("build --path foo/Cargo.toml")
+ .cwd(p.root().parent().unwrap())
+ .with_stderr(
+ "\
+error: unexpected argument '--path' found
+
+ tip: a similar argument exists: '--manifest-path'
+
+Usage: cargo[EXE] build [OPTIONS]
+
+For more information, try '--help'.
+",
+ )
+ .with_status(1)
+ .run();
+}
+
+#[cargo_test]
fn chdir_gated() {
let p = project()
.file("Cargo.toml", &basic_bin_manifest("foo"))
@@ -222,6 +269,33 @@ fn cargo_compile_directory_not_cwd() {
}
#[cargo_test]
+fn cargo_compile_with_unsupported_short_unstable_feature_flag() {
+ let p = project()
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/foo.rs", &main_file(r#""i am foo""#, &[]))
+ .file(".cargo/config.toml", &"")
+ .build();
+
+ p.cargo("-zunstable-options -C foo build")
+ .masquerade_as_nightly_cargo(&["chdir"])
+ .cwd(p.root().parent().unwrap())
+ .with_stderr(
+ "\
+error: unexpected argument '-z' found
+
+ tip: a similar argument exists: '-Z'
+
+Usage: cargo [..][OPTIONS] [COMMAND]
+ cargo [..][OPTIONS] -Zscript <MANIFEST_RS> [ARGS]...
+
+For more information, try '--help'.
+",
+ )
+ .with_status(1)
+ .run();
+}
+
+#[cargo_test]
fn cargo_compile_directory_not_cwd_with_invalid_config() {
let p = project()
.file("Cargo.toml", &basic_bin_manifest("foo"))
@@ -465,7 +539,7 @@ fn cargo_compile_with_forbidden_bin_target_name() {
[ERROR] failed to parse manifest at `[..]`
Caused by:
- the binary target name `build` is forbidden, it conflicts with with cargo's build directory names
+ the binary target name `build` is forbidden, it conflicts with cargo's build directory names
",
)
.run();
@@ -4190,6 +4264,30 @@ fn cargo_build_empty_target() {
}
#[cargo_test]
+fn cargo_build_with_unsupported_short_target_flag() {
+ let p = project()
+ .file("Cargo.toml", &basic_bin_manifest("foo"))
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ p.cargo("build -t")
+ .arg("")
+ .with_stderr(
+ "\
+error: unexpected argument '-t' found
+
+ tip: a similar argument exists: '--target'
+
+Usage: cargo[EXE] build [OPTIONS]
+
+For more information, try '--help'.
+",
+ )
+ .with_status(1)
+ .run();
+}
+
+#[cargo_test]
fn build_all_workspace() {
let p = project()
.file(
@@ -4255,6 +4353,43 @@ fn build_all_exclude() {
}
#[cargo_test]
+fn cargo_build_with_unsupported_short_exclude_flag() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [workspace]
+ members = ["bar", "baz"]
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
+ .file("bar/src/lib.rs", "pub fn bar() {}")
+ .file("baz/Cargo.toml", &basic_manifest("baz", "0.1.0"))
+ .file("baz/src/lib.rs", "pub fn baz() { break_the_build(); }")
+ .build();
+
+ p.cargo("build --workspace -x baz")
+ .with_stderr(
+ "\
+error: unexpected argument '-x' found
+
+ tip: a similar argument exists: '--exclude'
+
+Usage: cargo[EXE] build [OPTIONS]
+
+For more information, try '--help'.
+",
+ )
+ .with_status(1)
+ .run();
+}
+
+#[cargo_test]
fn build_all_exclude_not_found() {
let p = project()
.file(
diff --git a/src/tools/cargo/tests/testsuite/build_script.rs b/src/tools/cargo/tests/testsuite/build_script.rs
index 0ccbb4e27..408ce6457 100644
--- a/src/tools/cargo/tests/testsuite/build_script.rs
+++ b/src/tools/cargo/tests/testsuite/build_script.rs
@@ -1471,6 +1471,7 @@ fn testing_and_such() {
[DOCUMENTING] foo v0.5.0 ([CWD])
[RUNNING] `rustdoc [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -3778,8 +3779,8 @@ fn warnings_emitted() {
[COMPILING] foo v0.5.0 ([..])
[RUNNING] `rustc [..]`
[RUNNING] `[..]`
-warning: foo
-warning: bar
+warning: foo@0.5.0: foo
+warning: foo@0.5.0: bar
[RUNNING] `rustc [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
@@ -3816,7 +3817,7 @@ fn warnings_emitted_when_build_script_panics() {
p.cargo("build")
.with_status(101)
.with_stdout("")
- .with_stderr_contains("warning: foo\nwarning: bar")
+ .with_stderr_contains("warning: foo@0.5.0: foo\nwarning: foo@0.5.0: bar")
.run();
}
@@ -3929,8 +3930,8 @@ fn warnings_printed_on_vv() {
[COMPILING] bar v0.1.0
[RUNNING] `[..] rustc [..]`
[RUNNING] `[..]`
-warning: foo
-warning: bar
+warning: bar@0.1.0: foo
+warning: bar@0.1.0: bar
[RUNNING] `[..] rustc [..]`
[COMPILING] foo v0.5.0 ([..])
[RUNNING] `[..] rustc [..]`
diff --git a/src/tools/cargo/tests/testsuite/build_script_env.rs b/src/tools/cargo/tests/testsuite/build_script_env.rs
index df574600c..afa2925f1 100644
--- a/src/tools/cargo/tests/testsuite/build_script_env.rs
+++ b/src/tools/cargo/tests/testsuite/build_script_env.rs
@@ -137,12 +137,12 @@ fn rustc_bootstrap() {
// NOTE: uses RUSTC_BOOTSTRAP so it will be propagated to rustc
// (this matters when tests are being run with a beta or stable cargo)
.env("RUSTC_BOOTSTRAP", "1")
- .with_stderr_contains("warning: Cannot set `RUSTC_BOOTSTRAP=1` [..]")
+ .with_stderr_contains("warning: has-dashes@0.0.1: Cannot set `RUSTC_BOOTSTRAP=1` [..]")
.run();
// RUSTC_BOOTSTRAP set to the name of the library should warn
p.cargo("check")
.env("RUSTC_BOOTSTRAP", "has_dashes")
- .with_stderr_contains("warning: Cannot set `RUSTC_BOOTSTRAP=1` [..]")
+ .with_stderr_contains("warning: has-dashes@0.0.1: Cannot set `RUSTC_BOOTSTRAP=1` [..]")
.run();
// RUSTC_BOOTSTRAP set to some random value should error
p.cargo("check")
@@ -169,7 +169,7 @@ fn rustc_bootstrap() {
// NOTE: uses RUSTC_BOOTSTRAP so it will be propagated to rustc
// (this matters when tests are being run with a beta or stable cargo)
.env("RUSTC_BOOTSTRAP", "1")
- .with_stderr_contains("warning: Cannot set `RUSTC_BOOTSTRAP=1` [..]")
+ .with_stderr_contains("warning: foo@0.0.1: Cannot set `RUSTC_BOOTSTRAP=1` [..]")
.run();
// RUSTC_BOOTSTRAP conditionally set when there's no library should error (regardless of the value)
p.cargo("check")
@@ -181,6 +181,22 @@ fn rustc_bootstrap() {
}
#[cargo_test]
+fn build_script_env_verbose() {
+ let build_rs = r#"
+ fn main() {}
+ "#;
+ let p = project()
+ .file("Cargo.toml", &basic_manifest("verbose-build", "0.0.1"))
+ .file("src/lib.rs", "")
+ .file("build.rs", build_rs)
+ .build();
+
+ p.cargo("check -vv")
+ .with_stderr_contains("[RUNNING] `[..]CARGO=[..]build-script-build`")
+ .run();
+}
+
+#[cargo_test]
#[cfg(target_arch = "x86_64")]
fn build_script_sees_cfg_target_feature() {
let build_rs = r#"
diff --git a/src/tools/cargo/tests/testsuite/build_script_extra_link_arg.rs b/src/tools/cargo/tests/testsuite/build_script_extra_link_arg.rs
index ade262fec..964a55e97 100644
--- a/src/tools/cargo/tests/testsuite/build_script_extra_link_arg.rs
+++ b/src/tools/cargo/tests/testsuite/build_script_extra_link_arg.rs
@@ -204,7 +204,7 @@ fn cdylib_link_arg_transitive() {
[COMPILING] bar v1.0.0 [..]
[RUNNING] `rustc --crate-name build_script_build bar/build.rs [..]
[RUNNING] `[..]build-script-build[..]
-warning: cargo:rustc-link-arg-cdylib was specified in the build script of bar v1.0.0 \
+warning: bar@1.0.0: cargo:rustc-link-arg-cdylib was specified in the build script of bar v1.0.0 \
([ROOT]/foo/bar), but that package does not contain a cdylib target
Allowing this was an unintended change in the 1.50 release, and may become an error in \
diff --git a/src/tools/cargo/tests/testsuite/cache_lock.rs b/src/tools/cargo/tests/testsuite/cache_lock.rs
new file mode 100644
index 000000000..f5b681f3a
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cache_lock.rs
@@ -0,0 +1,304 @@
+//! Tests for `CacheLock`.
+
+use crate::config::ConfigBuilder;
+use cargo::util::cache_lock::{CacheLockMode, CacheLocker};
+use cargo_test_support::paths::{self, CargoPathExt};
+use cargo_test_support::{retry, thread_wait_timeout, threaded_timeout};
+use std::thread::JoinHandle;
+
+/// Helper to verify that it is OK to acquire the given lock (it shouldn't block).
+fn verify_lock_is_ok(mode: CacheLockMode) {
+ let root = paths::root();
+ threaded_timeout(10, move || {
+ let config = ConfigBuilder::new().root(root).build();
+ let locker = CacheLocker::new();
+ // This would block if it is held.
+ let _lock = locker.lock(&config, mode).unwrap();
+ assert!(locker.is_locked(mode));
+ });
+}
+
+/// Helper to acquire two locks from the same locker.
+fn a_b_nested(a: CacheLockMode, b: CacheLockMode) {
+ let config = ConfigBuilder::new().build();
+ let locker = CacheLocker::new();
+ let lock1 = locker.lock(&config, a).unwrap();
+ assert!(locker.is_locked(a));
+ let lock2 = locker.lock(&config, b).unwrap();
+ assert!(locker.is_locked(b));
+ drop(lock2);
+ drop(lock1);
+ // Verify locks were unlocked.
+ verify_lock_is_ok(CacheLockMode::Shared);
+ verify_lock_is_ok(CacheLockMode::DownloadExclusive);
+ verify_lock_is_ok(CacheLockMode::MutateExclusive);
+}
+
+/// Helper to acquire two locks from separate lockers, verifying that they
+/// don't block each other.
+fn a_then_b_separate_not_blocked(a: CacheLockMode, b: CacheLockMode, verify: CacheLockMode) {
+ let config = ConfigBuilder::new().build();
+ let locker1 = CacheLocker::new();
+ let lock1 = locker1.lock(&config, a).unwrap();
+ assert!(locker1.is_locked(a));
+ let locker2 = CacheLocker::new();
+ let lock2 = locker2.lock(&config, b).unwrap();
+ assert!(locker2.is_locked(b));
+ let thread = verify_lock_would_block(verify);
+ // Unblock the thread.
+ drop(lock1);
+ drop(lock2);
+ // Verify the thread is unblocked.
+ thread_wait_timeout::<()>(100, thread);
+}
+
+/// Helper to acquire two locks from separate lockers, verifying that the
+/// second one blocks.
+fn a_then_b_separate_blocked(a: CacheLockMode, b: CacheLockMode) {
+ let config = ConfigBuilder::new().build();
+ let locker = CacheLocker::new();
+ let lock = locker.lock(&config, a).unwrap();
+ assert!(locker.is_locked(a));
+ let thread = verify_lock_would_block(b);
+ // Unblock the thread.
+ drop(lock);
+ // Verify the thread is unblocked.
+ thread_wait_timeout::<()>(100, thread);
+}
+
+/// Helper to verify that acquiring the given mode would block.
+///
+/// Always call `thread_wait_timeout` on the result.
+#[must_use]
+fn verify_lock_would_block(mode: CacheLockMode) -> JoinHandle<()> {
+ let root = paths::root();
+ // Spawn a thread that will block on the lock.
+ let thread = std::thread::spawn(move || {
+ let config = ConfigBuilder::new().root(root).build();
+ let locker2 = CacheLocker::new();
+ let lock2 = locker2.lock(&config, mode).unwrap();
+ assert!(locker2.is_locked(mode));
+ drop(lock2);
+ });
+ // Verify that it blocked.
+ retry(100, || {
+ if let Ok(s) = std::fs::read_to_string(paths::root().join("shell.out")) {
+ if s.trim().starts_with("Blocking waiting for file lock on") {
+ return Some(());
+ } else {
+ eprintln!("unexpected output: {s}");
+ // Try again, it might have been partially written.
+ }
+ }
+ None
+ });
+ thread
+}
+
+#[test]
+fn new_is_unlocked() {
+ let locker = CacheLocker::new();
+ assert!(!locker.is_locked(CacheLockMode::Shared));
+ assert!(!locker.is_locked(CacheLockMode::DownloadExclusive));
+ assert!(!locker.is_locked(CacheLockMode::MutateExclusive));
+}
+
+#[cargo_test]
+fn multiple_shared() {
+ // Test that two nested shared locks from the same locker are safe to acquire.
+ a_b_nested(CacheLockMode::Shared, CacheLockMode::Shared);
+}
+
+#[cargo_test]
+fn multiple_shared_separate() {
+ // Test that two independent shared locks are safe to acquire at the same time.
+ a_then_b_separate_not_blocked(
+ CacheLockMode::Shared,
+ CacheLockMode::Shared,
+ CacheLockMode::MutateExclusive,
+ );
+}
+
+#[cargo_test]
+fn multiple_download() {
+ // That that two nested download locks from the same locker are safe to acquire.
+ a_b_nested(
+ CacheLockMode::DownloadExclusive,
+ CacheLockMode::DownloadExclusive,
+ );
+}
+
+#[cargo_test]
+fn multiple_mutate() {
+ // That that two nested mutate locks from the same locker are safe to acquire.
+ a_b_nested(
+ CacheLockMode::MutateExclusive,
+ CacheLockMode::MutateExclusive,
+ );
+}
+
+#[cargo_test]
+#[should_panic(expected = "lock is not allowed")]
+fn download_then_shared() {
+ // This sequence is not supported.
+ a_b_nested(CacheLockMode::DownloadExclusive, CacheLockMode::Shared);
+}
+
+#[cargo_test]
+#[should_panic(expected = "lock upgrade from shared to exclusive not supported")]
+fn shared_then_mutate() {
+ // This sequence is not supported.
+ a_b_nested(CacheLockMode::Shared, CacheLockMode::MutateExclusive);
+}
+
+#[cargo_test]
+fn shared_then_download() {
+ a_b_nested(CacheLockMode::Shared, CacheLockMode::DownloadExclusive);
+ // Verify drop actually unlocked.
+ verify_lock_is_ok(CacheLockMode::DownloadExclusive);
+ verify_lock_is_ok(CacheLockMode::MutateExclusive);
+}
+
+#[cargo_test]
+fn mutate_then_shared() {
+ a_b_nested(CacheLockMode::MutateExclusive, CacheLockMode::Shared);
+ // Verify drop actually unlocked.
+ verify_lock_is_ok(CacheLockMode::MutateExclusive);
+}
+
+#[cargo_test]
+fn download_then_mutate() {
+ a_b_nested(
+ CacheLockMode::DownloadExclusive,
+ CacheLockMode::MutateExclusive,
+ );
+ // Verify drop actually unlocked.
+ verify_lock_is_ok(CacheLockMode::DownloadExclusive);
+ verify_lock_is_ok(CacheLockMode::MutateExclusive);
+}
+
+#[cargo_test]
+fn mutate_then_download() {
+ a_b_nested(
+ CacheLockMode::MutateExclusive,
+ CacheLockMode::DownloadExclusive,
+ );
+ // Verify drop actually unlocked.
+ verify_lock_is_ok(CacheLockMode::MutateExclusive);
+ verify_lock_is_ok(CacheLockMode::DownloadExclusive);
+}
+
+#[cargo_test]
+fn readonly() {
+ // In a permission denied situation, it should still allow a lock. It just
+ // silently behaves as-if it was locked.
+ let cargo_home = paths::home().join(".cargo");
+ std::fs::create_dir_all(&cargo_home).unwrap();
+ let mut perms = std::fs::metadata(&cargo_home).unwrap().permissions();
+ perms.set_readonly(true);
+ std::fs::set_permissions(&cargo_home, perms).unwrap();
+ let config = ConfigBuilder::new().build();
+ let locker = CacheLocker::new();
+ for mode in [
+ CacheLockMode::Shared,
+ CacheLockMode::DownloadExclusive,
+ CacheLockMode::MutateExclusive,
+ ] {
+ let _lock1 = locker.lock(&config, mode).unwrap();
+ // Make sure it can recursively acquire the lock, too.
+ let _lock2 = locker.lock(&config, mode).unwrap();
+ }
+}
+
+#[cargo_test]
+fn download_then_shared_separate() {
+ a_then_b_separate_not_blocked(
+ CacheLockMode::DownloadExclusive,
+ CacheLockMode::Shared,
+ CacheLockMode::MutateExclusive,
+ );
+}
+
+#[cargo_test]
+fn shared_then_download_separate() {
+ a_then_b_separate_not_blocked(
+ CacheLockMode::Shared,
+ CacheLockMode::DownloadExclusive,
+ CacheLockMode::MutateExclusive,
+ );
+}
+
+#[cargo_test]
+fn multiple_download_separate() {
+ // Test that with two independent download locks, the second blocks until
+ // the first is released.
+ a_then_b_separate_blocked(
+ CacheLockMode::DownloadExclusive,
+ CacheLockMode::DownloadExclusive,
+ );
+}
+
+#[cargo_test]
+fn multiple_mutate_separate() {
+ // Test that with two independent mutate locks, the second blocks until
+ // the first is released.
+ a_then_b_separate_blocked(
+ CacheLockMode::MutateExclusive,
+ CacheLockMode::MutateExclusive,
+ );
+}
+
+#[cargo_test]
+fn shared_then_mutate_separate() {
+ a_then_b_separate_blocked(CacheLockMode::Shared, CacheLockMode::MutateExclusive);
+}
+
+#[cargo_test]
+fn download_then_mutate_separate() {
+ a_then_b_separate_blocked(
+ CacheLockMode::DownloadExclusive,
+ CacheLockMode::MutateExclusive,
+ );
+}
+
+#[cargo_test]
+fn mutate_then_download_separate() {
+ a_then_b_separate_blocked(
+ CacheLockMode::MutateExclusive,
+ CacheLockMode::DownloadExclusive,
+ );
+}
+
+#[cargo_test]
+fn mutate_then_shared_separate() {
+ a_then_b_separate_blocked(CacheLockMode::MutateExclusive, CacheLockMode::Shared);
+}
+
+#[cargo_test(ignore_windows = "no method to prevent creating or locking a file")]
+fn mutate_err_is_atomic() {
+ // Verifies that when getting a mutate lock, that if the first lock
+ // succeeds, but the second one fails, that the first lock is released.
+ let config = ConfigBuilder::new().build();
+ let locker = CacheLocker::new();
+ let cargo_home = config.home().as_path_unlocked();
+ let cache_path = cargo_home.join(".package-cache");
+ // This is a hacky way to force an error acquiring the download lock. By
+ // making it a directory, it is unable to open it.
+ // TODO: Unfortunately this doesn't work on Windows. I don't have any
+ // ideas on how to simulate an error on Windows.
+ cache_path.mkdir_p();
+ match locker.lock(&config, CacheLockMode::MutateExclusive) {
+ Ok(_) => panic!("did not expect lock to succeed"),
+ Err(e) => {
+ let msg = format!("{e:?}");
+ assert!(msg.contains("failed to open:"), "{msg}");
+ }
+ }
+ assert!(!locker.is_locked(CacheLockMode::MutateExclusive));
+ assert!(!locker.is_locked(CacheLockMode::DownloadExclusive));
+ assert!(!locker.is_locked(CacheLockMode::Shared));
+ cache_path.rm_rf();
+ verify_lock_is_ok(CacheLockMode::DownloadExclusive);
+ verify_lock_is_ok(CacheLockMode::Shared);
+ verify_lock_is_ok(CacheLockMode::MutateExclusive);
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/mod.rs
index 8c03b30dc..e8633b0c4 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/mod.rs
@@ -99,6 +99,7 @@ mod path_dev;
mod path_inferred_name;
mod path_inferred_name_conflicts_full_feature;
mod path_normalized_name;
+mod preserve_dep_std_table;
mod preserve_features_table;
mod preserve_sorted;
mod preserve_unsorted;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/in/Cargo.toml
new file mode 100644
index 000000000..aa8584ff4
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/in/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "xxx"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies.your-face]
+# Leading version
+version = "99999.0.0" # Trailing version
+# Leading optional
+optional = true # Trailing optional
+# Leading features
+features = [] # Trailing features
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/empty_dir/.keep b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/in/src/lib.rs
index e69de29bb..e69de29bb 100644
--- a/src/tools/cargo/tests/testsuite/cargo_init/empty_dir/.keep
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/in/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/mod.rs
new file mode 100644
index 000000000..1998fa742
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/mod.rs
@@ -0,0 +1,31 @@
+use cargo_test_support::compare::assert_ui;
+use cargo_test_support::prelude::*;
+use cargo_test_support::Project;
+
+use cargo_test_support::curr_dir;
+
+#[cargo_test]
+fn case() {
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package")
+ .feature("nose", &[])
+ .feature("mouth", &[])
+ .feature("eyes", &[])
+ .feature("ears", &[])
+ .publish();
+
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = project.root();
+ let cwd = &project_root;
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg("add")
+ .arg_line("your-face --no-optional")
+ .current_dir(cwd)
+ .assert()
+ .success()
+ .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_add/preserve_dep_std_table/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/out/Cargo.toml
new file mode 100644
index 000000000..76e50ce37
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/out/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "xxx"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies.your-face]
+# Leading version
+version = "99999.0.0" # Trailing version
+# Leading features
+features = [] # Trailing features
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/stderr.log
new file mode 100644
index 000000000..796b9601b
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/stderr.log
@@ -0,0 +1,7 @@
+ Updating `dummy-registry` index
+ Adding your-face v99999.0.0 to dependencies.
+ Features:
+ - ears
+ - eyes
+ - mouth
+ - nose
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/stdout.log b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_dep_std_table/stdout.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
index 430d8be42..95546b4a3 100644
--- a/src/tools/cargo/tests/testsuite/cargo_bench/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_bench/help/stdout.log
@@ -1,10 +1,10 @@
Execute all benchmarks of a local package
-Usage: cargo[EXE] bench [OPTIONS] [BENCHNAME] [-- [args]...]
+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
+ [ARGS]... Arguments for the bench binary
Options:
--no-run Compile, but don't run benchmarks
@@ -31,9 +31,9 @@ Target Selection:
--bin [<NAME>] Benchmark only the specified binary
--examples Benchmark all examples
--example [<NAME>] Benchmark only the specified example
- --tests Benchmark all tests
+ --tests Benchmark all test targets
--test [<NAME>] Benchmark only the specified test target
- --benches Benchmark all benches
+ --benches Benchmark all bench targets
--bench [<NAME>] Benchmark only the specified bench target
--all-targets Benchmark all targets
diff --git a/src/tools/cargo/tests/testsuite/cargo_bench/no_keep_going/stderr.log b/src/tools/cargo/tests/testsuite/cargo_bench/no_keep_going/stderr.log
index 7b94abbc4..daaa8f093 100644
--- a/src/tools/cargo/tests/testsuite/cargo_bench/no_keep_going/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_bench/no_keep_going/stderr.log
@@ -2,6 +2,6 @@ error: unexpected argument '--keep-going' found
tip: use `--no-fail-fast` to run as many tests as possible regardless of failure
-Usage: cargo[EXE] bench [OPTIONS] [BENCHNAME] [-- [args]...]
+Usage: cargo[EXE] bench [OPTIONS] [BENCHNAME] [-- [ARGS]...]
For more information, try '--help'.
diff --git a/src/tools/cargo/tests/testsuite/cargo_build/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_build/help/stdout.log
index 56b934cd1..58b12cdcd 100644
--- a/src/tools/cargo/tests/testsuite/cargo_build/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_build/help/stdout.log
@@ -26,9 +26,9 @@ Target Selection:
--bin [<NAME>] Build only the specified binary
--examples Build all examples
--example [<NAME>] Build only the specified example
- --tests Build all tests
+ --tests Build all test targets
--test [<NAME>] Build only the specified test target
- --benches Build all benches
+ --benches Build all bench targets
--bench [<NAME>] Build only the specified bench target
--all-targets Build all targets
diff --git a/src/tools/cargo/tests/testsuite/cargo_check/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_check/help/stdout.log
index 92d44a6de..bbf090d1d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_check/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_check/help/stdout.log
@@ -26,9 +26,9 @@ Target Selection:
--bin [<NAME>] Check only the specified binary
--examples Check all examples
--example [<NAME>] Check only the specified example
- --tests Check all tests
+ --tests Check all test targets
--test [<NAME>] Check only the specified test target
- --benches Check all benches
+ --benches Check all bench targets
--bench [<NAME>] Check only the specified bench target
--all-targets Check all targets
diff --git a/src/tools/cargo/tests/testsuite/cargo_command.rs b/src/tools/cargo/tests/testsuite/cargo_command.rs
index 62869387f..80885805b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_command.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_command.rs
@@ -294,7 +294,9 @@ fn find_closest_dont_correct_nonsense() {
"\
[ERROR] no such command: `there-is-no-way-that-there-is-a-command-close-to-this`
-<tab>View all installed commands with `cargo --list`",
+<tab>View all installed commands with `cargo --list`
+<tab>Find a package to install `there-is-no-way-that-there-is-a-command-close-to-this` with `cargo search cargo-there-is-no-way-that-there-is-a-command-close-to-this`
+",
)
.run();
}
@@ -307,7 +309,9 @@ fn displays_subcommand_on_error() {
"\
[ERROR] no such command: `invalid-command`
-<tab>View all installed commands with `cargo --list`",
+<tab>View all installed commands with `cargo --list`
+<tab>Find a package to install `invalid-command` with `cargo search cargo-invalid-command`
+",
)
.run();
}
@@ -529,7 +533,9 @@ error: no such command: `bluid`
<tab>Did you mean `build`?
-<tab>View all installed commands with `cargo --list`",
+<tab>View all installed commands with `cargo --list`
+<tab>Find a package to install `bluid` with `cargo search cargo-bluid`
+",
)
.run();
}
diff --git a/src/tools/cargo/tests/testsuite/cargo_features.rs b/src/tools/cargo/tests/testsuite/cargo_features.rs
index cf7ef0190..d319ed686 100644
--- a/src/tools/cargo/tests/testsuite/cargo_features.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_features.rs
@@ -594,7 +594,13 @@ fn z_flags_rejected() {
p.cargo("check -Zarg")
.masquerade_as_nightly_cargo(&["test-dummy-unstable"])
.with_status(101)
- .with_stderr("error: unknown `-Z` flag specified: arg")
+ .with_stderr(
+ r#"error: unknown `-Z` flag specified: arg
+
+For available unstable features, see https://doc.rust-lang.org/nightly/cargo/reference/unstable.html
+If you intended to use an unstable rustc feature, try setting `RUSTFLAGS="-Zarg"`
+"#,
+ )
.run();
p.cargo("check -Zprint-im-a-teapot")
diff --git a/src/tools/cargo/tests/testsuite/cargo_fix/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_fix/help/stdout.log
index dbbd11b77..3e8b1427f 100644
--- a/src/tools/cargo/tests/testsuite/cargo_fix/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_fix/help/stdout.log
@@ -31,9 +31,9 @@ Target Selection:
--bin [<NAME>] Fix only the specified binary
--examples Fix all examples
--example [<NAME>] Fix only the specified example
- --tests Fix all tests
+ --tests Fix all test targets
--test [<NAME>] Fix only the specified test target
- --benches Fix all benches
+ --benches Fix all bench targets
--bench [<NAME>] Fix only the specified bench target
--all-targets Fix all targets (default)
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/help/stdout.log
index 0eb4c976b..588b45ccf 100644
--- a/src/tools/cargo/tests/testsuite/cargo_init/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/help/stdout.log
@@ -1,9 +1,9 @@
Create a new cargo package in an existing directory
-Usage: cargo[EXE] init [OPTIONS] [path]
+Usage: cargo[EXE] init [OPTIONS] [PATH]
Arguments:
- [path] [default: .]
+ [PATH] [default: .]
Options:
--vcs <VCS> Initialize a new repository for the given version control system,
@@ -12,7 +12,7 @@ Options:
--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]
+ 2021, 2024]
--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
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/mod.rs
index a1988a06a..0b397111e 100644
--- a/src/tools/cargo/tests/testsuite/cargo_init/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_init/mod.rs
@@ -42,3 +42,4 @@ mod simple_hg_ignore_exists;
mod simple_lib;
mod unknown_flags;
mod with_argument;
+mod workspace_add_member;
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/stderr.log
index 980e8acd8..04a3c3ff0 100644
--- a/src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/unknown_flags/stderr.log
@@ -2,6 +2,6 @@ error: unexpected argument '--flag' found
tip: to pass '--flag' as a value, use '-- --flag'
-Usage: cargo[EXE] init <path>
+Usage: cargo[EXE] init <PATH>
For more information, try '--help'.
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/in/Cargo.toml
new file mode 100644
index 000000000..61bdb9cbf
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/in/Cargo.toml
@@ -0,0 +1,2 @@
+[workspace]
+resolver = "2"
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/in/crates/foo/.keep b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/in/crates/foo/.keep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/in/crates/foo/.keep
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/mod.rs b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/mod.rs
new file mode 100644
index 000000000..87e2af0e5
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/mod.rs
@@ -0,0 +1,21 @@
+use cargo_test_support::compare::assert_ui;
+use cargo_test_support::prelude::*;
+use cargo_test_support::Project;
+
+use cargo_test_support::curr_dir;
+
+#[cargo_test]
+fn case() {
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = &project.root();
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg_line("init --bin --vcs none")
+ .current_dir(project_root.join("crates").join("foo"))
+ .assert()
+ .success()
+ .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_init/workspace_add_member/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/Cargo.toml
new file mode 100644
index 000000000..18a4e7cf2
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = ["crates/foo"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/Cargo.toml
new file mode 100644
index 000000000..1d9cfe317
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "foo"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/out/crates/foo/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/stderr.log b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/stderr.log
new file mode 100644
index 000000000..3847e4e4a
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/stderr.log
@@ -0,0 +1 @@
+ Created binary (application) package
diff --git a/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/stdout.log b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_init/workspace_add_member/stdout.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
index 2267c5f6b..5e3458d37 100644
--- a/src/tools/cargo/tests/testsuite/cargo_install/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_install/help/stdout.log
@@ -1,9 +1,9 @@
Install a Rust binary. Default location is $HOME/.cargo/bin
-Usage: cargo[EXE] install [OPTIONS] [crate]...
+Usage: cargo[EXE] install [OPTIONS] [CRATE[@<VER>]]...
Arguments:
- [crate]...
+ [CRATE[@<VER>]]... Select the package from the given source
Options:
--version <VERSION> Specify a version to install
diff --git a/src/tools/cargo/tests/testsuite/cargo_login/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_login/help/stdout.log
index fd0f3eb3d..e0d5e7e69 100644
--- a/src/tools/cargo/tests/testsuite/cargo_login/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_login/help/stdout.log
@@ -1,9 +1,9 @@
Log in to a registry.
-Usage: cargo[EXE] login [OPTIONS] [token] [-- [args]...]
+Usage: cargo[EXE] login [OPTIONS] [TOKEN] [-- [args]...]
Arguments:
- [token]
+ [TOKEN]
[args]... Additional arguments for the credential provider
Options:
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/in/Cargo.toml
new file mode 100644
index 000000000..0614e21f7
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/in/Cargo.toml
@@ -0,0 +1,5 @@
+[workspace]
+resolver = "2"
+
+[workspace.package]
+authors = ["Rustaceans"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/mod.rs
new file mode 100644
index 000000000..9b9642468
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/mod.rs
@@ -0,0 +1,22 @@
+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() {
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = project.root();
+ let cwd = &project_root;
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg("new")
+ .args(["crates/foo"])
+ .current_dir(cwd)
+ .assert()
+ .success()
+ .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_new/add_members_to_workspace_format_previous_items/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/Cargo.toml
new file mode 100644
index 000000000..8df793a4f
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/Cargo.toml
@@ -0,0 +1,6 @@
+[workspace]
+resolver = "2"
+members = ["crates/foo"]
+
+[workspace.package]
+authors = ["Rustaceans"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/Cargo.toml
new file mode 100644
index 000000000..d97480c7c
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "foo"
+version = "0.1.0"
+edition = "2021"
+authors.workspace = true
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/out/crates/foo/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/stderr.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/stderr.log
new file mode 100644
index 000000000..90150cdf5
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/stderr.log
@@ -0,0 +1 @@
+ Created binary (application) `crates/foo` package
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/stdout.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_previous_items/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/Cargo.toml
new file mode 100644
index 000000000..1f3dfe4de
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = ["crates/bar", "crates/qux"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/bar/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/bar/Cargo.toml
new file mode 100644
index 000000000..825efac34
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/bar/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "bar"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/bar/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/bar/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/bar/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/qux/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/qux/Cargo.toml
new file mode 100644
index 000000000..30a9d52b2
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/qux/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "qux"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/qux/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/qux/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/in/crates/qux/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/mod.rs
new file mode 100644
index 000000000..9b9642468
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/mod.rs
@@ -0,0 +1,22 @@
+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() {
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = project.root();
+ let cwd = &project_root;
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg("new")
+ .args(["crates/foo"])
+ .current_dir(cwd)
+ .assert()
+ .success()
+ .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_new/add_members_to_workspace_format_sorted/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/Cargo.toml
new file mode 100644
index 000000000..cf27071a9
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = ["crates/bar", "crates/foo", "crates/qux"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/Cargo.toml
new file mode 100644
index 000000000..1d9cfe317
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "foo"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/out/crates/foo/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/stderr.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/stderr.log
new file mode 100644
index 000000000..90150cdf5
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/stderr.log
@@ -0,0 +1 @@
+ Created binary (application) `crates/foo` package
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/stdout.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_format_sorted/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/in/Cargo.toml
new file mode 100644
index 000000000..226fd80bc
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/in/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = []
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/mod.rs
new file mode 100644
index 000000000..8bf91be45
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/mod.rs
@@ -0,0 +1,23 @@
+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() {
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = project.root();
+ let cwd = &project_root;
+ let package_path = cwd.join("crates").join("foo");
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg("new")
+ .args([package_path])
+ .current_dir(cwd)
+ .assert()
+ .success()
+ .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_new/add_members_to_workspace_with_absolute_package_path/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/Cargo.toml
new file mode 100644
index 000000000..18a4e7cf2
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = ["crates/foo"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/Cargo.toml
new file mode 100644
index 000000000..1d9cfe317
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "foo"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/out/crates/foo/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/stderr.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/stderr.log
new file mode 100644
index 000000000..c93b25acb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/stderr.log
@@ -0,0 +1 @@
+ Created binary (application) `[ROOT]/case/crates/foo` package
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/stdout.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_absolute_package_path/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/in/Cargo.toml
new file mode 100644
index 000000000..226fd80bc
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/in/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = []
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/mod.rs
new file mode 100644
index 000000000..9b9642468
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/mod.rs
@@ -0,0 +1,22 @@
+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() {
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = project.root();
+ let cwd = &project_root;
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg("new")
+ .args(["crates/foo"])
+ .current_dir(cwd)
+ .assert()
+ .success()
+ .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_new/add_members_to_workspace_with_empty_members/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/Cargo.toml
new file mode 100644
index 000000000..18a4e7cf2
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = ["crates/foo"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/Cargo.toml
new file mode 100644
index 000000000..1d9cfe317
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "foo"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/out/crates/foo/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/stderr.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/stderr.log
new file mode 100644
index 000000000..90150cdf5
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/stderr.log
@@ -0,0 +1 @@
+ Created binary (application) `crates/foo` package
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/stdout.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_empty_members/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/in/Cargo.toml
new file mode 100644
index 000000000..4790076a8
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/in/Cargo.toml
@@ -0,0 +1,4 @@
+[workspace]
+resolver = "2"
+members = []
+exclude = ["crates/foo"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/in/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/in/src/lib.rs
new file mode 100644
index 000000000..7d12d9af8
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/in/src/lib.rs
@@ -0,0 +1,14 @@
+pub fn add(left: usize, right: usize) -> usize {
+ left + right
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn it_works() {
+ let result = add(2, 2);
+ assert_eq!(result, 4);
+ }
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/mod.rs
new file mode 100644
index 000000000..9b9642468
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/mod.rs
@@ -0,0 +1,22 @@
+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() {
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = project.root();
+ let cwd = &project_root;
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg("new")
+ .args(["crates/foo"])
+ .current_dir(cwd)
+ .assert()
+ .success()
+ .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_new/add_members_to_workspace_with_exclude_list/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/Cargo.toml
new file mode 100644
index 000000000..4790076a8
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/Cargo.toml
@@ -0,0 +1,4 @@
+[workspace]
+resolver = "2"
+members = []
+exclude = ["crates/foo"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/Cargo.toml
new file mode 100644
index 000000000..1d9cfe317
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "foo"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/out/crates/foo/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/stderr.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/stderr.log
new file mode 100644
index 000000000..90150cdf5
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/stderr.log
@@ -0,0 +1 @@
+ Created binary (application) `crates/foo` package
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/stdout.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_exclude_list/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/in/Cargo.toml
new file mode 100644
index 000000000..a848b85b4
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/in/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = ["crates/*"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/mod.rs
new file mode 100644
index 000000000..9b9642468
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/mod.rs
@@ -0,0 +1,22 @@
+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() {
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = project.root();
+ let cwd = &project_root;
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg("new")
+ .args(["crates/foo"])
+ .current_dir(cwd)
+ .assert()
+ .success()
+ .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_new/add_members_to_workspace_with_members_glob/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/Cargo.toml
new file mode 100644
index 000000000..a848b85b4
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/Cargo.toml
@@ -0,0 +1,3 @@
+[workspace]
+resolver = "2"
+members = ["crates/*"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/Cargo.toml
new file mode 100644
index 000000000..1d9cfe317
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "foo"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/src/main.rs
new file mode 100644
index 000000000..e7a11a969
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/out/crates/foo/src/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+ println!("Hello, world!");
+}
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/stderr.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/stderr.log
new file mode 100644
index 000000000..90150cdf5
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/stderr.log
@@ -0,0 +1 @@
+ Created binary (application) `crates/foo` package
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/stdout.log b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/add_members_to_workspace_with_members_glob/stdout.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
index a937f619b..3df5eceb8 100644
--- a/src/tools/cargo/tests/testsuite/cargo_new/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_new/help/stdout.log
@@ -1,9 +1,9 @@
Create a new cargo package at <path>
-Usage: cargo[EXE] new [OPTIONS] <path>
+Usage: cargo[EXE] new [OPTIONS] <PATH>
Arguments:
- <path>
+ <PATH>
Options:
--vcs <VCS> Initialize a new repository for the given version control system,
@@ -12,7 +12,7 @@ Options:
--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]
+ 2021, 2024]
--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
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/mod.rs
index 969b09f4f..da0304409 100644
--- a/src/tools/cargo/tests/testsuite/cargo_new/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_new/mod.rs
@@ -1,3 +1,9 @@
+mod add_members_to_workspace_format_previous_items;
+mod add_members_to_workspace_format_sorted;
+mod add_members_to_workspace_with_absolute_package_path;
+mod add_members_to_workspace_with_empty_members;
+mod add_members_to_workspace_with_exclude_list;
+mod add_members_to_workspace_with_members_glob;
mod help;
mod inherit_workspace_lints;
mod inherit_workspace_package_table;
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/out/Cargo.toml
index 2d204581c..8de6ed4bf 100644
--- a/src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/out/Cargo.toml
@@ -1,3 +1,5 @@
+[workspace]
+members = ["foo"]
[workspace.package]
authors = ["Rustaceans"]
description = "foo"
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/stderr.log b/src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/stderr.log
index 03b1ff6db..9b5015924 100644
--- a/src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_new/not_inherit_workspace_package_table_if_not_members/stderr.log
@@ -1,9 +1 @@
-warning: compiling this new package may not work due to invalid workspace configuration
-
-current package believes it's in a workspace when it's not:
-current: [ROOT]/case/foo/Cargo.toml
-workspace: [ROOT]/case/Cargo.toml
-
-this may be fixable by adding `foo` to the `workspace.members` array of the manifest located at: [ROOT]/case/Cargo.toml
-Alternatively, to keep it out of the workspace, add the package to the `workspace.exclude` array, or add an empty `[workspace]` table to the package's manifest.
Created binary (application) `foo` package
diff --git a/src/tools/cargo/tests/testsuite/cargo_owner/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_owner/help/stdout.log
index 580be3c88..110df8e9a 100644
--- a/src/tools/cargo/tests/testsuite/cargo_owner/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_owner/help/stdout.log
@@ -1,9 +1,9 @@
Manage the owners of a crate on the registry
-Usage: cargo[EXE] owner [OPTIONS] [crate]
+Usage: cargo[EXE] owner [OPTIONS] [CRATE]
Arguments:
- [crate]
+ [CRATE]
Options:
-a, --add <LOGIN> Name of a user or team to invite as an owner
diff --git a/src/tools/cargo/tests/testsuite/cargo_pkgid/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_pkgid/help/stdout.log
index ed48bb7ea..5971e88dc 100644
--- a/src/tools/cargo/tests/testsuite/cargo_pkgid/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_pkgid/help/stdout.log
@@ -1,9 +1,9 @@
Print a fully qualified package specification
-Usage: cargo[EXE] pkgid [OPTIONS] [spec]
+Usage: cargo[EXE] pkgid [OPTIONS] [SPEC]
Arguments:
- [spec]
+ [SPEC]
Options:
-q, --quiet Do not print cargo log messages
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_remove/help/stdout.log
index 8937fb9f3..47d2c87ad 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/help/stdout.log
@@ -15,9 +15,9 @@ Options:
-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
+ --dev Remove from dev-dependencies
+ --build Remove from build-dependencies
+ --target <TARGET> Remove from target-dependencies
Package Selection:
-p, --package [<SPEC>] Package to remove from
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/stderr.log
index eea124d65..9bb137d1f 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/stderr.log
@@ -1,2 +1,2 @@
Removing invalid_dependency_name from dependencies
-error: the dependency `invalid_dependency_name` could not be found in `dependencies`.
+error: the dependency `invalid_dependency_name` could not be found in `dependencies`
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/stderr.log
index fff5ff00a..8cbafa98e 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/stderr.log
@@ -1,2 +1,2 @@
Removing docopt from build-dependencies
-error: the dependency `docopt` could not be found in `build-dependencies`.
+error: the dependency `docopt` could not be found in `build-dependencies`; it is present in `dependencies`
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/stderr.log
index 1926f9577..60c0f8b41 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/stderr.log
@@ -1,2 +1,2 @@
Removing semver from dev-dependencies
-error: the dependency `semver` could not be found in `dev-dependencies`.
+error: the dependency `semver` could not be found in `dev-dependencies`; it is present in `dependencies`
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/stderr.log
index 5075b80b7..eae9f7887 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/stderr.log
@@ -1,2 +1,2 @@
Removing dbus from dependencies for target `powerpc-unknown-linux-gnu`
-error: the dependency `dbus` could not be found in `target.powerpc-unknown-linux-gnu.dependencies`.
+error: the dependency `dbus` could not be found in `target.powerpc-unknown-linux-gnu.dependencies`; it is present in `target.x86_64-unknown-linux-gnu.dependencies`
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/stderr.log
index 54bfe085f..635a7bd6c 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/stderr.log
@@ -1,2 +1,2 @@
Removing toml from dependencies for target `x86_64-unknown-linux-gnu`
-error: the dependency `toml` could not be found in `target.x86_64-unknown-linux-gnu.dependencies`.
+error: the dependency `toml` could not be found in `target.x86_64-unknown-linux-gnu.dependencies`; it is present in `dependencies`
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs
index 4403e2425..7b9190642 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs
@@ -20,6 +20,7 @@ mod multiple_dev;
mod no_arg;
mod offline;
mod optional_dep_feature;
+mod optional_dep_feature_formatting;
mod optional_feature;
mod package;
mod remove_basic;
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/out/Cargo.toml
index d961b2bb1..b435e33eb 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/out/Cargo.toml
@@ -17,4 +17,4 @@ toml = "0.1"
clippy = "0.4"
[features]
-std = ["semver/std"]
+std = [ "semver/std"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/out/Cargo.toml
index 63112d334..f9613bd30 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/out/Cargo.toml
@@ -20,4 +20,4 @@ clippy = "0.4"
regex = "0.1.1"
[features]
-std = ["semver/std"]
+std = [ "semver/std"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/in/Cargo.toml
new file mode 100644
index 000000000..01755d687
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/in/Cargo.toml
@@ -0,0 +1,42 @@
+[package]
+name = "cargo-remove-test-fixture"
+version = "0.1.0"
+
+[[bin]]
+name = "main"
+path = "src/main.rs"
+
+[build-dependencies]
+semver = "0.1.0"
+
+[dependencies]
+docopt = { version = "0.6", optional = true }
+rustc-serialize = { version = "0.4", optional = true }
+semver = "0.1"
+toml = { version = "0.1", optional = true }
+clippy = { version = "0.4", optional = true }
+
+[dev-dependencies]
+regex = "0.1.1"
+serde = "1.0.90"
+
+[features]
+std = [
+ # Leading clippy
+ "dep:clippy", # trailing clippy
+
+ # Leading docopt
+ "dep:docopt", # trailing docopt
+
+ # Leading rustc-serialize
+ "dep:rustc-serialize", # trailing rustc-serialize
+
+ # Leading serde/std
+ "serde/std", # trailing serde/std
+
+ # Leading semver/std
+ "semver/std", # trailing semver/std
+
+ # Leading toml
+ "dep:toml", # trailing toml
+]
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/in/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/in/src/lib.rs
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/in/src/lib.rs
@@ -0,0 +1 @@
+
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/mod.rs
new file mode 100644
index 000000000..69406387b
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/mod.rs
@@ -0,0 +1,35 @@
+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("clippy", "0.4.0+my-package").publish();
+ cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish();
+ cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish();
+ cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish();
+ cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish();
+ cargo_test_support::registry::Package::new("semver", "0.1.1")
+ .feature("std", &[])
+ .publish();
+ cargo_test_support::registry::Package::new("serde", "1.0.90")
+ .feature("std", &[])
+ .publish();
+
+ let project = Project::from_template(curr_dir!().join("in"));
+ let project_root = project.root();
+ let cwd = &project_root;
+
+ snapbox::cmd::Command::cargo_ui()
+ .arg("remove")
+ .args(["docopt", "toml"])
+ .current_dir(cwd)
+ .assert()
+ .success()
+ .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/optional_dep_feature_formatting/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/out/Cargo.toml
new file mode 100644
index 000000000..0398c8beb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/out/Cargo.toml
@@ -0,0 +1,40 @@
+[package]
+name = "cargo-remove-test-fixture"
+version = "0.1.0"
+
+[[bin]]
+name = "main"
+path = "src/main.rs"
+
+[build-dependencies]
+semver = "0.1.0"
+
+[dependencies]
+rustc-serialize = { version = "0.4", optional = true }
+semver = "0.1"
+clippy = { version = "0.4", optional = true }
+
+[dev-dependencies]
+regex = "0.1.1"
+serde = "1.0.90"
+
+[features]
+std = [
+ # Leading clippy
+ "dep:clippy", # trailing clippy
+
+ # Leading docopt
+ # trailing docopt
+
+ # Leading rustc-serialize
+ "dep:rustc-serialize", # trailing rustc-serialize
+
+ # Leading serde/std
+ "serde/std", # trailing serde/std
+
+ # Leading semver/std
+ "semver/std", # trailing semver/std
+
+ # Leading toml
+ # trailing toml
+]
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/stderr.log b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/stderr.log
new file mode 100644
index 000000000..7bceb0f94
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/stderr.log
@@ -0,0 +1,2 @@
+ Removing docopt from dependencies
+ Removing toml from dependencies
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/stdout.log b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature_formatting/stdout.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
index 4b39f30b3..97c13382a 100644
--- a/src/tools/cargo/tests/testsuite/cargo_run/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_run/help/stdout.log
@@ -1,9 +1,9 @@
Run a binary or example of the local package
-Usage: cargo[EXE] run [OPTIONS] [args]...
+Usage: cargo[EXE] run [OPTIONS] [ARGS]...
Arguments:
- [args]... Arguments for the binary or example to run
+ [ARGS]... Arguments for the binary or example to run
Options:
--ignore-rust-version Ignore `rust-version` specification in packages
diff --git a/src/tools/cargo/tests/testsuite/cargo_rustc/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_rustc/help/stdout.log
index 9d43841fe..60069f526 100644
--- a/src/tools/cargo/tests/testsuite/cargo_rustc/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_rustc/help/stdout.log
@@ -1,9 +1,9 @@
Compile a package, and pass extra options to the compiler
-Usage: cargo[EXE] rustc [OPTIONS] [args]...
+Usage: cargo[EXE] rustc [OPTIONS] [ARGS]...
Arguments:
- [args]... Extra rustc flags
+ [ARGS]... Extra rustc flags
Options:
--print <INFO> Output compiler information without compiling
@@ -28,9 +28,9 @@ Target Selection:
--bin [<NAME>] Build only the specified binary
--examples Build all examples
--example [<NAME>] Build only the specified example
- --tests Build all tests
+ --tests Build all test targets
--test [<NAME>] Build only the specified test target
- --benches Build all benches
+ --benches Build all bench targets
--bench [<NAME>] Build only the specified bench target
--all-targets Build all targets
diff --git a/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stdout.log
index 706072f24..67ee27e6b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stdout.log
@@ -1,9 +1,9 @@
Build a package's documentation, using specified custom flags.
-Usage: cargo[EXE] rustdoc [OPTIONS] [args]...
+Usage: cargo[EXE] rustdoc [OPTIONS] [ARGS]...
Arguments:
- [args]... Extra rustdoc flags
+ [ARGS]... Extra rustdoc flags
Options:
--open Opens the docs in a browser after the operation
@@ -26,9 +26,9 @@ Target Selection:
--bin [<NAME>] Build only the specified binary
--examples Build all examples
--example [<NAME>] Build only the specified example
- --tests Build all tests
+ --tests Build all test targets
--test [<NAME>] Build only the specified test target
- --benches Build all benches
+ --benches Build all bench targets
--bench [<NAME>] Build only the specified bench target
--all-targets Build all targets
diff --git a/src/tools/cargo/tests/testsuite/cargo_search/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_search/help/stdout.log
index 07170ad70..9cc508bba 100644
--- a/src/tools/cargo/tests/testsuite/cargo_search/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_search/help/stdout.log
@@ -1,9 +1,9 @@
Search packages in crates.io
-Usage: cargo[EXE] search [OPTIONS] [query]...
+Usage: cargo[EXE] search [OPTIONS] [QUERY]...
Arguments:
- [query]...
+ [QUERY]...
Options:
--limit <LIMIT> Limit the number of results (default: 10, max: 100)
diff --git a/src/tools/cargo/tests/testsuite/cargo_test/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_test/help/stdout.log
index 5df62d6bb..d7ec18f46 100644
--- a/src/tools/cargo/tests/testsuite/cargo_test/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_test/help/stdout.log
@@ -1,10 +1,10 @@
Execute all unit and integration tests and build examples of a local package
-Usage: cargo[EXE] test [OPTIONS] [TESTNAME] [-- [args]...]
+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
+ [ARGS]... Arguments for the test binary
Options:
--doc Test only this library's documentation
@@ -33,9 +33,9 @@ Target Selection:
--bin [<NAME>] Test only the specified binary
--examples Test all examples
--example [<NAME>] Test only the specified example
- --tests Test all tests
+ --tests Test all test targets
--test [<NAME>] Test only the specified test target
- --benches Test all benches
+ --benches Test all bench targets
--bench [<NAME>] Test only the specified bench target
--all-targets Test all targets (does not include doctests)
diff --git a/src/tools/cargo/tests/testsuite/cargo_test/no_keep_going/stderr.log b/src/tools/cargo/tests/testsuite/cargo_test/no_keep_going/stderr.log
index fd4ca9b2a..22323e651 100644
--- a/src/tools/cargo/tests/testsuite/cargo_test/no_keep_going/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_test/no_keep_going/stderr.log
@@ -2,6 +2,6 @@ error: unexpected argument '--keep-going' found
tip: use `--no-fail-fast` to run as many tests as possible regardless of failure
-Usage: cargo[EXE] test [OPTIONS] [TESTNAME] [-- [args]...]
+Usage: cargo[EXE] test [OPTIONS] [TESTNAME] [-- [ARGS]...]
For more information, try '--help'.
diff --git a/src/tools/cargo/tests/testsuite/cargo_uninstall/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_uninstall/help/stdout.log
index 2da1a5d57..efdf11c03 100644
--- a/src/tools/cargo/tests/testsuite/cargo_uninstall/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_uninstall/help/stdout.log
@@ -1,9 +1,9 @@
Remove a Rust binary
-Usage: cargo[EXE] uninstall [OPTIONS] [spec]...
+Usage: cargo[EXE] uninstall [OPTIONS] [SPEC]...
Arguments:
- [spec]...
+ [SPEC]...
Options:
--root <DIR> Directory to uninstall packages from
diff --git a/src/tools/cargo/tests/testsuite/cargo_yank/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_yank/help/stdout.log
index c6dbfeb9d..61dc800c7 100644
--- a/src/tools/cargo/tests/testsuite/cargo_yank/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_yank/help/stdout.log
@@ -1,9 +1,9 @@
Remove a pushed crate from the index
-Usage: cargo[EXE] yank [OPTIONS] [crate]
+Usage: cargo[EXE] yank [OPTIONS] [CRATE]
Arguments:
- [crate]
+ [CRATE]
Options:
--version <VERSION> The version to yank or un-yank
diff --git a/src/tools/cargo/tests/testsuite/check.rs b/src/tools/cargo/tests/testsuite/check.rs
index b74bd6209..03611ae67 100644
--- a/src/tools/cargo/tests/testsuite/check.rs
+++ b/src/tools/cargo/tests/testsuite/check.rs
@@ -1496,3 +1496,26 @@ fn check_unused_manifest_keys() {
)
.run();
}
+
+#[cargo_test]
+fn versionless_package() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ description = "foo"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+ p.cargo("check")
+ .with_stderr(
+ "\
+[CHECKING] foo v0.0.0 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/check_cfg.rs b/src/tools/cargo/tests/testsuite/check_cfg.rs
index c35da637d..57d5f8053 100644
--- a/src/tools/cargo/tests/testsuite/check_cfg.rs
+++ b/src/tools/cargo/tests/testsuite/check_cfg.rs
@@ -15,16 +15,16 @@ macro_rules! x {
$what, '(', $($who,)* ')', "'", "[..]")
}
}};
- ($tool:tt => $what:tt of $who:tt with $($values:tt)*) => {{
+ ($tool:tt => $what:tt of $who:tt with $($first_value:tt $($other_values:tt)*)?) => {{
#[cfg(windows)]
{
concat!("[RUNNING] [..]", $tool, "[..] --check-cfg \"",
- $what, '(', $who, $(", ", "/\"", $values, "/\"",)* ")", '"', "[..]")
+ $what, '(', $who, ", values(", $("/\"", $first_value, "/\"", $(", ", "/\"", $other_values, "/\"",)*)* "))", '"', "[..]")
}
#[cfg(not(windows))]
{
concat!("[RUNNING] [..]", $tool, "[..] --check-cfg '",
- $what, '(', $who, $(", ", "\"", $values, "\"",)* ")", "'", "[..]")
+ $what, '(', $who, ", values(", $("\"", $first_value, "\"", $(", ", "\"", $other_values, "\"",)*)* "))", "'", "[..]")
}
}};
}
@@ -47,9 +47,9 @@ fn features() {
.file("src/main.rs", "fn main() {}")
.build();
- p.cargo("check -v -Zcheck-cfg=features")
+ p.cargo("check -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b"))
.run();
}
@@ -76,10 +76,9 @@ fn features_with_deps() {
.file("bar/src/lib.rs", "#[allow(dead_code)] fn bar() {}")
.build();
- p.cargo("check -v -Zcheck-cfg=features")
+ p.cargo("check -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values" of "feature"))
- .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b"))
.run();
}
@@ -107,10 +106,9 @@ fn features_with_opt_deps() {
.file("bar/src/lib.rs", "#[allow(dead_code)] fn bar() {}")
.build();
- p.cargo("check -v -Zcheck-cfg=features")
+ p.cargo("check -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values" of "feature"))
- .with_stderr_contains(x!("rustc" => "values" of "feature" with "bar" "default" "f_a" "f_b"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "bar" "default" "f_a" "f_b"))
.run();
}
@@ -137,111 +135,22 @@ fn features_with_namespaced_features() {
.file("bar/src/lib.rs", "#[allow(dead_code)] fn bar() {}")
.build();
- p.cargo("check -v -Zcheck-cfg=features")
+ p.cargo("check -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b"))
.run();
}
#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn well_known_names() {
+fn well_known_names_values() {
let p = project()
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
.file("src/main.rs", "fn main() {}")
.build();
- p.cargo("check -v -Zcheck-cfg=names")
+ p.cargo("check -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "names"))
- .run();
-}
-
-#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn well_known_values() {
- let p = project()
- .file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
- .file("src/main.rs", "fn main() {}")
- .build();
-
- p.cargo("check -v -Zcheck-cfg=values")
- .masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values"))
- .run();
-}
-
-#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn cli_all_options() {
- let p = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.1.0"
-
- [features]
- f_a = []
- f_b = []
- "#,
- )
- .file("src/main.rs", "fn main() {}")
- .build();
-
- p.cargo("check -v -Zcheck-cfg=features,names,values")
- .masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "names"))
- .with_stderr_contains(x!("rustc" => "values"))
- .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b"))
- .run();
-}
-
-#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn features_with_cargo_check() {
- let p = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.1.0"
-
- [features]
- f_a = []
- f_b = []
- "#,
- )
- .file("src/main.rs", "fn main() {}")
- .build();
-
- p.cargo("check -v -Zcheck-cfg=features")
- .masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b"))
- .run();
-}
-
-#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn well_known_names_with_check() {
- let p = project()
- .file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
- .file("src/main.rs", "fn main() {}")
- .build();
-
- p.cargo("check -v -Zcheck-cfg=names")
- .masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "names"))
- .run();
-}
-
-#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn well_known_values_with_check() {
- let p = project()
- .file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
- .file("src/main.rs", "fn main() {}")
- .build();
-
- p.cargo("check -v -Zcheck-cfg=values")
- .masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with))
.run();
}
@@ -263,9 +172,9 @@ fn features_test() {
.file("src/main.rs", "fn main() {}")
.build();
- p.cargo("test -v -Zcheck-cfg=features")
+ p.cargo("test -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b"))
.run();
}
@@ -288,64 +197,37 @@ fn features_doctest() {
.file("src/lib.rs", "#[allow(dead_code)] fn foo() {}")
.build();
- p.cargo("test -v --doc -Zcheck-cfg=features")
+ p.cargo("test -v --doc -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values" of "feature" with "default" "f_a" "f_b"))
- .with_stderr_contains(x!("rustdoc" => "values" of "feature" with "default" "f_a" "f_b"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "default" "f_a" "f_b"))
+ .with_stderr_contains(x!("rustdoc" => "cfg" of "feature" with "default" "f_a" "f_b"))
.run();
}
#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn well_known_names_test() {
+fn well_known_names_values_test() {
let p = project()
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
.file("src/main.rs", "fn main() {}")
.build();
- p.cargo("test -v -Zcheck-cfg=names")
+ p.cargo("test -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "names"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with))
.run();
}
#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn well_known_values_test() {
- let p = project()
- .file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
- .file("src/main.rs", "fn main() {}")
- .build();
-
- p.cargo("test -v -Zcheck-cfg=values")
- .masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values"))
- .run();
-}
-
-#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn well_known_names_doctest() {
+fn well_known_names_values_doctest() {
let p = project()
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
.file("src/lib.rs", "#[allow(dead_code)] fn foo() {}")
.build();
- p.cargo("test -v --doc -Zcheck-cfg=names")
+ p.cargo("test -v --doc -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "names"))
- .with_stderr_contains(x!("rustdoc" => "names"))
- .run();
-}
-
-#[cargo_test(nightly, reason = "--check-cfg is unstable")]
-fn well_known_values_doctest() {
- let p = project()
- .file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
- .file("src/lib.rs", "#[allow(dead_code)] fn foo() {}")
- .build();
-
- p.cargo("test -v --doc -Zcheck-cfg=values")
- .masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "values"))
- .with_stderr_contains(x!("rustdoc" => "values"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with))
+ .with_stderr_contains(x!("rustdoc" => "cfg" of "feature" with))
.run();
}
@@ -368,9 +250,9 @@ fn features_doc() {
.file("src/lib.rs", "#[allow(dead_code)] fn foo() {}")
.build();
- p.cargo("doc -v -Zcheck-cfg=features")
+ p.cargo("doc -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustdoc" => "values" of "feature" with "default" "f_a" "f_b"))
+ .with_stderr_contains(x!("rustdoc" => "cfg" of "feature" with "default" "f_a" "f_b"))
.run();
}
@@ -390,13 +272,13 @@ fn build_script_feedback() {
.file("src/main.rs", "fn main() {}")
.file(
"build.rs",
- r#"fn main() { println!("cargo:rustc-check-cfg=names(foo)"); }"#,
+ r#"fn main() { println!("cargo:rustc-check-cfg=cfg(foo)"); }"#,
)
.build();
- p.cargo("check -v -Zcheck-cfg=output")
+ p.cargo("check -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "names" of "foo"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "foo"))
.run();
}
@@ -416,12 +298,13 @@ fn build_script_doc() {
.file("src/main.rs", "fn main() {}")
.file(
"build.rs",
- r#"fn main() { println!("cargo:rustc-check-cfg=names(foo)"); }"#,
+ r#"fn main() { println!("cargo:rustc-check-cfg=cfg(foo)"); }"#,
)
.build();
- p.cargo("doc -v -Zcheck-cfg=output")
+
+ p.cargo("doc -v -Zcheck-cfg")
.with_stderr_does_not_contain("rustc [..] --check-cfg [..]")
- .with_stderr_contains(x!("rustdoc" => "names" of "foo"))
+ .with_stderr_contains(x!("rustdoc" => "cfg" of "foo"))
.with_stderr(
"\
[COMPILING] foo v0.0.1 ([CWD])
@@ -429,7 +312,9 @@ fn build_script_doc() {
[RUNNING] `[..]/build-script-build`
[DOCUMENTING] foo [..]
[RUNNING] `rustdoc [..] src/main.rs [..]
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
+",
)
.masquerade_as_nightly_cargo(&["check-cfg"])
.run();
@@ -458,19 +343,54 @@ fn build_script_override() {
&format!(
r#"
[target.{}.a]
- rustc-check-cfg = ["names(foo)"]
+ rustc-check-cfg = ["cfg(foo)"]
"#,
target
),
)
.build();
- p.cargo("check -v -Zcheck-cfg=output")
- .with_stderr_contains(x!("rustc" => "names" of "foo"))
+ p.cargo("check -v -Zcheck-cfg")
+ .with_stderr_contains(x!("rustc" => "cfg" of "foo"))
.masquerade_as_nightly_cargo(&["check-cfg"])
.run();
}
+#[cargo_test]
+fn build_script_override_feature_gate() {
+ let target = cargo_test_support::rustc_host();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ links = "a"
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .file("build.rs", "fn main() {}")
+ .file(
+ ".cargo/config",
+ &format!(
+ r#"
+ [target.{}.a]
+ rustc-check-cfg = ["cfg(foo)"]
+ "#,
+ target
+ ),
+ )
+ .build();
+
+ p.cargo("check")
+ .with_stderr_contains(
+ "warning: target config[..]rustc-check-cfg[..] requires -Zcheck-cfg flag",
+ )
+ .run();
+}
+
#[cargo_test(nightly, reason = "--check-cfg is unstable")]
fn build_script_test() {
let p = project()
@@ -487,7 +407,7 @@ fn build_script_test() {
.file(
"build.rs",
r#"fn main() {
- println!("cargo:rustc-check-cfg=names(foo)");
+ println!("cargo:rustc-check-cfg=cfg(foo)");
println!("cargo:rustc-cfg=foo");
}"#,
)
@@ -516,9 +436,9 @@ fn build_script_test() {
.file("tests/test.rs", "#[cfg(foo)] #[test] fn test_bar() {}")
.build();
- p.cargo("test -v -Zcheck-cfg=output")
- .with_stderr_contains(x!("rustc" => "names" of "foo"))
- .with_stderr_contains(x!("rustdoc" => "names" of "foo"))
+ p.cargo("test -v -Zcheck-cfg")
+ .with_stderr_contains(x!("rustc" => "cfg" of "foo"))
+ .with_stderr_contains(x!("rustdoc" => "cfg" of "foo"))
.with_stdout_contains("test test_foo ... ok")
.with_stdout_contains("test test_bar ... ok")
.with_stdout_contains_n("test [..] ... ok", 3)
@@ -526,6 +446,34 @@ fn build_script_test() {
.run();
}
+#[cargo_test]
+fn build_script_feature_gate() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ build = "build.rs"
+ "#,
+ )
+ .file(
+ "build.rs",
+ r#"fn main() {
+ println!("cargo:rustc-check-cfg=cfg(foo)");
+ println!("cargo:rustc-cfg=foo");
+ }"#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .build();
+
+ p.cargo("check")
+ .with_stderr_contains("warning[..]cargo:rustc-check-cfg requires -Zcheck-cfg flag")
+ .with_status(0)
+ .run();
+}
+
#[cargo_test(nightly, reason = "--check-cfg is unstable")]
fn config_valid() {
let p = project()
@@ -546,16 +494,14 @@ fn config_valid() {
".cargo/config.toml",
r#"
[unstable]
- check-cfg = ["features", "names", "values"]
+ check-cfg = true
"#,
)
.build();
- p.cargo("check -v -Zcheck-cfg=features,names,values")
+ p.cargo("check -v")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "names"))
- .with_stderr_contains(x!("rustc" => "values"))
- .with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b"))
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b"))
.run();
}
@@ -582,7 +528,36 @@ fn config_invalid() {
p.cargo("check")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains("error: unstable check-cfg only takes `features`, `names`, `values` or `output` as valid inputs")
+ .with_stderr_contains("error:[..]`unstable.check-cfg` expected true/false[..]")
.with_status(101)
.run();
}
+
+#[cargo_test]
+fn config_feature_gate() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [features]
+ f_a = []
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .file(
+ ".cargo/config.toml",
+ r#"
+ [unstable]
+ check-cfg = true
+ "#,
+ )
+ .build();
+
+ p.cargo("check -v")
+ .with_stderr_does_not_contain("--check-cfg")
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/collisions.rs b/src/tools/cargo/tests/testsuite/collisions.rs
index 77e05dd9c..986619eb2 100644
--- a/src/tools/cargo/tests/testsuite/collisions.rs
+++ b/src/tools/cargo/tests/testsuite/collisions.rs
@@ -202,6 +202,7 @@ fn collision_doc_multiple_versions() {
[DOCUMENTING] bar v2.0.0
[FINISHED] [..]
[DOCUMENTING] foo v0.1.0 [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -384,6 +385,7 @@ fn collision_doc_profile_split() {
[DOCUMENTING] pm v0.1.0 [..]
[DOCUMENTING] foo v0.1.0 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -430,6 +432,7 @@ the same path; see <https://github.com/rust-lang/cargo/issues/6313>.
[CHECKING] bar v1.0.0
[DOCUMENTING] foo v0.1.0 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -478,6 +481,7 @@ fn collision_doc_target() {
[CHECKING] bar v1.0.0
[DOCUMENTING] foo v0.1.0 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/[..]/doc/foo/index.html
",
)
.run();
@@ -545,6 +549,8 @@ the same path; see <https://github.com/rust-lang/cargo/issues/6313>.
[DOCUMENTING] foo-macro v1.0.0 [..]
[DOCUMENTING] abc v1.0.0 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/abc/index.html
+[GENERATED] [CWD]/target/doc/foo_macro/index.html
")
.run();
}
diff --git a/src/tools/cargo/tests/testsuite/config.rs b/src/tools/cargo/tests/testsuite/config.rs
index 7078fc445..e5078bd8e 100644
--- a/src/tools/cargo/tests/testsuite/config.rs
+++ b/src/tools/cargo/tests/testsuite/config.rs
@@ -2,8 +2,9 @@
use cargo::core::{PackageIdSpec, Shell};
use cargo::util::config::{self, Config, Definition, JobsConfig, SslVersionConfig, StringList};
-use cargo::util::interning::InternedString;
-use cargo::util::toml::{self as cargo_toml, TomlDebugInfo, VecStringOrBool as VSOB};
+use cargo::util::toml::schema::TomlTrimPaths;
+use cargo::util::toml::schema::TomlTrimPathsValue;
+use cargo::util::toml::schema::{self as cargo_toml, TomlDebugInfo, VecStringOrBool as VSOB};
use cargo::CargoResult;
use cargo_test_support::compare;
use cargo_test_support::{panic_error, paths, project, symlink_supported, t};
@@ -21,6 +22,7 @@ pub struct ConfigBuilder {
unstable: Vec<String>,
config_args: Vec<String>,
cwd: Option<PathBuf>,
+ root: Option<PathBuf>,
enable_nightly_features: bool,
}
@@ -30,6 +32,7 @@ impl ConfigBuilder {
env: HashMap::new(),
unstable: Vec::new(),
config_args: Vec::new(),
+ root: None,
cwd: None,
enable_nightly_features: false,
}
@@ -60,8 +63,28 @@ impl ConfigBuilder {
}
/// Sets the current working directory where config files will be loaded.
+ ///
+ /// Default is the root from [`ConfigBuilder::root`] or [`paths::root`].
pub fn cwd(&mut self, path: impl AsRef<Path>) -> &mut Self {
- self.cwd = Some(paths::root().join(path.as_ref()));
+ let path = path.as_ref();
+ let cwd = self
+ .root
+ .as_ref()
+ .map_or_else(|| paths::root().join(path), |r| r.join(path));
+ self.cwd = Some(cwd);
+ self
+ }
+
+ /// Sets the test root directory.
+ ///
+ /// This generally should not be necessary. It is only useful if you want
+ /// to create a `Config` from within a thread. Since Cargo's testsuite
+ /// uses thread-local storage, this can be used to avoid accessing that
+ /// thread-local storage.
+ ///
+ /// Default is [`paths::root`].
+ pub fn root(&mut self, path: impl Into<PathBuf>) -> &mut Self {
+ self.root = Some(path.into());
self
}
@@ -72,14 +95,15 @@ impl ConfigBuilder {
/// Creates the `Config`, returning a Result.
pub fn build_err(&self) -> CargoResult<Config> {
- let output = Box::new(fs::File::create(paths::root().join("shell.out")).unwrap());
+ let root = self.root.clone().unwrap_or_else(|| paths::root());
+ let output = Box::new(fs::File::create(root.join("shell.out")).unwrap());
let shell = Shell::from_write(output);
- let cwd = self.cwd.clone().unwrap_or_else(|| paths::root());
- let homedir = paths::home();
+ let cwd = self.cwd.clone().unwrap_or_else(|| root.clone());
+ let homedir = root.join("home").join(".cargo");
let mut config = Config::new(shell, cwd, homedir);
config.nightly_features_allowed = self.enable_nightly_features || !self.unstable.is_empty();
config.set_env(self.env.clone());
- config.set_search_stop_path(paths::root());
+ config.set_search_stop_path(&root);
config.configure(
0,
false,
@@ -422,8 +446,8 @@ lto = false
p,
cargo_toml::TomlProfile {
lto: Some(cargo_toml::StringOrBool::Bool(false)),
- dir_name: Some(InternedString::new("without-lto")),
- inherits: Some(InternedString::new("dev")),
+ dir_name: Some(String::from("without-lto")),
+ inherits: Some(String::from("dev")),
..Default::default()
}
);
@@ -1503,7 +1527,7 @@ fn all_profile_options() {
let base_settings = cargo_toml::TomlProfile {
opt_level: Some(cargo_toml::TomlOptLevel("0".to_string())),
lto: Some(cargo_toml::StringOrBool::String("thin".to_string())),
- codegen_backend: Some(InternedString::new("example")),
+ codegen_backend: Some(String::from("example")),
codegen_units: Some(123),
debug: Some(cargo_toml::TomlDebugInfo::Limited),
split_debuginfo: Some("packed".to_string()),
@@ -1512,12 +1536,13 @@ fn all_profile_options() {
panic: Some("abort".to_string()),
overflow_checks: Some(true),
incremental: Some(true),
- dir_name: Some(InternedString::new("dir_name")),
- inherits: Some(InternedString::new("debug")),
+ dir_name: Some(String::from("dir_name")),
+ inherits: Some(String::from("debug")),
strip: Some(cargo_toml::StringOrBool::String("symbols".to_string())),
package: None,
build_override: None,
rustflags: None,
+ trim_paths: None,
};
let mut overrides = BTreeMap::new();
let key = cargo_toml::ProfilePackageSpec::Spec(PackageIdSpec::parse("foo").unwrap());
@@ -1705,3 +1730,63 @@ jobs = 2
JobsConfig::Integer(v) => assert_eq!(v, 2),
}
}
+
+#[cargo_test]
+fn trim_paths_parsing() {
+ let config = ConfigBuilder::new().build();
+ let p: cargo_toml::TomlProfile = config.get("profile.dev").unwrap();
+ assert_eq!(p.trim_paths, None);
+
+ let test_cases = [
+ (TomlTrimPathsValue::Diagnostics.into(), "diagnostics"),
+ (TomlTrimPathsValue::Macro.into(), "macro"),
+ (TomlTrimPathsValue::Object.into(), "object"),
+ ];
+ for (expected, val) in test_cases {
+ // env
+ let config = ConfigBuilder::new()
+ .env("CARGO_PROFILE_DEV_TRIM_PATHS", val)
+ .build();
+ let trim_paths: TomlTrimPaths = config.get("profile.dev.trim-paths").unwrap();
+ assert_eq!(trim_paths, expected, "failed to parse {val}");
+
+ // config.toml
+ let config = ConfigBuilder::new()
+ .config_arg(format!("profile.dev.trim-paths='{val}'"))
+ .build();
+ let trim_paths: TomlTrimPaths = config.get("profile.dev.trim-paths").unwrap();
+ assert_eq!(trim_paths, expected, "failed to parse {val}");
+ }
+
+ let test_cases = [(TomlTrimPaths::none(), false), (TomlTrimPaths::All, true)];
+
+ for (expected, val) in test_cases {
+ // env
+ let config = ConfigBuilder::new()
+ .env("CARGO_PROFILE_DEV_TRIM_PATHS", format!("{val}"))
+ .build();
+ let trim_paths: TomlTrimPaths = config.get("profile.dev.trim-paths").unwrap();
+ assert_eq!(trim_paths, expected, "failed to parse {val}");
+
+ // config.toml
+ let config = ConfigBuilder::new()
+ .config_arg(format!("profile.dev.trim-paths={val}"))
+ .build();
+ let trim_paths: TomlTrimPaths = config.get("profile.dev.trim-paths").unwrap();
+ assert_eq!(trim_paths, expected, "failed to parse {val}");
+ }
+
+ let expected = vec![
+ TomlTrimPathsValue::Diagnostics,
+ TomlTrimPathsValue::Macro,
+ TomlTrimPathsValue::Object,
+ ]
+ .into();
+ let val = r#"["diagnostics", "macro", "object"]"#;
+ // config.toml
+ let config = ConfigBuilder::new()
+ .config_arg(format!("profile.dev.trim-paths={val}"))
+ .build();
+ let trim_paths: TomlTrimPaths = config.get("profile.dev.trim-paths").unwrap();
+ assert_eq!(trim_paths, expected, "failed to parse {val}");
+}
diff --git a/src/tools/cargo/tests/testsuite/cross_compile.rs b/src/tools/cargo/tests/testsuite/cross_compile.rs
index 1bc0c277d..b57ba2c7a 100644
--- a/src/tools/cargo/tests/testsuite/cross_compile.rs
+++ b/src/tools/cargo/tests/testsuite/cross_compile.rs
@@ -411,92 +411,6 @@ fn linker() {
.run();
}
-#[cargo_test(nightly, reason = "plugins are unstable")]
-fn plugin_with_extra_dylib_dep() {
- if cross_compile::disabled() {
- return;
- }
-
- let foo = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
-
- [dependencies.bar]
- path = "../bar"
- "#,
- )
- .file(
- "src/main.rs",
- r#"
- #![feature(plugin)]
- #![plugin(bar)]
-
- fn main() {}
- "#,
- )
- .build();
- let _bar = project()
- .at("bar")
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "bar"
- version = "0.0.1"
- authors = []
-
- [lib]
- name = "bar"
- plugin = true
-
- [dependencies.baz]
- path = "../baz"
- "#,
- )
- .file(
- "src/lib.rs",
- r#"
- #![feature(rustc_private)]
-
- extern crate baz;
- extern crate rustc_driver;
-
- use rustc_driver::plugin::Registry;
-
- #[no_mangle]
- pub fn __rustc_plugin_registrar(reg: &mut Registry) {
- println!("{}", baz::baz());
- }
- "#,
- )
- .build();
- let _baz = project()
- .at("baz")
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "baz"
- version = "0.0.1"
- authors = []
-
- [lib]
- name = "baz"
- crate_type = ["dylib"]
- "#,
- )
- .file("src/lib.rs", "pub fn baz() -> i32 { 1 }")
- .build();
-
- let target = cross_compile::alternate();
- foo.cargo("build --target").arg(&target).run();
-}
-
#[cargo_test]
fn cross_tests() {
if !cross_compile::can_run_on_host() {
diff --git a/src/tools/cargo/tests/testsuite/custom_target.rs b/src/tools/cargo/tests/testsuite/custom_target.rs
index 491d3233c..a04029075 100644
--- a/src/tools/cargo/tests/testsuite/custom_target.rs
+++ b/src/tools/cargo/tests/testsuite/custom_target.rs
@@ -116,6 +116,8 @@ fn custom_target_dependency() {
}
#[cargo_test(nightly, reason = "requires features no_core, lang_items")]
+// This is randomly crashing in lld. See https://github.com/rust-lang/rust/issues/115985
+#[cfg_attr(all(windows, target_env = "gnu"), ignore = "windows-gnu lld crashing")]
fn custom_bin_target() {
let p = project()
.file(
diff --git a/src/tools/cargo/tests/testsuite/death.rs b/src/tools/cargo/tests/testsuite/death.rs
index f0e182d01..b61896dc9 100644
--- a/src/tools/cargo/tests/testsuite/death.rs
+++ b/src/tools/cargo/tests/testsuite/death.rs
@@ -1,12 +1,12 @@
//! Tests for ctrl-C handling.
+use cargo_test_support::{project, slow_cpu_multiplier};
use std::fs;
use std::io::{self, Read};
use std::net::TcpListener;
use std::process::{Child, Stdio};
use std::thread;
-
-use cargo_test_support::{project, slow_cpu_multiplier};
+use std::time;
#[cargo_test]
fn ctrl_c_kills_everyone() {
@@ -87,6 +87,155 @@ fn ctrl_c_kills_everyone() {
);
}
+#[cargo_test]
+fn kill_cargo_add_never_corrupts_cargo_toml() {
+ cargo_test_support::registry::init();
+ cargo_test_support::registry::Package::new("my-package", "0.1.1+my-package").publish();
+
+ let with_dependency = r#"
+[package]
+name = "foo"
+version = "0.0.1"
+authors = []
+
+[dependencies]
+my-package = "0.1.1"
+"#;
+ let without_dependency = r#"
+[package]
+name = "foo"
+version = "0.0.1"
+authors = []
+"#;
+
+ for sleep_time_ms in [30, 60, 90] {
+ let p = project()
+ .file("Cargo.toml", without_dependency)
+ .file("src/lib.rs", "")
+ .build();
+
+ let mut cargo = p.cargo("add").arg("my-package").build_command();
+ cargo
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped());
+
+ let mut child = cargo.spawn().unwrap();
+
+ thread::sleep(time::Duration::from_millis(sleep_time_ms));
+
+ assert!(child.kill().is_ok());
+ assert!(child.wait().is_ok());
+
+ // check the Cargo.toml
+ let contents = fs::read(p.root().join("Cargo.toml")).unwrap();
+
+ // not empty
+ assert_ne!(
+ contents, b"",
+ "Cargo.toml is empty, and should not be at {} milliseconds",
+ sleep_time_ms
+ );
+
+ // We should have the original Cargo.toml or the new one, nothing else.
+ if std::str::from_utf8(&contents)
+ .unwrap()
+ .contains("[dependencies]")
+ {
+ assert_eq!(
+ std::str::from_utf8(&contents).unwrap(),
+ with_dependency,
+ "Cargo.toml is with_dependency after add at {} milliseconds",
+ sleep_time_ms
+ );
+ } else {
+ assert_eq!(
+ std::str::from_utf8(&contents).unwrap(),
+ without_dependency,
+ "Cargo.toml is without_dependency after add at {} milliseconds",
+ sleep_time_ms
+ );
+ }
+ }
+}
+
+#[cargo_test]
+fn kill_cargo_remove_never_corrupts_cargo_toml() {
+ let with_dependency = r#"
+[package]
+name = "foo"
+version = "0.0.1"
+authors = []
+build = "build.rs"
+
+[dependencies]
+bar = "0.0.1"
+"#;
+ let without_dependency = r#"
+[package]
+name = "foo"
+version = "0.0.1"
+authors = []
+build = "build.rs"
+"#;
+
+ // This test depends on killing the cargo process at the right time to cause a failed write.
+ // Note that we're iterating and using the index as time in ms to sleep before killing the cargo process.
+ // If it is working correctly, we never fail, but can't hang out here all day...
+ // So we'll just run it a few times and hope for the best.
+ for sleep_time_ms in [30, 60, 90] {
+ // new basic project with a single dependency
+ let p = project()
+ .file("Cargo.toml", with_dependency)
+ .file("src/lib.rs", "")
+ .build();
+
+ // run cargo remove the dependency
+ let mut cargo = p.cargo("remove").arg("bar").build_command();
+ cargo
+ .stdin(Stdio::piped())
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped());
+
+ let mut child = cargo.spawn().unwrap();
+
+ thread::sleep(time::Duration::from_millis(sleep_time_ms));
+
+ assert!(child.kill().is_ok());
+ assert!(child.wait().is_ok());
+
+ // check the Cargo.toml
+ let contents = fs::read(p.root().join("Cargo.toml")).unwrap();
+
+ // not empty
+ assert_ne!(
+ contents, b"",
+ "Cargo.toml is empty, and should not be at {} milliseconds",
+ sleep_time_ms
+ );
+
+ // We should have the original Cargo.toml or the new one, nothing else.
+ if std::str::from_utf8(&contents)
+ .unwrap()
+ .contains("[dependencies]")
+ {
+ assert_eq!(
+ std::str::from_utf8(&contents).unwrap(),
+ with_dependency,
+ "Cargo.toml is not the same as the original at {} milliseconds",
+ sleep_time_ms
+ );
+ } else {
+ assert_eq!(
+ std::str::from_utf8(&contents).unwrap(),
+ without_dependency,
+ "Cargo.toml is not the same as expected at {} milliseconds",
+ sleep_time_ms
+ );
+ }
+ }
+}
+
#[cfg(unix)]
pub fn ctrl_c(child: &mut Child) {
let r = unsafe { libc::kill(-(child.id() as i32), libc::SIGINT) };
diff --git a/src/tools/cargo/tests/testsuite/doc.rs b/src/tools/cargo/tests/testsuite/doc.rs
index a16980912..65169d214 100644
--- a/src/tools/cargo/tests/testsuite/doc.rs
+++ b/src/tools/cargo/tests/testsuite/doc.rs
@@ -31,6 +31,7 @@ fn simple() {
[..] foo v0.0.1 ([CWD])
[..] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -69,6 +70,7 @@ fn doc_twice() {
"\
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -103,6 +105,7 @@ fn doc_deps() {
[..] bar v0.0.1 ([CWD]/bar)
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -151,6 +154,7 @@ fn doc_no_deps() {
[CHECKING] bar v0.0.1 ([CWD]/bar)
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -284,6 +288,8 @@ the same path; see <https://github.com/rust-lang/cargo/issues/6313>.
[DOCUMENTING] bar v0.1.0 ([ROOT]/foo/bar)
[DOCUMENTING] foo v0.1.0 ([ROOT]/foo/foo)
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo_lib/index.html
+[GENERATED] [CWD]/target/doc/foo_lib/index.html
",
)
.run();
@@ -398,6 +404,7 @@ fn doc_lib_bin_same_name_documents_lib() {
"\
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -433,6 +440,7 @@ fn doc_lib_bin_same_name_documents_lib_when_requested() {
"\
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -478,6 +486,7 @@ the same path; see <https://github.com/rust-lang/cargo/issues/6313>.
[CHECKING] foo v0.0.1 ([CWD])
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -523,6 +532,7 @@ the same path; see <https://github.com/rust-lang/cargo/issues/6313>.
[CHECKING] foo v0.0.1 ([CWD])
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -567,7 +577,9 @@ fn doc_lib_bin_example_same_name_documents_named_example_when_requested() {
"\
[CHECKING] foo v0.0.1 ([CWD])
[DOCUMENTING] foo v0.0.1 ([CWD])
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/ex1/index.html
+",
)
.run();
@@ -620,7 +632,10 @@ fn doc_lib_bin_example_same_name_documents_examples_when_requested() {
"\
[CHECKING] foo v0.0.1 ([CWD])
[DOCUMENTING] foo v0.0.1 ([CWD])
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/ex1/index.html
+[GENERATED] [CWD]/target/doc/ex2/index.html
+",
)
.run();
@@ -677,6 +692,7 @@ fn doc_dash_p() {
[..] b v0.0.1 ([CWD]/b)
[DOCUMENTING] a v0.0.1 ([CWD]/a)
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/a/index.html
",
)
.run();
@@ -704,6 +720,7 @@ fn doc_all_exclude() {
"\
[DOCUMENTING] bar v0.1.0 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/bar/index.html
",
)
.run();
@@ -731,6 +748,7 @@ fn doc_all_exclude_glob() {
"\
[DOCUMENTING] bar v0.1.0 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/bar/index.html
",
)
.run();
@@ -918,6 +936,7 @@ fn doc_release() {
[DOCUMENTING] foo v0.0.1 ([..])
[RUNNING] `rustdoc [..] src/lib.rs [..]`
[FINISHED] release [optimized] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -1006,6 +1025,7 @@ fn features() {
[DOCUMENTING] bar v0.0.1 [..]
[DOCUMENTING] foo v0.0.1 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -1020,6 +1040,7 @@ fn features() {
[DOCUMENTING] bar v0.0.1 [..]
[DOCUMENTING] foo v0.0.1 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -1032,6 +1053,7 @@ fn features() {
[DOCUMENTING] bar v0.0.1 [..]
[DOCUMENTING] foo v0.0.1 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -1202,6 +1224,7 @@ fn doc_virtual_manifest_one_project() {
"\
[DOCUMENTING] bar v0.1.0 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/bar/index.html
",
)
.run();
@@ -1229,6 +1252,7 @@ fn doc_virtual_manifest_glob() {
"\
[DOCUMENTING] baz v0.1.0 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/baz/index.html
",
)
.run();
@@ -1277,6 +1301,7 @@ the same path; see <https://github.com/rust-lang/cargo/issues/6313>.
[CHECKING] bar v0.1.0
[DOCUMENTING] bar v0.1.0 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/bar/index.html
",
)
.run();
@@ -1639,6 +1664,7 @@ fn doc_cap_lints() {
[CHECKING] a v0.5.0 ([..])
[DOCUMENTING] foo v0.0.1 ([..])
[FINISHED] dev [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -1903,6 +1929,7 @@ fn bin_private_items() {
"\
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -1963,6 +1990,7 @@ fn bin_private_items_deps() {
[CHECKING] bar v0.0.1 ([..])
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -1997,6 +2025,7 @@ fn crate_versions() {
[DOCUMENTING] foo v1.2.4 [..]
[RUNNING] `rustdoc --crate-type lib --crate-name foo src/lib.rs [..]--crate-version 1.2.4`
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -2406,7 +2435,8 @@ fn doc_fingerprint_unusual_behavior() {
p.cargo("doc")
.with_stderr(
"[DOCUMENTING] foo [..]\n\
- [FINISHED] [..]",
+ [FINISHED] [..]\n\
+ [GENERATED] [CWD]/target/doc/foo/index.html",
)
.run();
// This will delete somefile, but not .hidden.
@@ -2425,7 +2455,8 @@ fn doc_fingerprint_unusual_behavior() {
.masquerade_as_nightly_cargo(&["skip-rustdoc-fingerprint"])
.with_stderr(
"[DOCUMENTING] foo [..]\n\
- [FINISHED] [..]",
+ [FINISHED] [..]\n\
+ [GENERATED] [CWD]/target/doc/foo/index.html",
)
.run();
// Should not have deleted anything.
@@ -2467,6 +2498,8 @@ fn lib_before_bin() {
[RUNNING] `rustdoc --crate-type lib --crate-name foo src/lib.rs [..]
[RUNNING] `rustdoc --crate-type bin --crate-name somebin src/bin/somebin.rs [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
+[GENERATED] [CWD]/target/doc/somebin/index.html
",
)
.run();
@@ -2517,6 +2550,7 @@ fn doc_lib_false() {
[CHECKING] foo v0.1.0 [..]
[DOCUMENTING] foo v0.1.0 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/some_bin/index.html
",
)
.run();
@@ -2563,6 +2597,7 @@ fn doc_lib_false_dep() {
[CHECKING] bar v0.1.0 [..]
[DOCUMENTING] foo v0.1.0 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -2587,7 +2622,8 @@ fn link_to_private_item() {
p.cargo("doc")
.with_stderr(
"[DOCUMENTING] foo [..]\n\
- [FINISHED] [..]",
+ [FINISHED] [..]\n\
+ [GENERATED] [CWD]/target/doc/foo/index.html",
)
.run();
}
diff --git a/src/tools/cargo/tests/testsuite/docscrape.rs b/src/tools/cargo/tests/testsuite/docscrape.rs
index c536a6738..d4d011ff3 100644
--- a/src/tools/cargo/tests/testsuite/docscrape.rs
+++ b/src/tools/cargo/tests/testsuite/docscrape.rs
@@ -26,13 +26,18 @@ fn basic() {
[SCRAPING] foo v0.0.1 ([CWD])
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
p.cargo("doc -Zunstable-options -Z rustdoc-scrape-examples")
.masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"])
- .with_stderr("[FINISHED] [..]")
+ .with_stderr(
+ "[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
+",
+ )
.run();
let doc_html = p.read_file("target/doc/foo/fn.foo.html");
@@ -311,6 +316,7 @@ fn cache() {
[SCRAPING] foo v0.0.1 ([CWD])
[DOCUMENTING] foo v0.0.1 ([CWD])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -320,6 +326,7 @@ fn cache() {
.with_stderr(
"\
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -361,7 +368,9 @@ warning: failed to scan example \"ex2\" in package `foo` for example code usage
If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in Cargo.toml
warning: `foo` (example \"ex2\") generated 1 warning
[DOCUMENTING] foo v0.0.1 ([CWD])
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
+",
)
.run();
}
@@ -425,7 +434,9 @@ warning: failed to scan example \"ex1\" in package `foo` for example code usage
If an example should not be scanned, then consider adding `doc-scrape-examples = false` to its `[[example]]` definition in Cargo.toml
warning: `foo` (example \"ex1\") generated 1 warning
[DOCUMENTING] foo v0.0.1 ([CWD])
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
+",
)
.run();
@@ -448,7 +459,9 @@ error: expected one of `!` or `::`, found `NOT`
| ^^^ expected one of `!` or `::`
[DOCUMENTING] foo v0.0.1 ([CWD])
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
+",
)
.run();
@@ -499,7 +512,9 @@ warning: Rustdoc did not scrape the following examples because they require dev-
If you want Rustdoc to scrape these examples, then add `doc-scrape-examples = true`
to the [[example]] target configuration of at least one example.
[DOCUMENTING] foo v0.0.1 ([CWD])
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
+",
)
.run();
@@ -513,7 +528,9 @@ warning: Rustdoc did not scrape the following examples because they require dev-
[DOCUMENTING] a v0.0.1 ([CWD]/a)
[SCRAPING] foo v0.0.1 ([CWD])
[DOCUMENTING] foo v0.0.1 ([CWD])
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/ex/index.html
+",
)
.run();
}
@@ -560,7 +577,9 @@ fn use_dev_deps_if_explicitly_enabled() {
[CHECKING] a v0.0.1 ([CWD]/a)
[SCRAPING] foo v0.0.1 ([CWD])
[DOCUMENTING] foo v0.0.1 ([CWD])
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
+",
)
.run();
}
diff --git a/src/tools/cargo/tests/testsuite/features.rs b/src/tools/cargo/tests/testsuite/features.rs
index 557fab14a..4b7455c37 100644
--- a/src/tools/cargo/tests/testsuite/features.rs
+++ b/src/tools/cargo/tests/testsuite/features.rs
@@ -36,6 +36,37 @@ Caused by:
}
#[cargo_test]
+fn empty_feature_name() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [features]
+ "" = []
+ "#,
+ )
+ .file("src/main.rs", "")
+ .build();
+
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr(
+ "\
+[ERROR] failed to parse manifest at `[..]`
+
+Caused by:
+ feature name cannot be empty
+",
+ )
+ .run();
+}
+
+#[cargo_test]
fn same_name() {
// Feature with the same name as a dependency.
let p = project()
@@ -1144,6 +1175,61 @@ fn activating_feature_activates_dep() {
}
#[cargo_test]
+fn activating_feature_does_not_activate_transitive_dev_dependency() {
+ let p = project()
+ .no_manifest()
+ .file(
+ "a/Cargo.toml",
+ r#"
+ [package]
+ name = "a"
+ version = "0.0.0"
+ edition = "2021"
+
+ [features]
+ f = ["b/f"]
+
+ [dependencies]
+ b = { path = "../b" }
+ "#,
+ )
+ .file(
+ "b/Cargo.toml",
+ r#"
+ [package]
+ name = "b"
+ version = "0.0.0"
+ edition = "2021"
+
+ [features]
+ f = ["c/f"]
+
+ [dev-dependencies]
+ c = { path = "../c" }
+ "#,
+ )
+ .file(
+ "c/Cargo.toml",
+ r#"
+ [package]
+ name = "c"
+ version = "0.0.0"
+ edition = "2021"
+
+ [features]
+ f = []
+ "#,
+ )
+ .file("a/src/lib.rs", "")
+ .file("b/src/lib.rs", "")
+ .file("c/src/lib.rs", "compile_error!")
+ .build();
+
+ p.cargo("check --manifest-path a/Cargo.toml --features f")
+ .run();
+}
+
+#[cargo_test]
fn dep_feature_in_cmd_line() {
let p = project()
.file(
@@ -1990,7 +2076,7 @@ error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
Caused by:
invalid character `&` in feature `a&b` in package foo v0.1.0 ([ROOT]/foo), \
- characters must be Unicode XID characters, `+`, or `.` \
+ characters must be Unicode XID characters, '-', `+`, or `.` \
(numbers, `+`, `-`, `_`, `.`, or most letters)
",
)
diff --git a/src/tools/cargo/tests/testsuite/features2.rs b/src/tools/cargo/tests/testsuite/features2.rs
index 9238de2c6..125a293a0 100644
--- a/src/tools/cargo/tests/testsuite/features2.rs
+++ b/src/tools/cargo/tests/testsuite/features2.rs
@@ -1807,7 +1807,7 @@ fn shared_dep_same_but_dependencies() {
[COMPILING] dep [..]
[COMPILING] bin2 [..]
[COMPILING] bin1 [..]
-warning: feat: enabled
+warning: bin2@0.1.0: feat: enabled
[FINISHED] [..]
",
)
@@ -1823,7 +1823,7 @@ warning: feat: enabled
[FRESH] subdep [..]
[FRESH] dep [..]
[FRESH] bin1 [..]
-warning: feat: enabled
+warning: bin2@0.1.0: feat: enabled
[FRESH] bin2 [..]
[FINISHED] [..]
",
@@ -1955,6 +1955,7 @@ fn doc_optional() {
[CHECKING] bar v1.0.0
[DOCUMENTING] foo v0.1.0 [..]
[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
diff --git a/src/tools/cargo/tests/testsuite/glob_targets.rs b/src/tools/cargo/tests/testsuite/glob_targets.rs
index 8021dffa9..1eed4b1fa 100644
--- a/src/tools/cargo/tests/testsuite/glob_targets.rs
+++ b/src/tools/cargo/tests/testsuite/glob_targets.rs
@@ -137,6 +137,7 @@ fn doc_bin() {
[DOCUMENTING] foo v0.0.1 ([CWD])
[RUNNING] `rustdoc --crate-type bin --crate-name bin1 [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/bin1/index.html
",
)
.run();
@@ -407,6 +408,7 @@ fn rustdoc_example() {
[DOCUMENTING] foo v0.0.1 ([CWD])
[RUNNING] `rustdoc --crate-type bin --crate-name example1 [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/example1/index.html
",
)
.run();
@@ -421,6 +423,7 @@ fn rustdoc_bin() {
[DOCUMENTING] foo v0.0.1 ([CWD])
[RUNNING] `rustdoc --crate-type bin --crate-name bin1 [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/bin1/index.html
",
)
.run();
@@ -435,6 +438,7 @@ fn rustdoc_bench() {
[DOCUMENTING] foo v0.0.1 ([CWD])
[RUNNING] `rustdoc --crate-type bin --crate-name bench1 [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/bench1/index.html
",
)
.run();
@@ -449,6 +453,7 @@ fn rustdoc_test() {
[DOCUMENTING] foo v0.0.1 ([CWD])
[RUNNING] `rustdoc --crate-type bin --crate-name test1 [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/test1/index.html
",
)
.run();
diff --git a/src/tools/cargo/tests/testsuite/install.rs b/src/tools/cargo/tests/testsuite/install.rs
index 0a3670e6c..fd53b607b 100644
--- a/src/tools/cargo/tests/testsuite/install.rs
+++ b/src/tools/cargo/tests/testsuite/install.rs
@@ -58,6 +58,28 @@ fn simple() {
}
#[cargo_test]
+fn install_the_same_version_twice() {
+ pkg("foo", "0.0.1");
+
+ cargo_process("install foo foo")
+ .with_stderr(
+ "\
+[UPDATING] `[..]` index
+[DOWNLOADING] crates ...
+[DOWNLOADED] foo v0.0.1 (registry [..])
+[INSTALLING] foo v0.0.1
+[COMPILING] foo v0.0.1
+[FINISHED] release [optimized] target(s) in [..]
+[INSTALLING] [CWD]/home/.cargo/bin/foo[EXE]
+[INSTALLED] package `foo v0.0.1` (executable `foo[EXE]`)
+[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries
+",
+ )
+ .run();
+ assert_has_installed_exe(cargo_home(), "foo");
+}
+
+#[cargo_test]
fn toolchain() {
pkg("foo", "0.0.1");
@@ -1614,7 +1636,7 @@ fn inline_version_without_name() {
cargo_process("install @0.1.1")
.with_status(1)
.with_stderr(
- "error: invalid value '@0.1.1' for '[crate]...': missing crate name before '@'
+ "error: invalid value '@0.1.1' for '[CRATE[@<VER>]]...': missing crate name before '@'
For more information, try '--help'.
",
@@ -1844,7 +1866,9 @@ fn install_empty_argument() {
cargo_process("install")
.arg("")
.with_status(1)
- .with_stderr_contains("[ERROR] invalid value '' for '[crate]...': crate name is empty")
+ .with_stderr_contains(
+ "[ERROR] invalid value '' for '[CRATE[@<VER>]]...': crate name is empty",
+ )
.run();
}
@@ -2455,7 +2479,7 @@ error: unexpected argument '--release' found
tip: `--release` is the default for `cargo install`; instead `--debug` is supported
-Usage: cargo[EXE] install [OPTIONS] [crate]...
+Usage: cargo[EXE] install [OPTIONS] [CRATE[@<VER>]]...
For more information, try '--help'.
",
@@ -2463,3 +2487,23 @@ For more information, try '--help'.
.with_status(1)
.run();
}
+
+#[cargo_test]
+fn install_incompat_msrv() {
+ Package::new("foo", "0.1.0")
+ .file("src/main.rs", "fn main() {}")
+ .rust_version("1.30")
+ .publish();
+ Package::new("foo", "0.2.0")
+ .file("src/main.rs", "fn main() {}")
+ .rust_version("1.9876.0")
+ .publish();
+
+ cargo_process("install foo")
+ .with_stderr("\
+[UPDATING] `dummy-registry` index
+[ERROR] cannot install package `foo 0.2.0`, it requires rustc 1.9876.0 or newer, while the currently active rustc version is [..]
+`foo 0.1.0` supports rustc 1.30
+")
+ .with_status(101).run();
+}
diff --git a/src/tools/cargo/tests/testsuite/install_upgrade.rs b/src/tools/cargo/tests/testsuite/install_upgrade.rs
index 580117f5c..fe4f8c6c7 100644
--- a/src/tools/cargo/tests/testsuite/install_upgrade.rs
+++ b/src/tools/cargo/tests/testsuite/install_upgrade.rs
@@ -230,7 +230,7 @@ fn ambiguous_version_no_longer_allowed() {
cargo_process("install foo --version=1.0")
.with_stderr(
"\
-[ERROR] invalid value '1.0' for '--version <VERSION>': cannot parse '1.0' as a SemVer version
+[ERROR] invalid value '1.0' for '--version <VERSION>': unexpected end of input while parsing minor version number
tip: if you want to specify SemVer range, add an explicit qualifier, like '^1.0'
diff --git a/src/tools/cargo/tests/testsuite/list_availables.rs b/src/tools/cargo/tests/testsuite/list_availables.rs
index fe635a19b..ebd6e9c1c 100644
--- a/src/tools/cargo/tests/testsuite/list_availables.rs
+++ b/src/tools/cargo/tests/testsuite/list_availables.rs
@@ -59,7 +59,7 @@ Available binaries:
.with_stderr(
"\
error: \"--bench\" takes one argument.
-Available benches:
+Available bench targets:
bench1
bench2
@@ -75,7 +75,7 @@ Available benches:
.with_stderr(
"\
error: \"--test\" takes one argument.
-Available tests:
+Available test targets:
test1
test2
@@ -139,7 +139,7 @@ No binaries available.
.with_stderr(
"\
error: \"--bench\" takes one argument.
-No benches available.
+No bench targets available.
",
)
@@ -153,7 +153,7 @@ No benches available.
.with_stderr(
"\
error: \"--test\" takes one argument.
-No tests available.
+No test targets available.
",
)
diff --git a/src/tools/cargo/tests/testsuite/main.rs b/src/tools/cargo/tests/testsuite/main.rs
index 8279f5818..07f749e34 100644
--- a/src/tools/cargo/tests/testsuite/main.rs
+++ b/src/tools/cargo/tests/testsuite/main.rs
@@ -17,6 +17,7 @@ mod build_plan;
mod build_script;
mod build_script_env;
mod build_script_extra_link_arg;
+mod cache_lock;
mod cache_messages;
mod cargo;
mod cargo_add;
@@ -131,12 +132,12 @@ mod patch;
mod path;
mod paths;
mod pkgid;
-mod plugins;
mod proc_macro;
mod profile_config;
mod profile_custom;
mod profile_overrides;
mod profile_targets;
+mod profile_trim_paths;
mod profiles;
mod progress;
mod pub_priv;
diff --git a/src/tools/cargo/tests/testsuite/metadata.rs b/src/tools/cargo/tests/testsuite/metadata.rs
index fbead4dea..888cdce8c 100644
--- a/src/tools/cargo/tests/testsuite/metadata.rs
+++ b/src/tools/cargo/tests/testsuite/metadata.rs
@@ -4257,3 +4257,285 @@ fn workspace_metadata_with_dependencies_no_deps_artifact() {
)
.run();
}
+
+#[cargo_test]
+fn versionless_packages() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [workspace]
+ members = ["bar", "baz"]
+ "#,
+ )
+ .file(
+ "bar/Cargo.toml",
+ r#"
+ [package]
+ name = "bar"
+
+ [dependencies]
+ foobar = "0.0.1"
+ baz = { path = "../baz/" }
+ "#,
+ )
+ .file("bar/src/lib.rs", "")
+ .file(
+ "baz/Cargo.toml",
+ r#"
+ [package]
+ name = "baz"
+
+ [dependencies]
+ foobar = "0.0.1"
+ "#,
+ )
+ .file("baz/src/lib.rs", "")
+ .build();
+ Package::new("foobar", "0.0.1").publish();
+
+ p.cargo("metadata -q --format-version 1")
+ .with_json(
+ r#"
+{
+ "packages": [
+ {
+ "name": "bar",
+ "version": "0.0.0",
+ "id": "bar 0.0.0 [..]",
+ "license": null,
+ "license_file": null,
+ "description": null,
+ "source": null,
+ "dependencies": [
+ {
+ "name": "baz",
+ "source": null,
+ "req": "*",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null,
+ "path": "[..]/baz"
+ },
+ {
+ "name": "foobar",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.0.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "bar",
+ "src_path": "[..]/bar/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "[..]/bar/Cargo.toml",
+ "metadata": null,
+ "publish": [],
+ "authors": [],
+ "categories": [],
+ "keywords": [],
+ "readme": null,
+ "repository": null,
+ "homepage": null,
+ "documentation": null,
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "baz",
+ "version": "0.0.0",
+ "id": "baz 0.0.0 [..]",
+ "license": null,
+ "license_file": null,
+ "description": null,
+ "source": null,
+ "dependencies": [
+ {
+ "name": "foobar",
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "req": "^0.0.1",
+ "kind": null,
+ "rename": null,
+ "optional": false,
+ "uses_default_features": true,
+ "features": [],
+ "target": null,
+ "registry": null
+ }
+ ],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "baz",
+ "src_path": "[..]/baz/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "[..]/baz/Cargo.toml",
+ "metadata": null,
+ "publish": [],
+ "authors": [],
+ "categories": [],
+ "keywords": [],
+ "readme": null,
+ "repository": null,
+ "homepage": null,
+ "documentation": null,
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ },
+ {
+ "name": "foobar",
+ "version": "0.0.1",
+ "id": "foobar 0.0.1 [..]",
+ "license": null,
+ "license_file": null,
+ "description": null,
+ "source": "registry+https://github.com/rust-lang/crates.io-index",
+ "dependencies": [],
+ "targets": [
+ {
+ "kind": [
+ "lib"
+ ],
+ "crate_types": [
+ "lib"
+ ],
+ "name": "foobar",
+ "src_path": "[..]/foobar-0.0.1/src/lib.rs",
+ "edition": "2015",
+ "doc": true,
+ "doctest": true,
+ "test": true
+ }
+ ],
+ "features": {},
+ "manifest_path": "[..]/foobar-0.0.1/Cargo.toml",
+ "metadata": null,
+ "publish": null,
+ "authors": [],
+ "categories": [],
+ "keywords": [],
+ "readme": null,
+ "repository": null,
+ "homepage": null,
+ "documentation": null,
+ "edition": "2015",
+ "links": null,
+ "default_run": null,
+ "rust_version": null
+ }
+ ],
+ "workspace_members": [
+ "bar 0.0.0 [..]",
+ "baz 0.0.0 [..]"
+ ],
+ "workspace_default_members": [
+ "bar 0.0.0 [..]",
+ "baz 0.0.0 [..]"
+ ],
+ "resolve": {
+ "nodes": [
+ {
+ "id": "bar 0.0.0 [..]",
+ "dependencies": [
+ "baz 0.0.0 [..]",
+ "foobar 0.0.1 [..]"
+ ],
+ "deps": [
+ {
+ "name": "baz",
+ "pkg": "baz 0.0.0 [..]",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ },
+ {
+ "name": "foobar",
+ "pkg": "foobar 0.0.1 [..]",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "baz 0.0.0 [..]",
+ "dependencies": [
+ "foobar 0.0.1 [..]"
+ ],
+ "deps": [
+ {
+ "name": "foobar",
+ "pkg": "foobar 0.0.1 [..]",
+ "dep_kinds": [
+ {
+ "kind": null,
+ "target": null
+ }
+ ]
+ }
+ ],
+ "features": []
+ },
+ {
+ "id": "foobar 0.0.1 [..]",
+ "dependencies": [],
+ "deps": [],
+ "features": []
+ }
+ ],
+ "root": null
+ },
+ "target_directory": "[..]/foo/target",
+ "version": 1,
+ "workspace_root": "[..]",
+ "metadata": null
+}
+"#,
+ )
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/multitarget.rs b/src/tools/cargo/tests/testsuite/multitarget.rs
index 5f3543f01..30be9e97d 100644
--- a/src/tools/cargo/tests/testsuite/multitarget.rs
+++ b/src/tools/cargo/tests/testsuite/multitarget.rs
@@ -111,6 +111,34 @@ fn simple_doc() {
}
#[cargo_test]
+fn simple_doc_open() {
+ if cross_compile::disabled() {
+ return;
+ }
+ let t1 = cross_compile::alternate();
+ let t2 = rustc_host();
+ let p = project()
+ .file("Cargo.toml", &basic_manifest("foo", "1.0.0"))
+ .file("src/lib.rs", "//! empty lib")
+ .build();
+
+ p.cargo("doc")
+ .arg("--open")
+ .arg("--target")
+ .arg(&t1)
+ .arg("--target")
+ .arg(&t2)
+ .with_stderr(
+ "\
+[DOCUMENTING] foo v1.0.0 ([..])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[ERROR] only one `--target` argument is supported",
+ )
+ .with_status(101)
+ .run();
+}
+
+#[cargo_test]
fn simple_check() {
if cross_compile::disabled() {
return;
diff --git a/src/tools/cargo/tests/testsuite/new.rs b/src/tools/cargo/tests/testsuite/new.rs
index 91a2871e9..a34169e9d 100644
--- a/src/tools/cargo/tests/testsuite/new.rs
+++ b/src/tools/cargo/tests/testsuite/new.rs
@@ -124,7 +124,7 @@ fn no_argument() {
.with_stderr_contains(
"\
error: the following required arguments were not provided:
- <path>
+ <PATH>
",
)
.run();
@@ -451,6 +451,7 @@ fn non_ascii_name() {
"\
[WARNING] the name `Привет` contains non-ASCII characters
Non-ASCII crate names are not supported by Rust.
+[WARNING] the name `Привет` is not snake_case or kebab-case which is recommended for package names, consider `привет`
[CREATED] binary (application) `Привет` package
",
)
@@ -502,6 +503,29 @@ or change the name in Cargo.toml with:
}
#[cargo_test]
+fn non_snake_case_name() {
+ cargo_process("new UPPERcase_name")
+ .with_stderr(
+ "\
+[WARNING] the name `UPPERcase_name` is not snake_case or kebab-case which is recommended for package names, consider `uppercase_name`
+[CREATED] binary (application) `UPPERcase_name` package
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn kebab_case_name_is_accepted() {
+ cargo_process("new kebab-case-is-valid")
+ .with_stderr(
+ "\
+[CREATED] binary (application) `kebab-case-is-valid` package
+",
+ )
+ .run();
+}
+
+#[cargo_test]
fn git_default_branch() {
// Check for init.defaultBranch support.
create_default_gitconfig();
diff --git a/src/tools/cargo/tests/testsuite/out_dir.rs b/src/tools/cargo/tests/testsuite/out_dir.rs
index fe647f56e..83621a2d2 100644
--- a/src/tools/cargo/tests/testsuite/out_dir.rs
+++ b/src/tools/cargo/tests/testsuite/out_dir.rs
@@ -281,6 +281,29 @@ fn cargo_build_out_dir() {
);
}
+#[cargo_test]
+fn unsupported_short_out_dir_flag() {
+ let p = project()
+ .file("src/main.rs", r#"fn main() { println!("Hello, World!") }"#)
+ .build();
+
+ p.cargo("build -Z unstable-options -O")
+ .masquerade_as_nightly_cargo(&["out-dir"])
+ .with_stderr(
+ "\
+error: unexpected argument '-O' found
+
+ tip: a similar argument exists: '--out-dir'
+
+Usage: cargo[EXE] build [OPTIONS]
+
+For more information, try '--help'.
+",
+ )
+ .with_status(1)
+ .run();
+}
+
fn check_dir_contents(
out_dir: &Path,
expected_linux: &[&str],
diff --git a/src/tools/cargo/tests/testsuite/package.rs b/src/tools/cargo/tests/testsuite/package.rs
index 010523fda..4ec4fc0d6 100644
--- a/src/tools/cargo/tests/testsuite/package.rs
+++ b/src/tools/cargo/tests/testsuite/package.rs
@@ -1359,7 +1359,7 @@ Caused by:
failed to parse the `edition` key
Caused by:
- supported edition values are `2015`, `2018`, or `2021`, but `chicken` is unknown
+ supported edition values are `2015`, `2018`, `2021`, or `2024`, but `chicken` is unknown
"
.to_string(),
)
@@ -1391,7 +1391,7 @@ Caused by:
failed to parse the `edition` key
Caused by:
- this version of Cargo is older than the `2038` edition, and only supports `2015`, `2018`, and `2021` editions.
+ this version of Cargo is older than the `2038` edition, and only supports `2015`, `2018`, `2021`, and `2024` editions.
"
.to_string(),
)
@@ -3095,3 +3095,40 @@ src/main.rs
&[],
);
}
+
+#[cargo_test]
+fn versionless_package() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ description = "foo"
+ "#,
+ )
+ .file("src/main.rs", r#"fn main() { println!("hello"); }"#)
+ .build();
+
+ p.cargo("package")
+ .with_stderr(
+ "\
+warning: manifest has no license, license-file, documentation, homepage or repository.
+See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
+ Packaging foo v0.0.0 ([CWD])
+ Verifying foo v0.0.0 ([CWD])
+ Compiling foo v0.0.0 ([CWD]/target/package/foo-0.0.0)
+ Finished dev [unoptimized + debuginfo] target(s) in [..]s
+ Packaged 4 files, [..]B ([..]B compressed)
+",
+ )
+ .run();
+
+ let f = File::open(&p.root().join("target/package/foo-0.0.0.crate")).unwrap();
+ validate_crate_contents(
+ f,
+ "foo-0.0.0.crate",
+ &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"],
+ &[],
+ );
+}
diff --git a/src/tools/cargo/tests/testsuite/plugins.rs b/src/tools/cargo/tests/testsuite/plugins.rs
deleted file mode 100644
index 331ba32e0..000000000
--- a/src/tools/cargo/tests/testsuite/plugins.rs
+++ /dev/null
@@ -1,421 +0,0 @@
-//! Tests for rustc plugins.
-
-use cargo_test_support::rustc_host;
-use cargo_test_support::{basic_manifest, project};
-
-#[cargo_test(nightly, reason = "plugins are unstable")]
-fn plugin_to_the_max() {
- let foo = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
-
- [lib]
- name = "foo_lib"
-
- [dependencies.bar]
- path = "../bar"
- "#,
- )
- .file(
- "src/main.rs",
- r#"
- #![feature(plugin)]
- #![plugin(bar)]
- extern crate foo_lib;
-
- fn main() { foo_lib::foo(); }
- "#,
- )
- .file(
- "src/foo_lib.rs",
- r#"
- #![feature(plugin)]
- #![plugin(bar)]
-
- pub fn foo() {}
- "#,
- )
- .build();
- let _bar = project()
- .at("bar")
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "bar"
- version = "0.0.1"
- authors = []
-
- [lib]
- name = "bar"
- plugin = true
-
- [dependencies.baz]
- path = "../baz"
- "#,
- )
- .file(
- "src/lib.rs",
- r#"
- #![feature(rustc_private)]
-
- extern crate baz;
- extern crate rustc_driver;
-
- use rustc_driver::plugin::Registry;
-
- #[no_mangle]
- pub fn __rustc_plugin_registrar(_reg: &mut Registry) {
- println!("{}", baz::baz());
- }
- "#,
- )
- .build();
- let _baz = project()
- .at("baz")
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "baz"
- version = "0.0.1"
- authors = []
-
- [lib]
- name = "baz"
- crate_type = ["dylib"]
- "#,
- )
- .file("src/lib.rs", "pub fn baz() -> i32 { 1 }")
- .build();
-
- foo.cargo("build").run();
- foo.cargo("doc").run();
-}
-
-#[cargo_test(nightly, reason = "plugins are unstable")]
-fn plugin_with_dynamic_native_dependency() {
- let build = project()
- .at("builder")
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "builder"
- version = "0.0.1"
- authors = []
-
- [lib]
- name = "builder"
- crate-type = ["dylib"]
- "#,
- )
- .file("src/lib.rs", "#[no_mangle] pub extern fn foo() {}")
- .build();
-
- let foo = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
-
- [dependencies.bar]
- path = "bar"
- "#,
- )
- .file(
- "src/main.rs",
- r#"
- #![feature(plugin)]
- #![plugin(bar)]
-
- fn main() {}
- "#,
- )
- .file(
- "bar/Cargo.toml",
- r#"
- [package]
- name = "bar"
- version = "0.0.1"
- authors = []
- build = 'build.rs'
-
- [lib]
- name = "bar"
- plugin = true
- "#,
- )
- .file(
- "bar/build.rs",
- r#"
- use std::env;
- use std::fs;
- use std::path::PathBuf;
-
- fn main() {
- let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
- let root = PathBuf::from(env::var("BUILDER_ROOT").unwrap());
- let file = format!("{}builder{}",
- env::consts::DLL_PREFIX,
- env::consts::DLL_SUFFIX);
- let src = root.join(&file);
- let dst = out_dir.join(&file);
- fs::copy(src, dst).unwrap();
- if cfg!(target_env = "msvc") {
- fs::copy(root.join("builder.dll.lib"),
- out_dir.join("builder.dll.lib")).unwrap();
- }
- println!("cargo:rustc-flags=-L {}", out_dir.display());
- }
- "#,
- )
- .file(
- "bar/src/lib.rs",
- r#"
- #![feature(rustc_private)]
-
- extern crate rustc_driver;
- use rustc_driver::plugin::Registry;
-
- #[cfg_attr(not(target_env = "msvc"), link(name = "builder"))]
- #[cfg_attr(target_env = "msvc", link(name = "builder.dll"))]
- extern { fn foo(); }
-
- #[no_mangle]
- pub fn __rustc_plugin_registrar(_reg: &mut Registry) {
- unsafe { foo() }
- }
- "#,
- )
- .build();
-
- build.cargo("build").run();
-
- let root = build.root().join("target").join("debug");
- foo.cargo("build -v").env("BUILDER_ROOT", root).run();
-}
-
-#[cargo_test]
-fn plugin_integration() {
- let p = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
- build = "build.rs"
-
- [lib]
- name = "foo"
- plugin = true
- doctest = false
- "#,
- )
- .file("build.rs", "fn main() {}")
- .file("src/lib.rs", "")
- .file("tests/it_works.rs", "")
- .build();
-
- p.cargo("test -v").run();
-}
-
-#[cargo_test]
-fn doctest_a_plugin() {
- let p = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
-
- [dependencies]
- bar = { path = "bar" }
- "#,
- )
- .file("src/lib.rs", "#[macro_use] extern crate bar;")
- .file(
- "bar/Cargo.toml",
- r#"
- [package]
- name = "bar"
- version = "0.0.1"
- authors = []
-
- [lib]
- name = "bar"
- plugin = true
- "#,
- )
- .file("bar/src/lib.rs", "pub fn bar() {}")
- .build();
-
- p.cargo("test -v").run();
-}
-
-// See #1515
-#[cargo_test]
-fn native_plugin_dependency_with_custom_linker() {
- let target = rustc_host();
-
- let _foo = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
-
- [lib]
- plugin = true
- "#,
- )
- .file("src/lib.rs", "")
- .build();
-
- let bar = project()
- .at("bar")
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "bar"
- version = "0.0.1"
- authors = []
-
- [dependencies.foo]
- path = "../foo"
- "#,
- )
- .file("src/lib.rs", "")
- .file(
- ".cargo/config",
- &format!(
- r#"
- [target.{}]
- linker = "nonexistent-linker"
- "#,
- target
- ),
- )
- .build();
-
- bar.cargo("build --verbose")
- .with_status(101)
- .with_stderr_contains(
- "\
-[COMPILING] foo v0.0.1 ([..])
-[RUNNING] `rustc [..] -C linker=nonexistent-linker [..]`
-[ERROR] [..]linker[..]
-",
- )
- .run();
-}
-
-#[cargo_test(nightly, reason = "requires rustc_private")]
-fn panic_abort_plugins() {
- let p = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
-
- [profile.dev]
- panic = 'abort'
-
- [dependencies]
- bar = { path = "bar" }
- "#,
- )
- .file("src/lib.rs", "")
- .file(
- "bar/Cargo.toml",
- r#"
- [package]
- name = "bar"
- version = "0.0.1"
- authors = []
-
- [lib]
- plugin = true
- "#,
- )
- .file(
- "bar/src/lib.rs",
- r#"
- #![feature(rustc_private)]
- extern crate rustc_ast;
- extern crate rustc_driver;
- "#,
- )
- .build();
-
- p.cargo("build").run();
-}
-
-#[cargo_test(nightly, reason = "requires rustc_private")]
-fn shared_panic_abort_plugins() {
- let p = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
-
- [profile.dev]
- panic = 'abort'
-
- [dependencies]
- bar = { path = "bar" }
- baz = { path = "baz" }
- "#,
- )
- .file("src/lib.rs", "extern crate baz;")
- .file(
- "bar/Cargo.toml",
- r#"
- [package]
- name = "bar"
- version = "0.0.1"
- authors = []
-
- [lib]
- plugin = true
-
- [dependencies]
- baz = { path = "../baz" }
- "#,
- )
- .file(
- "bar/src/lib.rs",
- r#"
- #![feature(rustc_private)]
- extern crate rustc_ast;
- extern crate rustc_driver;
- extern crate baz;
- "#,
- )
- .file("baz/Cargo.toml", &basic_manifest("baz", "0.0.1"))
- .file("baz/src/lib.rs", "")
- .build();
-
- p.cargo("build -v").run();
-}
diff --git a/src/tools/cargo/tests/testsuite/proc_macro.rs b/src/tools/cargo/tests/testsuite/proc_macro.rs
index 7d6f6ba86..cabf251a0 100644
--- a/src/tools/cargo/tests/testsuite/proc_macro.rs
+++ b/src/tools/cargo/tests/testsuite/proc_macro.rs
@@ -202,52 +202,6 @@ fn impl_and_derive() {
p.cargo("run").with_stdout("X { success: true }").run();
}
-#[cargo_test(nightly, reason = "plugins are unstable")]
-fn plugin_and_proc_macro() {
- let p = project()
- .file(
- "Cargo.toml",
- r#"
- [package]
- name = "foo"
- version = "0.0.1"
- authors = []
-
- [lib]
- plugin = true
- proc-macro = true
- "#,
- )
- .file(
- "src/lib.rs",
- r#"
- #![feature(rustc_private)]
- #![feature(proc_macro, proc_macro_lib)]
-
- extern crate rustc_driver;
- use rustc_driver::plugin::Registry;
-
- extern crate proc_macro;
- use proc_macro::TokenStream;
-
- #[no_mangle]
- pub fn __rustc_plugin_registrar(reg: &mut Registry) {}
-
- #[proc_macro_derive(Questionable)]
- pub fn questionable(input: TokenStream) -> TokenStream {
- input
- }
- "#,
- )
- .build();
-
- let msg = " `lib.plugin` and `lib.proc-macro` cannot both be `true`";
- p.cargo("check")
- .with_status(101)
- .with_stderr_contains(msg)
- .run();
-}
-
#[cargo_test]
fn proc_macro_doctest() {
let foo = project()
diff --git a/src/tools/cargo/tests/testsuite/profile_config.rs b/src/tools/cargo/tests/testsuite/profile_config.rs
index 143c050f9..710a0d8ef 100644
--- a/src/tools/cargo/tests/testsuite/profile_config.rs
+++ b/src/tools/cargo/tests/testsuite/profile_config.rs
@@ -1,6 +1,6 @@
//! Tests for profiles defined in config files.
-use cargo::util::toml::TomlDebugInfo;
+use cargo::util::toml::schema::TomlDebugInfo;
use cargo_test_support::paths::CargoPathExt;
use cargo_test_support::registry::Package;
use cargo_test_support::{basic_lib_manifest, paths, project};
diff --git a/src/tools/cargo/tests/testsuite/profile_targets.rs b/src/tools/cargo/tests/testsuite/profile_targets.rs
index f2de169b9..9f00b73f3 100644
--- a/src/tools/cargo/tests/testsuite/profile_targets.rs
+++ b/src/tools/cargo/tests/testsuite/profile_targets.rs
@@ -667,5 +667,6 @@ fn profile_selection_doc() {
[DOCUMENTING] foo [..]
[RUNNING] `rustdoc [..]--crate-name foo src/lib.rs [..]
[FINISHED] dev [unoptimized + debuginfo] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
").run();
}
diff --git a/src/tools/cargo/tests/testsuite/profile_trim_paths.rs b/src/tools/cargo/tests/testsuite/profile_trim_paths.rs
new file mode 100644
index 000000000..1d24c159b
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/profile_trim_paths.rs
@@ -0,0 +1,614 @@
+//! Tests for `-Ztrim-paths`.
+
+use cargo_test_support::basic_manifest;
+use cargo_test_support::git;
+use cargo_test_support::paths;
+use cargo_test_support::project;
+use cargo_test_support::registry::Package;
+
+#[cargo_test]
+fn gated_manifest() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [profile.dev]
+ trim-paths = "macro"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("check")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_status(101)
+ .with_stderr_contains(
+ "\
+[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`
+
+Caused by:
+ feature `trim-paths` is required",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn gated_config_toml() {
+ let p = project()
+ .file(
+ ".cargo/config.toml",
+ r#"
+ [profile.dev]
+ trim-paths = "macro"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("check")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_status(101)
+ .with_stderr_contains(
+ "\
+[ERROR] config profile `dev` is not valid (defined in `[CWD]/.cargo/config.toml`)
+
+Caused by:
+ feature `trim-paths` is required",
+ )
+ .run();
+}
+
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn release_profile_default_to_object() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("build --release --verbose -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stderr(
+ "\
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[FINISHED] release [..]",
+ )
+ .run();
+}
+
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn one_option() {
+ let build = |option| {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [profile.dev]
+ trim-paths = "{option}"
+ "#
+ ),
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("build -v -Ztrim-paths")
+ };
+
+ for option in ["macro", "diagnostics", "object", "all"] {
+ build(option)
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stderr(&format!(
+ "\
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope={option} \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[FINISHED] dev [..]",
+ ))
+ .run();
+ }
+ build("none")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stderr_does_not_contain("[..]-Zremap-path-scope=[..]")
+ .with_stderr_does_not_contain("[..]--remap-path-prefix=[..]")
+ .run();
+}
+
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn multiple_options() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [profile.dev]
+ trim-paths = ["diagnostics", "macro", "object"]
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("build --verbose -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stderr(
+ "\
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=diagnostics,macro,object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[FINISHED] dev [..]",
+ )
+ .run();
+}
+
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn profile_merge_works() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [profile.dev]
+ trim-paths = ["macro"]
+
+ [profile.custom]
+ inherits = "dev"
+ trim-paths = ["diagnostics"]
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("build -v -Ztrim-paths --profile custom")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stderr(
+ "\
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=diagnostics \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[FINISHED] custom [..]",
+ )
+ .run();
+}
+
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn registry_dependency() {
+ Package::new("bar", "0.0.1")
+ .file("Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("src/lib.rs", r#"pub fn f() { println!("{}", file!()); }"#)
+ .publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ bar = "0.0.1"
+
+ [profile.dev]
+ trim-paths = "object"
+ "#,
+ )
+ .file("src/main.rs", "fn main() { bar::f(); }")
+ .build();
+
+ let registry_src = paths::home().join(".cargo/registry/src");
+ let pkg_remap = format!("{}/[..]/bar-0.0.1=bar-0.0.1", registry_src.display());
+
+ p.cargo("run --verbose -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stdout("bar-0.0.1/src/lib.rs")
+ .with_stderr(&format!(
+ "\
+[UPDATING] [..]
+[DOWNLOADING] crates ...
+[DOWNLOADED] bar v0.0.1 ([..])
+[COMPILING] bar v0.0.1
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix={pkg_remap} [..]
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[FINISHED] dev [..]
+[RUNNING] `target/debug/foo[EXE]`"
+ ))
+ .run();
+}
+
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn git_dependency() {
+ let git_project = git::new("bar", |project| {
+ project
+ .file("Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("src/lib.rs", r#"pub fn f() { println!("{}", file!()); }"#)
+ });
+ let url = git_project.url();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ bar = {{ git = "{url}" }}
+
+ [profile.dev]
+ trim-paths = "object"
+ "#
+ ),
+ )
+ .file("src/main.rs", "fn main() { bar::f(); }")
+ .build();
+
+ let git_checkouts_src = paths::home().join(".cargo/git/checkouts");
+ let pkg_remap = format!("{}/bar-[..]/[..]=bar-0.0.1", git_checkouts_src.display());
+
+ p.cargo("run --verbose -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stdout("bar-0.0.1/src/lib.rs")
+ .with_stderr(&format!(
+ "\
+[UPDATING] git repository `{url}`
+[COMPILING] bar v0.0.1 ({url}[..])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix={pkg_remap} [..]
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[FINISHED] dev [..]
+[RUNNING] `target/debug/foo[EXE]`"
+ ))
+ .run();
+}
+
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn path_dependency() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ bar = { path = "cocktail-bar" }
+
+ [profile.dev]
+ trim-paths = "object"
+ "#,
+ )
+ .file("src/main.rs", "fn main() { bar::f(); }")
+ .file("cocktail-bar/Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file(
+ "cocktail-bar/src/lib.rs",
+ r#"pub fn f() { println!("{}", file!()); }"#,
+ )
+ .build();
+
+ p.cargo("run --verbose -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stdout("cocktail-bar/src/lib.rs")
+ .with_stderr(&format!(
+ "\
+[COMPILING] bar v0.0.1 ([..]/cocktail-bar)
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[FINISHED] dev [..]
+[RUNNING] `target/debug/foo[EXE]`"
+ ))
+ .run();
+}
+
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn path_dependency_outside_workspace() {
+ let bar = project()
+ .at("bar")
+ .file("Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("src/lib.rs", r#"pub fn f() { println!("{}", file!()); }"#)
+ .build();
+ let bar_path = bar.url().to_file_path().unwrap();
+ let bar_path = bar_path.display();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ bar = { path = "../bar" }
+
+ [profile.dev]
+ trim-paths = "object"
+ "#,
+ )
+ .file("src/main.rs", "fn main() { bar::f(); }")
+ .build();
+
+ p.cargo("run --verbose -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stdout("bar-0.0.1/src/lib.rs")
+ .with_stderr(&format!(
+ "\
+[COMPILING] bar v0.0.1 ([..]/bar)
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix={bar_path}=bar-0.0.1 [..]
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[FINISHED] dev [..]
+[RUNNING] `target/debug/foo[EXE]`"
+ ))
+ .run();
+}
+
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn diagnostics_works() {
+ Package::new("bar", "0.0.1")
+ .file("Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("src/lib.rs", r#"pub fn f() { let unused = 0; }"#)
+ .publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ bar = "0.0.1"
+
+ [profile.dev]
+ trim-paths = "diagnostics"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ let registry_src = paths::home().join(".cargo/registry/src");
+ let registry_src = registry_src.display();
+ let pkg_remap = format!("{registry_src}/[..]/bar-0.0.1=bar-0.0.1");
+
+ p.cargo("build -vv -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stderr_line_without(
+ &["[..]bar-0.0.1/src/lib.rs:1[..]"],
+ &[&format!("{registry_src}")],
+ )
+ .with_stderr_contains("[..]unused_variables[..]")
+ .with_stderr_contains(&format!(
+ "\
+[RUNNING] [..]rustc [..]\
+ -Zremap-path-scope=diagnostics \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix={pkg_remap} [..]",
+ ))
+ .with_stderr_contains(
+ "\
+[RUNNING] [..]rustc [..]\
+ -Zremap-path-scope=diagnostics \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]",
+ )
+ .run();
+}
+
+#[cfg(target_os = "linux")]
+#[cargo_test(requires_readelf, nightly, reason = "-Zremap-path-scope is unstable")]
+fn object_works() {
+ use std::os::unix::ffi::OsStrExt;
+
+ let run_readelf = |path| {
+ std::process::Command::new("readelf")
+ .arg("-wi")
+ .arg(path)
+ .output()
+ .expect("readelf works")
+ };
+
+ let registry_src = paths::home().join(".cargo/registry/src");
+ let pkg_remap = format!("{}/[..]/bar-0.0.1=bar-0.0.1", registry_src.display());
+ let rust_src = "/lib/rustc/src/rust".as_bytes();
+ let registry_src_bytes = registry_src.as_os_str().as_bytes();
+
+ Package::new("bar", "0.0.1")
+ .file("Cargo.toml", &basic_manifest("bar", "0.0.1"))
+ .file("src/lib.rs", r#"pub fn f() { println!("{}", file!()); }"#)
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ bar = "0.0.1"
+ "#,
+ )
+ .file("src/main.rs", "fn main() { bar::f(); }")
+ .build();
+
+ let pkg_root = p.root();
+ let pkg_root = pkg_root.as_os_str().as_bytes();
+
+ p.cargo("build").run();
+
+ let bin_path = p.bin("foo");
+ assert!(bin_path.is_file());
+ let stdout = run_readelf(bin_path).stdout;
+ // TODO: re-enable this check when rustc bootstrap disables remapping
+ // <https://github.com/rust-lang/cargo/pull/12625#discussion_r1371714791>
+ // assert!(memchr::memmem::find(&stdout, rust_src).is_some());
+ assert!(memchr::memmem::find(&stdout, registry_src_bytes).is_some());
+ assert!(memchr::memmem::find(&stdout, pkg_root).is_some());
+
+ p.cargo("clean").run();
+
+ p.change_file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ bar = "0.0.1"
+
+ [profile.dev]
+ trim-paths = "object"
+ "#,
+ );
+
+ p.cargo("build --verbose -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stderr(&format!(
+ "\
+[COMPILING] bar v0.0.1
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix={pkg_remap} [..]
+[COMPILING] foo v0.0.1 ([CWD])
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
+ --remap-path-prefix=[CWD]= [..]
+[FINISHED] dev [..]",
+ ))
+ .run();
+
+ let bin_path = p.bin("foo");
+ assert!(bin_path.is_file());
+ let stdout = run_readelf(bin_path).stdout;
+ assert!(memchr::memmem::find(&stdout, rust_src).is_none());
+ assert!(memchr::memmem::find(&stdout, registry_src_bytes).is_none());
+ assert!(memchr::memmem::find(&stdout, pkg_root).is_none());
+}
+
+// TODO: might want to move to test/testsuite/build_script.rs once stabilized.
+#[cargo_test(nightly, reason = "-Zremap-path-scope is unstable")]
+fn custom_build_env_var_trim_paths() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file("build.rs", "")
+ .build();
+
+ let test_cases = [
+ ("[]", "none"),
+ ("\"all\"", "all"),
+ ("\"diagnostics\"", "diagnostics"),
+ ("\"macro\"", "macro"),
+ ("\"none\"", "none"),
+ ("\"object\"", "object"),
+ ("false", "none"),
+ ("true", "all"),
+ (
+ r#"["diagnostics", "macro", "object"]"#,
+ "diagnostics,macro,object",
+ ),
+ ];
+
+ for (opts, expected) in test_cases {
+ p.change_file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [profile.dev]
+ trim-paths = {opts}
+ "#
+ ),
+ );
+
+ p.change_file(
+ "build.rs",
+ &format!(
+ r#"
+ fn main() {{
+ assert_eq!(
+ std::env::var("CARGO_TRIM_PATHS").unwrap().as_str(),
+ "{expected}",
+ );
+ }}
+ "#
+ ),
+ );
+
+ p.cargo("build -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .run();
+ }
+}
diff --git a/src/tools/cargo/tests/testsuite/pub_priv.rs b/src/tools/cargo/tests/testsuite/pub_priv.rs
index 83c6a49f8..b2160e0fa 100644
--- a/src/tools/cargo/tests/testsuite/pub_priv.rs
+++ b/src/tools/cargo/tests/testsuite/pub_priv.rs
@@ -197,3 +197,52 @@ Caused by:
)
.run()
}
+
+#[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")]
+fn workspace_dep_made_public() {
+ Package::new("foo1", "0.1.0")
+ .file("src/lib.rs", "pub struct FromFoo;")
+ .publish();
+ Package::new("foo2", "0.1.0")
+ .file("src/lib.rs", "pub struct FromFoo;")
+ .publish();
+ Package::new("foo3", "0.1.0")
+ .file("src/lib.rs", "pub struct FromFoo;")
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ cargo-features = ["public-dependency"]
+
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [workspace.dependencies]
+ foo1 = "0.1.0"
+ foo2 = { version = "0.1.0", public = true }
+ foo3 = { version = "0.1.0", public = false }
+
+ [dependencies]
+ foo1 = { workspace = true, public = true }
+ foo2 = { workspace = true }
+ foo3 = { workspace = true, public = true }
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ "
+ #![deny(exported_private_dependencies)]
+ pub fn use_priv1(_: foo1::FromFoo) {}
+ pub fn use_priv2(_: foo2::FromFoo) {}
+ pub fn use_priv3(_: foo3::FromFoo) {}
+ ",
+ )
+ .build();
+
+ p.cargo("check")
+ .masquerade_as_nightly_cargo(&["public-dependency"])
+ .run()
+}
diff --git a/src/tools/cargo/tests/testsuite/publish.rs b/src/tools/cargo/tests/testsuite/publish.rs
index 67569bf3b..5d29ac88a 100644
--- a/src/tools/cargo/tests/testsuite/publish.rs
+++ b/src/tools/cargo/tests/testsuite/publish.rs
@@ -420,7 +420,7 @@ fn unpublishable_crate() {
.with_stderr(
"\
[ERROR] `foo` cannot be published.
-`package.publish` is set to `false` or an empty list in Cargo.toml and prevents publishing.
+`package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish.
",
)
.run();
@@ -794,7 +794,7 @@ fn publish_empty_list() {
.with_stderr(
"\
[ERROR] `foo` cannot be published.
-`package.publish` is set to `false` or an empty list in Cargo.toml and prevents publishing.
+`package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish.
",
)
.run();
@@ -1020,7 +1020,7 @@ fn block_publish_no_registry() {
.with_stderr(
"\
[ERROR] `foo` cannot be published.
-`package.publish` is set to `false` or an empty list in Cargo.toml and prevents publishing.
+`package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish.
",
)
.run();
@@ -3004,3 +3004,32 @@ Caused by:
.with_status(101)
.run();
}
+
+#[cargo_test]
+fn versionless_package() {
+ // Use local registry for faster test times since no publish will occur
+ let registry = registry::init();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ description = "foo"
+ "#,
+ )
+ .file("src/main.rs", r#"fn main() { println!("hello"); }"#)
+ .build();
+
+ p.cargo("publish")
+ .replace_crates_io(registry.index_url())
+ .with_status(101)
+ .with_stderr(
+ "\
+error: `foo` cannot be published.
+`package.publish` must be set to `true` or a non-empty list in Cargo.toml to publish.
+",
+ )
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/registry.rs b/src/tools/cargo/tests/testsuite/registry.rs
index f485180c9..b5dff2746 100644
--- a/src/tools/cargo/tests/testsuite/registry.rs
+++ b/src/tools/cargo/tests/testsuite/registry.rs
@@ -3600,4 +3600,55 @@ fn differ_only_by_metadata() {
",
)
.run();
+
+ Package::new("baz", "0.0.1+d").publish();
+
+ p.cargo("clean").run();
+ p.cargo("check")
+ .with_stderr_contains("[CHECKING] baz v0.0.1+b")
+ .run();
+}
+
+#[cargo_test]
+fn differ_only_by_metadata_with_lockfile() {
+ 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+a").publish();
+ Package::new("baz", "0.0.1+b").publish();
+ Package::new("baz", "0.0.1+c").publish();
+
+ p.cargo("update --package baz --precise 0.0.1+b")
+ .with_stderr(
+ "\
+[UPDATING] [..] index
+[..] baz v0.0.1+c -> v0.0.1+b
+",
+ )
+ .run();
+
+ p.cargo("check")
+ .with_stderr(
+ "\
+[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/run.rs b/src/tools/cargo/tests/testsuite/run.rs
index c58c239ac..c7ddd5d9e 100644
--- a/src/tools/cargo/tests/testsuite/run.rs
+++ b/src/tools/cargo/tests/testsuite/run.rs
@@ -50,7 +50,7 @@ error: unexpected argument '--silent' found
tip: a similar argument exists: '--quiet'
-Usage: cargo[EXE] run [OPTIONS] [args]...
+Usage: cargo[EXE] run [OPTIONS] [ARGS]...
For more information, try '--help'.
",
@@ -65,7 +65,7 @@ error: unexpected argument '--silent' found
tip: a similar argument exists: '--quiet'
-Usage: cargo[EXE] run [OPTIONS] [args]...
+Usage: cargo[EXE] run [OPTIONS] [ARGS]...
For more information, try '--help'.
",
diff --git a/src/tools/cargo/tests/testsuite/rustdoc.rs b/src/tools/cargo/tests/testsuite/rustdoc.rs
index 5650f3e0a..7ef768a80 100644
--- a/src/tools/cargo/tests/testsuite/rustdoc.rs
+++ b/src/tools/cargo/tests/testsuite/rustdoc.rs
@@ -15,6 +15,7 @@ fn rustdoc_simple() {
[..] \
-L dependency=[CWD]/target/debug/deps [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -35,6 +36,7 @@ fn rustdoc_args() {
-C metadata=[..] \
-L dependency=[CWD]/target/debug/deps [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -88,6 +90,7 @@ fn rustdoc_foo_with_bar_dependency() {
-L dependency=[CWD]/target/debug/deps \
--extern [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -127,6 +130,7 @@ fn rustdoc_only_bar_dependency() {
-C metadata=[..] \
-L dependency=[CWD]/target/debug/deps [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/bar/index.html
",
)
.run();
@@ -150,6 +154,7 @@ fn rustdoc_same_name_documents_lib() {
-C metadata=[..] \
-L dependency=[CWD]/target/debug/deps [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
@@ -225,7 +230,8 @@ fn rustdoc_target() {
[..] \
-L dependency=[CWD]/target/{target}/debug/deps \
-L dependency=[CWD]/target/debug/deps[..]`
-[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/[..]/doc/foo/index.html",
target = cross_compile::alternate()
))
.run();
diff --git a/src/tools/cargo/tests/testsuite/rustdocflags.rs b/src/tools/cargo/tests/testsuite/rustdocflags.rs
index c37d5a826..e7c2aa263 100644
--- a/src/tools/cargo/tests/testsuite/rustdocflags.rs
+++ b/src/tools/cargo/tests/testsuite/rustdocflags.rs
@@ -48,7 +48,10 @@ fn rerun() {
p.cargo("doc").env("RUSTDOCFLAGS", "--cfg=foo").run();
p.cargo("doc")
.env("RUSTDOCFLAGS", "--cfg=foo")
- .with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]")
+ .with_stderr(
+ "[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html",
+ )
.run();
p.cargo("doc")
.env("RUSTDOCFLAGS", "--cfg=bar")
@@ -56,6 +59,7 @@ fn rerun() {
"\
[DOCUMENTING] foo v0.0.1 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
",
)
.run();
diff --git a/src/tools/cargo/tests/testsuite/script.rs b/src/tools/cargo/tests/testsuite/script.rs
index 96f3a5eb4..0b1e5a6b9 100644
--- a/src/tools/cargo/tests/testsuite/script.rs
+++ b/src/tools/cargo/tests/testsuite/script.rs
@@ -108,6 +108,7 @@ error: no such command: `echo`
<tab>Did you mean `bench`?
<tab>View all installed commands with `cargo --list`
+<tab>Find a package to install `echo` with `cargo search cargo-echo`
",
)
.run();
diff --git a/src/tools/cargo/tests/testsuite/search.rs b/src/tools/cargo/tests/testsuite/search.rs
index 4c3155c8f..c76397ac7 100644
--- a/src/tools/cargo/tests/testsuite/search.rs
+++ b/src/tools/cargo/tests/testsuite/search.rs
@@ -1,5 +1,6 @@
//! Tests for the `cargo search` command.
+use cargo::util::cache_lock::CacheLockMode;
use cargo_test_support::cargo_process;
use cargo_test_support::paths;
use cargo_test_support::registry::{RegistryBuilder, Response};
@@ -100,7 +101,9 @@ fn not_update() {
paths::root(),
paths::home().join(".cargo"),
);
- let lock = cfg.acquire_package_cache_lock().unwrap();
+ let lock = cfg
+ .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)
+ .unwrap();
let mut regsrc = RegistrySource::remote(sid, &HashSet::new(), &cfg).unwrap();
regsrc.invalidate_cache();
regsrc.block_until_ready().unwrap();
diff --git a/src/tools/cargo/tests/testsuite/update.rs b/src/tools/cargo/tests/testsuite/update.rs
index fe1d86bd7..e636435b0 100644
--- a/src/tools/cargo/tests/testsuite/update.rs
+++ b/src/tools/cargo/tests/testsuite/update.rs
@@ -392,6 +392,104 @@ fn update_precise() {
}
#[cargo_test]
+fn update_precise_mismatched() {
+ Package::new("serde", "1.2.0").publish();
+ Package::new("serde", "1.2.1").publish();
+ Package::new("serde", "1.6.0").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "bar"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies]
+ serde = "~1.2"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("check").run();
+
+ // `1.6.0` does not match `"~1.2"`
+ p.cargo("update serde:1.2 --precise 1.6.0")
+ .with_stderr(
+ "\
+[UPDATING] `[..]` index
+[ERROR] failed to select a version for the requirement `serde = \"~1.2\"`
+candidate versions found which didn't match: 1.6.0
+location searched: `[..]` index (which is replacing registry `crates-io`)
+required by package `bar v0.0.1 ([..]/foo)`
+perhaps a crate was updated and forgotten to be re-vendored?
+",
+ )
+ .with_status(101)
+ .run();
+
+ // `1.9.0` does not exist
+ p.cargo("update serde:1.2 --precise 1.9.0")
+ // This terrible error message has been the same for a long time. A fix is more than welcome!
+ .with_stderr(
+ "\
+[UPDATING] `[..]` index
+[ERROR] no matching package named `serde` found
+location searched: registry `crates-io`
+required by package `bar v0.0.1 ([..]/foo)`
+",
+ )
+ .with_status(101)
+ .run();
+}
+
+#[cargo_test]
+fn update_precise_build_metadata() {
+ Package::new("serde", "0.0.1+first").publish();
+ Package::new("serde", "0.0.1+second").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.0"
+
+ [dependencies]
+ serde = "0.0.1"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("generate-lockfile").run();
+ p.cargo("update serde --precise 0.0.1+first").run();
+
+ p.cargo("update serde --precise 0.0.1+second")
+ .with_stderr(
+ "\
+[UPDATING] `[..]` index
+[UPDATING] serde v0.0.1+first -> v0.0.1+second
+",
+ )
+ .run();
+
+ // This is not considered "Downgrading". Build metadata are not assumed to
+ // be ordered.
+ p.cargo("update serde --precise 0.0.1+first")
+ .with_stderr(
+ "\
+[UPDATING] `[..]` index
+[UPDATING] serde v0.0.1+second -> v0.0.1+first
+",
+ )
+ .run();
+}
+
+#[cargo_test]
fn update_precise_do_not_force_update_deps() {
Package::new("log", "0.1.0").publish();
Package::new("serde", "0.2.1").dep("log", "0.1").publish();
diff --git a/src/tools/cargo/tests/testsuite/version.rs b/src/tools/cargo/tests/testsuite/version.rs
index f880c75a6..110e61003 100644
--- a/src/tools/cargo/tests/testsuite/version.rs
+++ b/src/tools/cargo/tests/testsuite/version.rs
@@ -13,6 +13,10 @@ fn simple() {
p.cargo("--version")
.with_stdout(&format!("cargo {}\n", cargo::version()))
.run();
+
+ p.cargo("-V")
+ .with_stdout(&format!("cargo {}\n", cargo::version()))
+ .run();
}
#[cargo_test]
diff --git a/src/tools/cargo/tests/testsuite/warn_on_failure.rs b/src/tools/cargo/tests/testsuite/warn_on_failure.rs
index 19cb01813..f2c2bb071 100644
--- a/src/tools/cargo/tests/testsuite/warn_on_failure.rs
+++ b/src/tools/cargo/tests/testsuite/warn_on_failure.rs
@@ -105,7 +105,7 @@ fn warning_on_lib_failure() {
.with_stderr_contains("[UPDATING] `[..]` index")
.with_stderr_contains("[DOWNLOADED] bar v0.0.1 ([..])")
.with_stderr_contains("[COMPILING] bar v0.0.1")
- .with_stderr_contains(&format!("[WARNING] {}", WARNING1))
- .with_stderr_contains(&format!("[WARNING] {}", WARNING2))
+ .with_stderr_contains(&format!("[WARNING] bar@0.0.1: {}", WARNING1))
+ .with_stderr_contains(&format!("[WARNING] bar@0.0.1: {}", WARNING2))
.run();
}
diff --git a/src/tools/cargo/tests/testsuite/workspaces.rs b/src/tools/cargo/tests/testsuite/workspaces.rs
index 4f8997b38..94b5142f4 100644
--- a/src/tools/cargo/tests/testsuite/workspaces.rs
+++ b/src/tools/cargo/tests/testsuite/workspaces.rs
@@ -1046,7 +1046,7 @@ fn members_include_path_deps() {
}
#[cargo_test]
-fn new_warns_you_this_will_not_work() {
+fn new_creates_members_list() {
let p = project()
.file(
"Cargo.toml",
@@ -1063,20 +1063,7 @@ fn new_warns_you_this_will_not_work() {
let p = p.build();
p.cargo("new --lib bar")
- .with_stderr(
- "\
-warning: compiling this new package may not work due to invalid workspace configuration
-
-current package believes it's in a workspace when it's not:
-current: [..]
-workspace: [..]
-
-this may be fixable by ensuring that this crate is depended on by the workspace \
-root: [..]
-[..]
-[CREATED] library `bar` package
-",
- )
+ .with_stderr(" Created library `bar` package")
.run();
}
diff --git a/src/tools/cargo/triagebot.toml b/src/tools/cargo/triagebot.toml
index c92b4ce8c..cdf1090a1 100644
--- a/src/tools/cargo/triagebot.toml
+++ b/src/tools/cargo/triagebot.toml
@@ -36,6 +36,15 @@ warn_non_default_branch = true
[assign.owners]
"*" = ["@ehuss", "@epage", "@weihanglo"]
+
+[review-submitted]
+reviewed_label = "S-waiting-on-author"
+review_labels = ["S-waiting-on-review"]
+
+[review-requested]
+remove_labels = ["S-waiting-on-author"]
+add_labels = ["S-waiting-on-review"]
+
[autolabel."A-build-execution"]
trigger_files = [
"src/cargo/core/compiler/compilation.rs",
@@ -192,7 +201,6 @@ trigger_files = ["src/cargo/util/auth/"]
trigger_files = [
"crates/semver-check",
"src/cargo/util/semver_ext.rs",
- "src/cargo/util/to_semver.rs",
]
[autolabel."A-source-replacement"]