summaryrefslogtreecommitdiffstats
path: root/src/tools/cargo
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/cargo')
-rw-r--r--src/tools/cargo/.github/PULL_REQUEST_TEMPLATE.md7
-rw-r--r--src/tools/cargo/.github/renovate.json518
-rw-r--r--src/tools/cargo/.github/workflows/main.yml8
-rw-r--r--src/tools/cargo/CHANGELOG.md189
-rw-r--r--src/tools/cargo/Cargo.lock1097
-rw-r--r--src/tools/cargo/Cargo.toml72
-rw-r--r--src/tools/cargo/benches/README.md39
-rw-r--r--src/tools/cargo/benches/benchsuite/Cargo.toml9
-rw-r--r--src/tools/cargo/benches/benchsuite/benches/global_cache_tracker.rs159
-rw-r--r--src/tools/cargo/benches/benchsuite/global-cache-tracker/global-cache-samplebin0 -> 2449408 bytes
-rw-r--r--src/tools/cargo/benches/benchsuite/global-cache-tracker/random-sample500
-rw-r--r--src/tools/cargo/benches/benchsuite/src/bin/capture-last-use.rs148
-rw-r--r--src/tools/cargo/benches/benchsuite/src/lib.rs2
-rw-r--r--src/tools/cargo/benches/capture/Cargo.toml3
-rw-r--r--src/tools/cargo/benches/capture/src/main.rs3
-rw-r--r--src/tools/cargo/build.rs32
-rw-r--r--src/tools/cargo/clippy.toml3
-rw-r--r--src/tools/cargo/crates/cargo-platform/Cargo.toml5
-rw-r--r--src/tools/cargo/crates/cargo-platform/examples/matches.rs2
-rw-r--r--src/tools/cargo/crates/cargo-test-macro/Cargo.toml3
-rw-r--r--src/tools/cargo/crates/cargo-test-macro/src/lib.rs9
-rw-r--r--src/tools/cargo/crates/cargo-test-support/Cargo.toml4
-rw-r--r--src/tools/cargo/crates/cargo-test-support/build.rs2
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/compare.rs37
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/lib.rs27
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/paths.rs12
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/registry.rs59
-rw-r--r--src/tools/cargo/crates/cargo-test-support/src/tools.rs32
-rw-r--r--src/tools/cargo/crates/cargo-util/Cargo.toml6
-rw-r--r--src/tools/cargo/crates/cargo-util/src/du.rs78
-rw-r--r--src/tools/cargo/crates/cargo-util/src/lib.rs4
-rw-r--r--src/tools/cargo/crates/cargo-util/src/paths.rs13
-rw-r--r--src/tools/cargo/crates/crates-io/Cargo.toml5
-rw-r--r--src/tools/cargo/crates/crates-io/lib.rs11
-rw-r--r--src/tools/cargo/crates/home/CHANGELOG.md9
-rw-r--r--src/tools/cargo/crates/home/Cargo.toml7
-rw-r--r--src/tools/cargo/crates/home/src/lib.rs10
-rw-r--r--src/tools/cargo/crates/home/src/windows.rs23
-rw-r--r--src/tools/cargo/crates/mdman/Cargo.toml5
-rw-r--r--src/tools/cargo/crates/mdman/src/main.rs2
-rw-r--r--src/tools/cargo/crates/mdman/tests/compare.rs16
-rw-r--r--src/tools/cargo/crates/mdman/tests/invalid.rs6
-rw-r--r--src/tools/cargo/crates/resolver-tests/Cargo.toml3
-rw-r--r--src/tools/cargo/crates/resolver-tests/src/lib.rs174
-rw-r--r--src/tools/cargo/crates/resolver-tests/tests/resolve.rs225
-rw-r--r--src/tools/cargo/crates/rustfix/Cargo.toml34
-rw-r--r--src/tools/cargo/crates/rustfix/Changelog.md79
l---------src/tools/cargo/crates/rustfix/LICENSE-APACHE1
l---------src/tools/cargo/crates/rustfix/LICENSE-MIT1
-rw-r--r--src/tools/cargo/crates/rustfix/Readme.md29
-rw-r--r--src/tools/cargo/crates/rustfix/examples/fix-json.rs44
-rw-r--r--src/tools/cargo/crates/rustfix/proptest-regressions/replace.txt8
-rw-r--r--src/tools/cargo/crates/rustfix/src/diagnostics.rs115
-rw-r--r--src/tools/cargo/crates/rustfix/src/error.rs21
-rw-r--r--src/tools/cargo/crates/rustfix/src/lib.rs306
-rw-r--r--src/tools/cargo/crates/rustfix/src/replace.rs329
-rw-r--r--src/tools/cargo/crates/rustfix/tests/edge-cases/empty.json42
-rw-r--r--src/tools/cargo/crates/rustfix/tests/edge-cases/empty.rs0
-rw-r--r--src/tools/cargo/crates/rustfix/tests/edge-cases/indented_whitespace.json60
-rw-r--r--src/tools/cargo/crates/rustfix/tests/edge-cases/no_main.json33
-rw-r--r--src/tools/cargo/crates/rustfix/tests/edge-cases/no_main.rs1
-rw-r--r--src/tools/cargo/crates/rustfix/tests/edge-cases/out_of_bounds.recorded.json43
-rw-r--r--src/tools/cargo/crates/rustfix/tests/edge-cases/utf8_idents.recorded.json59
-rw-r--r--src/tools/cargo/crates/rustfix/tests/edge_cases.rs25
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/E0178.fixed.rs10
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/E0178.json70
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/E0178.rs10
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/closure-immutable-outer-variable.fixed.rs10
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/closure-immutable-outer-variable.json70
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/closure-immutable-outer-variable.rs10
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/handle-insert-only.fixed.rs8
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/handle-insert-only.json68
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/handle-insert-only.rs8
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/lt-generic-comp.fixed.rs7
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/lt-generic-comp.json87
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/lt-generic-comp.rs7
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/multiple-solutions.fixed.rs5
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/multiple-solutions.json114
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/multiple-solutions.rs5
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/replace-only-one-char.fixed.rs3
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/replace-only-one-char.json70
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/replace-only-one-char.rs3
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/str-lit-type-mismatch.fixed.rs5
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/str-lit-type-mismatch.json218
-rw-r--r--src/tools/cargo/crates/rustfix/tests/everything/str-lit-type-mismatch.rs5
-rw-r--r--src/tools/cargo/crates/rustfix/tests/parse_and_replace.rs234
-rw-r--r--src/tools/cargo/crates/semver-check/Cargo.toml3
-rw-r--r--src/tools/cargo/crates/semver-check/src/main.rs2
-rw-r--r--src/tools/cargo/crates/xtask-build-man/Cargo.toml3
-rw-r--r--src/tools/cargo/crates/xtask-build-man/src/main.rs2
-rw-r--r--src/tools/cargo/crates/xtask-bump-check/Cargo.toml3
-rw-r--r--src/tools/cargo/crates/xtask-bump-check/src/xtask.rs21
-rw-r--r--src/tools/cargo/crates/xtask-stale-label/Cargo.toml3
-rw-r--r--src/tools/cargo/crates/xtask-stale-label/src/main.rs2
-rw-r--r--src/tools/cargo/credential/cargo-credential-1password/Cargo.toml3
-rw-r--r--src/tools/cargo/credential/cargo-credential-1password/src/main.rs3
-rw-r--r--src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml5
-rw-r--r--src/tools/cargo/credential/cargo-credential-macos-keychain/Cargo.toml5
-rw-r--r--src/tools/cargo/credential/cargo-credential-macos-keychain/src/lib.rs2
-rw-r--r--src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml5
-rw-r--r--src/tools/cargo/credential/cargo-credential/Cargo.toml5
-rw-r--r--src/tools/cargo/credential/cargo-credential/examples/stdout-redirected.rs3
-rw-r--r--src/tools/cargo/credential/cargo-credential/src/lib.rs3
-rwxr-xr-xsrc/tools/cargo/publish.py1
-rw-r--r--src/tools/cargo/src/bin/cargo/cli.rs20
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/add.rs21
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/bench.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/build.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/check.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/clean.rs160
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/config.rs10
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/doc.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/fetch.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/fix.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/generate_lockfile.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/init.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/install.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/locate_project.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/login.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/logout.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/metadata.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/new.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/owner.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/package.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/pkgid.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/publish.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/read_manifest.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/remove.rs3
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/run.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/rustc.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/rustdoc.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/search.rs4
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/tree.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/uninstall.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/update.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/vendor.rs1
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/verify_project.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/version.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/commands/yank.rs2
-rw-r--r--src/tools/cargo/src/bin/cargo/main.rs6
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/fingerprint/dirty_reason.rs7
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/fingerprint/mod.rs23
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/future_incompat.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/job_queue/mod.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/compiler/mod.rs69
-rw-r--r--src/tools/cargo/src/cargo/core/features.rs31
-rw-r--r--src/tools/cargo/src/cargo/core/gc.rs509
-rw-r--r--src/tools/cargo/src/cargo/core/global_cache_tracker.rs1827
-rw-r--r--src/tools/cargo/src/cargo/core/manifest.rs5
-rw-r--r--src/tools/cargo/src/cargo/core/mod.rs8
-rw-r--r--src/tools/cargo/src/cargo/core/package.rs12
-rw-r--r--src/tools/cargo/src/cargo/core/package_id.rs35
-rw-r--r--src/tools/cargo/src/cargo/core/package_id_spec.rs479
-rw-r--r--src/tools/cargo/src/cargo/core/profiles.rs38
-rw-r--r--src/tools/cargo/src/cargo/core/registry.rs48
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/context.rs205
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/dep_cache.rs25
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/encode.rs4
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/errors.rs14
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/mod.rs58
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/resolve.rs2
-rw-r--r--src/tools/cargo/src/cargo/core/resolver/version_prefs.rs58
-rw-r--r--src/tools/cargo/src/cargo/core/source_id.rs207
-rw-r--r--src/tools/cargo/src/cargo/core/summary.rs85
-rw-r--r--src/tools/cargo/src/cargo/core/workspace.rs14
-rw-r--r--src/tools/cargo/src/cargo/lib.rs22
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_add/crate_spec.rs5
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_add/mod.rs64
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_clean.rs4
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs1
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_compile/packages.rs12
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_fetch.rs1
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_generate_lockfile.rs22
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_install.rs6
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_new.rs8
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_package.rs52
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_pkgid.rs4
-rw-r--r--src/tools/cargo/src/cargo/ops/cargo_uninstall.rs25
-rw-r--r--src/tools/cargo/src/cargo/ops/common_for_install_and_uninstall.rs46
-rw-r--r--src/tools/cargo/src/cargo/ops/mod.rs2
-rw-r--r--src/tools/cargo/src/cargo/ops/registry/publish.rs1
-rw-r--r--src/tools/cargo/src/cargo/ops/resolve.rs13
-rw-r--r--src/tools/cargo/src/cargo/ops/tree/mod.rs2
-rw-r--r--src/tools/cargo/src/cargo/sources/directory.rs7
-rw-r--r--src/tools/cargo/src/cargo/sources/git/known_hosts.rs2
-rw-r--r--src/tools/cargo/src/cargo/sources/git/source.rs46
-rw-r--r--src/tools/cargo/src/cargo/sources/mod.rs4
-rw-r--r--src/tools/cargo/src/cargo/sources/path.rs14
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/download.rs18
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/http_remote.rs21
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/index.rs14
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/mod.rs40
-rw-r--r--src/tools/cargo/src/cargo/sources/registry/remote.rs20
-rw-r--r--src/tools/cargo/src/cargo/sources/replaced.rs7
-rw-r--r--src/tools/cargo/src/cargo/sources/source.rs23
-rw-r--r--src/tools/cargo/src/cargo/util/command_prelude.rs31
-rw-r--r--src/tools/cargo/src/cargo/util/config/mod.rs34
-rw-r--r--src/tools/cargo/src/cargo/util/errors.rs2
-rw-r--r--src/tools/cargo/src/cargo/util/graph.rs32
-rw-r--r--src/tools/cargo/src/cargo/util/mod.rs4
-rw-r--r--src/tools/cargo/src/cargo/util/restricted_names.rs151
-rw-r--r--src/tools/cargo/src/cargo/util/rustc.rs22
-rw-r--r--src/tools/cargo/src/cargo/util/semver_ext.rs47
-rw-r--r--src/tools/cargo/src/cargo/util/sqlite.rs118
-rw-r--r--src/tools/cargo/src/cargo/util/toml/embedded.rs3
-rw-r--r--src/tools/cargo/src/cargo/util/toml/mod.rs3241
-rw-r--r--src/tools/cargo/src/cargo/util/toml/targets.rs12
-rw-r--r--src/tools/cargo/src/cargo/util/toml_mut/dependency.rs39
-rw-r--r--src/tools/cargo/src/cargo/util/toml_mut/manifest.rs2
-rw-r--r--src/tools/cargo/src/cargo/util_schemas/core/mod.rs6
-rw-r--r--src/tools/cargo/src/cargo/util_schemas/core/package_id_spec.rs593
-rw-r--r--src/tools/cargo/src/cargo/util_schemas/core/source_kind.rs201
-rw-r--r--src/tools/cargo/src/cargo/util_schemas/manifest.rs (renamed from src/tools/cargo/src/cargo/util/toml/schema.rs)528
-rw-r--r--src/tools/cargo/src/cargo/util_schemas/mod.rs11
-rw-r--r--src/tools/cargo/src/cargo/util_schemas/restricted_names.rs218
-rw-r--r--src/tools/cargo/src/doc/contrib/src/issues.md8
-rw-r--r--src/tools/cargo/src/doc/contrib/src/process/release.md8
-rw-r--r--src/tools/cargo/src/doc/contrib/src/team.md25
-rw-r--r--src/tools/cargo/src/doc/man/cargo-search.md3
-rw-r--r--src/tools/cargo/src/doc/man/generated_txt/cargo-search.txt3
-rw-r--r--src/tools/cargo/src/doc/src/commands/cargo-search.md3
-rw-r--r--src/tools/cargo/src/doc/src/guide/continuous-integration.md24
-rw-r--r--src/tools/cargo/src/doc/src/reference/environment-variables.md3
-rw-r--r--src/tools/cargo/src/doc/src/reference/manifest.md11
-rw-r--r--src/tools/cargo/src/doc/src/reference/pkgid-spec.md42
-rw-r--r--src/tools/cargo/src/doc/src/reference/profiles.md2
-rw-r--r--src/tools/cargo/src/doc/src/reference/registry-authentication.md10
-rw-r--r--src/tools/cargo/src/doc/src/reference/registry-web-api.md2
-rw-r--r--src/tools/cargo/src/doc/src/reference/semver.md2
-rw-r--r--src/tools/cargo/src/doc/src/reference/specifying-dependencies.md1
-rw-r--r--src/tools/cargo/src/doc/src/reference/unstable.md76
-rw-r--r--src/tools/cargo/src/doc/src/reference/workspaces.md2
-rw-r--r--src/tools/cargo/src/etc/cargo.bashcomp.sh4
-rw-r--r--src/tools/cargo/src/etc/man/cargo-search.12
-rw-r--r--src/tools/cargo/tests/build-std/main.rs45
-rw-r--r--src/tools/cargo/tests/testsuite/alt_registry.rs92
-rw-r--r--src/tools/cargo/tests/testsuite/artifact_dep.rs7
-rw-r--r--src/tools/cargo/tests/testsuite/build.rs236
-rw-r--r--src/tools/cargo/tests/testsuite/build_script.rs13
-rw-r--r--src/tools/cargo/tests/testsuite/build_script_env.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/change_rename_target/out/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/primary/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/Cargo.toml5
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/dependency/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/dependency/src/lib.rs0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/primary/Cargo.toml4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/primary/src/lib.rs0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/mod.rs26
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/out/Cargo.toml5
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/out/dependency/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/out/primary/Cargo.toml7
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/stdout.log0
l---------src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/in1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/mod.rs24
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/out/Cargo.toml5
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/help/stdout.log17
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/mod.rs9
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/no_optional/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/no_optional/out/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/no_optional/stderr.log3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/no_public/in/Cargo.toml6
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/no_public/in/src/lib.rs0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/no_public/mod.rs26
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/no_public/out/Cargo.toml9
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/no_public/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/no_public/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/optional/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/optional/out/Cargo.toml6
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/optional/stderr.log3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_git_with_path/out/primary/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/primary/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_noop/out/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/in/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/out/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/stderr.log3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/in/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/out/Cargo.toml6
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/stderr.log3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/in/Cargo.toml9
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/in/src/lib.rs0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/mod.rs26
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/out/Cargo.toml9
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/in/Cargo.toml9
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/in/src/lib.rs0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/mod.rs26
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/out/Cargo.toml9
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/in/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/mod.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/out/Cargo.toml6
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/stderr.log3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/in/Cargo.toml1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/mod.rs14
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/out/Cargo.toml1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/in/Cargo.toml11
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/in/src/lib.rs0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/mod.rs26
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/out/Cargo.toml11
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_noop/out/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_with_version/out/primary/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/in/Cargo.toml9
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/in/src/lib.rs0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/mod.rs26
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/out/Cargo.toml9
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/in/Cargo.toml9
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/in/src/lib.rs0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/mod.rs26
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/out/Cargo.toml9
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/out/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_git/out/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_path/out/primary/Cargo.toml3
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/public/in/Cargo.toml6
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/public/in/src/lib.rs0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/public/mod.rs26
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/public/out/Cargo.toml9
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/public/stderr.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/public/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/rust_version_ignore/mod.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_add/rust_version_ignore/stderr.log5
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_bench/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_build/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_check/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_clean/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_config/help/stdout.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_doc/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_features.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_fetch/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_fix/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_generate_lockfile/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_help/help/stdout.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_init/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_install/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_locate_project/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_login/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_logout/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_metadata/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/empty_name/in/.keep0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/empty_name/mod.rs22
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/empty_name/out/.keep0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/empty_name/stderr.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/empty_name/stdout.log0
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_new/mod.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_owner/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_package/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_pkgid/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_publish/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_read_manifest/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/in/Cargo.lock4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/out/Cargo.lock4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_report/help/stdout.log1
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_run/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_rustc/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_search/help/stdout.log4
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_tree/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_uninstall/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_update/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_vendor/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_verify_project/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_version/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/cargo_yank/help/stdout.log2
-rw-r--r--src/tools/cargo/tests/testsuite/check.rs49
-rw-r--r--src/tools/cargo/tests/testsuite/check_cfg.rs85
-rw-r--r--src/tools/cargo/tests/testsuite/clean.rs55
-rw-r--r--src/tools/cargo/tests/testsuite/config.rs6
-rw-r--r--src/tools/cargo/tests/testsuite/custom_target.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/doc.rs19
-rw-r--r--src/tools/cargo/tests/testsuite/docscrape.rs78
-rw-r--r--src/tools/cargo/tests/testsuite/features.rs37
-rw-r--r--src/tools/cargo/tests/testsuite/features_namespaced.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/fetch.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/fix.rs164
-rw-r--r--src/tools/cargo/tests/testsuite/freshness.rs8
-rw-r--r--src/tools/cargo/tests/testsuite/generate_lockfile.rs4
-rw-r--r--src/tools/cargo/tests/testsuite/git.rs31
-rw-r--r--src/tools/cargo/tests/testsuite/git_auth.rs10
-rw-r--r--src/tools/cargo/tests/testsuite/global_cache_tracker.rs1862
-rw-r--r--src/tools/cargo/tests/testsuite/install.rs122
-rw-r--r--src/tools/cargo/tests/testsuite/main.rs7
-rw-r--r--src/tools/cargo/tests/testsuite/messages.rs2
-rw-r--r--src/tools/cargo/tests/testsuite/metabuild.rs1
-rw-r--r--src/tools/cargo/tests/testsuite/package.rs239
-rw-r--r--src/tools/cargo/tests/testsuite/patch.rs83
-rw-r--r--src/tools/cargo/tests/testsuite/path.rs11
-rw-r--r--src/tools/cargo/tests/testsuite/pkgid.rs98
-rw-r--r--src/tools/cargo/tests/testsuite/profile_config.rs6
-rw-r--r--src/tools/cargo/tests/testsuite/profile_custom.rs16
-rw-r--r--src/tools/cargo/tests/testsuite/profile_trim_paths.rs267
-rw-r--r--src/tools/cargo/tests/testsuite/pub_priv.rs235
-rw-r--r--src/tools/cargo/tests/testsuite/publish_lockfile.rs12
-rw-r--r--src/tools/cargo/tests/testsuite/registry.rs23
-rw-r--r--src/tools/cargo/tests/testsuite/replace.rs14
-rw-r--r--src/tools/cargo/tests/testsuite/rust_version.rs81
-rw-r--r--src/tools/cargo/tests/testsuite/rustflags.rs6
-rw-r--r--src/tools/cargo/tests/testsuite/test.rs37
-rw-r--r--src/tools/cargo/tests/testsuite/update.rs94
-rw-r--r--src/tools/cargo/tests/testsuite/workspaces.rs1
-rw-r--r--src/tools/cargo/triagebot.toml1
-rw-r--r--src/tools/cargo/windows.manifest.xml28
416 files changed, 15651 insertions, 4751 deletions
diff --git a/src/tools/cargo/.github/PULL_REQUEST_TEMPLATE.md b/src/tools/cargo/.github/PULL_REQUEST_TEMPLATE.md
index 141ac5ed4..f8ff10d15 100644
--- a/src/tools/cargo/.github/PULL_REQUEST_TEMPLATE.md
+++ b/src/tools/cargo/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,13 +1,8 @@
<!-- homu-ignore:start -->
<!--
-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.
-
Thanks for submitting a pull request 🎉! Here are some tips for you:
-* If this is your first contribution, read "Cargo Contribution Guide":
+* If this is your first contribution, read "Cargo Contribution Guide" first:
https://doc.crates.io/contrib/
* Run `cargo fmt --all` to format your code changes.
* Small commits and pull requests are always preferable and easy to review.
diff --git a/src/tools/cargo/.github/renovate.json5 b/src/tools/cargo/.github/renovate.json5
index 03e6d8da8..ca9c2a813 100644
--- a/src/tools/cargo/.github/renovate.json5
+++ b/src/tools/cargo/.github/renovate.json5
@@ -6,7 +6,7 @@
configMigration: true,
dependencyDashboard: false,
ignorePaths: [
- "**/tests/**",
+ '**/tests/**',
],
customManagers: [
{
@@ -40,7 +40,7 @@
{
commitMessageTopic: 'MSRV (1 version)',
matchManagers: [
- 'regex',
+ 'custom.regex',
],
matchPackageNames: [
'MSRV:1',
@@ -53,12 +53,12 @@
{
commitMessageTopic: 'MSRV (3 versions)',
matchManagers: [
- 'regex',
+ 'custom.regex',
],
matchPackageNames: [
'MSRV:3',
],
- "extractVersion": "^(?<version>\\d+\\.\\d+)", // Drop the patch version
+ extractVersion: '^(?<version>\\d+\\.\\d+)', // Drop the patch version
schedule: [
'* * * * *',
],
@@ -66,6 +66,16 @@
internalChecksFilter: 'strict',
groupName: 'msrv',
},
+ {
+ matchManagers: [
+ 'cargo',
+ ],
+ matchPackageNames: [
+ 'gix-features-for-configuration-only',
+ 'gix',
+ ],
+ groupName: 'gix',
+ },
// Goals:
// - Rollup safe upgrades to reduce CI runner load
// - Have lockfile and manifest in-sync (implicit rules)
diff --git a/src/tools/cargo/.github/workflows/main.yml b/src/tools/cargo/.github/workflows/main.yml
index 7b8055223..a49ac0988 100644
--- a/src/tools/cargo/.github/workflows/main.yml
+++ b/src/tools/cargo/.github/workflows/main.yml
@@ -66,9 +66,7 @@ jobs:
- uses: actions/checkout@v4
- run: rustup update stable && rustup default stable
- run: rustup component add clippy
- # Only check cargo lib for now
- # TODO: check every members
- - run: cargo clippy -p cargo --lib --no-deps -- -D warnings
+ - run: cargo clippy --workspace --all-targets --no-deps -- -D warnings
stale-label:
runs-on: ubuntu-latest
@@ -110,8 +108,6 @@ jobs:
CARGO_PROFILE_TEST_DEBUG: 1
CARGO_INCREMENTAL: 0
CARGO_PUBLIC_NETWORK_TESTS: 1
- # Deny warnings on CI to avoid warnings getting into the codebase.
- RUSTFLAGS: -D warnings
strategy:
matrix:
include:
@@ -152,7 +148,7 @@ jobs:
- run: rustup target add ${{ matrix.other }}
- run: rustup component add rustc-dev llvm-tools-preview rust-docs
if: startsWith(matrix.rust, 'nightly')
- - run: sudo apt update -y && sudo apt install gcc-multilib libsecret-1-0 libsecret-1-dev -y
+ - run: sudo apt update -y && sudo apt install lldb gcc-multilib libsecret-1-0 libsecret-1-dev -y
if: matrix.os == 'ubuntu-latest'
- run: rustup component add rustfmt || echo "rustfmt not available"
- name: Configure extra test environment
diff --git a/src/tools/cargo/CHANGELOG.md b/src/tools/cargo/CHANGELOG.md
index 08be017a1..eb0e46d0b 100644
--- a/src/tools/cargo/CHANGELOG.md
+++ b/src/tools/cargo/CHANGELOG.md
@@ -1,23 +1,156 @@
# Changelog
+## Cargo 1.76 (2024-02-08)
+[6790a512...HEAD](https://github.com/rust-lang/cargo/compare/6790a512...HEAD)
+
+### Added
+
+### Changed
+
+### Fixed
+
+### Nightly only
+
+- 🔥 The `-Zgc` flag enables garbage collection for deleting old, unused files
+ in cargo's cache. That is, downloaded source files and registry index under
+ the `CARGO_HOME` directory.
+ [docs](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#gc)
+ [#12634](https://github.com/rust-lang/cargo/pull/12634)
+ [#12958](https://github.com/rust-lang/cargo/pull/12958)
+
+### Documentation
+
+### Internal
+
+- Refactored `Cargo.toml` parsing code in preparation of extracting an offical schema API.
+ [#12954](https://github.com/rust-lang/cargo/pull/12954)
+ [#12960](https://github.com/rust-lang/cargo/pull/12960)
+ [#12961](https://github.com/rust-lang/cargo/pull/12961)
+- Fixed `--quiet` being used with nested subcommands.
+ [#12959](https://github.com/rust-lang/cargo/pull/12959)
+
## Cargo 1.75 (2023-12-28)
-[59596f0f...HEAD](https://github.com/rust-lang/cargo/compare/59596f0f...HEAD)
+[59596f0f...rust-1.75.0](https://github.com/rust-lang/cargo/compare/59596f0f...rust-1.75.0)
### Added
+- `package.version` field in `Cargo.toml` is now optional and defaults to `0.0.0`.
+ Packages without the `package.version` field cannot be published.
+ [#12786](https://github.com/rust-lang/cargo/pull/12786)
+- Links in `--timings` and `cargo doc` outputs are clickable on supported terminals,
+ controllable through `term.hyperlinks` config value.
+ [#12889](https://github.com/rust-lang/cargo/pull/12889)
+- Print environment variables for build script executions with `-vv`.
+ [#12829](https://github.com/rust-lang/cargo/pull/12829)
+- cargo-new: add new packages to [workspace.members] automatically.
+ [#12779](https://github.com/rust-lang/cargo/pull/12779)
+- cargo-doc: print a new `Generated` status displaying the full path.
+ [#12859](https://github.com/rust-lang/cargo/pull/12859)
+
### Changed
+- cargo-new: warn if crate name doesn't follow snake_case or kebab-case.
+ [#12766](https://github.com/rust-lang/cargo/pull/12766)
+- cargo-install: clarify the arg `<crate>` to install is positional.
+ [#12841](https://github.com/rust-lang/cargo/pull/12841)
+- cargo-install: Suggest an alternative version on MSRV failure.
+ [#12798](https://github.com/rust-lang/cargo/pull/12798)
+- cargo-install: reports more detailed SemVer errors.
+ [#12924](https://github.com/rust-lang/cargo/pull/12924)
+- cargo-install: install only once if there are crates duplicated.
+ [#12868](https://github.com/rust-lang/cargo/pull/12868)
+- cargo-remove: Clarify flag behavior of different dependency kinds.
+ [#12823](https://github.com/rust-lang/cargo/pull/12823)
+- cargo-remove: suggest the dependency to remove exists only in the other section.
+ [#12865](https://github.com/rust-lang/cargo/pull/12865)
+- cargo-update: Do not call it "Downgrading" when difference is only build metadata.
+ [#12796](https://github.com/rust-lang/cargo/pull/12796)
+- Enhanced help text to clarify `--test` flag is for Cargo targets, not test functions.
+ [#12915](https://github.com/rust-lang/cargo/pull/12915)
+- Included package name/version in build script warnings.
+ [#12799](https://github.com/rust-lang/cargo/pull/12799)
+- Provide next steps for bad -Z flag.
+ [#12857](https://github.com/rust-lang/cargo/pull/12857)
+- Suggest `cargo search` when `cargo-<command>` cannot be found.
+ [#12840](https://github.com/rust-lang/cargo/pull/12840)
+- Do not allow empty feature name.
+ [#12928](https://github.com/rust-lang/cargo/pull/12928)
+- Added unsupported short flag suggestion for `--target` and `--exclude` flags.
+ [#12805](https://github.com/rust-lang/cargo/pull/12805)
+- Added unsupported short flag suggestion for `--out-dir` flag.
+ [#12755](https://github.com/rust-lang/cargo/pull/12755)
+- Added unsupported lowercase `-z` flag suggestion for `-Z` flag.
+ [#12788](https://github.com/rust-lang/cargo/pull/12788)
+- Added better suggestion for unsupported `--path` flag.
+ [#12811](https://github.com/rust-lang/cargo/pull/12811)
+- Added detailed message when target directory path is invalid.
+ [#12820](https://github.com/rust-lang/cargo/pull/12820)
+
### Fixed
- Fixed corruption when cargo was killed while writing to files.
[#12744](https://github.com/rust-lang/cargo/pull/12744)
+- cargo-add: Preserve more comments
+ [#12838](https://github.com/rust-lang/cargo/pull/12838)
+- cargo-fix: preserve jobserver file descriptors on rustc invocation.
+ [#12951](https://github.com/rust-lang/cargo/pull/12951)
+- cargo-remove: Preserve feature comments
+ [#12837](https://github.com/rust-lang/cargo/pull/12837)
+- Removed unnecessary backslash in timings HTML report when error happens.
+ [#12934](https://github.com/rust-lang/cargo/pull/12934)
+- Fixed error message that invalid a feature name can contain `-`.
+ [#12939](https://github.com/rust-lang/cargo/pull/12939)
+- When there's a version of a dependency in the lockfile,
+ Cargo would use that "exact" version, including the build metadata.
+ [#12772](https://github.com/rust-lang/cargo/pull/12772)
### Nightly only
+- Added `Edition2024` unstable feature.
+ [docs](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#edition-2024)
+ [#12771](https://github.com/rust-lang/cargo/pull/12771)
+- 🔥 The `-Ztrim-paths` feature adds a new profile setting to control how paths
+ are sanitized in the resulting binary.
+ ([RFC 3127](https://github.com/rust-lang/rfcs/blob/master/text/3127-trim-paths.md))
+ ([docs](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#profile-trim-paths-option))
+ [#12625](https://github.com/rust-lang/cargo/pull/12625)
+ [#12900](https://github.com/rust-lang/cargo/pull/12900)
+ [#12908](https://github.com/rust-lang/cargo/pull/12908)
+- `-Zcheck-cfg`: Adjusted for new rustc syntax and behavior.
+ [#12845](https://github.com/rust-lang/cargo/pull/12845)
+- `-Zcheck-cfg`: Remove outdated option to `-Zcheck-cfg` warnings.
+ [#12884](https://github.com/rust-lang/cargo/pull/12884)
+- `public-dependency`: Support `public` dependency configuration with workspace deps.
+ [#12817](https://github.com/rust-lang/cargo/pull/12817)
+
### Documentation
- profile: add missing `strip` info.
[#12754](https://github.com/rust-lang/cargo/pull/12754)
+- features: a note about the new limit on number of features.
+ [#12913](https://github.com/rust-lang/cargo/pull/12913)
+- crates-io: Add doc comment for `NewCrate` struct.
+ [#12782](https://github.com/rust-lang/cargo/pull/12782)
+- resolver: Highlight commands to answer dep resolution questions.
+ [#12903](https://github.com/rust-lang/cargo/pull/12903)
+- cargo-bench: `--bench` is passed in unconditionally to bench harnesses.
+ [#12850](https://github.com/rust-lang/cargo/pull/12850)
+- cargo-login: mention args after `--` in manpage.
+ [#12832](https://github.com/rust-lang/cargo/pull/12832)
+- cargo-vendor: clarify config to use vendored source is printed to stdout
+ [#12893](https://github.com/rust-lang/cargo/pull/12893)
+- manifest: update to SPDX 2.3 license expression and 3.20 license list.
+ [#12827](https://github.com/rust-lang/cargo/pull/12827)
+- contrib: Policy on manifest editing
+ [#12836](https://github.com/rust-lang/cargo/pull/12836)
+- contrib: use `AND` search terms in mdbook search and fixed broken links.
+ [#12812](https://github.com/rust-lang/cargo/pull/12812)
+ [#12813](https://github.com/rust-lang/cargo/pull/12813)
+ [#12814](https://github.com/rust-lang/cargo/pull/12814)
+- contrib: Describe how to add a new package
+ [#12878](https://github.com/rust-lang/cargo/pull/12878)
+- contrib: Removed review capacity notice.
+ [#12842](https://github.com/rust-lang/cargo/pull/12842)
### Internal
@@ -25,8 +158,61 @@
[#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)
+- Updated to `curl-sys` 0.4.68, which corresponds to curl 8.4.0.
+ [#12808](https://github.com/rust-lang/cargo/pull/12808)
+- Updated to `toml` 0.8.2.
+ [#12760](https://github.com/rust-lang/cargo/pull/12760)
+- Updated to `toml_edit` 0.20.2.
+ [#12761](https://github.com/rust-lang/cargo/pull/12761)
+- Updated to `gix` to 0.55.2
+ [#12906](https://github.com/rust-lang/cargo/pull/12906)
- Disabled the `custom_target::custom_bin_target` test on windows-gnu.
[#12763](https://github.com/rust-lang/cargo/pull/12763)
+- Refactored `Cargo.toml` parsing code in preparation of extracting an official
+ schema API.
+ [#12768](https://github.com/rust-lang/cargo/pull/12768)
+ [#12881](https://github.com/rust-lang/cargo/pull/12881)
+ [#12902](https://github.com/rust-lang/cargo/pull/12902)
+ [#12911](https://github.com/rust-lang/cargo/pull/12911)
+ [#12948](https://github.com/rust-lang/cargo/pull/12948)
+- Split out SemVer logic to its own module.
+ [#12926](https://github.com/rust-lang/cargo/pull/12926)
+ [#12940](https://github.com/rust-lang/cargo/pull/12940)
+- source: Prepare for new `PackageIDSpec` syntax
+ [#12938](https://github.com/rust-lang/cargo/pull/12938)
+- resolver: Consolidate logic in `VersionPreferences`
+ [#12930](https://github.com/rust-lang/cargo/pull/12930)
+- Make the `SourceId::precise` field an Enum.
+ [#12849](https://github.com/rust-lang/cargo/pull/12849)
+- shell: Write at once rather than in fragments.
+ [#12880](https://github.com/rust-lang/cargo/pull/12880)
+- Move up looking at index summary enum
+ [#12749](https://github.com/rust-lang/cargo/pull/12749)
+ [#12923](https://github.com/rust-lang/cargo/pull/12923)
+- Generate redirection HTML pages in CI for Cargo Contributor Guide.
+ [#12846](https://github.com/rust-lang/cargo/pull/12846)
+- Add new package cache lock modes.
+ [#12706](https://github.com/rust-lang/cargo/pull/12706)
+- Add regression test for issue 6915: features and transitive dev deps.
+ [#12907](https://github.com/rust-lang/cargo/pull/12907)
+- Auto-labeling when PR review state changes.
+ [#12856](https://github.com/rust-lang/cargo/pull/12856)
+- credential: include license files in all published crates.
+ [#12953](https://github.com/rust-lang/cargo/pull/12953)
+- credential: Filter `cargo-credential-*` dependencies by OS.
+ [#12949](https://github.com/rust-lang/cargo/pull/12949)
+- ci: bump cargo-semver-checks to 0.24.0
+ [#12795](https://github.com/rust-lang/cargo/pull/12795)
+- ci: set and verify all MSRVs for Cargo's crates automatically.
+ [#12767](https://github.com/rust-lang/cargo/pull/12767)
+ [#12654](https://github.com/rust-lang/cargo/pull/12654)
+- ci: use separate concurrency group for publishing Cargo Contributor Book.
+ [#12834](https://github.com/rust-lang/cargo/pull/12834)
+ [#12835](https://github.com/rust-lang/cargo/pull/12835)
+- ci: update `actions/checkout` action to v4
+ [#12762](https://github.com/rust-lang/cargo/pull/12762)
+- cargo-search: improved the margin calculation for the output.
+ [#12890](https://github.com/rust-lang/cargo/pull/12890)
## Cargo 1.74 (2023-11-16)
[80eca0e5...rust-1.74.0](https://github.com/rust-lang/cargo/compare/80eca0e5...rust-1.74.0)
@@ -70,6 +256,7 @@
- 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)
+ [#12806](https://github.com/rust-lang/cargo/pull/12806)
### Changed
diff --git a/src/tools/cargo/Cargo.lock b/src/tools/cargo/Cargo.lock
index a2d339b0c..36cf8804a 100644
--- a/src/tools/cargo/Cargo.lock
+++ b/src/tools/cargo/Cargo.lock
@@ -9,15 +9,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
+name = "ahash"
+version = "0.8.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "version_check",
+ "zerocopy",
+]
+
+[[package]]
name = "aho-corasick"
-version = "1.0.2"
+version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41"
+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
dependencies = [
"memchr",
]
[[package]]
+name = "allocator-api2"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
+
+[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -25,9 +43,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "anstream"
-version = "0.6.4"
+version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44"
+checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6"
dependencies = [
"anstyle",
"anstyle-parse",
@@ -45,30 +63,30 @@ checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87"
[[package]]
name = "anstyle-parse"
-version = "0.2.0"
+version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee"
+checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
-version = "1.0.0"
+version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b"
+checksum = "a3a318f1f38d2418400f8209655bfd825785afd25aa30bb7ba6cc792e4596748"
dependencies = [
- "windows-sys",
+ "windows-sys 0.52.0",
]
[[package]]
name = "anstyle-wincon"
-version = "3.0.1"
+version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628"
+checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
dependencies = [
"anstyle",
- "windows-sys",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -112,8 +130,10 @@ name = "benchsuite"
version = "0.0.0"
dependencies = [
"cargo",
+ "cargo-util",
"criterion",
"flate2",
+ "rand",
"tar",
"url",
]
@@ -141,9 +161,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
-version = "2.4.0"
+version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635"
+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07"
[[package]]
name = "bitmaps"
@@ -165,12 +185,12 @@ dependencies = [
[[package]]
name = "bstr"
-version = "1.6.2"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a"
+checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c"
dependencies = [
"memchr",
- "regex-automata 0.3.8",
+ "regex-automata 0.4.3",
"serde",
]
@@ -185,15 +205,15 @@ dependencies = [
[[package]]
name = "bumpalo"
-version = "3.13.0"
+version = "3.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1"
+checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec"
[[package]]
name = "bytes"
-version = "1.4.0"
+version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223"
[[package]]
name = "bytesize"
@@ -203,9 +223,9 @@ checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc"
[[package]]
name = "camino"
-version = "1.1.4"
+version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c530edf18f37068ac2d977409ed5cd50d53d73bc653c7647b48eb78976ac9ae2"
+checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c"
dependencies = [
"serde",
]
@@ -222,7 +242,7 @@ dependencies = [
[[package]]
name = "cargo"
-version = "0.76.0"
+version = "0.77.0"
dependencies = [
"anstream",
"anstyle",
@@ -233,7 +253,7 @@ dependencies = [
"cargo-credential-libsecret",
"cargo-credential-macos-keychain",
"cargo-credential-wincred",
- "cargo-platform 0.1.6",
+ "cargo-platform 0.1.7",
"cargo-test-macro",
"cargo-test-support",
"cargo-util",
@@ -257,7 +277,7 @@ dependencies = [
"ignore",
"im-rc",
"indexmap",
- "itertools 0.11.0",
+ "itertools 0.12.0",
"jobserver",
"lazycell",
"libc",
@@ -270,6 +290,8 @@ dependencies = [
"pathdiff",
"pulldown-cmark",
"rand",
+ "regex",
+ "rusqlite",
"rustfix",
"same-file",
"semver",
@@ -282,7 +304,7 @@ dependencies = [
"shell-escape",
"snapbox",
"supports-hyperlinks",
- "syn 2.0.38",
+ "syn 2.0.39",
"tar",
"tempfile",
"time",
@@ -295,12 +317,12 @@ dependencies = [
"unicode-xid",
"url",
"walkdir",
- "windows-sys",
+ "windows-sys 0.52.0",
]
[[package]]
name = "cargo-credential"
-version = "0.4.1"
+version = "0.4.2"
dependencies = [
"anyhow",
"libc",
@@ -309,7 +331,7 @@ dependencies = [
"snapbox",
"thiserror",
"time",
- "windows-sys",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -323,7 +345,7 @@ dependencies = [
[[package]]
name = "cargo-credential-libsecret"
-version = "0.4.1"
+version = "0.4.2"
dependencies = [
"anyhow",
"cargo-credential",
@@ -332,7 +354,7 @@ dependencies = [
[[package]]
name = "cargo-credential-macos-keychain"
-version = "0.4.1"
+version = "0.4.2"
dependencies = [
"cargo-credential",
"security-framework",
@@ -340,24 +362,24 @@ dependencies = [
[[package]]
name = "cargo-credential-wincred"
-version = "0.4.1"
+version = "0.4.2"
dependencies = [
"cargo-credential",
- "windows-sys",
+ "windows-sys 0.52.0",
]
[[package]]
name = "cargo-platform"
-version = "0.1.2"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbdb825da8a5df079a43676dbe042702f1707b1109f713a01420fbb4cc71fa27"
+checksum = "e34637b3140142bdf929fb439e8aa4ebad7651ebf7b1080b3930aa16ac1459ff"
dependencies = [
"serde",
]
[[package]]
name = "cargo-platform"
-version = "0.1.6"
+version = "0.1.7"
dependencies = [
"serde",
]
@@ -380,7 +402,7 @@ dependencies = [
"flate2",
"git2",
"glob",
- "itertools 0.11.0",
+ "itertools 0.12.0",
"pasetors",
"serde",
"serde_json",
@@ -389,17 +411,19 @@ dependencies = [
"time",
"toml",
"url",
- "windows-sys",
+ "walkdir",
+ "windows-sys 0.52.0",
]
[[package]]
name = "cargo-util"
-version = "0.2.8"
+version = "0.2.9"
dependencies = [
"anyhow",
"core-foundation",
"filetime",
"hex",
+ "ignore",
"jobserver",
"libc",
"miow",
@@ -409,7 +433,7 @@ dependencies = [
"tempfile",
"tracing",
"walkdir",
- "windows-sys",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -419,7 +443,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037"
dependencies = [
"camino",
- "cargo-platform 0.1.2",
+ "cargo-platform 0.1.5",
"semver",
"serde",
"serde_json",
@@ -434,11 +458,12 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
-version = "1.0.79"
+version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
+checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"jobserver",
+ "libc",
]
[[package]]
@@ -476,18 +501,18 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.4.7"
+version = "4.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b"
+checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
-version = "4.4.7"
+version = "4.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663"
+checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb"
dependencies = [
"anstream",
"anstyle",
@@ -537,9 +562,9 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "const-oid"
-version = "0.9.2"
+version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913"
+checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f"
[[package]]
name = "content_inspector"
@@ -552,9 +577,9 @@ dependencies = [
[[package]]
name = "core-foundation"
-version = "0.9.3"
+version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
dependencies = [
"core-foundation-sys",
"libc",
@@ -562,22 +587,22 @@ dependencies = [
[[package]]
name = "core-foundation-sys"
-version = "0.8.4"
+version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa"
+checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
[[package]]
name = "cpufeatures"
-version = "0.2.7"
+version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58"
+checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
dependencies = [
"libc",
]
[[package]]
name = "crates-io"
-version = "0.39.1"
+version = "0.39.2"
dependencies = [
"curl",
"percent-encoding",
@@ -677,9 +702,9 @@ dependencies = [
[[package]]
name = "crypto-bigint"
-version = "0.5.2"
+version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15"
+checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
dependencies = [
"generic-array",
"rand_core",
@@ -720,9 +745,9 @@ dependencies = [
[[package]]
name = "curl-sys"
-version = "0.4.68+curl-8.4.0"
+version = "0.4.70+curl-8.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b4a0d18d88360e374b16b2273c832b5e57258ffc1d4aa4f96b108e0738d5752f"
+checksum = "3c0333d8849afe78a4c8102a429a446bfdd055832af071945520e835ae2d841e"
dependencies = [
"cc",
"libc",
@@ -731,14 +756,14 @@ dependencies = [
"openssl-sys",
"pkg-config",
"vcpkg",
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
name = "der"
-version = "0.7.6"
+version = "0.7.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56acb310e15652100da43d130af8d97b509e95af61aab1c5a7939ef24337ee17"
+checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c"
dependencies = [
"const-oid",
"pem-rfc7468",
@@ -747,20 +772,15 @@ dependencies = [
[[package]]
name = "deranged"
-version = "0.3.8"
+version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946"
+checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc"
dependencies = [
+ "powerfmt",
"serde",
]
[[package]]
-name = "diff"
-version = "0.1.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
-
-[[package]]
name = "digest"
version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -780,9 +800,9 @@ checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
[[package]]
name = "ecdsa"
-version = "0.16.7"
+version = "0.16.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428"
+checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
dependencies = [
"der",
"digest",
@@ -803,15 +823,15 @@ dependencies = [
[[package]]
name = "either"
-version = "1.8.1"
+version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
+checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "elliptic-curve"
-version = "0.13.5"
+version = "0.13.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b"
+checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
dependencies = [
"base16ct",
"crypto-bigint",
@@ -854,23 +874,12 @@ dependencies = [
[[package]]
name = "errno"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a"
-dependencies = [
- "errno-dragonfly",
- "libc",
- "windows-sys",
-]
-
-[[package]]
-name = "errno-dragonfly"
-version = "0.1.2"
+version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
+checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
dependencies = [
- "cc",
"libc",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -886,6 +895,18 @@ dependencies = [
]
[[package]]
+name = "fallible-iterator"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
+
+[[package]]
+name = "fallible-streaming-iterator"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
+
+[[package]]
name = "faster-hex"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -895,10 +916,19 @@ dependencies = [
]
[[package]]
+name = "faster-hex"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183"
+dependencies = [
+ "serde",
+]
+
+[[package]]
name = "fastrand"
-version = "2.0.0"
+version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764"
+checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "ff"
@@ -912,20 +942,20 @@ dependencies = [
[[package]]
name = "fiat-crypto"
-version = "0.1.20"
+version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e825f6987101665dea6ec934c09ec6d721de7bc1bf92248e1d5810c8cd636b77"
+checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7"
[[package]]
name = "filetime"
-version = "0.2.22"
+version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0"
+checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall 0.3.5",
- "windows-sys",
+ "redox_syscall",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -962,9 +992,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
[[package]]
name = "form_urlencoded"
-version = "1.2.0"
+version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652"
+checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456"
dependencies = [
"percent-encoding",
]
@@ -982,9 +1012,9 @@ dependencies = [
[[package]]
name = "getrandom"
-version = "0.2.10"
+version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
+checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
dependencies = [
"cfg-if",
"js-sys",
@@ -999,7 +1029,7 @@ version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbf97ba92db08df386e10c8ede66a2a0369bd277090afd8710e19e38de9ec0cd"
dependencies = [
- "bitflags 2.4.0",
+ "bitflags 2.4.1",
"libc",
"libgit2-sys",
"log",
@@ -1022,19 +1052,20 @@ dependencies = [
[[package]]
name = "gix"
-version = "0.55.2"
+version = "0.56.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "002667cd1ebb789313d0d0afe3d23b2821cf3b0e91605095f0e6d8751f0ceeea"
+checksum = "5b0dcdc9c60d66535897fa40a7ea2a635e72f99456b1d9ae86b7e170e80618cb"
dependencies = [
"gix-actor",
"gix-attributes",
+ "gix-command",
"gix-commitgraph",
"gix-config",
"gix-credentials",
"gix-date",
"gix-diff",
"gix-discover",
- "gix-features 0.36.0",
+ "gix-features 0.36.1",
"gix-filter",
"gix-fs",
"gix-glob",
@@ -1076,23 +1107,23 @@ dependencies = [
[[package]]
name = "gix-actor"
-version = "0.28.0"
+version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "948a5f9e43559d16faf583694f1c742eb401ce24ce8e6f2238caedea7486433c"
+checksum = "2eadca029ef716b4378f7afb19f7ee101fde9e58ba1f1445971315ac866db417"
dependencies = [
"bstr",
"btoi",
"gix-date",
- "itoa 1.0.6",
+ "itoa 1.0.9",
"thiserror",
"winnow",
]
[[package]]
name = "gix-attributes"
-version = "0.20.0"
+version = "0.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dca120f0c6562d2d7cae467f2466e576d9f7f189beec2af2e026145107c729e2"
+checksum = "0f395469d38c76ec47cd1a6c5a53fbc3f13f737b96eaf7535f4e6b367e643381"
dependencies = [
"bstr",
"gix-glob",
@@ -1107,40 +1138,43 @@ dependencies = [
[[package]]
name = "gix-bitmap"
-version = "0.2.7"
+version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ccab4bc576844ddb51b78d81b4a42d73e6229660fa614dfc3d3999c874d1959"
+checksum = "d49e1a13a30d3f88be4bceae184dd13a2d3fb9ffa7515f7ed7ae771b857f4916"
dependencies = [
"thiserror",
]
[[package]]
name = "gix-chunk"
-version = "0.4.4"
+version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b42ea64420f7994000130328f3c7a2038f639120518870436d31b8bde704493"
+checksum = "d411ecd9b558b0c20b3252b7e409eec48eabc41d18324954fe526bac6e2db55f"
dependencies = [
"thiserror",
]
[[package]]
name = "gix-command"
-version = "0.2.10"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c576cfbf577f72c097b5f88aedea502cd62952bdc1fb3adcab4531d5525a4c7"
+checksum = "b3b54c1d8d63e6ef2adbd9b94d6e49ff168858510b44d3811cdd02dfacc4f0c9"
dependencies = [
"bstr",
+ "gix-path",
+ "gix-trace",
+ "shell-words",
]
[[package]]
name = "gix-commitgraph"
-version = "0.22.0"
+version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e8bc78b1a6328fa6d8b3a53b6c73997af37fd6bfc1d6c49f149e63bda5cbb36"
+checksum = "85a7007ba021f059803afaf6f8a48872422abc20550ac12ede6ddea2936cec36"
dependencies = [
"bstr",
"gix-chunk",
- "gix-features 0.36.0",
+ "gix-features 0.36.1",
"gix-hash",
"memmap2",
"thiserror",
@@ -1148,13 +1182,13 @@ dependencies = [
[[package]]
name = "gix-config"
-version = "0.31.0"
+version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cae98c6b4c66c09379bc35274b172587d6b0ac369a416c39128ad8c6454f9bb"
+checksum = "0341471d55d8676e98b88e121d7065dfa4c9c5acea4b6d6ecdd2846e85cce0c3"
dependencies = [
"bstr",
"gix-config-value",
- "gix-features 0.36.0",
+ "gix-features 0.36.1",
"gix-glob",
"gix-path",
"gix-ref",
@@ -1169,11 +1203,11 @@ dependencies = [
[[package]]
name = "gix-config-value"
-version = "0.14.0"
+version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea7505b97f4d8e7933e29735a568ba2f86d8de466669d9f0e8321384f9972f47"
+checksum = "6419db582ea84dfb58c7e7b0af7fd62c808aa14954af2936a33f89b0f4ed018e"
dependencies = [
- "bitflags 2.4.0",
+ "bitflags 2.4.1",
"bstr",
"gix-path",
"libc",
@@ -1182,9 +1216,9 @@ dependencies = [
[[package]]
name = "gix-credentials"
-version = "0.21.0"
+version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c5c5d74069b842a1861e581027ac6b7ad9ff66f5911c89b9f45484d7ebda6a4"
+checksum = "513dac42450b27946bd0a0535a3a5a88e473d6522e5e3439a129cab779c88f3d"
dependencies = [
"bstr",
"gix-command",
@@ -1192,28 +1226,30 @@ dependencies = [
"gix-path",
"gix-prompt",
"gix-sec",
+ "gix-trace",
"gix-url",
"thiserror",
]
[[package]]
name = "gix-date"
-version = "0.8.0"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc7df669639582dc7c02737642f76890b03b5544e141caba68a7d6b4eb551e0d"
+checksum = "468dfbe411f335f01525a1352271727f8e7772075a93fa747260f502086b30be"
dependencies = [
"bstr",
- "itoa 1.0.6",
+ "itoa 1.0.9",
"thiserror",
"time",
]
[[package]]
name = "gix-diff"
-version = "0.37.0"
+version = "0.38.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "931394f69fb8c9ed6afc0aae3487bd869e936339bcc13ed8884472af072e0554"
+checksum = "8119a985887cfe68f4bdf92e51bd64bc758a73882d82fcfc03ebcb164441c85d"
dependencies = [
+ "bstr",
"gix-hash",
"gix-object",
"thiserror",
@@ -1221,9 +1257,9 @@ dependencies = [
[[package]]
name = "gix-discover"
-version = "0.26.0"
+version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a45d5cf0321178883e38705ab2b098f625d609a7d4c391b33ac952eff2c490f2"
+checksum = "6fad89416ebe0b3b7df78464124e2a02417b6cd3743d48ad93df86f4d2929c07"
dependencies = [
"bstr",
"dunce",
@@ -1249,9 +1285,9 @@ dependencies = [
[[package]]
name = "gix-features"
-version = "0.36.0"
+version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51f4365ba17c4f218d7fd9ec102b8d2d3cb0ca200a835e81151ace7778aec827"
+checksum = "4d46a4a5c6bb5bebec9c0d18b65ada20e6517dbd7cf855b87dd4bbdce3a771b2"
dependencies = [
"bytes",
"crc32fast",
@@ -1268,9 +1304,9 @@ dependencies = [
[[package]]
name = "gix-filter"
-version = "0.6.0"
+version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92f674d3fdb6b1987b04521ec9a5b7be8650671f2c4bbd17c3c81e2a364242ff"
+checksum = "6d6a5c9d8e55c364e7c226919c19c9a28be1392d6208b5008059fa94ff7e2bf0"
dependencies = [
"bstr",
"encoding_rs",
@@ -1282,46 +1318,47 @@ dependencies = [
"gix-path",
"gix-quote",
"gix-trace",
+ "gix-utils",
"smallvec",
"thiserror",
]
[[package]]
name = "gix-fs"
-version = "0.8.0"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cd171c0cae97cd0dc57e7b4601cb1ebf596450e263ef3c02be9107272c877bd"
+checksum = "20e86eb040f5776a5ade092282e51cdcad398adb77d948b88d17583c2ae4e107"
dependencies = [
- "gix-features 0.36.0",
+ "gix-features 0.36.1",
]
[[package]]
name = "gix-glob"
-version = "0.14.0"
+version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fac08925dbc14d414bd02eb45ffb4cecd912d1fce3883f867bd0103c192d3e4"
+checksum = "5db19298c5eeea2961e5b3bf190767a2d1f09b8802aeb5f258e42276350aff19"
dependencies = [
- "bitflags 2.4.0",
+ "bitflags 2.4.1",
"bstr",
- "gix-features 0.36.0",
+ "gix-features 0.36.1",
"gix-path",
]
[[package]]
name = "gix-hash"
-version = "0.13.1"
+version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1884c7b41ea0875217c1be9ce91322f90bde433e91d374d0e1276073a51ccc60"
+checksum = "1f8cf8c2266f63e582b7eb206799b63aa5fa68ee510ad349f637dfe2d0653de0"
dependencies = [
- "faster-hex",
+ "faster-hex 0.9.0",
"thiserror",
]
[[package]]
name = "gix-hashtable"
-version = "0.4.0"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "409268480841ad008e81c17ca5a293393fbf9f2b6c2f85b8ab9de1f0c5176a16"
+checksum = "feb61880816d7ec4f0b20606b498147d480860ddd9133ba542628df2f548d3ca"
dependencies = [
"gix-hash",
"hashbrown",
@@ -1330,9 +1367,9 @@ dependencies = [
[[package]]
name = "gix-ignore"
-version = "0.9.0"
+version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e73c07763a8005ae02cb5cf83040729cea9bb70c7cef68ec6c24159904c499a"
+checksum = "a215cc8cf21645bca131fcf6329d3ebd46299c47dbbe27df71bb1ca9e328b879"
dependencies = [
"bstr",
"gix-glob",
@@ -1342,32 +1379,34 @@ dependencies = [
[[package]]
name = "gix-index"
-version = "0.26.0"
+version = "0.27.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c83a4fcc121b2f2e109088f677f89f85e7a8ebf39e8e6659c0ae54d4283b1650"
+checksum = "f3f308f5cd2992e96a274b0d1931e9a0e44fdcba87695ead3f6df30d8a697e9c"
dependencies = [
- "bitflags 2.4.0",
+ "bitflags 2.4.1",
"bstr",
"btoi",
"filetime",
"gix-bitmap",
- "gix-features 0.36.0",
+ "gix-features 0.36.1",
"gix-fs",
"gix-hash",
"gix-lock",
"gix-object",
"gix-traverse",
- "itoa 1.0.6",
+ "itoa 1.0.9",
+ "libc",
"memmap2",
+ "rustix",
"smallvec",
"thiserror",
]
[[package]]
name = "gix-lock"
-version = "11.0.0"
+version = "11.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4feb1dcd304fe384ddc22edba9dd56a42b0800032de6537728cea2f033a4f37"
+checksum = "7e5c65e6a29830a435664891ced3f3c1af010f14900226019590ee0971a22f37"
dependencies = [
"gix-tempfile",
"gix-utils",
@@ -1376,22 +1415,22 @@ dependencies = [
[[package]]
name = "gix-macros"
-version = "0.1.0"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d8acb5ee668d55f0f2d19a320a3f9ef67a6999ad483e11135abcc2464ed18b6"
+checksum = "02a5bcaf6704d9354a3071cede7e77d366a5980c7352e102e2c2f9b645b1d3ae"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.38",
+ "syn 2.0.39",
]
[[package]]
name = "gix-negotiate"
-version = "0.9.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a5cdcf491ecc9ce39dcc227216c540355fe0024ae7c38e94557752ca5ebb67f"
+checksum = "979f6accd9c051b3dd018b50adf29c0a2459edddf6105cc70b767976cd6f8014"
dependencies = [
- "bitflags 2.4.0",
+ "bitflags 2.4.1",
"gix-commitgraph",
"gix-date",
"gix-hash",
@@ -1403,18 +1442,18 @@ dependencies = [
[[package]]
name = "gix-object"
-version = "0.38.0"
+version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "740f2a44267f58770a1cb3a3d01d14e67b089c7136c48d4bddbb3cfd2bf86a51"
+checksum = "febf79c5825720c1c63fe974c7bbe695d0cb54aabad73f45671c60ce0e501e33"
dependencies = [
"bstr",
"btoi",
"gix-actor",
"gix-date",
- "gix-features 0.36.0",
+ "gix-features 0.36.1",
"gix-hash",
"gix-validate",
- "itoa 1.0.6",
+ "itoa 1.0.9",
"smallvec",
"thiserror",
"winnow",
@@ -1422,13 +1461,13 @@ dependencies = [
[[package]]
name = "gix-odb"
-version = "0.54.0"
+version = "0.55.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8630b56cb80d8fa684d383dad006a66401ee8314e12fbf0e566ddad8c115143b"
+checksum = "1fae5f971540c99c6ecc8d4368ecc9d18a9dc8b9391025c68c4399747dc93bac"
dependencies = [
"arc-swap",
"gix-date",
- "gix-features 0.36.0",
+ "gix-features 0.36.1",
"gix-hash",
"gix-object",
"gix-pack",
@@ -1441,13 +1480,13 @@ dependencies = [
[[package]]
name = "gix-pack"
-version = "0.44.0"
+version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1431ba2e30deff1405920693d54ab231c88d7c240dd6ccc936ee223d8f8697c3"
+checksum = "4569491c92446fddf373456ff360aff9a9effd627b40a70f2d7914dcd75a3205"
dependencies = [
"clru",
"gix-chunk",
- "gix-features 0.36.0",
+ "gix-features 0.36.1",
"gix-hash",
"gix-hashtable",
"gix-object",
@@ -1461,31 +1500,33 @@ dependencies = [
[[package]]
name = "gix-packetline"
-version = "0.16.7"
+version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a8384b1e964151aff0d5632dd9b191059d07dff358b96bd940f1b452600d7ab"
+checksum = "03b9fcc4425bd64c585440d14e5d2405a399f323429401571ba56a2c6d111865"
dependencies = [
"bstr",
- "faster-hex",
+ "faster-hex 0.8.1",
+ "gix-trace",
"thiserror",
]
[[package]]
name = "gix-packetline-blocking"
-version = "0.16.6"
+version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d8395f7501c84d6a1fe902035fdfd8cd86d89e2dd6be0200ec1a72fd3c92d39"
+checksum = "50052c0f76c5af5acda41177fb55b60c1e484cc246ae919d8d21129cd1000a4e"
dependencies = [
"bstr",
- "faster-hex",
+ "faster-hex 0.8.1",
+ "gix-trace",
"thiserror",
]
[[package]]
name = "gix-path"
-version = "0.10.0"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a1d370115171e3ae03c5c6d4f7d096f2981a40ddccb98dfd704c773530ba73b"
+checksum = "d86d6fac2fabe07b67b7835f46d07571f68b11aa1aaecae94fe722ea4ef305e1"
dependencies = [
"bstr",
"gix-trace",
@@ -1496,11 +1537,11 @@ dependencies = [
[[package]]
name = "gix-pathspec"
-version = "0.4.0"
+version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9cc7194fdcf43b4a1ccfa13ffae1d79f83beb4becff7761d88dd99faeafe625"
+checksum = "1dbbb92f75a38ef043c8bb830b339b38d0698d7f3746968b5fcbade7a880494d"
dependencies = [
- "bitflags 2.4.0",
+ "bitflags 2.4.1",
"bstr",
"gix-attributes",
"gix-config-value",
@@ -1511,9 +1552,9 @@ dependencies = [
[[package]]
name = "gix-prompt"
-version = "0.7.0"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c9a913769516f5e9d937afac206fb76428e3d7238e538845842887fda584678"
+checksum = "4967b921304a5355e65a6257280eddf6e0f9ce3df111256531460adca3771305"
dependencies = [
"gix-command",
"gix-config-value",
@@ -1524,15 +1565,15 @@ dependencies = [
[[package]]
name = "gix-protocol"
-version = "0.41.1"
+version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "391e3feabdfa5f90dad6673ce59e3291ac28901b2ff248d86c5a7fbde0391e0e"
+checksum = "95736ef407db0bd15a5bdea791fbfcf523b9f13b96c852c240cd86a9ee0ef817"
dependencies = [
"bstr",
"btoi",
"gix-credentials",
"gix-date",
- "gix-features 0.36.0",
+ "gix-features 0.36.1",
"gix-hash",
"gix-transport",
"maybe-async",
@@ -1542,9 +1583,9 @@ dependencies = [
[[package]]
name = "gix-quote"
-version = "0.4.7"
+version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "475c86a97dd0127ba4465fbb239abac9ea10e68301470c9791a6dd5351cdc905"
+checksum = "4f84845efa535468bc79c5a87b9d29219f1da0313c8ecf0365a5daa7e72786f2"
dependencies = [
"bstr",
"btoi",
@@ -1553,13 +1594,13 @@ dependencies = [
[[package]]
name = "gix-ref"
-version = "0.38.0"
+version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ec2f6d07ac88d2fb8007ee3fa3e801856fb9d82e7366ec0ca332eb2c9d74a52"
+checksum = "1ac23ed741583c792f573c028785db683496a6dfcd672ec701ee54ba6a77e1ff"
dependencies = [
"gix-actor",
"gix-date",
- "gix-features 0.36.0",
+ "gix-features 0.36.1",
"gix-fs",
"gix-hash",
"gix-lock",
@@ -1574,9 +1615,9 @@ dependencies = [
[[package]]
name = "gix-refspec"
-version = "0.19.0"
+version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccb0974cc41dbdb43a180c7f67aa481e1c1e160fcfa8f4a55291fd1126c1a6e7"
+checksum = "76d9d3b82e1ee78fc0dc1c37ea5ea76c2dbc73f407db155f0dfcea285e583bee"
dependencies = [
"bstr",
"gix-hash",
@@ -1588,9 +1629,9 @@ dependencies = [
[[package]]
name = "gix-revision"
-version = "0.23.0"
+version = "0.24.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2ca97ac73459a7f3766aa4a5638a6e37d56d4c7962bc1986fbaf4883d0772588"
+checksum = "fe5dd51710ce5434bc315ea30394fab483c5377276494edd79222b321a5a9544"
dependencies = [
"bstr",
"gix-date",
@@ -1604,9 +1645,9 @@ dependencies = [
[[package]]
name = "gix-revwalk"
-version = "0.9.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a16d8c892e4cd676d86f0265bf9d40cefd73d8d94f86b213b8b77d50e77efae0"
+checksum = "69d4ed2493ca94a475fdf147138e1ef8bab3b6ebb56abf3d9bda1c05372ec1dd"
dependencies = [
"gix-commitgraph",
"gix-date",
@@ -1619,11 +1660,11 @@ dependencies = [
[[package]]
name = "gix-sec"
-version = "0.10.0"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "92b9542ac025a8c02ed5d17b3fc031a111a384e859d0be3532ec4d58c40a0f28"
+checksum = "a36ea2c5907d64a9b4b5d3cc9f430e6c30f0509646b5e38eb275ca57c5bf29e2"
dependencies = [
- "bitflags 2.4.0",
+ "bitflags 2.4.1",
"gix-path",
"libc",
"windows",
@@ -1631,9 +1672,9 @@ dependencies = [
[[package]]
name = "gix-submodule"
-version = "0.5.0"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bba78c8d12aa24370178453ec3a472ff08dfaa657d116229f57f2c9cd469a1c2"
+checksum = "02a3d7f60a95bdcaeb8981663c99d1c9f4de42aab1169524c949e948989809f9"
dependencies = [
"bstr",
"gix-config",
@@ -1646,9 +1687,9 @@ dependencies = [
[[package]]
name = "gix-tempfile"
-version = "11.0.0"
+version = "11.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05cc2205cf10d99f70b96e04e16c55d4c7cf33efc151df1f793e29fd12a931f8"
+checksum = "388dd29114a86ec69b28d1e26d6d63a662300ecf61ab3f4cc578f7d7dc9e7e23"
dependencies = [
"gix-fs",
"libc",
@@ -1659,22 +1700,22 @@ dependencies = [
[[package]]
name = "gix-trace"
-version = "0.1.3"
+version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96b6d623a1152c3facb79067d6e2ecdae48130030cf27d6eb21109f13bd7b836"
+checksum = "b686a35799b53a9825575ca3f06481d0a053a409c4d97ffcf5ddd67a8760b497"
[[package]]
name = "gix-transport"
-version = "0.38.0"
+version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f209a93364e24f20319751bc11092272e2f3fe82bb72592b2822679cf5be752"
+checksum = "f731cfefc4d62468c6dd2053f5c6707828256a6d2f5488c1811e3f42c178b144"
dependencies = [
"base64",
"bstr",
"curl",
"gix-command",
"gix-credentials",
- "gix-features 0.36.0",
+ "gix-features 0.36.1",
"gix-packetline",
"gix-quote",
"gix-sec",
@@ -1684,9 +1725,9 @@ dependencies = [
[[package]]
name = "gix-traverse"
-version = "0.34.0"
+version = "0.35.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "14d050ec7d4e1bb76abf0636cf4104fb915b70e54e3ced9a4427c999100ff38a"
+checksum = "df2112088122a0206592c84fbd42020db63b2ccaed66a0293779f2e5fbf80474"
dependencies = [
"gix-commitgraph",
"gix-date",
@@ -1700,12 +1741,12 @@ dependencies = [
[[package]]
name = "gix-url"
-version = "0.25.1"
+version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1b9ac8ed32ad45f9fc6c5f8c0be2ed911e544a5a19afd62d95d524ebaa95671"
+checksum = "0c427a1a11ccfa53a4a2da47d9442c2241deee63a154bc15cc14b8312fbc4005"
dependencies = [
"bstr",
- "gix-features 0.36.0",
+ "gix-features 0.36.1",
"gix-path",
"home 0.5.5",
"thiserror",
@@ -1714,18 +1755,18 @@ dependencies = [
[[package]]
name = "gix-utils"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b85d89dc728613e26e0ed952a19583744e7f5240fcd4aa30d6c824ffd8b52f0f"
+checksum = "9f82c41937f00e15a1f6cb0b55307f0ca1f77f4407ff2bf440be35aa688c6a3e"
dependencies = [
"fastrand",
]
[[package]]
name = "gix-validate"
-version = "0.8.0"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e05cab2b03a45b866156e052aa38619f4ece4adcb2f79978bfc249bc3b21b8c5"
+checksum = "75b7d8e4274be69f284bbc7e6bb2ccf7065dbcdeba22d8c549f2451ae426883f"
dependencies = [
"bstr",
"thiserror",
@@ -1733,13 +1774,13 @@ dependencies = [
[[package]]
name = "gix-worktree"
-version = "0.27.0"
+version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddaf79e721dba64fe726a42f297a3c8ed42e55cdc0d81ca68452f2def3c2d7fd"
+checksum = "7f1d0ae01dee14abe8c8117d78d7518f9a507de2dc4522546fbf4c444e9860b4"
dependencies = [
"bstr",
"gix-attributes",
- "gix-features 0.36.0",
+ "gix-features 0.36.1",
"gix-fs",
"gix-glob",
"gix-hash",
@@ -1757,15 +1798,15 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "globset"
-version = "0.4.13"
+version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d"
+checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1"
dependencies = [
"aho-corasick",
"bstr",
- "fnv",
"log",
- "regex",
+ "regex-automata 0.4.3",
+ "regex-syntax 0.8.2",
]
[[package]]
@@ -1802,24 +1843,28 @@ dependencies = [
[[package]]
name = "hashbrown"
-version = "0.14.0"
+version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
+dependencies = [
+ "ahash",
+ "allocator-api2",
+]
[[package]]
-name = "hermit-abi"
-version = "0.2.6"
+name = "hashlink"
+version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
+checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
dependencies = [
- "libc",
+ "hashbrown",
]
[[package]]
name = "hermit-abi"
-version = "0.3.2"
+version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
+checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
[[package]]
name = "hex"
@@ -1851,14 +1896,14 @@ version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb"
dependencies = [
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
name = "home"
-version = "0.5.8"
+version = "0.5.9"
dependencies = [
- "windows-sys",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -1878,9 +1923,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "idna"
-version = "0.4.0"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c"
+checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
dependencies = [
"unicode-bidi",
"unicode-normalization",
@@ -1888,17 +1933,16 @@ dependencies = [
[[package]]
name = "ignore"
-version = "0.4.20"
+version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492"
+checksum = "747ad1b4ae841a78e8aba0d63adbfbeaea26b517b63705d47856b73015d27060"
dependencies = [
+ "crossbeam-deque",
"globset",
- "lazy_static",
"log",
"memchr",
- "regex",
+ "regex-automata 0.4.3",
"same-file",
- "thread_local",
"walkdir",
"winapi-util",
]
@@ -1919,9 +1963,9 @@ dependencies = [
[[package]]
name = "indexmap"
-version = "2.0.0"
+version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
+checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f"
dependencies = [
"equivalent",
"hashbrown",
@@ -1933,9 +1977,9 @@ version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [
- "hermit-abi 0.3.2",
+ "hermit-abi",
"rustix",
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -1949,9 +1993,9 @@ dependencies = [
[[package]]
name = "itertools"
-version = "0.11.0"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57"
+checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
@@ -1964,9 +2008,9 @@ checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "itoa"
-version = "1.0.6"
+version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
+checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "jobserver"
@@ -1979,9 +2023,9 @@ dependencies = [
[[package]]
name = "js-sys"
-version = "0.3.64"
+version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a"
+checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca"
dependencies = [
"wasm-bindgen",
]
@@ -2015,9 +2059,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
-version = "0.2.149"
+version = "0.2.150"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
+checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c"
[[package]]
name = "libgit2-sys"
@@ -2040,26 +2084,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
dependencies = [
"cfg-if",
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
name = "libm"
-version = "0.2.7"
+version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4"
+checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058"
[[package]]
name = "libnghttp2-sys"
-version = "0.1.7+1.45.0"
+version = "0.1.8+1.55.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "57ed28aba195b38d5ff02b9170cbff627e336a20925e43b4945390401c5dc93f"
+checksum = "4fae956c192dadcdb5dace96db71fa0b827333cce7c7b38dc71446f024d8a340"
dependencies = [
"cc",
"libc",
]
[[package]]
+name = "libsqlite3-sys"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716"
+dependencies = [
+ "cc",
+ "pkg-config",
+ "vcpkg",
+]
+
+[[package]]
name = "libssh2-sys"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2075,9 +2130,9 @@ dependencies = [
[[package]]
name = "libz-sys"
-version = "1.1.9"
+version = "1.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db"
+checksum = "d97137b25e321a73eef1418d1d5d2eda4d77e12813f8e6dead84bc52c5870a7b"
dependencies = [
"cc",
"libc",
@@ -2087,15 +2142,15 @@ dependencies = [
[[package]]
name = "linux-raw-sys"
-version = "0.4.10"
+version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f"
+checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
[[package]]
name = "lock_api"
-version = "0.4.10"
+version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
+checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45"
dependencies = [
"autocfg",
"scopeguard",
@@ -2103,9 +2158,9 @@ dependencies = [
[[package]]
name = "log"
-version = "0.4.19"
+version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
+checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "matchers"
@@ -2133,10 +2188,10 @@ version = "0.0.0"
dependencies = [
"anyhow",
"handlebars",
- "pretty_assertions",
"pulldown-cmark",
"same-file",
"serde_json",
+ "snapbox",
"url",
]
@@ -2148,9 +2203,9 @@ checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "memmap2"
-version = "0.7.1"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6"
+checksum = "deaba38d7abf1d4cca21cc89e932e542ba2b9258664d2a9ef0e61512039c9375"
dependencies = [
"libc",
]
@@ -2185,7 +2240,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "359f76430b20a79f9e20e115b3428614e654f04fab314482fc0fda0ebd3c6044"
dependencies = [
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -2210,7 +2265,7 @@ version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec60c60a693226186f5d6edf073232bfb6464ed97eb22cf3b01c1e8198fd97f5"
dependencies = [
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -2225,25 +2280,15 @@ dependencies = [
[[package]]
name = "num-traits"
-version = "0.2.15"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
+checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c"
dependencies = [
"autocfg",
"libm",
]
[[package]]
-name = "num_cpus"
-version = "1.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
-dependencies = [
- "hermit-abi 0.2.6",
- "libc",
-]
-
-[[package]]
name = "num_threads"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2281,7 +2326,7 @@ version = "0.10.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c"
dependencies = [
- "bitflags 2.4.0",
+ "bitflags 2.4.1",
"cfg-if",
"foreign-types",
"libc",
@@ -2298,7 +2343,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.38",
+ "syn 2.0.39",
]
[[package]]
@@ -2309,9 +2354,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-src"
-version = "111.26.0+1.1.1u"
+version = "111.28.1+1.1.1w"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efc62c9f12b22b8f5208c23a7200a442b2e5999f8bdf80233852122b5a4f6f37"
+checksum = "4bf7e82ffd6d3d6e6524216a0bfd85509f68b5b28354e8e7800057e44cefa9b4"
dependencies = [
"cc",
]
@@ -2331,18 +2376,18 @@ dependencies = [
[[package]]
name = "ordered-float"
-version = "2.10.0"
+version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7940cf2ca942593318d07fcf2596cdca60a85c9e7fab408a5e21a4f9dcd40d87"
+checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
dependencies = [
"num-traits",
]
[[package]]
name = "orion"
-version = "0.17.4"
+version = "0.17.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbe74a766292f94f7e69db5a7bf010eadd944f24186c463fe578a7e637582066"
+checksum = "7abdb10181903c8c4b016ba45d6d6d5af1a1e2a461aa4763a83b87f5df4695e5"
dependencies = [
"fiat-crypto",
"subtle",
@@ -2390,15 +2435,15 @@ dependencies = [
[[package]]
name = "parking_lot_core"
-version = "0.9.8"
+version = "0.9.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447"
+checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e"
dependencies = [
"cfg-if",
"libc",
- "redox_syscall 0.3.5",
+ "redox_syscall",
"smallvec",
- "windows-targets",
+ "windows-targets 0.48.5",
]
[[package]]
@@ -2459,25 +2504,26 @@ dependencies = [
[[package]]
name = "percent-encoding"
-version = "2.3.0"
+version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94"
+checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
[[package]]
name = "pest"
-version = "2.6.0"
+version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70"
+checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5"
dependencies = [
+ "memchr",
"thiserror",
"ucd-trie",
]
[[package]]
name = "pest_derive"
-version = "2.6.0"
+version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb"
+checksum = "81d78524685f5ef2a3b3bd1cafbc9fcabb036253d9b1463e726a91cd16e2dfc2"
dependencies = [
"pest",
"pest_generator",
@@ -2485,22 +2531,22 @@ dependencies = [
[[package]]
name = "pest_generator"
-version = "2.6.0"
+version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e"
+checksum = "68bd1206e71118b5356dae5ddc61c8b11e28b09ef6a31acbd15ea48a28e0c227"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
- "syn 2.0.38",
+ "syn 2.0.39",
]
[[package]]
name = "pest_meta"
-version = "2.6.0"
+version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411"
+checksum = "7c747191d4ad9e4a4ab9c8798f1e82a39affe7ef9648390b7e5548d18e099de6"
dependencies = [
"once_cell",
"pest",
@@ -2509,9 +2555,9 @@ dependencies = [
[[package]]
name = "pin-project-lite"
-version = "0.2.10"
+version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57"
+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
[[package]]
name = "pkcs8"
@@ -2531,9 +2577,9 @@ checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964"
[[package]]
name = "plotters"
-version = "0.3.4"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97"
+checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45"
dependencies = [
"num-traits",
"plotters-backend",
@@ -2544,49 +2590,45 @@ dependencies = [
[[package]]
name = "plotters-backend"
-version = "0.3.4"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"
+checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609"
[[package]]
name = "plotters-svg"
-version = "0.3.3"
+version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f"
+checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab"
dependencies = [
"plotters-backend",
]
[[package]]
-name = "ppv-lite86"
-version = "0.2.17"
+name = "powerfmt"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
[[package]]
-name = "pretty_assertions"
-version = "1.4.0"
+name = "ppv-lite86"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66"
-dependencies = [
- "diff",
- "yansi",
-]
+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]]
name = "primeorder"
-version = "0.13.2"
+version = "0.13.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c2fcef82c0ec6eefcc179b978446c399b3cdf73c392c35604e399eee6df1ee3"
+checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
dependencies = [
"elliptic-curve",
]
[[package]]
name = "proc-macro2"
-version = "1.0.67"
+version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
+checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
dependencies = [
"unicode-ident",
]
@@ -2602,19 +2644,19 @@ dependencies = [
[[package]]
name = "proptest"
-version = "1.3.1"
+version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c003ac8c77cb07bb74f5f198bce836a689bcd5a42574612bf14d17bfd08c20e"
+checksum = "31b476131c3c86cb68032fdc5cb6d5a1045e3e42d96b69fa599fd77701e1f5bf"
dependencies = [
"bit-set",
"bit-vec",
- "bitflags 2.4.0",
+ "bitflags 2.4.1",
"lazy_static",
"num-traits",
"rand",
"rand_chacha",
"rand_xorshift",
- "regex-syntax 0.7.2",
+ "regex-syntax 0.8.2",
"rusty-fork",
"tempfile",
"unarray",
@@ -2645,9 +2687,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quote"
-version = "1.0.32"
+version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965"
+checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
@@ -2702,9 +2744,9 @@ dependencies = [
[[package]]
name = "rayon"
-version = "1.7.0"
+version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
+checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
@@ -2712,23 +2754,12 @@ dependencies = [
[[package]]
name = "rayon-core"
-version = "1.11.0"
+version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
+checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
- "crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
- "num_cpus",
-]
-
-[[package]]
-name = "redox_syscall"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
-dependencies = [
- "bitflags 1.3.2",
]
[[package]]
@@ -2742,13 +2773,14 @@ dependencies = [
[[package]]
name = "regex"
-version = "1.8.4"
+version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f"
+checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343"
dependencies = [
"aho-corasick",
"memchr",
- "regex-syntax 0.7.2",
+ "regex-automata 0.4.3",
+ "regex-syntax 0.8.2",
]
[[package]]
@@ -2762,9 +2794,14 @@ dependencies = [
[[package]]
name = "regex-automata"
-version = "0.3.8"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795"
+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax 0.8.2",
+]
[[package]]
name = "regex-syntax"
@@ -2774,9 +2811,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "regex-syntax"
-version = "0.7.2"
+version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78"
+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "resolver-tests"
@@ -2799,6 +2836,20 @@ dependencies = [
]
[[package]]
+name = "rusqlite"
+version = "0.30.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d"
+dependencies = [
+ "bitflags 2.4.1",
+ "fallible-iterator",
+ "fallible-streaming-iterator",
+ "hashlink",
+ "libsqlite3-sys",
+ "smallvec",
+]
+
+[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2806,27 +2857,30 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustfix"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ecd2853d9e26988467753bd9912c3a126f642d05d229a4b53f5752ee36c56481"
+version = "0.7.0"
dependencies = [
"anyhow",
- "log",
+ "proptest",
"serde",
"serde_json",
+ "similar",
+ "tempfile",
+ "thiserror",
+ "tracing",
+ "tracing-subscriber",
]
[[package]]
name = "rustix"
-version = "0.38.21"
+version = "0.38.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3"
+checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a"
dependencies = [
- "bitflags 2.4.0",
+ "bitflags 2.4.1",
"errno",
"libc",
"linux-raw-sys",
- "windows-sys",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -2843,9 +2897,9 @@ dependencies = [
[[package]]
name = "ryu"
-version = "1.0.13"
+version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
+checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "same-file"
@@ -2862,20 +2916,20 @@ version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88"
dependencies = [
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
name = "scopeguard"
-version = "1.1.0"
+version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sec1"
-version = "0.7.2"
+version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0aec48e813d6b90b15f0b8948af3c63483992dee44c03e9930b3eebdabe046e"
+checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
dependencies = [
"base16ct",
"der",
@@ -2900,9 +2954,9 @@ dependencies = [
[[package]]
name = "security-framework-sys"
-version = "2.9.0"
+version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7"
+checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a"
dependencies = [
"core-foundation-sys",
"libc",
@@ -2926,9 +2980,9 @@ dependencies = [
[[package]]
name = "serde"
-version = "1.0.190"
+version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7"
+checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
dependencies = [
"serde_derive",
]
@@ -2955,13 +3009,13 @@ dependencies = [
[[package]]
name = "serde_derive"
-version = "1.0.190"
+version = "1.0.193"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3"
+checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.38",
+ "syn 2.0.39",
]
[[package]]
@@ -2979,7 +3033,7 @@ version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
- "itoa 1.0.6",
+ "itoa 1.0.9",
"ryu",
"serde",
]
@@ -3023,9 +3077,9 @@ dependencies = [
[[package]]
name = "sharded-slab"
-version = "0.1.4"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
dependencies = [
"lazy_static",
]
@@ -3037,10 +3091,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45bb67a18fa91266cc7807181f62f9178a6873bfad7dc788c42e6430db40184f"
[[package]]
+name = "shell-words"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
+
+[[package]]
name = "signature"
-version = "2.1.0"
+version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e1788eed21689f9cf370582dfc467ef36ed9c707f073528ddafa8d83e3b8500"
+checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
dependencies = [
"digest",
"rand_core",
@@ -3048,9 +3108,9 @@ dependencies = [
[[package]]
name = "similar"
-version = "2.2.1"
+version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf"
+checksum = "2aeaf503862c419d66959f5d7ca015337d864e9c49485d771b732e2a20453597"
[[package]]
name = "sized-chunks"
@@ -3064,9 +3124,9 @@ dependencies = [
[[package]]
name = "smallvec"
-version = "1.10.0"
+version = "1.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
+checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970"
[[package]]
name = "snapbox"
@@ -3098,9 +3158,9 @@ dependencies = [
[[package]]
name = "socket2"
-version = "0.4.9"
+version = "0.4.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662"
+checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d"
dependencies = [
"libc",
"winapi",
@@ -3108,9 +3168,9 @@ dependencies = [
[[package]]
name = "spki"
-version = "0.7.2"
+version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a"
+checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
dependencies = [
"base64ct",
"der",
@@ -3156,9 +3216,9 @@ dependencies = [
[[package]]
name = "syn"
-version = "2.0.38"
+version = "2.0.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
+checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
dependencies = [
"proc-macro2",
"quote",
@@ -3195,9 +3255,9 @@ checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5"
dependencies = [
"cfg-if",
"fastrand",
- "redox_syscall 0.4.1",
+ "redox_syscall",
"rustix",
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -3207,7 +3267,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
dependencies = [
"rustix",
- "windows-sys",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -3227,7 +3287,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.38",
+ "syn 2.0.39",
]
[[package]]
@@ -3242,14 +3302,15 @@ dependencies = [
[[package]]
name = "time"
-version = "0.3.29"
+version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe"
+checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5"
dependencies = [
"deranged",
- "itoa 1.0.6",
+ "itoa 1.0.9",
"libc",
"num_threads",
+ "powerfmt",
"serde",
"time-core",
"time-macros",
@@ -3297,9 +3358,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
-version = "0.8.6"
+version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ff9e3abce27ee2c9a37f9ad37238c1bdd4e789c84ba37df76aa4d528f5072cc"
+checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35"
dependencies = [
"serde",
"serde_spanned",
@@ -3318,9 +3379,9 @@ dependencies = [
[[package]]
name = "toml_edit"
-version = "0.20.7"
+version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81"
+checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03"
dependencies = [
"indexmap",
"serde",
@@ -3348,7 +3409,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.38",
+ "syn 2.0.39",
]
[[package]]
@@ -3363,20 +3424,20 @@ dependencies = [
[[package]]
name = "tracing-log"
-version = "0.1.3"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78ddad33d2d10b1ed7eb9d1f518a5674713876e97e5bb9b7345a7984fbb4f922"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
dependencies = [
- "lazy_static",
"log",
+ "once_cell",
"tracing-core",
]
[[package]]
name = "tracing-subscriber"
-version = "0.3.17"
+version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77"
+checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b"
dependencies = [
"matchers",
"nu-ansi-term",
@@ -3392,15 +3453,15 @@ dependencies = [
[[package]]
name = "typenum"
-version = "1.16.0"
+version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
+checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "ucd-trie"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
+checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
[[package]]
name = "unarray"
@@ -3425,15 +3486,15 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]]
name = "unicode-bom"
-version = "2.0.2"
+version = "2.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "98e90c70c9f0d4d1ee6d0a7d04aa06cb9bbd53d8cfbdd62a0269a7c2eb640552"
+checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217"
[[package]]
name = "unicode-ident"
-version = "1.0.9"
+version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
+checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-normalization"
@@ -3458,9 +3519,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "url"
-version = "2.4.1"
+version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
+checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633"
dependencies = [
"form_urlencoded",
"idna",
@@ -3605,9 +3666,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
-version = "0.2.87"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342"
+checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -3615,24 +3676,24 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.87"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd"
+checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
- "syn 2.0.38",
+ "syn 2.0.39",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.87"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d"
+checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -3640,28 +3701,28 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.87"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b"
+checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.38",
+ "syn 2.0.39",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.87"
+version = "0.2.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1"
+checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
[[package]]
name = "web-sys"
-version = "0.3.64"
+version = "0.3.66"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b"
+checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -3685,9 +3746,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
-version = "0.1.5"
+version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
+checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
dependencies = [
"winapi",
]
@@ -3704,7 +3765,7 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
- "windows-targets",
+ "windows-targets 0.48.5",
]
[[package]]
@@ -3713,71 +3774,137 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
- "windows-targets",
+ "windows-targets 0.48.5",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
+dependencies = [
+ "windows-targets 0.52.0",
]
[[package]]
name = "windows-targets"
-version = "0.48.0"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
+dependencies = [
+ "windows_aarch64_gnullvm 0.48.5",
+ "windows_aarch64_msvc 0.48.5",
+ "windows_i686_gnu 0.48.5",
+ "windows_i686_msvc 0.48.5",
+ "windows_x86_64_gnu 0.48.5",
+ "windows_x86_64_gnullvm 0.48.5",
+ "windows_x86_64_msvc 0.48.5",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
+checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_aarch64_gnullvm 0.52.0",
+ "windows_aarch64_msvc 0.52.0",
+ "windows_i686_gnu 0.52.0",
+ "windows_i686_msvc 0.52.0",
+ "windows_x86_64_gnu 0.52.0",
+ "windows_x86_64_gnullvm 0.52.0",
+ "windows_x86_64_msvc 0.52.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
-version = "0.48.0"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
+checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
+checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
-version = "0.48.0"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
+checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
-version = "0.48.0"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
+checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
-version = "0.48.0"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
+checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
-version = "0.48.0"
+version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
+checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
-version = "0.48.0"
+version = "0.48.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
+checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "winnow"
-version = "0.5.15"
+version = "0.5.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc"
+checksum = "b7e87b8dfbe3baffbe687eef2e164e32286eff31a5ee16463ce03d991643ec94"
dependencies = [
"memchr",
]
@@ -3808,13 +3935,27 @@ dependencies = [
]
[[package]]
-name = "yansi"
-version = "0.5.1"
+name = "zerocopy"
+version = "0.7.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
+checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d"
+dependencies = [
+ "zerocopy-derive",
+]
+
+[[package]]
+name = "zerocopy-derive"
+version = "0.7.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.39",
+]
[[package]]
name = "zeroize"
-version = "1.6.0"
+version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
+checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
diff --git a/src/tools/cargo/Cargo.toml b/src/tools/cargo/Cargo.toml
index 3fb36b44e..c07b0047b 100644
--- a/src/tools/cargo/Cargo.toml
+++ b/src/tools/cargo/Cargo.toml
@@ -16,33 +16,33 @@ edition = "2021"
license = "MIT OR Apache-2.0"
[workspace.dependencies]
-anstream = "0.6.4"
+anstream = "0.6.5"
anstyle = "1.0.4"
anyhow = "1.0.75"
base64 = "0.21.5"
bytesize = "1.3"
cargo = { path = "" }
-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-credential = { version = "0.4.2", path = "credential/cargo-credential" }
+cargo-credential-libsecret = { version = "0.4.2", path = "credential/cargo-credential-libsecret" }
+cargo-credential-macos-keychain = { version = "0.4.2", path = "credential/cargo-credential-macos-keychain" }
+cargo-credential-wincred = { version = "0.4.2", 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.18.1"
-clap = "4.4.7"
+clap = "4.4.10"
color-print = "0.3.5"
-core-foundation = { version = "0.9.3", features = ["mac_os_10_7_support"] }
+core-foundation = { version = "0.9.4", 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"
+curl-sys = "0.4.70"
filetime = "0.2.22"
flate2 = { version = "1.0.28", default-features = false, features = ["zlib"] }
git2 = "0.18.1"
git2-curl = "0.19.0"
-gix = { version = "0.55.2", default-features = false, features = ["blocking-http-transport-curl", "progress-tree", "revision"] }
+gix = { version = "0.56.0", 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"] }
@@ -51,33 +51,34 @@ hmac = "0.12.1"
home = "0.5.5"
http-auth = { version = "0.1.8", default-features = false }
humantime = "2.1.0"
-ignore = "0.4.20"
+ignore = "0.4.21"
im-rc = "15.1.0"
indexmap = "2"
-itertools = "0.11.0"
+itertools = "0.12.0"
jobserver = "0.1.27"
lazycell = "1.3.0"
-libc = "0.2.149"
+libc = "0.2.150"
libgit2-sys = "0.16.1"
libloading = "0.8.1"
memchr = "2.6.4"
miow = "0.6.0"
opener = "0.6.1"
-openssl ="0.10.57"
+openssl = "0.10.57"
os_info = "3.7.0"
pasetors = { version = "0.6.7", features = ["v3", "paserk", "std", "serde"] }
pathdiff = "0.2"
percent-encoding = "2.3"
pkg-config = "0.3.27"
-pretty_assertions = "1.4.0"
-proptest = "1.3.1"
+proptest = "1.4.0"
pulldown-cmark = { version = "0.9.3", default-features = false }
rand = "0.8.5"
-rustfix = "0.6.1"
+regex = "1.10.2"
+rusqlite = { version = "0.30.0", features = ["bundled"] }
+rustfix = { version = "0.7.0", path = "crates/rustfix" }
same-file = "1.0.6"
security-framework = "2.9.2"
semver = { version = "1.0.20", features = ["serde"] }
-serde = "1.0.190"
+serde = "1.0.193"
serde-untagged = "0.1.1"
serde-value = "0.7.0"
serde_ignored = "0.1.9"
@@ -87,26 +88,40 @@ sha2 = "0.10.8"
shell-escape = "0.1.5"
supports-hyperlinks = "2.1.0"
snapbox = { version = "0.4.14", features = ["diff", "path"] }
-syn = { version = "2.0.38", features = ["extra-traits", "full"] }
+syn = { version = "2.0.39", features = ["extra-traits", "full"] }
tar = { version = "0.4.40", default-features = false }
tempfile = "3.8.1"
thiserror = "1.0.50"
time = { version = "0.3", features = ["parsing", "formatting", "serde"] }
-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"] }
+toml = "0.8.8"
+toml_edit = { version = "0.21.0", features = ["serde"] }
+tracing = "0.1.37" # be compatible with rustc_log: https://github.com/rust-lang/rust/blob/e51e98dde6a/compiler/rustc_log/Cargo.toml#L9
+tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
unicase = "2.7.0"
unicode-width = "0.1.11"
unicode-xid = "0.2.4"
-url = "2.4.1"
+url = "2.5.0"
varisat = "0.2.2"
walkdir = "2.4.0"
-windows-sys = "0.48"
+windows-sys = "0.52"
+
+[workspace.lints.rust]
+rust_2018_idioms = "warn" # TODO: could this be removed?
+
+[workspace.lints.rustdoc]
+private_intra_doc_links = "allow"
+
+[workspace.lints.clippy]
+all = { level = "allow", priority = -1 }
+dbg_macro = "warn"
+disallowed_methods = "warn"
+print_stderr = "warn"
+print_stdout = "warn"
+self_named_module_files = "warn"
[package]
name = "cargo"
-version = "0.76.0"
+version = "0.77.0"
edition.workspace = true
license.workspace = true
rust-version.workspace = true
@@ -162,6 +177,8 @@ pasetors.workspace = true
pathdiff.workspace = true
pulldown-cmark.workspace = true
rand.workspace = true
+regex.workspace = true
+rusqlite.workspace = true
rustfix.workspace = true
semver.workspace = true
serde = { workspace = true, features = ["derive"] }
@@ -229,4 +246,7 @@ doc = false
vendored-openssl = ["openssl/vendored"]
vendored-libgit2 = ["libgit2-sys/vendored"]
# This is primarily used by rust-lang/rust distributing cargo the executable.
-all-static = ['vendored-openssl', 'curl/static-curl', 'curl/force-system-lib-on-osx']
+all-static = ['vendored-openssl', 'curl/static-curl', 'curl/force-system-lib-on-osx', 'vendored-libgit2']
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/benches/README.md b/src/tools/cargo/benches/README.md
index b4b8b190a..900bf084c 100644
--- a/src/tools/cargo/benches/README.md
+++ b/src/tools/cargo/benches/README.md
@@ -9,7 +9,23 @@ cd benches/benchsuite
cargo bench
```
-The tests involve downloading the index and benchmarking against some
+However, running all benchmarks would take many minutes, so in most cases it
+is recommended to just run the benchmarks relevant to whatever section of code
+you are working on.
+
+## Benchmarks
+
+There are several different kinds of benchmarks in the `benchsuite/benches` directory:
+
+* `global_cache_tracker` — Benchmarks saving data to the global cache tracker
+ database using samples of real-world data.
+* `resolve` — Benchmarks the resolver against simulations of real-world workspaces.
+* `workspace_initialization` — Benchmarks initialization of a workspace
+ against simulations of real-world workspaces.
+
+### Resolve benchmarks
+
+The resolve benchmarks involve downloading the index and benchmarking against some
real-world and artificial workspaces located in the [`workspaces`](workspaces)
directory.
@@ -21,7 +37,7 @@ faster. You can (and probably should) specify individual benchmarks to run to
narrow it down to a more reasonable set, for example:
```sh
-cargo bench -- resolve_ws/rust
+cargo bench -p benchsuite --bench resolve -- resolve_ws/rust
```
This will only download what's necessary for the rust-lang/rust workspace
@@ -29,7 +45,24 @@ This will only download what's necessary for the rust-lang/rust workspace
about a minute). To get a list of all the benchmarks, run:
```sh
-cargo bench -- --list
+cargo bench -p benchsuite --bench resolve -- --list
+```
+
+### Global cache tracker
+
+The `global_cache_tracker` benchmark tests saving data to the global cache
+tracker database using samples of real-world data. This benchmark should run
+relatively quickly.
+
+The real-world data is based on a capture of my personal development
+environment which has accumulated a large cache. So it is somewhat arbitrary,
+but hopefully representative of a challenging environment. Capturing of the
+data is done with the `capture-last-use` binary, which you can run if you need
+to rebuild the database. Just try to run on a system with a relatively full
+cache in your cargo home directory.
+
+```sh
+cargo bench -p benchsuite --bench global_cache_tracker
```
## Viewing reports
diff --git a/src/tools/cargo/benches/benchsuite/Cargo.toml b/src/tools/cargo/benches/benchsuite/Cargo.toml
index 81413e761..25c160438 100644
--- a/src/tools/cargo/benches/benchsuite/Cargo.toml
+++ b/src/tools/cargo/benches/benchsuite/Cargo.toml
@@ -11,8 +11,10 @@ publish = false
[dependencies]
cargo.workspace = true
+cargo-util.workspace = true
criterion.workspace = true
flate2.workspace = true
+rand.workspace = true
tar.workspace = true
url.workspace = true
@@ -26,3 +28,10 @@ harness = false
[[bench]]
name = "workspace_initialization"
harness = false
+
+[[bench]]
+name = "global_cache_tracker"
+harness = false
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/benches/benchsuite/benches/global_cache_tracker.rs b/src/tools/cargo/benches/benchsuite/benches/global_cache_tracker.rs
new file mode 100644
index 000000000..71d5d5262
--- /dev/null
+++ b/src/tools/cargo/benches/benchsuite/benches/global_cache_tracker.rs
@@ -0,0 +1,159 @@
+//! Benchmarks for the global cache tracker.
+
+use cargo::core::global_cache_tracker::{self, DeferredGlobalLastUse, GlobalCacheTracker};
+use cargo::util::cache_lock::CacheLockMode;
+use cargo::util::interning::InternedString;
+use cargo::util::Config;
+use criterion::{criterion_group, criterion_main, Criterion};
+use std::fs;
+use std::path::{Path, PathBuf};
+
+// Samples of real-world data.
+const GLOBAL_CACHE_SAMPLE: &str = "global-cache-tracker/global-cache-sample";
+const GLOBAL_CACHE_RANDOM: &str = "global-cache-tracker/random-sample";
+
+/// A scratch directory where the benchmark can place some files.
+fn root() -> PathBuf {
+ let mut p = PathBuf::from(env!("CARGO_TARGET_TMPDIR"));
+ p.push("bench_global_cache_tracker");
+ p
+}
+
+fn cargo_home() -> PathBuf {
+ let mut p = root();
+ p.push("chome");
+ p
+}
+
+fn initialize_config() -> Config {
+ // Set up config.
+ let shell = cargo::core::Shell::new();
+ let homedir = cargo_home();
+ if !homedir.exists() {
+ fs::create_dir_all(&homedir).unwrap();
+ }
+ let cwd = homedir.clone();
+ let mut config = Config::new(shell, cwd, homedir);
+ config.nightly_features_allowed = true;
+ config.set_search_stop_path(root());
+ config
+ .configure(
+ 0,
+ false,
+ None,
+ false,
+ false,
+ false,
+ &None,
+ &["gc".to_string()],
+ &[],
+ )
+ .unwrap();
+ // Set up database sample.
+ let db_path = GlobalCacheTracker::db_path(&config).into_path_unlocked();
+ if db_path.exists() {
+ fs::remove_file(&db_path).unwrap();
+ }
+ let sample = Path::new(env!("CARGO_MANIFEST_DIR")).join(GLOBAL_CACHE_SAMPLE);
+ fs::copy(sample, &db_path).unwrap();
+ config
+}
+
+/// Benchmarks how long it takes to initialize `GlobalCacheTracker` with an already
+/// existing full database.
+fn global_tracker_init(c: &mut Criterion) {
+ let config = initialize_config();
+ let _lock = config
+ .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)
+ .unwrap();
+ c.bench_function("global_tracker_init", |b| {
+ b.iter(|| {
+ GlobalCacheTracker::new(&config).unwrap();
+ })
+ });
+}
+
+/// Benchmarks how long it takes to save a `GlobalCacheTracker` when there are zero
+/// updates.
+fn global_tracker_empty_save(c: &mut Criterion) {
+ let config = initialize_config();
+ let _lock = config
+ .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)
+ .unwrap();
+ let mut deferred = DeferredGlobalLastUse::new();
+ let mut tracker = GlobalCacheTracker::new(&config).unwrap();
+
+ c.bench_function("global_tracker_empty_save", |b| {
+ b.iter(|| {
+ deferred.save(&mut tracker).unwrap();
+ })
+ });
+}
+
+fn load_random_sample() -> Vec<(InternedString, InternedString, u64)> {
+ let path = Path::new(env!("CARGO_MANIFEST_DIR")).join(GLOBAL_CACHE_RANDOM);
+ fs::read_to_string(path)
+ .unwrap()
+ .lines()
+ .map(|s| {
+ let mut s = s.split(',');
+ (
+ s.next().unwrap().into(),
+ s.next().unwrap().into(),
+ s.next().unwrap().parse().unwrap(),
+ )
+ })
+ .collect()
+}
+
+/// Tests performance of updating the last-use timestamps in an already
+/// populated database.
+///
+/// This runs for different sizes of number of crates to update (selecting
+/// from the random sample stored on disk).
+fn global_tracker_update(c: &mut Criterion) {
+ let config = initialize_config();
+ let _lock = config
+ .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)
+ .unwrap();
+ let sample = Path::new(env!("CARGO_MANIFEST_DIR")).join(GLOBAL_CACHE_SAMPLE);
+ let db_path = GlobalCacheTracker::db_path(&config).into_path_unlocked();
+
+ let random_sample = load_random_sample();
+
+ let mut group = c.benchmark_group("global_tracker_update");
+ for size in [1, 10, 100, 500] {
+ if db_path.exists() {
+ fs::remove_file(&db_path).unwrap();
+ }
+
+ fs::copy(&sample, &db_path).unwrap();
+ let mut deferred = DeferredGlobalLastUse::new();
+ let mut tracker = GlobalCacheTracker::new(&config).unwrap();
+ group.bench_with_input(size.to_string(), &size, |b, &size| {
+ b.iter(|| {
+ for (encoded_registry_name, name, size) in &random_sample[..size] {
+ deferred.mark_registry_crate_used(global_cache_tracker::RegistryCrate {
+ encoded_registry_name: *encoded_registry_name,
+ crate_filename: format!("{}.crate", name).into(),
+ size: *size,
+ });
+ deferred.mark_registry_src_used(global_cache_tracker::RegistrySrc {
+ encoded_registry_name: *encoded_registry_name,
+ package_dir: *name,
+ size: Some(*size),
+ });
+ }
+ deferred.save(&mut tracker).unwrap();
+ })
+ });
+ }
+}
+
+criterion_group!(
+ benches,
+ global_tracker_init,
+ global_tracker_empty_save,
+ global_tracker_update
+);
+criterion_main!(benches);
diff --git a/src/tools/cargo/benches/benchsuite/global-cache-tracker/global-cache-sample b/src/tools/cargo/benches/benchsuite/global-cache-tracker/global-cache-sample
new file mode 100644
index 000000000..dc134f538
--- /dev/null
+++ b/src/tools/cargo/benches/benchsuite/global-cache-tracker/global-cache-sample
Binary files differ
diff --git a/src/tools/cargo/benches/benchsuite/global-cache-tracker/random-sample b/src/tools/cargo/benches/benchsuite/global-cache-tracker/random-sample
new file mode 100644
index 000000000..62b611cff
--- /dev/null
+++ b/src/tools/cargo/benches/benchsuite/global-cache-tracker/random-sample
@@ -0,0 +1,500 @@
+github.com-1ecc6299db9ec823,tungstenite-0.18.0,218740
+github.com-1ecc6299db9ec823,integer-encoding-1.1.5,30672
+github.com-1ecc6299db9ec823,tungstenite-0.14.0,315676
+github.com-1ecc6299db9ec823,oxcable-0.5.1,163196
+github.com-1ecc6299db9ec823,swc_ecma_transforms_typescript-0.32.0,245522
+github.com-1ecc6299db9ec823,hyper-0.12.35,601153
+github.com-1ecc6299db9ec823,resiter-0.4.0,59880
+github.com-1ecc6299db9ec823,net2-0.2.37,115813
+github.com-1ecc6299db9ec823,str_inflector-0.12.0,182460
+github.com-1ecc6299db9ec823,derive_builder_macro-0.10.2,16441
+github.com-1ecc6299db9ec823,smol_str-0.1.23,42436
+github.com-1ecc6299db9ec823,wasm-bindgen-multi-value-xform-0.2.83,35347
+github.com-1ecc6299db9ec823,time-macros-0.1.0,1620
+github.com-1ecc6299db9ec823,unicode-bidi-0.3.7,140153
+github.com-1ecc6299db9ec823,socket2-0.4.0,167295
+github.com-1ecc6299db9ec823,ppv-lite86-0.2.10,125234
+github.com-1ecc6299db9ec823,tracing-wasm-0.2.1,31449
+github.com-1ecc6299db9ec823,eframe-0.19.0,158130
+github.com-1ecc6299db9ec823,block-modes-0.7.0,42530
+github.com-1ecc6299db9ec823,rangemap-0.1.11,144157
+github.com-1ecc6299db9ec823,metal-0.23.1,1038699
+github.com-1ecc6299db9ec823,os_str_bytes-6.0.1,86390
+github.com-1ecc6299db9ec823,plotters-backend-0.3.4,53018
+github.com-1ecc6299db9ec823,spidev-0.4.0,45301
+github.com-1ecc6299db9ec823,axum-macros-0.2.3,102058
+github.com-1ecc6299db9ec823,embedded-time-0.12.1,246450
+github.com-1ecc6299db9ec823,envmnt-0.10.4,2328079
+github.com-1ecc6299db9ec823,camino-1.1.1,133976
+github.com-1ecc6299db9ec823,siphasher-0.3.5,46666
+github.com-1ecc6299db9ec823,lexical-write-integer-0.8.5,388374
+github.com-1ecc6299db9ec823,reqwest-0.11.14,686608
+github.com-1ecc6299db9ec823,enum-map-2.4.1,51184
+github.com-1ecc6299db9ec823,sentry-panic-0.29.0,18211
+github.com-1ecc6299db9ec823,msf-srtp-0.2.0,73164
+github.com-1ecc6299db9ec823,near-sandbox-utils-0.4.1,7543
+github.com-1ecc6299db9ec823,ablescript-0.5.2,129318
+github.com-1ecc6299db9ec823,apecs-derive-0.2.3,10620
+github.com-1ecc6299db9ec823,libc-0.2.133,3417382
+github.com-1ecc6299db9ec823,tracing-0.1.35,380627
+github.com-1ecc6299db9ec823,serde-wasm-bindgen-0.3.1,55371
+github.com-1ecc6299db9ec823,compiler_builtins-0.1.71,692853
+github.com-1ecc6299db9ec823,mockito-0.7.2,1179718
+github.com-1ecc6299db9ec823,tonic-0.5.2,420299
+github.com-1ecc6299db9ec823,tracing-core-0.1.30,240058
+github.com-1ecc6299db9ec823,tower-timeout-0.3.0-alpha.2,7486
+github.com-1ecc6299db9ec823,js-intern-0.3.1,7026
+github.com-1ecc6299db9ec823,json-ld-context-processing-0.12.1,78101
+github.com-1ecc6299db9ec823,generic-array-0.14.6,67349
+github.com-1ecc6299db9ec823,synstructure-0.12.3,93523
+github.com-1ecc6299db9ec823,version-compare-0.0.10,74950
+github.com-1ecc6299db9ec823,dirs-1.0.5,51075
+github.com-1ecc6299db9ec823,worker-kv-0.5.1,67351
+github.com-1ecc6299db9ec823,vsimd-0.8.0,170805
+github.com-1ecc6299db9ec823,mockall-0.9.1,187734
+github.com-1ecc6299db9ec823,nan-preserving-float-0.1.0,6341
+github.com-1ecc6299db9ec823,wasmer-types-2.3.0,192436
+github.com-1ecc6299db9ec823,sodiumoxide-0.2.7,5131115
+github.com-1ecc6299db9ec823,tracing-attributes-0.1.11,74857
+github.com-1ecc6299db9ec823,treediff-4.0.2,72588
+github.com-1ecc6299db9ec823,wiggle-generate-5.0.0,103044
+github.com-1ecc6299db9ec823,lapin-1.6.6,497368
+github.com-1ecc6299db9ec823,cranelift-entity-0.93.1,114206
+github.com-1ecc6299db9ec823,pcap-parser-0.13.3,184131
+github.com-1ecc6299db9ec823,rustfft-5.1.1,1638221
+github.com-1ecc6299db9ec823,string_cache-0.7.5,75074
+github.com-1ecc6299db9ec823,maybe-uninit-2.0.0,38492
+github.com-1ecc6299db9ec823,diesel_full_text_search-2.0.0,10179
+github.com-1ecc6299db9ec823,quinn-proto-0.8.4,687565
+github.com-1ecc6299db9ec823,semver-0.5.1,73365
+github.com-1ecc6299db9ec823,rocket_http-0.5.0-rc.2,409939
+github.com-1ecc6299db9ec823,dialoguer-0.7.1,95159
+github.com-1ecc6299db9ec823,fallible_collections-0.4.5,244152
+github.com-1ecc6299db9ec823,parking_lot_core-0.9.0,138932
+github.com-1ecc6299db9ec823,relative-path-1.6.0,103315
+github.com-1ecc6299db9ec823,lua52-sys-0.1.2,584054
+github.com-1ecc6299db9ec823,actix-files-0.6.0,126121
+github.com-1ecc6299db9ec823,crates-io-0.35.1,29498
+github.com-1ecc6299db9ec823,sentry-backtrace-0.19.1,20268
+github.com-1ecc6299db9ec823,text_unit-0.1.10,26100
+github.com-1ecc6299db9ec823,ascii-1.0.0,143025
+github.com-1ecc6299db9ec823,crossbeam-utils-0.8.6,169542
+github.com-1ecc6299db9ec823,nelf-0.1.0,28868
+github.com-1ecc6299db9ec823,colorsys-0.6.5,86989
+github.com-1ecc6299db9ec823,enum-iterator-1.2.0,31042
+github.com-1ecc6299db9ec823,ansi-str-0.7.2,111689
+github.com-1ecc6299db9ec823,anyhow-1.0.68,209123
+github.com-1ecc6299db9ec823,gix-lock-5.0.1,65110
+github.com-1ecc6299db9ec823,nom-supreme-0.8.0,147530
+github.com-1ecc6299db9ec823,path-slash-0.1.4,28655
+github.com-1ecc6299db9ec823,crates-io-0.35.0,29406
+github.com-1ecc6299db9ec823,stb_truetype-0.2.8,22939
+github.com-1ecc6299db9ec823,proc-macro2-1.0.50,185288
+github.com-1ecc6299db9ec823,snapbox-0.4.1,169526
+github.com-1ecc6299db9ec823,hyper-0.14.9,764075
+github.com-1ecc6299db9ec823,ab_glyph-0.2.15,61722
+github.com-1ecc6299db9ec823,uuid-0.1.18,47889
+github.com-1ecc6299db9ec823,data-url-0.2.0,123480
+github.com-1ecc6299db9ec823,threadpool-1.7.1,59558
+github.com-1ecc6299db9ec823,thiserror-impl-1.0.29,65149
+github.com-1ecc6299db9ec823,sha1-0.6.0,31102
+github.com-1ecc6299db9ec823,tokio-tls-0.2.1,51467
+github.com-1ecc6299db9ec823,locspan-derive-0.6.0,59360
+github.com-1ecc6299db9ec823,ureq-1.5.1,249335
+github.com-1ecc6299db9ec823,protoc-rust-2.24.1,13459
+github.com-1ecc6299db9ec823,serde-1.0.159,509060
+github.com-1ecc6299db9ec823,unescape-0.1.0,6047
+github.com-1ecc6299db9ec823,data-encoding-2.2.0,113191
+github.com-1ecc6299db9ec823,bytestring-1.1.0,23705
+github.com-1ecc6299db9ec823,ab_glyph_rasterizer-0.1.8,34773
+github.com-1ecc6299db9ec823,syn-0.12.15,912964
+github.com-1ecc6299db9ec823,reqwest-0.11.9,656209
+github.com-1ecc6299db9ec823,rustls-0.17.0,903717
+github.com-1ecc6299db9ec823,term_size-0.3.2,36226
+github.com-1ecc6299db9ec823,ordered-float-3.1.0,91357
+github.com-1ecc6299db9ec823,cookie-0.2.5,44912
+github.com-1ecc6299db9ec823,debugid-0.8.0,44521
+github.com-1ecc6299db9ec823,conrod-0.51.1,2154016
+github.com-1ecc6299db9ec823,indexmap-1.6.1,247801
+github.com-1ecc6299db9ec823,target-spec-1.3.1,68315
+github.com-1ecc6299db9ec823,lexical-parse-integer-0.8.6,139671
+github.com-1ecc6299db9ec823,time-0.1.38,131629
+github.com-1ecc6299db9ec823,glib-macros-0.14.1,102959
+github.com-1ecc6299db9ec823,metrics-macros-0.6.0,37750
+github.com-1ecc6299db9ec823,structopt-0.3.12,224213
+github.com-1ecc6299db9ec823,criterion-0.3.2,439241
+github.com-1ecc6299db9ec823,lyon_path-0.17.7,186745
+github.com-1ecc6299db9ec823,miette-5.5.0,312945
+github.com-1ecc6299db9ec823,tokio-codec-0.2.0-alpha.6,118193
+github.com-1ecc6299db9ec823,structopt-derive-0.4.14,84883
+github.com-1ecc6299db9ec823,objekt-0.1.2,24191
+github.com-1ecc6299db9ec823,sqlx-macros-0.5.7,110890
+github.com-1ecc6299db9ec823,systemstat-0.1.10,127295
+github.com-1ecc6299db9ec823,colorful-0.2.2,99698
+github.com-1ecc6299db9ec823,quick-xml-0.20.0,645935
+github.com-1ecc6299db9ec823,selinux-sys-0.6.2,27060
+github.com-1ecc6299db9ec823,vsmtp-mail-parser-1.4.0-rc.10,137699
+github.com-1ecc6299db9ec823,sec1-0.7.2,64870
+github.com-1ecc6299db9ec823,nix-0.22.1,1161830
+github.com-1ecc6299db9ec823,snow-0.9.0,2658286
+github.com-1ecc6299db9ec823,per_test_directory_macros-0.1.0,2962
+github.com-1ecc6299db9ec823,syn-helpers-0.4.3,58801
+github.com-1ecc6299db9ec823,terminal_size-0.2.2,29633
+github.com-1ecc6299db9ec823,bevy_hierarchy-0.7.0,41018
+github.com-1ecc6299db9ec823,dynamic_reload-0.4.0,74455
+github.com-1ecc6299db9ec823,http-signature-normalization-actix-0.5.0-beta.14,126857
+github.com-1ecc6299db9ec823,http-body-0.4.1,24138
+github.com-1ecc6299db9ec823,gix-index-0.13.0,207795
+github.com-1ecc6299db9ec823,darling_macro-0.13.1,4156
+github.com-1ecc6299db9ec823,serde_json-1.0.66,543072
+github.com-1ecc6299db9ec823,minreq-1.4.1,41355
+github.com-1ecc6299db9ec823,sct-0.6.1,60974
+github.com-1ecc6299db9ec823,openssl-0.10.50,1173941
+github.com-1ecc6299db9ec823,bevy_pbr-0.6.0,201163
+github.com-1ecc6299db9ec823,security-framework-2.3.1,290512
+github.com-1ecc6299db9ec823,pin-project-internal-0.4.30,128419
+github.com-1ecc6299db9ec823,serde_yaml-0.7.5,158524
+github.com-1ecc6299db9ec823,cid-0.3.2,17269
+github.com-1ecc6299db9ec823,plotters-backend-0.3.0,51995
+github.com-1ecc6299db9ec823,serde_yaml-0.8.12,179579
+github.com-1ecc6299db9ec823,cosmwasm-schema-derive-1.1.9,34956
+github.com-1ecc6299db9ec823,docopt-0.6.86,175553
+github.com-1ecc6299db9ec823,git-testament-0.2.4,27685
+github.com-1ecc6299db9ec823,htmlescape-0.3.1,143378
+github.com-1ecc6299db9ec823,is_proc_translated-0.1.1,16533
+github.com-1ecc6299db9ec823,futures-macro-0.3.4,33147
+github.com-1ecc6299db9ec823,futures-intrusive-0.4.2,520476
+github.com-1ecc6299db9ec823,rustix-0.35.13,1581355
+github.com-1ecc6299db9ec823,glsl-layout-0.3.2,75515
+github.com-1ecc6299db9ec823,darling-0.12.0,67446
+github.com-1ecc6299db9ec823,blake3-0.1.5,394136
+github.com-1ecc6299db9ec823,async-stripe-0.15.0,3157635
+github.com-1ecc6299db9ec823,hbs-common-sys-0.2.1,1034
+github.com-1ecc6299db9ec823,base58-0.1.0,7019
+github.com-1ecc6299db9ec823,time-0.2.23,342720
+github.com-1ecc6299db9ec823,memoffset-0.5.6,27595
+github.com-1ecc6299db9ec823,colored-1.9.3,85161
+github.com-1ecc6299db9ec823,lrpar-0.13.1,153317
+github.com-1ecc6299db9ec823,clap-2.34.0,975823
+github.com-1ecc6299db9ec823,chalk-engine-0.55.0,203718
+github.com-1ecc6299db9ec823,cosmic-space-0.3.6,800331
+github.com-1ecc6299db9ec823,syn-1.0.93,1886902
+github.com-1ecc6299db9ec823,futures-core-0.3.5,43430
+github.com-1ecc6299db9ec823,prost-derive-0.11.6,99428
+github.com-1ecc6299db9ec823,toml_edit-0.15.0,491549
+github.com-1ecc6299db9ec823,pcb-llvm-0.2.0,17328
+github.com-1ecc6299db9ec823,rusticata-macros-2.1.0,35537
+github.com-1ecc6299db9ec823,rustyline-with-hint-fix-10.1.0,548833
+github.com-1ecc6299db9ec823,sharded-slab-0.1.1,239224
+github.com-1ecc6299db9ec823,literally-0.1.3,20415
+github.com-1ecc6299db9ec823,riff-1.0.1,20582
+github.com-1ecc6299db9ec823,futures-macro-0.3.23,38691
+github.com-1ecc6299db9ec823,criterion-0.3.1,431723
+github.com-1ecc6299db9ec823,atty-0.2.14,14567
+github.com-1ecc6299db9ec823,vergen-3.1.0,49089
+github.com-1ecc6299db9ec823,peeking_take_while-0.1.2,18604
+github.com-1ecc6299db9ec823,serde_derive-1.0.156,316173
+github.com-1ecc6299db9ec823,geo-0.23.1,1022596
+github.com-1ecc6299db9ec823,persy-1.4.3,778219
+github.com-1ecc6299db9ec823,futures-lite-1.13.0,214632
+github.com-1ecc6299db9ec823,ms_dtyp-0.0.3,44387
+github.com-1ecc6299db9ec823,thiserror-1.0.33,66618
+github.com-1ecc6299db9ec823,marksman_escape-0.1.2,587235
+github.com-1ecc6299db9ec823,serde_derive-1.0.101,289156
+github.com-1ecc6299db9ec823,gix-ref-0.29.0,214105
+github.com-1ecc6299db9ec823,der-0.7.5,384316
+github.com-1ecc6299db9ec823,promptly-0.3.0,35216
+github.com-1ecc6299db9ec823,libc-0.2.115,3166629
+github.com-1ecc6299db9ec823,ppv-lite86-0.1.2,33514
+github.com-1ecc6299db9ec823,gfx-hal-0.6.0,254453
+github.com-1ecc6299db9ec823,as-slice-0.1.3,20306
+github.com-1ecc6299db9ec823,gpu-alloc-0.3.0,78823
+github.com-1ecc6299db9ec823,arc-swap-0.4.8,167950
+github.com-1ecc6299db9ec823,libusb1-sys-0.5.0,1458763
+github.com-1ecc6299db9ec823,sysinfo-0.26.8,609932
+github.com-1ecc6299db9ec823,refinery-macros-0.8.7,6514
+github.com-1ecc6299db9ec823,assert_float_eq-1.1.3,38445
+github.com-1ecc6299db9ec823,tinyvec-1.1.0,363582
+github.com-1ecc6299db9ec823,predicates-1.0.7,1168580
+github.com-1ecc6299db9ec823,pulldown-cmark-0.9.3,595681
+github.com-1ecc6299db9ec823,aws-sigv4-0.46.0,97885
+github.com-1ecc6299db9ec823,fastrand-1.5.0,39175
+github.com-1ecc6299db9ec823,futures-channel-0.3.17,131816
+github.com-1ecc6299db9ec823,usbd_scsi-0.1.0,172205
+github.com-1ecc6299db9ec823,tinyvec-1.4.0,379505
+github.com-1ecc6299db9ec823,structsy-0.5.1,513822
+github.com-1ecc6299db9ec823,aws-sdk-ssm-0.21.0,9755619
+github.com-1ecc6299db9ec823,pin-project-lite-0.1.1,63942
+github.com-1ecc6299db9ec823,tokio-rustls-0.13.0,78252
+github.com-1ecc6299db9ec823,tinyvec_macros-0.1.0,2912
+github.com-1ecc6299db9ec823,extended_matrix_float-1.0.0,6233
+github.com-1ecc6299db9ec823,displaydoc-0.2.3,68676
+github.com-1ecc6299db9ec823,typed-arena-2.0.2,43549
+github.com-1ecc6299db9ec823,cranelift-0.86.1,16294
+github.com-1ecc6299db9ec823,modular-bitfield-impl-0.10.0,64389
+github.com-1ecc6299db9ec823,schemafy_core-0.5.2,7696
+github.com-1ecc6299db9ec823,sea-orm-macros-0.8.0,86930
+github.com-1ecc6299db9ec823,core-foundation-sys-0.4.6,61859
+github.com-1ecc6299db9ec823,move-symbol-pool-0.3.2,14473
+github.com-1ecc6299db9ec823,glutin-0.25.1,300518
+github.com-1ecc6299db9ec823,postcard-cobs-0.2.0,41524
+github.com-1ecc6299db9ec823,quote-0.6.11,69636
+github.com-1ecc6299db9ec823,encoding_rs-0.8.32,5022316
+github.com-1ecc6299db9ec823,clap-2.32.0,946148
+github.com-1ecc6299db9ec823,term-0.6.1,181220
+github.com-1ecc6299db9ec823,enumset-1.0.12,85911
+github.com-1ecc6299db9ec823,ctest2-0.4.1,100745
+github.com-1ecc6299db9ec823,serde-xml-any-0.0.3,70554
+github.com-1ecc6299db9ec823,proc-macro-hack-0.5.11,39025
+github.com-1ecc6299db9ec823,remove_dir_all-0.5.1,23418
+github.com-1ecc6299db9ec823,weezl-0.1.5,134218
+github.com-1ecc6299db9ec823,windows_x86_64_gnullvm-0.42.1,3254874
+github.com-1ecc6299db9ec823,rocket-0.5.0-rc.2,1225987
+github.com-1ecc6299db9ec823,pin-project-0.4.27,282004
+github.com-1ecc6299db9ec823,criterion-cycles-per-byte-0.1.3,18296
+github.com-1ecc6299db9ec823,coco-0.1.1,107143
+github.com-1ecc6299db9ec823,solana-bloom-1.15.1,22207
+github.com-1ecc6299db9ec823,qoqo_calculator-1.1.1,163666
+github.com-1ecc6299db9ec823,aes-gcm-0.9.4,381036
+github.com-1ecc6299db9ec823,blowfish-0.9.1,39658
+github.com-1ecc6299db9ec823,pango-0.14.3,258440
+github.com-1ecc6299db9ec823,clap_derive-3.0.0,129105
+github.com-1ecc6299db9ec823,content_inspector-0.2.4,27568
+github.com-1ecc6299db9ec823,jsona-0.2.0,104104
+github.com-1ecc6299db9ec823,gix-quote-0.4.3,32314
+github.com-1ecc6299db9ec823,bcs-0.1.3,93194
+github.com-1ecc6299db9ec823,statrs-0.14.0,681982
+github.com-1ecc6299db9ec823,cw-controllers-0.16.0,32195
+github.com-1ecc6299db9ec823,hyper-0.12.36,578470
+github.com-1ecc6299db9ec823,argon2-0.4.1,112707
+github.com-1ecc6299db9ec823,fraction-0.12.2,482976
+github.com-1ecc6299db9ec823,quickcheck-0.7.2,89884
+github.com-1ecc6299db9ec823,typetag-0.1.8,135149
+github.com-1ecc6299db9ec823,object-0.20.0,916661
+github.com-1ecc6299db9ec823,pest_derive-2.2.1,60318
+github.com-1ecc6299db9ec823,coremidi-sys-3.1.0,40849
+github.com-1ecc6299db9ec823,either-1.6.0,48881
+github.com-1ecc6299db9ec823,tarpc-0.29.0,244416
+github.com-1ecc6299db9ec823,num-integer-0.1.42,88403
+github.com-1ecc6299db9ec823,oid-registry-0.6.0,46996
+github.com-1ecc6299db9ec823,historian-3.0.11,23818
+github.com-1ecc6299db9ec823,ui-sys-0.1.3,1784250
+github.com-1ecc6299db9ec823,cranelift-frontend-0.92.0,166902
+github.com-1ecc6299db9ec823,pin-project-lite-0.1.12,77882
+github.com-1ecc6299db9ec823,piston2d-gfx_graphics-0.72.0,91826
+github.com-1ecc6299db9ec823,stylist-macros-0.9.2,78647
+github.com-1ecc6299db9ec823,valico-3.4.0,1394467
+github.com-1ecc6299db9ec823,inventory-0.3.3,40329
+github.com-1ecc6299db9ec823,wrapping_arithmetic-0.1.0,8774
+github.com-1ecc6299db9ec823,serde-1.0.138,502921
+github.com-1ecc6299db9ec823,ra_common-0.1.3,16920
+github.com-1ecc6299db9ec823,markup5ever-0.10.0,213742
+github.com-1ecc6299db9ec823,libp2p-core-0.20.1,460422
+github.com-1ecc6299db9ec823,inout-0.1.2,40474
+github.com-1ecc6299db9ec823,flatbuffers-23.1.21,103944
+github.com-1ecc6299db9ec823,gdk-pixbuf-sys-0.10.0,42914
+github.com-1ecc6299db9ec823,miniz_oxide-0.5.1,223551
+github.com-1ecc6299db9ec823,merge-0.1.0,70214
+github.com-1ecc6299db9ec823,pagecache-0.6.0,260742
+github.com-1ecc6299db9ec823,ritelinked-0.3.2,142063
+github.com-1ecc6299db9ec823,ethers-contract-1.0.2,589452
+github.com-1ecc6299db9ec823,color_quant-1.1.0,21284
+github.com-1ecc6299db9ec823,libykpers-sys-0.3.1,14270
+github.com-1ecc6299db9ec823,cgmath-0.17.0,367702
+github.com-1ecc6299db9ec823,clap-4.0.18,1096299
+github.com-1ecc6299db9ec823,ears-0.5.1,165152
+github.com-1ecc6299db9ec823,h2-0.2.5,765073
+github.com-1ecc6299db9ec823,image-0.22.5,725576
+github.com-1ecc6299db9ec823,digest-0.10.1,83013
+github.com-1ecc6299db9ec823,js-sys-0.3.46,410849
+github.com-1ecc6299db9ec823,psl-types-2.0.11,25329
+github.com-1ecc6299db9ec823,apub-core-0.2.0,52434
+github.com-1ecc6299db9ec823,thiserror-1.0.22,59077
+github.com-1ecc6299db9ec823,num-complex-0.4.3,139539
+github.com-1ecc6299db9ec823,autocfg-1.0.1,41521
+github.com-1ecc6299db9ec823,amethyst_locale-0.15.3,4896
+github.com-1ecc6299db9ec823,tokio-timer-0.2.11,167147
+github.com-1ecc6299db9ec823,pipe-trait-0.2.1,11031
+github.com-1ecc6299db9ec823,http-muncher-0.3.2,259101
+github.com-1ecc6299db9ec823,thin-dst-1.1.0,46297
+github.com-1ecc6299db9ec823,float-ord-0.2.0,21145
+github.com-1ecc6299db9ec823,trust-dns-proto-0.21.2,1312809
+github.com-1ecc6299db9ec823,ordered-multimap-0.4.3,178966
+github.com-1ecc6299db9ec823,bitflags-0.4.0,33932
+github.com-1ecc6299db9ec823,windows_x86_64_gnullvm-0.42.0,3240134
+github.com-1ecc6299db9ec823,cargo-util-0.1.2,72189
+github.com-1ecc6299db9ec823,serde_with_macros-1.5.2,72325
+github.com-1ecc6299db9ec823,wasmer-2.3.0,529984
+github.com-1ecc6299db9ec823,tokio-codec-0.1.2,30428
+github.com-1ecc6299db9ec823,pico-args-0.5.0,54991
+github.com-1ecc6299db9ec823,migformatting-0.1.1,1680
+github.com-1ecc6299db9ec823,lexical-core-0.6.7,2382284
+github.com-1ecc6299db9ec823,katex-wasmbind-0.10.0,274096
+github.com-1ecc6299db9ec823,blender-armature-0.0.1,51371
+github.com-1ecc6299db9ec823,twoway-0.2.1,129719
+github.com-1ecc6299db9ec823,sha3-0.10.0,540582
+github.com-1ecc6299db9ec823,ringbuf-0.2.8,92733
+github.com-1ecc6299db9ec823,pest_meta-2.1.3,175833
+github.com-1ecc6299db9ec823,selectme-macros-0.7.1,79130
+github.com-1ecc6299db9ec823,secp256k1-sys-0.7.0,5303296
+github.com-1ecc6299db9ec823,panic-probe-0.3.0,18841
+github.com-1ecc6299db9ec823,ron-0.6.6,208755
+github.com-1ecc6299db9ec823,defmt-macros-0.3.3,78405
+github.com-1ecc6299db9ec823,winapi-x86_64-pc-windows-gnu-0.4.0,53158182
+github.com-1ecc6299db9ec823,aph-0.2.0,30088
+github.com-1ecc6299db9ec823,winnow-0.4.6,959730
+github.com-1ecc6299db9ec823,syntex_syntax-0.54.0,1272567
+github.com-1ecc6299db9ec823,prost-derive-0.11.9,99428
+github.com-1ecc6299db9ec823,commoncrypto-sys-0.2.0,16095
+github.com-1ecc6299db9ec823,yew-router-macro-0.15.0,42667
+github.com-1ecc6299db9ec823,http-range-header-0.3.0,29647
+github.com-1ecc6299db9ec823,crossbeam-queue-0.2.3,60131
+github.com-1ecc6299db9ec823,slice-deque-0.3.0,271889
+github.com-1ecc6299db9ec823,libc-0.2.65,2334946
+github.com-1ecc6299db9ec823,minidom-0.14.0,102507
+github.com-1ecc6299db9ec823,tokio-native-tls-0.3.0,60313
+github.com-1ecc6299db9ec823,glam-0.17.3,1191013
+github.com-1ecc6299db9ec823,semver-1.0.6,114819
+github.com-1ecc6299db9ec823,cortex-m-rtfm-macros-0.5.1,112048
+github.com-1ecc6299db9ec823,bitvec-1.0.0,1006982
+github.com-1ecc6299db9ec823,gfx-backend-metal-0.6.5,660301
+github.com-1ecc6299db9ec823,object-0.30.1,1467041
+github.com-1ecc6299db9ec823,proc-macro-error-attr-0.4.11,18220
+github.com-1ecc6299db9ec823,proteus-0.5.0,179567
+github.com-1ecc6299db9ec823,crunchy-0.1.6,6678
+github.com-1ecc6299db9ec823,once_cell-1.7.2,121632
+github.com-1ecc6299db9ec823,rel-0.2.0,14524
+github.com-1ecc6299db9ec823,lexical-core-0.7.5,2355166
+github.com-1ecc6299db9ec823,windows_x86_64_gnu-0.42.1,10581222
+github.com-1ecc6299db9ec823,thread_local-1.1.5,49409
+github.com-1ecc6299db9ec823,openssl-sys-0.9.63,285709
+github.com-1ecc6299db9ec823,simplelog-0.11.2,85170
+github.com-1ecc6299db9ec823,thiserror-impl-1.0.25,55249
+github.com-1ecc6299db9ec823,quanta-0.10.0,82241
+github.com-1ecc6299db9ec823,vsmtp-common-1.4.0-rc.10,122740
+github.com-1ecc6299db9ec823,tonic-0.1.0-alpha.6,302938
+github.com-1ecc6299db9ec823,ecdsa-0.16.1,121203
+github.com-1ecc6299db9ec823,deltae-0.3.0,2871017
+github.com-1ecc6299db9ec823,phf_shared-0.11.1,30454
+github.com-1ecc6299db9ec823,trustfall-rustdoc-adapter-22.5.2,5348192
+github.com-1ecc6299db9ec823,mockall_derive-0.11.0,227736
+github.com-1ecc6299db9ec823,wasm-bindgen-0.2.64,584320
+github.com-1ecc6299db9ec823,sg-std-0.12.0,27020
+github.com-1ecc6299db9ec823,chalk-ir-0.87.0,288472
+github.com-1ecc6299db9ec823,environment-0.1.1,9957
+github.com-1ecc6299db9ec823,crash-handler-0.3.3,125183
+github.com-1ecc6299db9ec823,bindgen-0.59.2,958852
+github.com-1ecc6299db9ec823,serde_path_to_error-0.1.7,101591
+github.com-1ecc6299db9ec823,tinyvec-0.3.3,77508
+github.com-1ecc6299db9ec823,precomputed-hash-0.1.1,2853
+github.com-1ecc6299db9ec823,rustc-rayon-core-0.4.1,264995
+github.com-1ecc6299db9ec823,gix-sec-0.6.2,57428
+github.com-1ecc6299db9ec823,pistoncore-input-0.19.0,83490
+github.com-1ecc6299db9ec823,gloo-utils-0.1.5,15602
+github.com-1ecc6299db9ec823,redox_intelflash-0.1.3,28056
+github.com-1ecc6299db9ec823,block2-0.2.0-alpha.6,39192
+github.com-1ecc6299db9ec823,fastly-shared-0.9.1,19292
+github.com-1ecc6299db9ec823,ibc-chain-registry-0.1.0,48243
+github.com-1ecc6299db9ec823,socket2-0.4.4,205035
+github.com-1ecc6299db9ec823,futures-channel-0.3.19,132274
+github.com-1ecc6299db9ec823,structopt-0.3.16,217443
+github.com-1ecc6299db9ec823,rusty-fork-0.2.2,64570
+github.com-1ecc6299db9ec823,parking_lot_core-0.9.7,139601
+github.com-1ecc6299db9ec823,async-lock-2.6.0,99844
+github.com-1ecc6299db9ec823,bindgen-0.56.0,923373
+github.com-1ecc6299db9ec823,quad-rand-0.2.1,9108
+github.com-1ecc6299db9ec823,wasmflow-codec-0.10.0,12343
+github.com-1ecc6299db9ec823,gix-0.38.0,883190
+github.com-1ecc6299db9ec823,futures-macro-0.3.27,38519
+github.com-1ecc6299db9ec823,portable-atomic-0.3.13,549649
+github.com-1ecc6299db9ec823,portable-atomic-1.3.2,799707
+github.com-1ecc6299db9ec823,bevy-crevice-derive-0.6.0,16165
+github.com-1ecc6299db9ec823,gltf-json-0.15.2,118263
+github.com-1ecc6299db9ec823,struple-impl-0.1.0,4096
+github.com-1ecc6299db9ec823,annotate-snippets-0.9.1,153174
+github.com-1ecc6299db9ec823,futures-core-0.3.28,46207
+github.com-1ecc6299db9ec823,wezterm-bidi-0.2.2,361283
+github.com-1ecc6299db9ec823,mildew-0.1.2,3002
+github.com-1ecc6299db9ec823,bytecount-0.6.3,46567
+github.com-1ecc6299db9ec823,numext-fixed-hash-core-0.1.6,7403
+github.com-1ecc6299db9ec823,bytesize-1.1.0,34012
+github.com-1ecc6299db9ec823,oxsdatatypes-0.1.0,174662
+github.com-1ecc6299db9ec823,hostname-0.1.5,4811
+github.com-1ecc6299db9ec823,io-lifetimes-1.0.4,207652
+github.com-1ecc6299db9ec823,derive_builder_core-0.11.2,135502
+github.com-1ecc6299db9ec823,ttf-parser-0.15.2,711615
+github.com-1ecc6299db9ec823,tracing-opentelemetry-0.17.4,187675
+github.com-1ecc6299db9ec823,ab_glyph_rasterizer-0.1.7,34278
+github.com-1ecc6299db9ec823,bevy_diagnostic-0.6.0,14396
+github.com-1ecc6299db9ec823,toml_datetime-0.5.0,34801
+github.com-1ecc6299db9ec823,wasm-parser-0.1.7,39726
+github.com-1ecc6299db9ec823,ppv-null-0.1.2,26098
+github.com-1ecc6299db9ec823,ci_info-0.10.2,1197933
+github.com-1ecc6299db9ec823,jobserver-0.1.21,72720
+github.com-1ecc6299db9ec823,sentencepiece-sys-0.10.0,10055292
+github.com-1ecc6299db9ec823,zstd-sys-2.0.1+zstd.1.5.2,3387955
+github.com-1ecc6299db9ec823,byte-strings-proc_macros-0.2.2,7886
+github.com-1ecc6299db9ec823,snapbox-0.4.11,193312
+github.com-1ecc6299db9ec823,ron-0.6.4,198516
+github.com-1ecc6299db9ec823,gix-object-0.28.0,102536
+github.com-1ecc6299db9ec823,strum_macros-0.23.1,87403
+github.com-1ecc6299db9ec823,defmt-0.3.2,93568
+github.com-1ecc6299db9ec823,openssl-0.10.35,971227
+github.com-1ecc6299db9ec823,gtk-sys-0.14.0,1376726
+github.com-1ecc6299db9ec823,gpu-alloc-0.4.7,99476
+github.com-1ecc6299db9ec823,colored-2.0.0,91075
+github.com-1ecc6299db9ec823,fixedbitset-0.4.2,67872
+github.com-1ecc6299db9ec823,argparse-0.2.2,95032
+github.com-1ecc6299db9ec823,bevy_mod_raycast-0.6.2,456756
+github.com-1ecc6299db9ec823,byte-strings-0.2.2,35209
+github.com-1ecc6299db9ec823,mem_tools-0.1.0,937956
+github.com-1ecc6299db9ec823,deno_core-0.167.0,11067700
+github.com-1ecc6299db9ec823,rocksdb-0.19.0,628015
+github.com-1ecc6299db9ec823,num-traits-0.2.12,231414
+github.com-1ecc6299db9ec823,type-info-derive-0.2.0,56221
+github.com-1ecc6299db9ec823,structopt-derive-0.3.4,68017
+github.com-1ecc6299db9ec823,extendr-macros-0.3.1,49695
+github.com-1ecc6299db9ec823,secret-cosmwasm-std-1.0.0,632711
+github.com-1ecc6299db9ec823,skim-0.7.0,380243
+github.com-1ecc6299db9ec823,serde-1.0.135,501463
+github.com-1ecc6299db9ec823,lock_api-0.1.5,109183
+github.com-1ecc6299db9ec823,cw-multi-test-0.16.2,445599
+github.com-1ecc6299db9ec823,quote-1.0.10,120640
+github.com-1ecc6299db9ec823,safemem-0.3.2,17382
+github.com-1ecc6299db9ec823,gloo-dialogs-0.1.1,4653
+github.com-1ecc6299db9ec823,dashmap-4.0.2,105438
+github.com-1ecc6299db9ec823,oorandom-11.1.0,31893
+github.com-1ecc6299db9ec823,polars-core-0.21.1,1678691
+github.com-1ecc6299db9ec823,claxon-0.4.2,259276
+github.com-1ecc6299db9ec823,cc-1.0.35,179169
+github.com-1ecc6299db9ec823,cocoa-0.19.1,296083
+github.com-1ecc6299db9ec823,tokio-1.9.0,2490393
+github.com-1ecc6299db9ec823,gix-refspec-0.10.1,105495
+github.com-1ecc6299db9ec823,futures-task-0.3.12,39561
+github.com-1ecc6299db9ec823,sqlx-core-0.4.2,1064795
+github.com-1ecc6299db9ec823,futures-task-0.3.14,39566
+github.com-1ecc6299db9ec823,datastore_grpc-0.4.0,18233399
+github.com-1ecc6299db9ec823,directories-4.0.1,74013
+github.com-1ecc6299db9ec823,wgpu-hal-0.15.1,1201034
+github.com-1ecc6299db9ec823,discard-1.0.4,14342
+github.com-1ecc6299db9ec823,tinytga-0.1.0,102322
+github.com-1ecc6299db9ec823,prost-types-0.10.1,126121
+github.com-1ecc6299db9ec823,assert2-0.3.6,36145
+github.com-1ecc6299db9ec823,syn-inline-mod-0.5.0,35740
+github.com-1ecc6299db9ec823,bat-0.22.1,5407476
+github.com-1ecc6299db9ec823,minidumper-child-0.1.0,32329
+github.com-1ecc6299db9ec823,libp2p-kad-0.21.0,416675
+github.com-1ecc6299db9ec823,asn1_der-0.6.3,1102166
+github.com-1ecc6299db9ec823,h2-0.2.4,764682
+github.com-1ecc6299db9ec823,ena-0.14.2,90713
+github.com-1ecc6299db9ec823,prost-build-0.8.0,31248726
+github.com-1ecc6299db9ec823,wasmer-compiler-cranelift-3.1.1,300456
+github.com-1ecc6299db9ec823,gfx-hal-0.7.0,238750
+github.com-1ecc6299db9ec823,nom-4.2.3,644514
+github.com-1ecc6299db9ec823,os_str_bytes-2.4.0,52159
+github.com-1ecc6299db9ec823,sourcemap-6.2.1,135303
+github.com-1ecc6299db9ec823,actix-router-0.5.1,150753
+github.com-1ecc6299db9ec823,markup5ever-0.9.0,229731
+github.com-1ecc6299db9ec823,gloo-worker-0.2.1,31624
+github.com-1ecc6299db9ec823,object-0.25.3,1313095
+github.com-1ecc6299db9ec823,rustversion-1.0.0,41602
diff --git a/src/tools/cargo/benches/benchsuite/src/bin/capture-last-use.rs b/src/tools/cargo/benches/benchsuite/src/bin/capture-last-use.rs
new file mode 100644
index 000000000..3034d49ac
--- /dev/null
+++ b/src/tools/cargo/benches/benchsuite/src/bin/capture-last-use.rs
@@ -0,0 +1,148 @@
+//! Utility for capturing a global cache last-use database based on the files
+//! on a real-world system.
+//!
+//! This will look in the CARGO_HOME of the current system and record last-use
+//! data for all files in the cache. This is intended to provide a real-world
+//! example for a benchmark that should be close to what a real set of data
+//! should look like.
+//!
+//! See `benches/global_cache_tracker.rs` for the benchmark that uses this
+//! data.
+//!
+//! The database is kept in git. It usually shouldn't need to be re-generated
+//! unless there is a change in the schema or the benchmark.
+
+use cargo::core::global_cache_tracker::{self, DeferredGlobalLastUse, GlobalCacheTracker};
+use cargo::util::cache_lock::CacheLockMode;
+use cargo::util::interning::InternedString;
+use cargo::Config;
+use rand::prelude::SliceRandom;
+use std::collections::HashMap;
+use std::fs;
+use std::fs::File;
+use std::io::Write;
+use std::path::Path;
+
+fn main() {
+ // Set up config.
+ let shell = cargo::core::Shell::new();
+ let homedir = Path::new(env!("CARGO_MANIFEST_DIR")).join("global-cache-tracker");
+ let cwd = homedir.clone();
+ let mut config = Config::new(shell, cwd, homedir.clone());
+ config
+ .configure(
+ 0,
+ false,
+ None,
+ false,
+ false,
+ false,
+ &None,
+ &["gc".to_string()],
+ &[],
+ )
+ .unwrap();
+ let db_path = GlobalCacheTracker::db_path(&config).into_path_unlocked();
+ if db_path.exists() {
+ fs::remove_file(&db_path).unwrap();
+ }
+
+ let _lock = config
+ .acquire_package_cache_lock(CacheLockMode::DownloadExclusive)
+ .unwrap();
+ let mut deferred = DeferredGlobalLastUse::new();
+ let mut tracker = GlobalCacheTracker::new(&config).unwrap();
+
+ let real_home = cargo::util::homedir(&std::env::current_dir().unwrap()).unwrap();
+
+ let cache_dir = real_home.join("registry/cache");
+ for dir_ent in fs::read_dir(cache_dir).unwrap() {
+ let registry = dir_ent.unwrap();
+ let encoded_registry_name = InternedString::new(&registry.file_name().to_string_lossy());
+ for krate in fs::read_dir(registry.path()).unwrap() {
+ let krate = krate.unwrap();
+ let meta = krate.metadata().unwrap();
+ deferred.mark_registry_crate_used_stamp(
+ global_cache_tracker::RegistryCrate {
+ encoded_registry_name,
+ crate_filename: krate.file_name().to_string_lossy().as_ref().into(),
+ size: meta.len(),
+ },
+ Some(&meta.modified().unwrap()),
+ );
+ }
+ }
+
+ let mut src_entries = Vec::new();
+
+ let cache_dir = real_home.join("registry/src");
+ for dir_ent in fs::read_dir(cache_dir).unwrap() {
+ let registry = dir_ent.unwrap();
+ let encoded_registry_name = InternedString::new(&registry.file_name().to_string_lossy());
+ for krate in fs::read_dir(registry.path()).unwrap() {
+ let krate = krate.unwrap();
+ let meta = krate.metadata().unwrap();
+ let src = global_cache_tracker::RegistrySrc {
+ encoded_registry_name,
+ package_dir: krate.file_name().to_string_lossy().as_ref().into(),
+ size: Some(cargo_util::du(&krate.path(), &[]).unwrap()),
+ };
+ src_entries.push(src.clone());
+ let timestamp = meta.modified().unwrap();
+ deferred.mark_registry_src_used_stamp(src, Some(&timestamp));
+ }
+ }
+
+ let git_co_dir = real_home.join("git/checkouts");
+ for dir_ent in fs::read_dir(git_co_dir).unwrap() {
+ let git_source = dir_ent.unwrap();
+ let encoded_git_name = InternedString::new(&git_source.file_name().to_string_lossy());
+ for co in fs::read_dir(git_source.path()).unwrap() {
+ let co = co.unwrap();
+ let meta = co.metadata().unwrap();
+ deferred.mark_git_checkout_used_stamp(
+ global_cache_tracker::GitCheckout {
+ encoded_git_name,
+ short_name: co.file_name().to_string_lossy().as_ref().into(),
+ size: Some(cargo_util::du(&co.path(), &[]).unwrap()),
+ },
+ Some(&meta.modified().unwrap()),
+ );
+ }
+ }
+
+ deferred.save(&mut tracker).unwrap();
+ drop(deferred);
+ drop(tracker);
+ fs::rename(&db_path, homedir.join("global-cache-sample")).unwrap();
+ // Clean up the lock file created above.
+ fs::remove_file(homedir.join(".package-cache")).unwrap();
+
+ // Save a random sample of crates that the benchmark should update.
+ // Pick whichever registry has the most entries. This is to be somewhat
+ // realistic for the common case that all dependencies come from one
+ // registry (crates.io).
+ let mut counts = HashMap::new();
+ for src in &src_entries {
+ let c: &mut u32 = counts.entry(src.encoded_registry_name).or_default();
+ *c += 1;
+ }
+ let mut counts: Vec<_> = counts.into_iter().map(|(k, v)| (v, k)).collect();
+ counts.sort();
+ let biggest = counts.last().unwrap().1;
+
+ src_entries.retain(|src| src.encoded_registry_name == biggest);
+ let mut rng = &mut rand::thread_rng();
+ let sample: Vec<_> = src_entries.choose_multiple(&mut rng, 500).collect();
+ let mut f = File::create(homedir.join("random-sample")).unwrap();
+ for src in sample {
+ writeln!(
+ f,
+ "{},{},{}",
+ src.encoded_registry_name,
+ src.package_dir,
+ src.size.unwrap()
+ )
+ .unwrap();
+ }
+}
diff --git a/src/tools/cargo/benches/benchsuite/src/lib.rs b/src/tools/cargo/benches/benchsuite/src/lib.rs
index e470a03b9..f27710841 100644
--- a/src/tools/cargo/benches/benchsuite/src/lib.rs
+++ b/src/tools/cargo/benches/benchsuite/src/lib.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::disallowed_methods)]
+
use cargo::Config;
use std::fs;
use std::path::{Path, PathBuf};
diff --git a/src/tools/cargo/benches/capture/Cargo.toml b/src/tools/cargo/benches/capture/Cargo.toml
index e300815d5..16f2c5071 100644
--- a/src/tools/cargo/benches/capture/Cargo.toml
+++ b/src/tools/cargo/benches/capture/Cargo.toml
@@ -12,3 +12,6 @@ cargo_metadata.workspace = true
flate2.workspace = true
tar.workspace = true
toml.workspace = true
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/benches/capture/src/main.rs b/src/tools/cargo/benches/capture/src/main.rs
index f6f02c4ba..dcded3b1a 100644
--- a/src/tools/cargo/benches/capture/src/main.rs
+++ b/src/tools/cargo/benches/capture/src/main.rs
@@ -4,6 +4,9 @@
//! Use the `-f` flag to allow it to overwrite existing captures.
//! The workspace will be saved in a `.tgz` file in the `../workspaces` directory.
+#![allow(clippy::disallowed_methods)]
+#![allow(clippy::print_stderr)]
+
use flate2::{Compression, GzBuilder};
use std::fs;
use std::path::{Path, PathBuf};
diff --git a/src/tools/cargo/build.rs b/src/tools/cargo/build.rs
index 752221f8c..60fda40e3 100644
--- a/src/tools/cargo/build.rs
+++ b/src/tools/cargo/build.rs
@@ -7,6 +7,7 @@ use std::process::Command;
fn main() {
commit_info();
compress_man();
+ windows_manifest();
// ALLOWED: Accessing environment during build time shouldn't be prohibited.
#[allow(clippy::disallowed_methods)]
let target = std::env::var("TARGET").unwrap();
@@ -50,6 +51,14 @@ fn commit_info() {
if !Path::new(".git").exists() {
return;
}
+
+ // Var set by bootstrap whenever omit-git-hash is enabled in rust-lang/rust's config.toml.
+ println!("cargo:rerun-if-env-changed=CFG_OMIT_GIT_HASH");
+ #[allow(clippy::disallowed_methods)]
+ if std::env::var_os("CFG_OMIT_GIT_HASH").is_some() {
+ return;
+ }
+
let output = match Command::new("git")
.arg("log")
.arg("-1")
@@ -68,3 +77,26 @@ fn commit_info() {
println!("cargo:rustc-env=CARGO_COMMIT_SHORT_HASH={}", next());
println!("cargo:rustc-env=CARGO_COMMIT_DATE={}", next())
}
+
+#[allow(clippy::disallowed_methods)]
+fn windows_manifest() {
+ use std::env;
+ let target_os = env::var("CARGO_CFG_TARGET_OS");
+ let target_env = env::var("CARGO_CFG_TARGET_ENV");
+ if Ok("windows") == target_os.as_deref() && Ok("msvc") == target_env.as_deref() {
+ static WINDOWS_MANIFEST_FILE: &str = "windows.manifest.xml";
+
+ let mut manifest = env::current_dir().unwrap();
+ manifest.push(WINDOWS_MANIFEST_FILE);
+
+ println!("cargo:rerun-if-changed={WINDOWS_MANIFEST_FILE}");
+ // Embed the Windows application manifest file.
+ println!("cargo:rustc-link-arg-bin=cargo=/MANIFEST:EMBED");
+ println!(
+ "cargo:rustc-link-arg-bin=cargo=/MANIFESTINPUT:{}",
+ manifest.to_str().unwrap()
+ );
+ // Turn linker warnings into errors.
+ println!("cargo:rustc-link-arg-bin=cargo=/WX");
+ }
+}
diff --git a/src/tools/cargo/clippy.toml b/src/tools/cargo/clippy.toml
index f50e36588..dff120e95 100644
--- a/src/tools/cargo/clippy.toml
+++ b/src/tools/cargo/clippy.toml
@@ -6,3 +6,6 @@ disallowed-methods = [
{ path = "std::env::vars", reason = "not recommended to use in Cargo. See rust-lang/cargo#11588" },
{ path = "std::env::vars_os", reason = "not recommended to use in Cargo. See rust-lang/cargo#11588" },
]
+disallowed-types = [
+ { path = "std::sync::atomic::AtomicU64", reason = "not portable. See rust-lang/cargo#12988" },
+]
diff --git a/src/tools/cargo/crates/cargo-platform/Cargo.toml b/src/tools/cargo/crates/cargo-platform/Cargo.toml
index 786948ff3..baa179291 100644
--- a/src/tools/cargo/crates/cargo-platform/Cargo.toml
+++ b/src/tools/cargo/crates/cargo-platform/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "cargo-platform"
-version = "0.1.6"
+version = "0.1.7"
edition.workspace = true
license.workspace = true
rust-version = "1.70.0" # MSRV:3
@@ -11,3 +11,6 @@ description = "Cargo's representation of a target platform."
[dependencies]
serde.workspace = true
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/crates/cargo-platform/examples/matches.rs b/src/tools/cargo/crates/cargo-platform/examples/matches.rs
index 1b438fb11..11318a7df 100644
--- a/src/tools/cargo/crates/cargo-platform/examples/matches.rs
+++ b/src/tools/cargo/crates/cargo-platform/examples/matches.rs
@@ -1,6 +1,8 @@
//! This example demonstrates how to filter a Platform based on the current
//! host target.
+#![allow(clippy::print_stdout)]
+
use cargo_platform::{Cfg, Platform};
use std::process::Command;
use std::str::FromStr;
diff --git a/src/tools/cargo/crates/cargo-test-macro/Cargo.toml b/src/tools/cargo/crates/cargo-test-macro/Cargo.toml
index 1e81ab314..17ca326f6 100644
--- a/src/tools/cargo/crates/cargo-test-macro/Cargo.toml
+++ b/src/tools/cargo/crates/cargo-test-macro/Cargo.toml
@@ -12,3 +12,6 @@ publish = false
[lib]
proc-macro = true
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/crates/cargo-test-macro/src/lib.rs b/src/tools/cargo/crates/cargo-test-macro/src/lib.rs
index 937fbce6b..14672ab94 100644
--- a/src/tools/cargo/crates/cargo-test-macro/src/lib.rs
+++ b/src/tools/cargo/crates/cargo-test-macro/src/lib.rs
@@ -208,10 +208,11 @@ fn has_command(command: &str) -> bool {
let output = match Command::new(command).arg("--version").output() {
Ok(output) => output,
Err(e) => {
- // hg is not installed on GitHub macOS or certain constrained
- // environments like Docker. Consider installing it if Cargo gains
- // more hg support, but otherwise it isn't critical.
- if is_ci() && command != "hg" {
+ // * hg is not installed on GitHub macOS or certain constrained
+ // environments like Docker. Consider installing it if Cargo
+ // gains more hg support, but otherwise it isn't critical.
+ // * lldb is not pre-installed on Ubuntu and Windows, so skip.
+ if is_ci() && !["hg", "lldb"].contains(&command) {
panic!(
"expected command `{}` to be somewhere in PATH: {}",
command, e
diff --git a/src/tools/cargo/crates/cargo-test-support/Cargo.toml b/src/tools/cargo/crates/cargo-test-support/Cargo.toml
index fc32e1c9c..1098d598d 100644
--- a/src/tools/cargo/crates/cargo-test-support/Cargo.toml
+++ b/src/tools/cargo/crates/cargo-test-support/Cargo.toml
@@ -29,6 +29,10 @@ tar.workspace = true
time.workspace = true
toml.workspace = true
url.workspace = true
+walkdir.workspace = true
[target.'cfg(windows)'.dependencies]
windows-sys = { workspace = true, features = ["Win32_Storage_FileSystem"] }
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/crates/cargo-test-support/build.rs b/src/tools/cargo/crates/cargo-test-support/build.rs
index 478da7d99..8854f461a 100644
--- a/src/tools/cargo/crates/cargo-test-support/build.rs
+++ b/src/tools/cargo/crates/cargo-test-support/build.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::disallowed_methods)]
+
fn main() {
println!(
"cargo:rustc-env=NATIVE_ARCH={}",
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 d9e8d5454..fc1663d34 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/compare.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/compare.rs
@@ -591,15 +591,36 @@ fn find_json_mismatch_r<'a>(
.next()
}
(&Object(ref l), &Object(ref r)) => {
- let same_keys = l.len() == r.len() && l.keys().all(|k| r.contains_key(k));
- if !same_keys {
- return Some((expected, actual));
+ let mut expected_entries = l.iter();
+ let mut actual_entries = r.iter();
+
+ // Compilers older than 1.76 do not produce $message_type.
+ // Treat it as optional for now.
+ let mut expected_entries_without_message_type;
+ let expected_entries: &mut dyn Iterator<Item = _> =
+ if l.contains_key("$message_type") && !r.contains_key("$message_type") {
+ expected_entries_without_message_type =
+ expected_entries.filter(|entry| entry.0 != "$message_type");
+ &mut expected_entries_without_message_type
+ } else {
+ &mut expected_entries
+ };
+
+ loop {
+ match (expected_entries.next(), actual_entries.next()) {
+ (None, None) => return None,
+ (Some((expected_key, expected_value)), Some((actual_key, actual_value)))
+ if expected_key == actual_key =>
+ {
+ if let mismatch @ Some(_) =
+ find_json_mismatch_r(expected_value, actual_value, cwd)
+ {
+ return mismatch;
+ }
+ }
+ _ => return Some((expected, actual)),
+ }
}
-
- l.values()
- .zip(r.values())
- .filter_map(|(l, r)| find_json_mismatch_r(l, r, cwd))
- .next()
}
(&Null, &Null) => None,
// Magic string literal `"{...}"` acts as wildcard for any sub-JSON.
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 ec74ce0b2..4e3ef6118 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/lib.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/lib.rs
@@ -2,7 +2,9 @@
//!
//! See <https://rust-lang.github.io/cargo/contrib/> for a guide on writing tests.
-#![allow(clippy::all)]
+#![allow(clippy::disallowed_methods)]
+#![allow(clippy::print_stderr)]
+#![allow(clippy::print_stdout)]
use std::env;
use std::ffi::OsStr;
@@ -521,29 +523,6 @@ pub fn cargo_exe() -> PathBuf {
snapbox::cmd::cargo_bin("cargo")
}
-/// A wrapper around `rustc` instead of calling `clippy`.
-pub fn wrapped_clippy_driver() -> PathBuf {
- let clippy_driver = project()
- .at(paths::global_root().join("clippy-driver"))
- .file("Cargo.toml", &basic_manifest("clippy-driver", "0.0.1"))
- .file(
- "src/main.rs",
- r#"
- fn main() {
- let mut args = std::env::args_os();
- let _me = args.next().unwrap();
- let rustc = args.next().unwrap();
- let status = std::process::Command::new(rustc).args(args).status().unwrap();
- std::process::exit(status.code().unwrap_or(1));
- }
- "#,
- )
- .build();
- clippy_driver.cargo("build").run();
-
- clippy_driver.bin("clippy-driver")
-}
-
/// This is the raw output from the process.
///
/// This is similar to `std::process::Output`, however the `status` is
diff --git a/src/tools/cargo/crates/cargo-test-support/src/paths.rs b/src/tools/cargo/crates/cargo-test-support/src/paths.rs
index 50040e1d4..a07491bcc 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/paths.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/paths.rs
@@ -114,6 +114,10 @@ pub trait CargoPathExt {
fn rm_rf(&self);
fn mkdir_p(&self);
+ /// Returns a list of all files and directories underneath the given
+ /// directory, recursively, including the starting path.
+ fn ls_r(&self) -> Vec<PathBuf>;
+
fn move_into_the_past(&self) {
self.move_in_time(|sec, nsec| (sec - 3600, nsec))
}
@@ -155,6 +159,14 @@ impl CargoPathExt for Path {
.unwrap_or_else(|e| panic!("failed to mkdir_p {}: {}", self.display(), e))
}
+ fn ls_r(&self) -> Vec<PathBuf> {
+ walkdir::WalkDir::new(self)
+ .sort_by_file_name()
+ .into_iter()
+ .filter_map(|e| e.map(|e| e.path().to_owned()).ok())
+ .collect()
+ }
+
fn move_in_time<F>(&self, travel_amount: F)
where
F: Fn(i64, u32) -> (i64, u32),
diff --git a/src/tools/cargo/crates/cargo-test-support/src/registry.rs b/src/tools/cargo/crates/cargo-test-support/src/registry.rs
index 853829c56..6f9d558a9 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/registry.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/registry.rs
@@ -557,6 +557,7 @@ pub struct Dependency {
registry: Option<String>,
package: Option<String>,
optional: bool,
+ default_features: bool,
}
/// Entry with data that corresponds to [`tar::EntryType`].
@@ -1161,12 +1162,15 @@ fn save_new_crate(
"name": name,
"req": dep.version_req,
"features": dep.features,
- "default_features": true,
+ "default_features": dep.default_features,
"target": dep.target,
"optional": dep.optional,
"kind": dep.kind,
"registry": dep.registry,
"package": package,
+ "artifact": dep.artifact,
+ "bindep_target": dep.bindep_target,
+ "lib": dep.lib,
})
})
.collect::<Vec<_>>();
@@ -1179,7 +1183,7 @@ fn save_new_crate(
new_crate.features,
false,
new_crate.links,
- None,
+ new_crate.rust_version.as_deref(),
None,
);
@@ -1415,7 +1419,7 @@ impl Package {
"name": dep.name,
"req": dep.vers,
"features": dep.features,
- "default_features": true,
+ "default_features": dep.default_features,
"target": dep.target,
"artifact": artifact,
"bindep_target": dep.bindep_target,
@@ -1523,6 +1527,30 @@ impl Package {
manifest.push_str(&format!("rust-version = \"{}\"", version));
}
+ if !self.features.is_empty() {
+ let features: Vec<String> = self
+ .features
+ .iter()
+ .map(|(feature, features)| {
+ if features.is_empty() {
+ format!("{} = []", feature)
+ } else {
+ format!(
+ "{} = [{}]",
+ feature,
+ features
+ .iter()
+ .map(|s| format!("\"{}\"", s))
+ .collect::<Vec<_>>()
+ .join(", ")
+ )
+ }
+ })
+ .collect();
+
+ manifest.push_str(&format!("\n[features]\n{}", features.join("\n")));
+ }
+
for dep in self.deps.iter() {
let target = match dep.target {
None => String::new(),
@@ -1540,6 +1568,9 @@ impl Package {
"#,
target, kind, dep.name, dep.vers
));
+ if dep.optional {
+ manifest.push_str("optional = true\n");
+ }
if let Some(artifact) = &dep.artifact {
manifest.push_str(&format!("artifact = \"{}\"\n", artifact));
}
@@ -1553,6 +1584,21 @@ impl Package {
assert_eq!(registry, "alternative");
manifest.push_str(&format!("registry-index = \"{}\"", alt_registry_url()));
}
+ if !dep.default_features {
+ manifest.push_str("default-features = false\n");
+ }
+ if !dep.features.is_empty() {
+ let mut features = String::new();
+ serde::Serialize::serialize(
+ &dep.features,
+ toml::ser::ValueSerializer::new(&mut features),
+ )
+ .unwrap();
+ manifest.push_str(&format!("features = {}\n", features));
+ }
+ if let Some(package) = &dep.package {
+ manifest.push_str(&format!("package = \"{}\"\n", package));
+ }
}
if self.proc_macro {
manifest.push_str("[lib]\nproc-macro = true\n");
@@ -1631,6 +1677,7 @@ impl Dependency {
package: None,
optional: false,
registry: None,
+ default_features: true,
}
}
@@ -1683,4 +1730,10 @@ impl Dependency {
self.optional = optional;
self
}
+
+ /// Adds `default-features = false` if the argument is `false`.
+ pub fn default_features(&mut self, default_features: bool) -> &mut Self {
+ self.default_features = default_features;
+ self
+ }
}
diff --git a/src/tools/cargo/crates/cargo-test-support/src/tools.rs b/src/tools/cargo/crates/cargo-test-support/src/tools.rs
index 2ce2849ae..b6fa4092f 100644
--- a/src/tools/cargo/crates/cargo-test-support/src/tools.rs
+++ b/src/tools/cargo/crates/cargo-test-support/src/tools.rs
@@ -7,6 +7,7 @@ use std::sync::OnceLock;
static ECHO_WRAPPER: OnceLock<Mutex<Option<PathBuf>>> = OnceLock::new();
static ECHO: OnceLock<Mutex<Option<PathBuf>>> = OnceLock::new();
+static CLIPPY_DRIVER: OnceLock<Mutex<Option<PathBuf>>> = OnceLock::new();
/// Returns the path to an executable that works as a wrapper around rustc.
///
@@ -107,3 +108,34 @@ pub fn echo_subcommand() -> Project {
p.cargo("build").run();
p
}
+
+/// A wrapper around `rustc` instead of calling `clippy`.
+pub fn wrapped_clippy_driver() -> PathBuf {
+ let mut lock = CLIPPY_DRIVER
+ .get_or_init(|| Default::default())
+ .lock()
+ .unwrap();
+ if let Some(path) = &*lock {
+ return path.clone();
+ }
+ let clippy_driver = project()
+ .at(paths::global_root().join("clippy-driver"))
+ .file("Cargo.toml", &basic_manifest("clippy-driver", "0.0.1"))
+ .file(
+ "src/main.rs",
+ r#"
+ fn main() {
+ let mut args = std::env::args_os();
+ let _me = args.next().unwrap();
+ let rustc = args.next().unwrap();
+ let status = std::process::Command::new(rustc).args(args).status().unwrap();
+ std::process::exit(status.code().unwrap_or(1));
+ }
+ "#,
+ )
+ .build();
+ clippy_driver.cargo("build").run();
+ let path = clippy_driver.bin("clippy-driver");
+ *lock = Some(path.clone());
+ path
+}
diff --git a/src/tools/cargo/crates/cargo-util/Cargo.toml b/src/tools/cargo/crates/cargo-util/Cargo.toml
index 616a79c5e..3fd6bdeca 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.8"
+version = "0.2.9"
rust-version.workspace = true
edition.workspace = true
license.workspace = true
@@ -12,6 +12,7 @@ description = "Miscellaneous support code used by Cargo."
anyhow.workspace = true
filetime.workspace = true
hex.workspace = true
+ignore.workspace = true
jobserver.workspace = true
libc.workspace = true
same-file.workspace = true
@@ -27,3 +28,6 @@ core-foundation.workspace = true
[target.'cfg(windows)'.dependencies]
miow.workspace = true
windows-sys = { workspace = true, features = ["Win32_Storage_FileSystem", "Win32_Foundation", "Win32_System_Console"] }
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/crates/cargo-util/src/du.rs b/src/tools/cargo/crates/cargo-util/src/du.rs
new file mode 100644
index 000000000..14634c47b
--- /dev/null
+++ b/src/tools/cargo/crates/cargo-util/src/du.rs
@@ -0,0 +1,78 @@
+//! A simple disk usage estimator.
+
+use anyhow::{Context, Result};
+use ignore::overrides::OverrideBuilder;
+use ignore::{WalkBuilder, WalkState};
+use std::path::Path;
+use std::sync::{Arc, Mutex};
+
+/// Determines the disk usage of all files in the given directory.
+///
+/// The given patterns are gitignore style patterns relative to the given
+/// path. If there are patterns, it will only count things matching that
+/// pattern. `!` can be used to exclude things. See [`OverrideBuilder::add`]
+/// for more info.
+///
+/// This is a primitive implementation that doesn't handle hard links, and
+/// isn't particularly fast (for example, not using `getattrlistbulk` on
+/// macOS). It also only uses actual byte sizes instead of block counts (and
+/// thus vastly undercounts directories with lots of small files). It would be
+/// nice to improve this or replace it with something better.
+pub fn du(path: &Path, patterns: &[&str]) -> Result<u64> {
+ du_inner(path, patterns).with_context(|| format!("failed to walk `{}`", path.display()))
+}
+
+fn du_inner(path: &Path, patterns: &[&str]) -> Result<u64> {
+ let mut builder = OverrideBuilder::new(path);
+ for pattern in patterns {
+ builder.add(pattern)?;
+ }
+ let overrides = builder.build()?;
+
+ let mut builder = WalkBuilder::new(path);
+ builder
+ .overrides(overrides)
+ .hidden(false)
+ .parents(false)
+ .ignore(false)
+ .git_global(false)
+ .git_ignore(false)
+ .git_exclude(false);
+ let walker = builder.build_parallel();
+ let total = Arc::new(Mutex::new(0u64));
+ // A slot used to indicate there was an error while walking.
+ //
+ // It is possible that more than one error happens (such as in different
+ // threads). The error returned is arbitrary in that case.
+ let err = Arc::new(Mutex::new(None));
+ walker.run(|| {
+ Box::new(|entry| {
+ match entry {
+ Ok(entry) => match entry.metadata() {
+ Ok(meta) => {
+ if meta.is_file() {
+ let mut lock = total.lock().unwrap();
+ *lock += meta.len();
+ }
+ }
+ Err(e) => {
+ *err.lock().unwrap() = Some(e.into());
+ return WalkState::Quit;
+ }
+ },
+ Err(e) => {
+ *err.lock().unwrap() = Some(e.into());
+ return WalkState::Quit;
+ }
+ }
+ WalkState::Continue
+ })
+ });
+
+ if let Some(e) = err.lock().unwrap().take() {
+ return Err(e);
+ }
+
+ let total = *total.lock().unwrap();
+ Ok(total)
+}
diff --git a/src/tools/cargo/crates/cargo-util/src/lib.rs b/src/tools/cargo/crates/cargo-util/src/lib.rs
index 0cbc920ec..717e89ba4 100644
--- a/src/tools/cargo/crates/cargo-util/src/lib.rs
+++ b/src/tools/cargo/crates/cargo-util/src/lib.rs
@@ -1,10 +1,14 @@
//! Miscellaneous support code used by Cargo.
+#![allow(clippy::disallowed_methods)]
+
pub use self::read2::read2;
+pub use du::du;
pub use process_builder::ProcessBuilder;
pub use process_error::{exit_status_to_string, is_simple_exit_code, ProcessError};
pub use sha256::Sha256;
+mod du;
pub mod paths;
mod process_builder;
mod process_error;
diff --git a/src/tools/cargo/crates/cargo-util/src/paths.rs b/src/tools/cargo/crates/cargo-util/src/paths.rs
index f405c8f97..743e3f3a8 100644
--- a/src/tools/cargo/crates/cargo-util/src/paths.rs
+++ b/src/tools/cargo/crates/cargo-util/src/paths.rs
@@ -719,14 +719,17 @@ pub fn exclude_from_backups_and_indexing(p: impl AsRef<Path>) {
/// * CACHEDIR.TAG files supported by various tools in a platform-independent way
fn exclude_from_backups(path: &Path) {
exclude_from_time_machine(path);
- let _ = std::fs::write(
- path.join("CACHEDIR.TAG"),
- "Signature: 8a477f597d28d172789f06886806bc55
+ let file = path.join("CACHEDIR.TAG");
+ if !file.exists() {
+ let _ = std::fs::write(
+ file,
+ "Signature: 8a477f597d28d172789f06886806bc55
# This file is a cache directory tag created by cargo.
# For information about cache directory tags see https://bford.info/cachedir/
",
- );
- // Similarly to exclude_from_time_machine() we ignore errors here as it's an optional feature.
+ );
+ // Similarly to exclude_from_time_machine() we ignore errors here as it's an optional feature.
+ }
}
/// Marks the directory as excluded from content indexing.
diff --git a/src/tools/cargo/crates/crates-io/Cargo.toml b/src/tools/cargo/crates/crates-io/Cargo.toml
index f1b92602e..bf2b20cf7 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.1"
+version = "0.39.2"
rust-version.workspace = true
edition.workspace = true
license.workspace = true
@@ -20,3 +20,6 @@ serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
thiserror.workspace = true
url.workspace = true
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/crates/crates-io/lib.rs b/src/tools/cargo/crates/crates-io/lib.rs
index 1764ce527..468900e55 100644
--- a/src/tools/cargo/crates/crates-io/lib.rs
+++ b/src/tools/cargo/crates/crates-io/lib.rs
@@ -1,5 +1,3 @@
-#![allow(clippy::all)]
-
use std::collections::BTreeMap;
use std::fs::File;
use std::io::prelude::*;
@@ -438,7 +436,8 @@ impl Registry {
.map(|s| s.errors.into_iter().map(|s| s.detail).collect::<Vec<_>>());
match (self.handle.response_code()?, errors) {
- (0, None) | (200, None) => Ok(body),
+ (0, None) => Ok(body),
+ (code, None) if is_success(code) => Ok(body),
(code, Some(errors)) => Err(Error::Api {
code,
headers,
@@ -453,8 +452,12 @@ impl Registry {
}
}
+fn is_success(code: u32) -> bool {
+ code >= 200 && code < 300
+}
+
fn status(code: u32) -> String {
- if code == 200 {
+ if is_success(code) {
String::new()
} else {
let reason = reason(code);
diff --git a/src/tools/cargo/crates/home/CHANGELOG.md b/src/tools/cargo/crates/home/CHANGELOG.md
index 58f960cc3..5b1a2f8ea 100644
--- a/src/tools/cargo/crates/home/CHANGELOG.md
+++ b/src/tools/cargo/crates/home/CHANGELOG.md
@@ -4,7 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
-## 0.5.6
+## 0.5.9 - 2023-12-15
+
+- Replace SHGetFolderPathW with SHGetKnownFolderPath
+ [#13173](https://github.com/rust-lang/cargo/pull/13173)
+- Update windows-sys to 0.52
+ [#13089](https://github.com/rust-lang/cargo/pull/13089)
+- Set MSRV to 1.70.0
+ [#12654](https://github.com/rust-lang/cargo/pull/12654)
- Fixed & enhanced documentation.
[#12047](https://github.com/rust-lang/cargo/pull/12047)
diff --git a/src/tools/cargo/crates/home/Cargo.toml b/src/tools/cargo/crates/home/Cargo.toml
index 702a14e55..33cd6ba5a 100644
--- a/src/tools/cargo/crates/home/Cargo.toml
+++ b/src/tools/cargo/crates/home/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "home"
-version = "0.5.8"
+version = "0.5.9"
authors = ["Brian Anderson <andersrb@gmail.com>"]
rust-version = "1.70.0" # MSRV:3
documentation = "https://docs.rs/home"
@@ -17,4 +17,7 @@ repository = "https://github.com/rust-lang/cargo"
description = "Shared definitions of home directories."
[target.'cfg(windows)'.dependencies]
-windows-sys = { workspace = true, features = ["Win32_Foundation", "Win32_UI_Shell"] }
+windows-sys = { workspace = true, features = ["Win32_Foundation", "Win32_UI_Shell", "Win32_System_Com"] }
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/crates/home/src/lib.rs b/src/tools/cargo/crates/home/src/lib.rs
index 4aee7383b..bbe7c32ca 100644
--- a/src/tools/cargo/crates/home/src/lib.rs
+++ b/src/tools/cargo/crates/home/src/lib.rs
@@ -18,7 +18,7 @@
//!
//! [discussion]: https://github.com/rust-lang/rust/pull/46799#issuecomment-361156935
-#![deny(rust_2018_idioms)]
+#![allow(clippy::disallowed_methods)]
pub mod env;
@@ -44,11 +44,11 @@ use std::path::{Path, PathBuf};
///
/// Returns the value of the `USERPROFILE` environment variable if it is set
/// **and** it is not an empty string. Otherwise, it tries to determine the
-/// home directory by invoking the [`SHGetFolderPathW`][shgfp] function with
-/// [`CSIDL_PROFILE`][csidl].
+/// home directory by invoking the [`SHGetKnownFolderPath`][shgkfp] function with
+/// [`FOLDERID_Profile`][knownfolderid].
///
-/// [shgfp]: https://docs.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetfolderpathw
-/// [csidl]: https://learn.microsoft.com/en-us/windows/win32/shell/csidl
+/// [shgkfp]: https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath
+/// [knownfolderid]: https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid
///
/// # Examples
///
diff --git a/src/tools/cargo/crates/home/src/windows.rs b/src/tools/cargo/crates/home/src/windows.rs
index a35dc9c57..c9a63d97b 100644
--- a/src/tools/cargo/crates/home/src/windows.rs
+++ b/src/tools/cargo/crates/home/src/windows.rs
@@ -2,9 +2,12 @@ use std::env;
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use std::path::PathBuf;
+use std::ptr;
+use std::slice;
-use windows_sys::Win32::Foundation::{MAX_PATH, S_OK};
-use windows_sys::Win32::UI::Shell::{SHGetFolderPathW, CSIDL_PROFILE};
+use windows_sys::Win32::Foundation::S_OK;
+use windows_sys::Win32::System::Com::CoTaskMemFree;
+use windows_sys::Win32::UI::Shell::{FOLDERID_Profile, SHGetKnownFolderPath, KF_FLAG_DONT_VERIFY};
pub fn home_dir_inner() -> Option<PathBuf> {
env::var_os("USERPROFILE")
@@ -16,15 +19,19 @@ pub fn home_dir_inner() -> Option<PathBuf> {
#[cfg(not(target_vendor = "uwp"))]
fn home_dir_crt() -> Option<PathBuf> {
unsafe {
- let mut path: Vec<u16> = Vec::with_capacity(MAX_PATH as usize);
- match SHGetFolderPathW(0, CSIDL_PROFILE as i32, 0, 0, path.as_mut_ptr()) {
+ let mut path = ptr::null_mut();
+ match SHGetKnownFolderPath(&FOLDERID_Profile, KF_FLAG_DONT_VERIFY as u32, 0, &mut path) {
S_OK => {
- let len = wcslen(path.as_ptr());
- path.set_len(len);
- let s = OsString::from_wide(&path);
+ let path_slice = slice::from_raw_parts(path, wcslen(path));
+ let s = OsString::from_wide(&path_slice);
+ CoTaskMemFree(path.cast());
Some(PathBuf::from(s))
}
- _ => None,
+ _ => {
+ // Free any allocated memory even on failure. A null ptr is a no-op for `CoTaskMemFree`.
+ CoTaskMemFree(path.cast());
+ None
+ }
}
}
}
diff --git a/src/tools/cargo/crates/mdman/Cargo.toml b/src/tools/cargo/crates/mdman/Cargo.toml
index fd33da3c2..4e86b8e1a 100644
--- a/src/tools/cargo/crates/mdman/Cargo.toml
+++ b/src/tools/cargo/crates/mdman/Cargo.toml
@@ -16,4 +16,7 @@ serde_json.workspace = true
url.workspace = true
[dev-dependencies]
-pretty_assertions.workspace = true
+snapbox.workspace = true
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/crates/mdman/src/main.rs b/src/tools/cargo/crates/mdman/src/main.rs
index facaa5120..3c09bc4dd 100644
--- a/src/tools/cargo/crates/mdman/src/main.rs
+++ b/src/tools/cargo/crates/mdman/src/main.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::print_stderr)]
+
use anyhow::{bail, format_err, Context, Error};
use mdman::{Format, ManMap};
use std::collections::HashMap;
diff --git a/src/tools/cargo/crates/mdman/tests/compare.rs b/src/tools/cargo/crates/mdman/tests/compare.rs
index 3e679d127..fde2c235d 100644
--- a/src/tools/cargo/crates/mdman/tests/compare.rs
+++ b/src/tools/cargo/crates/mdman/tests/compare.rs
@@ -1,11 +1,8 @@
//! Compares input to expected output.
-//!
-//! Use the MDMAN_BLESS environment variable to automatically update the
-//! expected output.
-use mdman::{Format, ManMap};
-use pretty_assertions::assert_eq;
use std::path::PathBuf;
+
+use mdman::{Format, ManMap};
use url::Url;
fn run(name: &str) {
@@ -25,14 +22,7 @@ fn run(name: &str) {
name,
format.extension(section)
);
- if std::env::var("MDMAN_BLESS").is_ok() {
- std::fs::write(&expected_path, result).unwrap();
- } else {
- let expected = std::fs::read_to_string(&expected_path).unwrap();
- // Fix if Windows checked out with autocrlf.
- let expected = expected.replace("\r\n", "\n");
- assert_eq!(expected, result);
- }
+ snapbox::assert_eq_path(expected_path, result);
}
}
diff --git a/src/tools/cargo/crates/mdman/tests/invalid.rs b/src/tools/cargo/crates/mdman/tests/invalid.rs
index cc81d06c4..b8be1ed24 100644
--- a/src/tools/cargo/crates/mdman/tests/invalid.rs
+++ b/src/tools/cargo/crates/mdman/tests/invalid.rs
@@ -1,9 +1,9 @@
//! Tests for errors and invalid input.
-use mdman::{Format, ManMap};
-use pretty_assertions::assert_eq;
use std::path::PathBuf;
+use mdman::{Format, ManMap};
+
fn run(name: &str, expected_error: &str) {
let input = PathBuf::from(format!("tests/invalid/{}", name));
match mdman::convert(&input, Format::Man, None, ManMap::new()) {
@@ -11,7 +11,7 @@ fn run(name: &str, expected_error: &str) {
panic!("expected {} to fail", name);
}
Err(e) => {
- assert_eq!(expected_error, e.to_string());
+ snapbox::assert_eq(expected_error, e.to_string());
}
}
}
diff --git a/src/tools/cargo/crates/resolver-tests/Cargo.toml b/src/tools/cargo/crates/resolver-tests/Cargo.toml
index 8750a3d97..947c44569 100644
--- a/src/tools/cargo/crates/resolver-tests/Cargo.toml
+++ b/src/tools/cargo/crates/resolver-tests/Cargo.toml
@@ -10,3 +10,6 @@ cargo.workspace = true
cargo-util.workspace = true
proptest.workspace = true
varisat.workspace = true
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/crates/resolver-tests/src/lib.rs b/src/tools/cargo/crates/resolver-tests/src/lib.rs
index e2cbcee62..2df7a36bb 100644
--- a/src/tools/cargo/crates/resolver-tests/src/lib.rs
+++ b/src/tools/cargo/crates/resolver-tests/src/lib.rs
@@ -1,9 +1,9 @@
-#![allow(clippy::all)]
+#![allow(clippy::print_stderr)]
use std::cell::RefCell;
use std::cmp::PartialEq;
use std::cmp::{max, min};
-use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
+use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt;
use std::fmt::Write;
use std::rc::Rc;
@@ -17,7 +17,9 @@ use cargo::core::Resolve;
use cargo::core::{Dependency, PackageId, Registry, Summary};
use cargo::core::{GitReference, SourceId};
use cargo::sources::source::QueryKind;
-use cargo::util::{CargoResult, Config, Graph, IntoUrl, RustVersion};
+use cargo::sources::IndexSummary;
+use cargo::util::{CargoResult, Config, IntoUrl};
+use cargo::util_schemas::manifest::RustVersion;
use proptest::collection::{btree_map, vec};
use proptest::prelude::*;
@@ -69,33 +71,6 @@ pub fn resolve_and_validated(
let out = resolve.sort();
assert_eq!(out.len(), used.len());
- let mut pub_deps: HashMap<PackageId, HashSet<_>> = HashMap::new();
- for &p in out.iter() {
- // make the list of `p` public dependencies
- let mut self_pub_dep = HashSet::new();
- self_pub_dep.insert(p);
- for (dp, deps) in resolve.deps(p) {
- if deps.iter().any(|d| d.is_public()) {
- self_pub_dep.extend(pub_deps[&dp].iter().cloned())
- }
- }
- pub_deps.insert(p, self_pub_dep);
-
- // check if `p` has a public dependencies conflicts
- let seen_dep: BTreeSet<_> = resolve
- .deps(p)
- .flat_map(|(dp, _)| pub_deps[&dp].iter().cloned())
- .collect();
- let seen_dep: Vec<_> = seen_dep.iter().collect();
- for a in seen_dep.windows(2) {
- if a[0].name() == a[1].name() {
- panic!(
- "the package {:?} can publicly see {:?} and {:?}",
- p, a[0], a[1]
- )
- }
- }
- }
let sat_resolve = sat_resolve.unwrap_or_else(|| SatResolve::new(registry));
if !sat_resolve.sat_is_valid_solution(&out) {
panic!(
@@ -131,7 +106,7 @@ pub fn resolve_with_config_raw(
&mut self,
dep: &Dependency,
kind: QueryKind,
- f: &mut dyn FnMut(Summary),
+ f: &mut dyn FnMut(IndexSummary),
) -> Poll<CargoResult<()>> {
for summary in self.list.iter() {
let matched = match kind {
@@ -140,7 +115,7 @@ pub fn resolve_with_config_raw(
};
if matched {
self.used.insert(summary.package_id());
- f(summary.clone());
+ f(IndexSummary::Candidate(summary.clone()));
}
}
Poll::Ready(Ok(()))
@@ -200,7 +175,6 @@ pub fn resolve_with_config_raw(
&mut registry,
&version_prefs,
Some(config),
- true,
);
// The largest test in our suite takes less then 30 sec.
@@ -294,7 +268,7 @@ impl SatResolve {
);
// no two semver compatible versions of the same package
- let by_activations_keys = sat_at_most_one_by_key(
+ sat_at_most_one_by_key(
&mut cnf,
var_for_is_packages_used
.iter()
@@ -312,119 +286,22 @@ impl SatResolve {
let empty_vec = vec![];
- let mut graph: Graph<PackageId, ()> = Graph::new();
-
- let mut version_selected_for: HashMap<
- PackageId,
- HashMap<Dependency, HashMap<_, varisat::Var>>,
- > = HashMap::new();
// active packages need each of there `deps` to be satisfied
for p in registry.iter() {
- graph.add(p.package_id());
for dep in p.dependencies() {
- // This can more easily be written as:
- // !is_active(p) or one of the things that match dep is_active
- // All the complexity, from here to the end, is to support public and private dependencies!
- let mut by_key: HashMap<_, Vec<varisat::Lit>> = HashMap::new();
- for &m in by_name
+ let mut matches: Vec<varisat::Lit> = by_name
.get(dep.package_name().as_str())
.unwrap_or(&empty_vec)
.iter()
.filter(|&p| dep.matches_id(*p))
- {
- graph.link(p.package_id(), m);
- by_key
- .entry(m.as_activations_key())
- .or_default()
- .push(var_for_is_packages_used[&m].positive());
- }
- let keys: HashMap<_, _> = by_key.keys().map(|&k| (k, cnf.new_var())).collect();
-
- // if `p` is active then we need to select one of the keys
- let matches: Vec<_> = keys
- .values()
- .map(|v| v.positive())
- .chain(Some(var_for_is_packages_used[&p.package_id()].negative()))
+ .map(|p| var_for_is_packages_used[&p].positive())
.collect();
+ // ^ the `dep` is satisfied or `p` is not active
+ matches.push(var_for_is_packages_used[&p.package_id()].negative());
cnf.add_clause(&matches);
-
- // if a key is active then we need to select one of the versions
- for (key, vars) in by_key.iter() {
- let mut matches = vars.clone();
- matches.push(keys[key].negative());
- cnf.add_clause(&matches);
- }
-
- version_selected_for
- .entry(p.package_id())
- .or_default()
- .insert(dep.clone(), keys);
}
}
- let topological_order = graph.sort();
-
- // we already ensure there is only one version for each `activations_key` so we can think of
- // `publicly_exports` as being in terms of a set of `activations_key`s
- let mut publicly_exports: HashMap<_, HashMap<_, varisat::Var>> = HashMap::new();
-
- for &key in by_activations_keys.keys() {
- // everything publicly depends on itself
- let var = publicly_exports
- .entry(key)
- .or_default()
- .entry(key)
- .or_insert_with(|| cnf.new_var());
- cnf.add_clause(&[var.positive()]);
- }
-
- // if a `dep` is public then `p` `publicly_exports` all the things that the selected version `publicly_exports`
- for &p in topological_order.iter() {
- if let Some(deps) = version_selected_for.get(&p) {
- let mut p_exports = publicly_exports.remove(&p.as_activations_key()).unwrap();
- for (_, versions) in deps.iter().filter(|(d, _)| d.is_public()) {
- for (ver, sel) in versions {
- for (&export_pid, &export_var) in publicly_exports[ver].iter() {
- let our_var =
- p_exports.entry(export_pid).or_insert_with(|| cnf.new_var());
- cnf.add_clause(&[
- sel.negative(),
- export_var.negative(),
- our_var.positive(),
- ]);
- }
- }
- }
- publicly_exports.insert(p.as_activations_key(), p_exports);
- }
- }
-
- // we already ensure there is only one version for each `activations_key` so we can think of
- // `can_see` as being in terms of a set of `activations_key`s
- // and if `p` `publicly_exports` `export` then it `can_see` `export`
- let mut can_see: HashMap<_, HashMap<_, varisat::Var>> = HashMap::new();
-
- // if `p` has a `dep` that selected `ver` then it `can_see` all the things that the selected version `publicly_exports`
- for (&p, deps) in version_selected_for.iter() {
- let p_can_see = can_see.entry(p).or_default();
- for (_, versions) in deps.iter() {
- for (&ver, sel) in versions {
- for (&export_pid, &export_var) in publicly_exports[&ver].iter() {
- let our_var = p_can_see.entry(export_pid).or_insert_with(|| cnf.new_var());
- cnf.add_clause(&[
- sel.negative(),
- export_var.negative(),
- our_var.positive(),
- ]);
- }
- }
- }
- }
-
- // a package `can_see` only one version by each name
- for (_, see) in can_see.iter() {
- sat_at_most_one_by_key(&mut cnf, see.iter().map(|((name, _, _), &v)| (name, v)));
- }
let mut solver = varisat::Solver::new();
solver.add_formula(&cnf);
@@ -543,14 +420,14 @@ impl ToPkgId for PackageId {
impl<'a> ToPkgId for &'a str {
fn to_pkgid(&self) -> PackageId {
- PackageId::new(*self, "1.0.0", registry_loc()).unwrap()
+ PackageId::try_new(*self, "1.0.0", registry_loc()).unwrap()
}
}
impl<T: AsRef<str>, U: AsRef<str>> ToPkgId for (T, U) {
fn to_pkgid(&self) -> PackageId {
let (name, vers) = self;
- PackageId::new(name.as_ref(), vers.as_ref(), registry_loc()).unwrap()
+ PackageId::try_new(name.as_ref(), vers.as_ref(), registry_loc()).unwrap()
}
}
@@ -596,7 +473,7 @@ pub fn pkg_dep<T: ToPkgId>(name: T, dep: Vec<Dependency>) -> Summary {
}
pub fn pkg_id(name: &str) -> PackageId {
- PackageId::new(name, "1.0.0", registry_loc()).unwrap()
+ PackageId::try_new(name, "1.0.0", registry_loc()).unwrap()
}
fn pkg_id_loc(name: &str, loc: &str) -> PackageId {
@@ -604,7 +481,7 @@ fn pkg_id_loc(name: &str, loc: &str) -> PackageId {
let master = GitReference::Branch("master".to_string());
let source_id = SourceId::for_git(&remote.unwrap(), master).unwrap();
- PackageId::new(name, "1.0.0", source_id).unwrap()
+ PackageId::try_new(name, "1.0.0", source_id).unwrap()
}
pub fn pkg_loc(name: &str, loc: &str) -> Summary {
@@ -643,10 +520,9 @@ pub fn dep(name: &str) -> Dependency {
pub fn dep_req(name: &str, req: &str) -> Dependency {
Dependency::parse(name, Some(req), registry_loc()).unwrap()
}
-pub fn dep_req_kind(name: &str, req: &str, kind: DepKind, public: bool) -> Dependency {
+pub fn dep_req_kind(name: &str, req: &str, kind: DepKind) -> Dependency {
let mut dep = dep_req(name, req);
dep.set_kind(kind);
- dep.set_public(public);
dep
}
@@ -739,8 +615,8 @@ fn meta_test_deep_pretty_print_registry() {
pkg!(("bar", "2.0.0") => [dep_req("baz", "=1.0.1")]),
pkg!(("baz", "1.0.2") => [dep_req("other", "2")]),
pkg!(("baz", "1.0.1")),
- pkg!(("cat", "1.0.2") => [dep_req_kind("other", "2", DepKind::Build, false)]),
- pkg!(("cat", "1.0.3") => [dep_req_kind("other", "2", DepKind::Development, false)]),
+ pkg!(("cat", "1.0.2") => [dep_req_kind("other", "2", DepKind::Build)]),
+ pkg!(("cat", "1.0.3") => [dep_req_kind("other", "2", DepKind::Development)]),
pkg!(("dep_req", "1.0.0")),
pkg!(("dep_req", "2.0.0")),
])
@@ -803,14 +679,7 @@ pub fn registry_strategy(
let max_deps = max_versions * (max_crates * (max_crates - 1)) / shrinkage;
let raw_version_range = (any::<Index>(), any::<Index>());
- let raw_dependency = (
- any::<Index>(),
- any::<Index>(),
- raw_version_range,
- 0..=1,
- Just(false),
- // TODO: ^ this needs to be set back to `any::<bool>()` and work before public & private dependencies can stabilize
- );
+ let raw_dependency = (any::<Index>(), any::<Index>(), raw_version_range, 0..=1);
fn order_index(a: Index, b: Index, size: usize) -> (usize, usize) {
let (a, b) = (a.index(size), b.index(size));
@@ -837,7 +706,7 @@ pub fn registry_strategy(
.collect();
let len_all_pkgid = list_of_pkgid.len();
let mut dependency_by_pkgid = vec![vec![]; len_all_pkgid];
- for (a, b, (c, d), k, p) in raw_dependencies {
+ for (a, b, (c, d), k) in raw_dependencies {
let (a, b) = order_index(a, b, len_all_pkgid);
let (a, b) = if reverse_alphabetical { (b, a) } else { (a, b) };
let ((dep_name, _), _) = list_of_pkgid[a];
@@ -867,7 +736,6 @@ pub fn registry_strategy(
// => DepKind::Development, // Development has no impact so don't gen
_ => panic!("bad index for DepKind"),
},
- p && k == 0,
))
}
diff --git a/src/tools/cargo/crates/resolver-tests/tests/resolve.rs b/src/tools/cargo/crates/resolver-tests/tests/resolve.rs
index dd21502d8..662bad90f 100644
--- a/src/tools/cargo/crates/resolver-tests/tests/resolve.rs
+++ b/src/tools/cargo/crates/resolver-tests/tests/resolve.rs
@@ -6,8 +6,8 @@ use cargo::util::Config;
use cargo_util::is_ci;
use resolver_tests::{
- assert_contains, assert_same, dep, dep_kind, dep_loc, dep_req, dep_req_kind, loc_names, names,
- pkg, pkg_id, pkg_loc, registry, registry_strategy, remove_dep, resolve, resolve_and_validated,
+ assert_contains, assert_same, dep, dep_kind, dep_loc, dep_req, loc_names, names, pkg, pkg_id,
+ pkg_loc, registry, registry_strategy, remove_dep, resolve, resolve_and_validated,
resolve_with_config, PrettyPrintRegistry, SatResolve, ToDep, ToPkgId,
};
@@ -288,192 +288,6 @@ proptest! {
}
#[test]
-#[should_panic(expected = "pub dep")] // The error handling is not yet implemented.
-fn pub_fail() {
- let input = vec![
- pkg!(("a", "0.0.4")),
- pkg!(("a", "0.0.5")),
- pkg!(("e", "0.0.6") => [dep_req_kind("a", "<= 0.0.4", DepKind::Normal, true),]),
- pkg!(("kB", "0.0.3") => [dep_req("a", ">= 0.0.5"),dep("e"),]),
- ];
- let reg = registry(input);
- assert!(resolve_and_validated(vec![dep("kB")], &reg, None).is_err());
-}
-
-#[test]
-fn basic_public_dependency() {
- let reg = registry(vec![
- pkg!(("A", "0.1.0")),
- pkg!(("A", "0.2.0")),
- pkg!("B" => [dep_req_kind("A", "0.1", DepKind::Normal, true)]),
- pkg!("C" => [dep("A"), dep("B")]),
- ]);
-
- let res = resolve_and_validated(vec![dep("C")], &reg, None).unwrap();
- assert_same(
- &res,
- &names(&[
- ("root", "1.0.0"),
- ("C", "1.0.0"),
- ("B", "1.0.0"),
- ("A", "0.1.0"),
- ]),
- );
-}
-
-#[test]
-fn public_dependency_filling_in() {
- // The resolver has an optimization where if a candidate to resolve a dependency
- // has already bean activated then we skip looking at the candidates dependencies.
- // However, we have to be careful as the new path may make pub dependencies invalid.
-
- // Triggering this case requires dependencies to be resolved in a specific order.
- // Fuzzing found this unintuitive case, that triggers this unfortunate order of operations:
- // 1. `d`'s dep on `c` is resolved
- // 2. `d`'s dep on `a` is resolved with `0.1.1`
- // 3. `c`'s dep on `b` is resolved with `0.0.2`
- // 4. `b`'s dep on `a` is resolved with `0.0.6` no pub dev conflict as `b` is private to `c`
- // 5. `d`'s dep on `b` is resolved with `0.0.2` triggering the optimization.
- // Do we notice that `d` has a pub dep conflict on `a`? Lets try it and see.
- let reg = registry(vec![
- pkg!(("a", "0.0.6")),
- pkg!(("a", "0.1.1")),
- pkg!(("b", "0.0.0") => [dep("bad")]),
- pkg!(("b", "0.0.1") => [dep("bad")]),
- pkg!(("b", "0.0.2") => [dep_req_kind("a", "=0.0.6", DepKind::Normal, true)]),
- pkg!("c" => [dep_req("b", ">=0.0.1")]),
- pkg!("d" => [dep("c"), dep("a"), dep("b")]),
- ]);
-
- let res = resolve_and_validated(vec![dep("d")], &reg, None).unwrap();
- assert_same(
- &res,
- &names(&[
- ("root", "1.0.0"),
- ("d", "1.0.0"),
- ("c", "1.0.0"),
- ("b", "0.0.2"),
- ("a", "0.0.6"),
- ]),
- );
-}
-
-#[test]
-fn public_dependency_filling_in_and_update() {
- // The resolver has an optimization where if a candidate to resolve a dependency
- // has already bean activated then we skip looking at the candidates dependencies.
- // However, we have to be careful as the new path may make pub dependencies invalid.
-
- // Triggering this case requires dependencies to be resolved in a specific order.
- // Fuzzing found this unintuitive case, that triggers this unfortunate order of operations:
- // 1. `D`'s dep on `B` is resolved
- // 2. `D`'s dep on `C` is resolved
- // 3. `B`'s dep on `A` is resolved with `0.0.0`
- // 4. `C`'s dep on `B` triggering the optimization.
- // So did we add `A 0.0.0` to the deps `C` can see?
- // Or are we going to resolve `C`'s dep on `A` with `0.0.2`?
- // Lets try it and see.
- let reg = registry(vec![
- pkg!(("A", "0.0.0")),
- pkg!(("A", "0.0.2")),
- pkg!("B" => [dep_req_kind("A", "=0.0.0", DepKind::Normal, true),]),
- pkg!("C" => [dep("A"),dep("B")]),
- pkg!("D" => [dep("B"),dep("C")]),
- ]);
- let res = resolve_and_validated(vec![dep("D")], &reg, None).unwrap();
- assert_same(
- &res,
- &names(&[
- ("root", "1.0.0"),
- ("D", "1.0.0"),
- ("C", "1.0.0"),
- ("B", "1.0.0"),
- ("A", "0.0.0"),
- ]),
- );
-}
-
-#[test]
-fn public_dependency_skipping() {
- // When backtracking due to a failed dependency, if Cargo is
- // trying to be clever and skip irrelevant dependencies, care must
- // the effects of pub dep must be accounted for.
- let input = vec![
- pkg!(("a", "0.2.0")),
- pkg!(("a", "2.0.0")),
- pkg!(("b", "0.0.0") => [dep("bad")]),
- pkg!(("b", "0.2.1") => [dep_req_kind("a", "0.2.0", DepKind::Normal, true)]),
- pkg!("c" => [dep("a"),dep("b")]),
- ];
- let reg = registry(input);
-
- resolve_and_validated(vec![dep("c")], &reg, None).unwrap();
-}
-
-#[test]
-fn public_dependency_skipping_in_backtracking() {
- // When backtracking due to a failed dependency, if Cargo is
- // trying to be clever and skip irrelevant dependencies, care must
- // the effects of pub dep must be accounted for.
- let input = vec![
- pkg!(("A", "0.0.0") => [dep("bad")]),
- pkg!(("A", "0.0.1") => [dep("bad")]),
- pkg!(("A", "0.0.2") => [dep("bad")]),
- pkg!(("A", "0.0.3") => [dep("bad")]),
- pkg!(("A", "0.0.4")),
- pkg!(("A", "0.0.5")),
- pkg!("B" => [dep_req_kind("A", ">= 0.0.3", DepKind::Normal, true)]),
- pkg!("C" => [dep_req("A", "<= 0.0.4"), dep("B")]),
- ];
- let reg = registry(input);
-
- resolve_and_validated(vec![dep("C")], &reg, None).unwrap();
-}
-
-#[test]
-fn public_sat_topological_order() {
- let input = vec![
- pkg!(("a", "0.0.1")),
- pkg!(("a", "0.0.0")),
- pkg!(("b", "0.0.1") => [dep_req_kind("a", "= 0.0.1", DepKind::Normal, true),]),
- pkg!(("b", "0.0.0") => [dep("bad"),]),
- pkg!("A" => [dep_req("a", "= 0.0.0"),dep_req_kind("b", "*", DepKind::Normal, true)]),
- ];
-
- let reg = registry(input);
- assert!(resolve_and_validated(vec![dep("A")], &reg, None).is_err());
-}
-
-#[test]
-fn public_sat_unused_makes_things_pub() {
- let input = vec![
- pkg!(("a", "0.0.1")),
- pkg!(("a", "0.0.0")),
- pkg!(("b", "8.0.1") => [dep_req_kind("a", "= 0.0.1", DepKind::Normal, true),]),
- pkg!(("b", "8.0.0") => [dep_req("a", "= 0.0.1"),]),
- pkg!("c" => [dep_req("b", "= 8.0.0"),dep_req("a", "= 0.0.0"),]),
- ];
- let reg = registry(input);
-
- resolve_and_validated(vec![dep("c")], &reg, None).unwrap();
-}
-
-#[test]
-fn public_sat_unused_makes_things_pub_2() {
- let input = vec![
- pkg!(("c", "0.0.2")),
- pkg!(("c", "0.0.1")),
- pkg!(("a-sys", "0.0.2")),
- pkg!(("a-sys", "0.0.1") => [dep_req_kind("c", "= 0.0.1", DepKind::Normal, true),]),
- pkg!("P" => [dep_req_kind("a-sys", "*", DepKind::Normal, true),dep_req("c", "= 0.0.1"),]),
- pkg!("A" => [dep("P"),dep_req("c", "= 0.0.2"),]),
- ];
- let reg = registry(input);
-
- resolve_and_validated(vec![dep("A")], &reg, None).unwrap();
-}
-
-#[test]
#[should_panic(expected = "assertion failed: !name.is_empty()")]
fn test_dependency_with_empty_name() {
// Bug 5229, dependency-names must not be empty
@@ -1116,41 +930,6 @@ fn resolving_with_constrained_sibling_backtrack_activation() {
}
#[test]
-fn resolving_with_public_constrained_sibling() {
- // It makes sense to resolve most-constrained deps first, but
- // with that logic the backtrack traps here come between the two
- // attempted resolutions of 'constrained'. When backtracking,
- // cargo should skip past them and resume resolution once the
- // number of activations for 'constrained' changes.
- let mut reglist = vec![
- pkg!(("foo", "1.0.0") => [dep_req("bar", "=1.0.0"),
- dep_req("backtrack_trap1", "1.0"),
- dep_req("backtrack_trap2", "1.0"),
- dep_req("constrained", "<=60")]),
- pkg!(("bar", "1.0.0") => [dep_req_kind("constrained", ">=60", DepKind::Normal, true)]),
- ];
- // Bump these to make the test harder, but you'll also need to
- // change the version constraints on `constrained` above. To correctly
- // exercise Cargo, the relationship between the values is:
- // NUM_CONSTRAINED - vsn < NUM_TRAPS < vsn
- // to make sure the traps are resolved between `constrained`.
- const NUM_TRAPS: usize = 45; // min 1
- const NUM_CONSTRAINED: usize = 100; // min 1
- for i in 0..NUM_TRAPS {
- let vsn = format!("1.0.{}", i);
- reglist.push(pkg!(("backtrack_trap1", vsn.clone())));
- reglist.push(pkg!(("backtrack_trap2", vsn.clone())));
- }
- for i in 0..NUM_CONSTRAINED {
- let vsn = format!("{}.0.0", i);
- reglist.push(pkg!(("constrained", vsn.clone())));
- }
- let reg = registry(reglist);
-
- let _ = resolve_and_validated(vec![dep_req("foo", "1")], &reg, None);
-}
-
-#[test]
fn resolving_with_constrained_sibling_transitive_dep_effects() {
// When backtracking due to a failed dependency, if Cargo is
// trying to be clever and skip irrelevant dependencies, care must
diff --git a/src/tools/cargo/crates/rustfix/Cargo.toml b/src/tools/cargo/crates/rustfix/Cargo.toml
new file mode 100644
index 000000000..7947f0268
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/Cargo.toml
@@ -0,0 +1,34 @@
+[package]
+name = "rustfix"
+version = "0.7.0"
+authors = [
+ "Pascal Hertleif <killercup@gmail.com>",
+ "Oliver Schneider <oli-obk@users.noreply.github.com>",
+]
+rust-version = "1.70.0" # MSRV:3
+edition.workspace = true
+license.workspace = true
+homepage = "https://github.com/rust-lang/cargo"
+repository = "https://github.com/rust-lang/cargo"
+description = "Automatically apply the suggestions made by rustc"
+documentation = "https://docs.rs/rustfix"
+exclude = [
+ "examples/*",
+ "tests/*",
+]
+
+[dependencies]
+serde = { workspace = true, features = ["derive"] }
+serde_json.workspace = true
+thiserror.workspace = true
+tracing.workspace = true
+
+[dev-dependencies]
+anyhow.workspace = true
+proptest.workspace = true
+similar = "2.3.0"
+tempfile.workspace = true
+tracing-subscriber.workspace = true
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/crates/rustfix/Changelog.md b/src/tools/cargo/crates/rustfix/Changelog.md
new file mode 100644
index 000000000..1b57320e6
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/Changelog.md
@@ -0,0 +1,79 @@
+# Changelog
+
+All notable changes to this project will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+## [0.4.6] - 2019-07-16
+
+### Changed
+
+Internal changes:
+
+- Change example to automatically determine filename
+- Migrate to Rust 2018
+- use `derive` feature over `serde_derive` crate
+
+## [0.4.5] - 2019-03-26
+
+### Added
+
+- Implement common traits for Diagnostic and related types
+
+### Fixed
+
+- Fix out of bounds access in parse_snippet
+
+## [0.4.4] - 2018-12-13
+
+### Added
+
+- Make Diagnostic::rendered public.
+
+### Changed
+
+- Revert faulty "Allow multiple solutions in a suggestion"
+
+## [0.4.3] - 2018-12-09 - *yanked!*
+
+### Added
+
+- Allow multiple solutions in a suggestion
+
+### Changed
+
+- use `RUSTC` environment var if present
+
+## [0.4.2] - 2018-07-31
+
+### Added
+
+- Expose an interface to apply fixes on-by-one
+
+### Changed
+
+- Handle invalid snippets instead of panicking
+
+## [0.4.1] - 2018-07-26
+
+### Changed
+
+- Ignore duplicate replacements
+
+## [0.4.0] - 2018-05-23
+
+### Changed
+
+- Filter by machine applicability by default
+
+[Unreleased]: https://github.com/rust-lang-nursery/rustfix/compare/rustfix-0.4.6...HEAD
+[0.4.6]: https://github.com/rust-lang-nursery/rustfix/compare/rustfix-0.4.5...rustfix-0.4.6
+[0.4.5]: https://github.com/rust-lang-nursery/rustfix/compare/rustfix-0.4.4...rustfix-0.4.5
+[0.4.4]: https://github.com/rust-lang-nursery/rustfix/compare/rustfix-0.4.3...rustfix-0.4.4
+[0.4.3]: https://github.com/rust-lang-nursery/rustfix/compare/rustfix-0.4.2...rustfix-0.4.3
+[0.4.2]: https://github.com/rust-lang-nursery/rustfix/compare/rustfix-0.4.1...rustfix-0.4.2
+[0.4.1]: https://github.com/rust-lang-nursery/rustfix/compare/rustfix-0.4.0...rustfix-0.4.1
+[0.4.0]: https://github.com/rust-lang-nursery/rustfix/compare/rustfix-0.4.0
diff --git a/src/tools/cargo/crates/rustfix/LICENSE-APACHE b/src/tools/cargo/crates/rustfix/LICENSE-APACHE
new file mode 120000
index 000000000..1cd601d0a
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/LICENSE-APACHE
@@ -0,0 +1 @@
+../../LICENSE-APACHE \ No newline at end of file
diff --git a/src/tools/cargo/crates/rustfix/LICENSE-MIT b/src/tools/cargo/crates/rustfix/LICENSE-MIT
new file mode 120000
index 000000000..b2cfbdc7b
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/LICENSE-MIT
@@ -0,0 +1 @@
+../../LICENSE-MIT \ No newline at end of file
diff --git a/src/tools/cargo/crates/rustfix/Readme.md b/src/tools/cargo/crates/rustfix/Readme.md
new file mode 100644
index 000000000..0546e6018
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/Readme.md
@@ -0,0 +1,29 @@
+# rustfix
+
+[![Latest Version](https://img.shields.io/crates/v/rustfix.svg)](https://crates.io/crates/rustfix)
+[![Rust Documentation](https://docs.rs/rustfix/badge.svg)](https://docs.rs/rustfix)
+
+Rustfix is a library defining useful structures that represent fix suggestions from rustc.
+
+This is a low-level library. You pass it the JSON output from `rustc`, and you can then use it to apply suggestions to in-memory strings. This library doesn't execute commands, or read or write from the filesystem.
+
+If you are looking for the [`cargo fix`] implementation, the core of it is located in [`cargo::ops::fix`].
+
+[`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html
+[`cargo::ops::fix`]: https://github.com/rust-lang/cargo/blob/master/src/cargo/ops/fix.rs
+
+## License
+
+Licensed under either of
+
+- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or <http://www.apache.org/licenses/LICENSE-2.0>)
+- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally
+submitted for inclusion in the work by you, as defined in the Apache-2.0
+license, shall be dual licensed as above, without any additional terms or
+conditions.
diff --git a/src/tools/cargo/crates/rustfix/examples/fix-json.rs b/src/tools/cargo/crates/rustfix/examples/fix-json.rs
new file mode 100644
index 000000000..676171106
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/examples/fix-json.rs
@@ -0,0 +1,44 @@
+#![allow(clippy::disallowed_methods, clippy::print_stdout, clippy::print_stderr)]
+
+use anyhow::Error;
+use std::io::{stdin, BufReader, Read};
+use std::{collections::HashMap, collections::HashSet, env, fs};
+
+fn main() -> Result<(), Error> {
+ let suggestions_file = env::args().nth(1).expect("USAGE: fix-json <file or -->");
+ let suggestions = if suggestions_file == "--" {
+ let mut buffer = String::new();
+ BufReader::new(stdin()).read_to_string(&mut buffer)?;
+ buffer
+ } else {
+ fs::read_to_string(&suggestions_file)?
+ };
+ let suggestions = rustfix::get_suggestions_from_json(
+ &suggestions,
+ &HashSet::new(),
+ rustfix::Filter::Everything,
+ )?;
+
+ let mut files = HashMap::new();
+ for suggestion in suggestions {
+ let file = suggestion.solutions[0].replacements[0]
+ .snippet
+ .file_name
+ .clone();
+ files.entry(file).or_insert_with(Vec::new).push(suggestion);
+ }
+
+ for (source_file, suggestions) in &files {
+ let source = fs::read_to_string(source_file)?;
+ let mut fix = rustfix::CodeFix::new(&source);
+ for suggestion in suggestions.iter().rev() {
+ if let Err(e) = fix.apply(suggestion) {
+ eprintln!("Failed to apply suggestion to {}: {}", source_file, e);
+ }
+ }
+ let fixes = fix.finish()?;
+ fs::write(source_file, fixes)?;
+ }
+
+ Ok(())
+}
diff --git a/src/tools/cargo/crates/rustfix/proptest-regressions/replace.txt b/src/tools/cargo/crates/rustfix/proptest-regressions/replace.txt
new file mode 100644
index 000000000..fc5bd1a8b
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/proptest-regressions/replace.txt
@@ -0,0 +1,8 @@
+# Seeds for failure cases proptest has generated in the past. It is
+# automatically read and these particular cases re-run before any
+# novel cases are generated.
+#
+# It is recommended to check this file in to source control so that
+# everyone who runs the test benefits from these saved cases.
+xs 358148376 3634975642 2528447681 3675516813 # shrinks to ref s = ""
+xs 3127423015 3362740891 2605681441 2390162043 # shrinks to ref data = "", ref replacements = [(0..0, [])]
diff --git a/src/tools/cargo/crates/rustfix/src/diagnostics.rs b/src/tools/cargo/crates/rustfix/src/diagnostics.rs
new file mode 100644
index 000000000..ad1899b2c
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/src/diagnostics.rs
@@ -0,0 +1,115 @@
+//! Rustc Diagnostic JSON Output.
+//!
+//! The following data types are copied from [rust-lang/rust](https://github.com/rust-lang/rust/blob/4fd68eb47bad1c121417ac4450b2f0456150db86/compiler/rustc_errors/src/json.rs).
+//!
+//! For examples of the JSON output, see JSON fixture files under `tests/` directory.
+
+use serde::Deserialize;
+
+/// The root diagnostic JSON output emitted by the compiler.
+#[derive(Clone, Deserialize, Debug, Hash, Eq, PartialEq)]
+pub struct Diagnostic {
+ /// The primary error message.
+ pub message: String,
+ pub code: Option<DiagnosticCode>,
+ /// "error: internal compiler error", "error", "warning", "note", "help".
+ level: String,
+ pub spans: Vec<DiagnosticSpan>,
+ /// Associated diagnostic messages.
+ pub children: Vec<Diagnostic>,
+ /// The message as rustc would render it.
+ pub rendered: Option<String>,
+}
+
+/// Span information of a diagnostic item.
+#[derive(Clone, Deserialize, Debug, Hash, Eq, PartialEq)]
+pub struct DiagnosticSpan {
+ pub file_name: String,
+ pub byte_start: u32,
+ pub byte_end: u32,
+ /// 1-based.
+ pub line_start: usize,
+ pub line_end: usize,
+ /// 1-based, character offset.
+ pub column_start: usize,
+ pub column_end: usize,
+ /// Is this a "primary" span -- meaning the point, or one of the points,
+ /// where the error occurred?
+ pub is_primary: bool,
+ /// Source text from the start of line_start to the end of line_end.
+ pub text: Vec<DiagnosticSpanLine>,
+ /// Label that should be placed at this location (if any)
+ label: Option<String>,
+ /// If we are suggesting a replacement, this will contain text
+ /// that should be sliced in atop this span.
+ pub suggested_replacement: Option<String>,
+ /// If the suggestion is approximate
+ pub suggestion_applicability: Option<Applicability>,
+ /// Macro invocations that created the code at this span, if any.
+ expansion: Option<Box<DiagnosticSpanMacroExpansion>>,
+}
+
+/// Indicates the confidence in the correctness of a suggestion.
+///
+/// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion
+/// to determine whether it should be automatically applied or if the user should be consulted
+/// before applying the suggestion.
+#[derive(Copy, Clone, Debug, PartialEq, Deserialize, Hash, Eq)]
+pub enum Applicability {
+ /// The suggestion is definitely what the user intended, or maintains the exact meaning of the code.
+ /// This suggestion should be automatically applied.
+ ///
+ /// In case of multiple `MachineApplicable` suggestions (whether as part of
+ /// the same `multipart_suggestion` or not), all of them should be
+ /// automatically applied.
+ MachineApplicable,
+
+ /// The suggestion may be what the user intended, but it is uncertain. The suggestion should
+ /// result in valid Rust code if it is applied.
+ MaybeIncorrect,
+
+ /// The suggestion contains placeholders like `(...)` or `{ /* fields */ }`. The suggestion
+ /// cannot be applied automatically because it will not result in valid Rust code. The user
+ /// will need to fill in the placeholders.
+ HasPlaceholders,
+
+ /// The applicability of the suggestion is unknown.
+ Unspecified,
+}
+
+/// Span information of a single line.
+#[derive(Clone, Deserialize, Debug, Eq, PartialEq, Hash)]
+pub struct DiagnosticSpanLine {
+ pub text: String,
+
+ /// 1-based, character offset in self.text.
+ pub highlight_start: usize,
+
+ pub highlight_end: usize,
+}
+
+/// Span information for macro expansions.
+#[derive(Clone, Deserialize, Debug, Eq, PartialEq, Hash)]
+struct DiagnosticSpanMacroExpansion {
+ /// span where macro was applied to generate this code; note that
+ /// this may itself derive from a macro (if
+ /// `span.expansion.is_some()`)
+ span: DiagnosticSpan,
+
+ /// name of macro that was applied (e.g., "foo!" or "#[derive(Eq)]")
+ macro_decl_name: String,
+
+ /// span where macro was defined (if known)
+ def_site_span: Option<DiagnosticSpan>,
+}
+
+/// The error code emitted by the compiler. See [Rust error codes index].
+///
+/// [Rust error codes index]: https://doc.rust-lang.org/error_codes/error-index.html
+#[derive(Clone, Deserialize, Debug, Eq, PartialEq, Hash)]
+pub struct DiagnosticCode {
+ /// The code itself.
+ pub code: String,
+ /// An explanation for the code.
+ explanation: Option<String>,
+}
diff --git a/src/tools/cargo/crates/rustfix/src/error.rs b/src/tools/cargo/crates/rustfix/src/error.rs
new file mode 100644
index 000000000..171864504
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/src/error.rs
@@ -0,0 +1,21 @@
+//! Error types.
+
+use std::ops::Range;
+
+#[derive(Debug, thiserror::Error)]
+pub enum Error {
+ #[error("invalid range {0:?}, start is larger than end")]
+ InvalidRange(Range<usize>),
+
+ #[error("invalid range {0:?}, original data is only {1} byte long")]
+ DataLengthExceeded(Range<usize>, usize),
+
+ #[error("could not replace range {0:?}, maybe parts of it were already replaced?")]
+ MaybeAlreadyReplaced(Range<usize>),
+
+ #[error("cannot replace slice of data that was already replaced")]
+ AlreadyReplaced,
+
+ #[error(transparent)]
+ Utf8(#[from] std::string::FromUtf8Error),
+}
diff --git a/src/tools/cargo/crates/rustfix/src/lib.rs b/src/tools/cargo/crates/rustfix/src/lib.rs
new file mode 100644
index 000000000..5fdb37b56
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/src/lib.rs
@@ -0,0 +1,306 @@
+//! Library for applying diagnostic suggestions to source code.
+//!
+//! This is a low-level library. You pass it the [JSON output] from `rustc`,
+//! and you can then use it to apply suggestions to in-memory strings.
+//! This library doesn't execute commands, or read or write from the filesystem.
+//!
+//! If you are looking for the [`cargo fix`] implementation, the core of it is
+//! located in [`cargo::ops::fix`].
+//!
+//! [`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html
+//! [`cargo::ops::fix`]: https://github.com/rust-lang/cargo/blob/master/src/cargo/ops/fix.rs
+//! [JSON output]: diagnostics
+//!
+//! The general outline of how to use this library is:
+//!
+//! 1. Call `rustc` and collect the JSON data.
+//! 2. Pass the json data to [`get_suggestions_from_json`].
+//! 3. Create a [`CodeFix`] with the source of a file to modify.
+//! 4. Call [`CodeFix::apply`] to apply a change.
+//! 5. Call [`CodeFix::finish`] to get the result and write it back to disk.
+
+use std::collections::HashSet;
+use std::ops::Range;
+
+pub mod diagnostics;
+mod error;
+mod replace;
+
+use diagnostics::Diagnostic;
+use diagnostics::DiagnosticSpan;
+pub use error::Error;
+
+/// A filter to control which suggestion should be applied.
+#[derive(Debug, Clone, Copy)]
+pub enum Filter {
+ /// For [`diagnostics::Applicability::MachineApplicable`] only.
+ MachineApplicableOnly,
+ /// Everything is included. YOLO!
+ Everything,
+}
+
+/// Collects code [`Suggestion`]s from one or more compiler diagnostic lines.
+///
+/// Fails if any of diagnostic line `input` is not a valid [`Diagnostic`] JSON.
+///
+/// * `only` --- only diagnostics with code in a set of error codes would be collected.
+pub fn get_suggestions_from_json<S: ::std::hash::BuildHasher>(
+ input: &str,
+ only: &HashSet<String, S>,
+ filter: Filter,
+) -> serde_json::error::Result<Vec<Suggestion>> {
+ let mut result = Vec::new();
+ for cargo_msg in serde_json::Deserializer::from_str(input).into_iter::<Diagnostic>() {
+ // One diagnostic line might have multiple suggestions
+ result.extend(collect_suggestions(&cargo_msg?, only, filter));
+ }
+ Ok(result)
+}
+
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub struct LinePosition {
+ pub line: usize,
+ pub column: usize,
+}
+
+impl std::fmt::Display for LinePosition {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}:{}", self.line, self.column)
+ }
+}
+
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub struct LineRange {
+ pub start: LinePosition,
+ pub end: LinePosition,
+}
+
+impl std::fmt::Display for LineRange {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}-{}", self.start, self.end)
+ }
+}
+
+/// An error/warning and possible solutions for fixing it
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct Suggestion {
+ pub message: String,
+ pub snippets: Vec<Snippet>,
+ pub solutions: Vec<Solution>,
+}
+
+/// Solution to a diagnostic item.
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct Solution {
+ /// The error message of the diagnostic item.
+ pub message: String,
+ /// Possible solutions to fix the error.
+ pub replacements: Vec<Replacement>,
+}
+
+/// Represents code that will get replaced.
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct Snippet {
+ pub file_name: String,
+ pub line_range: LineRange,
+ pub range: Range<usize>,
+ /// leading surrounding text, text to replace, trailing surrounding text
+ ///
+ /// This split is useful for higlighting the part that gets replaced
+ pub text: (String, String, String),
+}
+
+/// Represents a replacement of a `snippet`.
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct Replacement {
+ /// Code snippet that gets replaced.
+ pub snippet: Snippet,
+ /// The replacement of the snippet.
+ pub replacement: String,
+}
+
+/// Parses a [`Snippet`] from a diagnostic span item.
+fn parse_snippet(span: &DiagnosticSpan) -> Option<Snippet> {
+ // unindent the snippet
+ let indent = span
+ .text
+ .iter()
+ .map(|line| {
+ let indent = line
+ .text
+ .chars()
+ .take_while(|&c| char::is_whitespace(c))
+ .count();
+ std::cmp::min(indent, line.highlight_start - 1)
+ })
+ .min()?;
+
+ let text_slice = span.text[0].text.chars().collect::<Vec<char>>();
+
+ // We subtract `1` because these highlights are 1-based
+ // Check the `min` so that it doesn't attempt to index out-of-bounds when
+ // the span points to the "end" of the line. For example, a line of
+ // "foo\n" with a highlight_start of 5 is intended to highlight *after*
+ // the line. This needs to compensate since the newline has been removed
+ // from the text slice.
+ let start = (span.text[0].highlight_start - 1).min(text_slice.len());
+ let end = (span.text[0].highlight_end - 1).min(text_slice.len());
+ let lead = text_slice[indent..start].iter().collect();
+ let mut body: String = text_slice[start..end].iter().collect();
+
+ for line in span.text.iter().take(span.text.len() - 1).skip(1) {
+ body.push('\n');
+ body.push_str(&line.text[indent..]);
+ }
+ let mut tail = String::new();
+ let last = &span.text[span.text.len() - 1];
+
+ // If we get a DiagnosticSpanLine where highlight_end > text.len(), we prevent an 'out of
+ // bounds' access by making sure the index is within the array bounds.
+ // `saturating_sub` is used in case of an empty file
+ let last_tail_index = last.highlight_end.min(last.text.len()).saturating_sub(1);
+ let last_slice = last.text.chars().collect::<Vec<char>>();
+
+ if span.text.len() > 1 {
+ body.push('\n');
+ body.push_str(
+ &last_slice[indent..last_tail_index]
+ .iter()
+ .collect::<String>(),
+ );
+ }
+ tail.push_str(&last_slice[last_tail_index..].iter().collect::<String>());
+ Some(Snippet {
+ file_name: span.file_name.clone(),
+ line_range: LineRange {
+ start: LinePosition {
+ line: span.line_start,
+ column: span.column_start,
+ },
+ end: LinePosition {
+ line: span.line_end,
+ column: span.column_end,
+ },
+ },
+ range: (span.byte_start as usize)..(span.byte_end as usize),
+ text: (lead, body, tail),
+ })
+}
+
+/// Converts a [`DiagnosticSpan`] into a [`Replacement`].
+fn collect_span(span: &DiagnosticSpan) -> Option<Replacement> {
+ let snippet = parse_snippet(span)?;
+ let replacement = span.suggested_replacement.clone()?;
+ Some(Replacement {
+ snippet,
+ replacement,
+ })
+}
+
+/// Collects code [`Suggestion`]s from a single compiler diagnostic line.
+///
+/// * `only` --- only diagnostics with code in a set of error codes would be collected.
+pub fn collect_suggestions<S: ::std::hash::BuildHasher>(
+ diagnostic: &Diagnostic,
+ only: &HashSet<String, S>,
+ filter: Filter,
+) -> Option<Suggestion> {
+ if !only.is_empty() {
+ if let Some(ref code) = diagnostic.code {
+ if !only.contains(&code.code) {
+ // This is not the code we are looking for
+ return None;
+ }
+ } else {
+ // No code, probably a weird builtin warning/error
+ return None;
+ }
+ }
+
+ let snippets = diagnostic.spans.iter().filter_map(parse_snippet).collect();
+
+ let solutions: Vec<_> = diagnostic
+ .children
+ .iter()
+ .filter_map(|child| {
+ let replacements: Vec<_> = child
+ .spans
+ .iter()
+ .filter(|span| {
+ use crate::diagnostics::Applicability::*;
+ use crate::Filter::*;
+
+ match (filter, &span.suggestion_applicability) {
+ (MachineApplicableOnly, Some(MachineApplicable)) => true,
+ (MachineApplicableOnly, _) => false,
+ (Everything, _) => true,
+ }
+ })
+ .filter_map(collect_span)
+ .collect();
+ if !replacements.is_empty() {
+ Some(Solution {
+ message: child.message.clone(),
+ replacements,
+ })
+ } else {
+ None
+ }
+ })
+ .collect();
+
+ if solutions.is_empty() {
+ None
+ } else {
+ Some(Suggestion {
+ message: diagnostic.message.clone(),
+ snippets,
+ solutions,
+ })
+ }
+}
+
+/// Represents a code fix. This doesn't write to disks but is only in memory.
+///
+/// The general way to use this is:
+///
+/// 1. Feeds the source of a file to [`CodeFix::new`].
+/// 2. Calls [`CodeFix::apply`] to apply suggestions to the source code.
+/// 3. Calls [`CodeFix::finish`] to get the "fixed" code.
+pub struct CodeFix {
+ data: replace::Data,
+}
+
+impl CodeFix {
+ /// Creates a `CodeFix` with the source of a file to modify.
+ pub fn new(s: &str) -> CodeFix {
+ CodeFix {
+ data: replace::Data::new(s.as_bytes()),
+ }
+ }
+
+ /// Applies a suggestion to the code.
+ pub fn apply(&mut self, suggestion: &Suggestion) -> Result<(), Error> {
+ for sol in &suggestion.solutions {
+ for r in &sol.replacements {
+ self.data
+ .replace_range(r.snippet.range.clone(), r.replacement.as_bytes())?;
+ }
+ }
+ Ok(())
+ }
+
+ /// Gets the result of the "fixed" code.
+ pub fn finish(&self) -> Result<String, Error> {
+ Ok(String::from_utf8(self.data.to_vec())?)
+ }
+}
+
+/// Applies multiple `suggestions` to the given `code`.
+pub fn apply_suggestions(code: &str, suggestions: &[Suggestion]) -> Result<String, Error> {
+ let mut fix = CodeFix::new(code);
+ for suggestion in suggestions.iter().rev() {
+ fix.apply(suggestion)?;
+ }
+ fix.finish()
+}
diff --git a/src/tools/cargo/crates/rustfix/src/replace.rs b/src/tools/cargo/crates/rustfix/src/replace.rs
new file mode 100644
index 000000000..ed467dcba
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/src/replace.rs
@@ -0,0 +1,329 @@
+//! A small module giving you a simple container that allows easy and cheap
+//! replacement of parts of its content, with the ability to prevent changing
+//! the same parts multiple times.
+
+use std::rc::Rc;
+
+use crate::error::Error;
+
+/// Indicates the change state of a [`Span`].
+#[derive(Debug, Clone, PartialEq, Eq)]
+enum State {
+ /// The initial state. No change applied.
+ Initial,
+ /// Has been replaced.
+ Replaced(Rc<[u8]>),
+ /// Has been inserted.
+ Inserted(Rc<[u8]>),
+}
+
+impl State {
+ fn is_inserted(&self) -> bool {
+ matches!(*self, State::Inserted(..))
+ }
+}
+
+/// Span with a change [`State`].
+#[derive(Debug, Clone, PartialEq, Eq)]
+struct Span {
+ /// Start of this span in parent data
+ start: usize,
+ /// up to end excluding
+ end: usize,
+ /// Whether the span is inserted, replaced or still fresh.
+ data: State,
+}
+
+/// A container that allows easily replacing chunks of its data
+#[derive(Debug, Clone, Default)]
+pub struct Data {
+ /// Original data.
+ original: Vec<u8>,
+ /// [`Span`]s covering the full range of the original data.
+ parts: Vec<Span>,
+}
+
+impl Data {
+ /// Create a new data container from a slice of bytes
+ pub fn new(data: &[u8]) -> Self {
+ Data {
+ original: data.into(),
+ parts: vec![Span {
+ data: State::Initial,
+ start: 0,
+ end: data.len(),
+ }],
+ }
+ }
+
+ /// Render this data as a vector of bytes
+ pub fn to_vec(&self) -> Vec<u8> {
+ if self.original.is_empty() {
+ return Vec::new();
+ }
+
+ self.parts.iter().fold(Vec::new(), |mut acc, d| {
+ match d.data {
+ State::Initial => acc.extend_from_slice(&self.original[d.start..d.end]),
+ State::Replaced(ref d) | State::Inserted(ref d) => acc.extend_from_slice(d),
+ };
+ acc
+ })
+ }
+
+ /// Replace a chunk of data with the given slice, erroring when this part
+ /// was already changed previously.
+ pub fn replace_range(
+ &mut self,
+ range: std::ops::Range<usize>,
+ data: &[u8],
+ ) -> Result<(), Error> {
+ if range.start > range.end {
+ return Err(Error::InvalidRange(range));
+ }
+
+ if range.end > self.original.len() {
+ return Err(Error::DataLengthExceeded(range, self.original.len()));
+ }
+
+ let insert_only = range.start == range.end;
+
+ // Since we error out when replacing an already replaced chunk of data,
+ // we can take some shortcuts here. For example, there can be no
+ // overlapping replacements -- we _always_ split a chunk of 'initial'
+ // data into three[^empty] parts, and there can't ever be two 'initial'
+ // parts touching.
+ //
+ // [^empty]: Leading and trailing ones might be empty if we replace
+ // the whole chunk. As an optimization and without loss of generality we
+ // don't add empty parts.
+ let new_parts = {
+ let Some(index_of_part_to_split) = self.parts.iter().position(|p| {
+ !p.data.is_inserted() && p.start <= range.start && p.end >= range.end
+ }) else {
+ if tracing::enabled!(tracing::Level::DEBUG) {
+ let slices = self
+ .parts
+ .iter()
+ .map(|p| {
+ (
+ p.start,
+ p.end,
+ match p.data {
+ State::Initial => "initial",
+ State::Replaced(..) => "replaced",
+ State::Inserted(..) => "inserted",
+ },
+ )
+ })
+ .collect::<Vec<_>>();
+ tracing::debug!(
+ "no single slice covering {}..{}, current slices: {:?}",
+ range.start,
+ range.end,
+ slices,
+ );
+ }
+
+ return Err(Error::MaybeAlreadyReplaced(range));
+ };
+
+ let part_to_split = &self.parts[index_of_part_to_split];
+
+ // If this replacement matches exactly the part that we would
+ // otherwise split then we ignore this for now. This means that you
+ // can replace the exact same range with the exact same content
+ // multiple times and we'll process and allow it.
+ //
+ // This is currently done to alleviate issues like
+ // rust-lang/rust#51211 although this clause likely wants to be
+ // removed if that's fixed deeper in the compiler.
+ if part_to_split.start == range.start && part_to_split.end == range.end {
+ if let State::Replaced(ref replacement) = part_to_split.data {
+ if &**replacement == data {
+ return Ok(());
+ }
+ }
+ }
+
+ if part_to_split.data != State::Initial {
+ return Err(Error::AlreadyReplaced);
+ }
+
+ let mut new_parts = Vec::with_capacity(self.parts.len() + 2);
+
+ // Previous parts
+ if let Some(ps) = self.parts.get(..index_of_part_to_split) {
+ new_parts.extend_from_slice(ps);
+ }
+
+ // Keep initial data on left side of part
+ if range.start > part_to_split.start {
+ new_parts.push(Span {
+ start: part_to_split.start,
+ end: range.start,
+ data: State::Initial,
+ });
+ }
+
+ // New part
+ new_parts.push(Span {
+ start: range.start,
+ end: range.end,
+ data: if insert_only {
+ State::Inserted(data.into())
+ } else {
+ State::Replaced(data.into())
+ },
+ });
+
+ // Keep initial data on right side of part
+ if range.end < part_to_split.end {
+ new_parts.push(Span {
+ start: range.end,
+ end: part_to_split.end,
+ data: State::Initial,
+ });
+ }
+
+ // Following parts
+ if let Some(ps) = self.parts.get(index_of_part_to_split + 1..) {
+ new_parts.extend_from_slice(ps);
+ }
+
+ new_parts
+ };
+
+ self.parts = new_parts;
+
+ Ok(())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use proptest::prelude::*;
+
+ fn str(i: &[u8]) -> &str {
+ ::std::str::from_utf8(i).unwrap()
+ }
+
+ #[test]
+ fn insert_at_beginning() {
+ let mut d = Data::new(b"foo bar baz");
+ d.replace_range(0..0, b"oh no ").unwrap();
+ assert_eq!("oh no foo bar baz", str(&d.to_vec()));
+ }
+
+ #[test]
+ fn insert_at_end() {
+ let mut d = Data::new(b"foo bar baz");
+ d.replace_range(11..11, b" oh no").unwrap();
+ assert_eq!("foo bar baz oh no", str(&d.to_vec()));
+ }
+
+ #[test]
+ fn replace_some_stuff() {
+ let mut d = Data::new(b"foo bar baz");
+ d.replace_range(4..7, b"lol").unwrap();
+ assert_eq!("foo lol baz", str(&d.to_vec()));
+ }
+
+ #[test]
+ fn replace_a_single_char() {
+ let mut d = Data::new(b"let y = true;");
+ d.replace_range(4..5, b"mut y").unwrap();
+ assert_eq!("let mut y = true;", str(&d.to_vec()));
+ }
+
+ #[test]
+ fn replace_multiple_lines() {
+ let mut d = Data::new(b"lorem\nipsum\ndolor");
+
+ d.replace_range(6..11, b"lol").unwrap();
+ assert_eq!("lorem\nlol\ndolor", str(&d.to_vec()));
+
+ d.replace_range(12..17, b"lol").unwrap();
+ assert_eq!("lorem\nlol\nlol", str(&d.to_vec()));
+ }
+
+ #[test]
+ fn replace_multiple_lines_with_insert_only() {
+ let mut d = Data::new(b"foo!");
+
+ d.replace_range(3..3, b"bar").unwrap();
+ assert_eq!("foobar!", str(&d.to_vec()));
+
+ d.replace_range(0..3, b"baz").unwrap();
+ assert_eq!("bazbar!", str(&d.to_vec()));
+
+ d.replace_range(3..4, b"?").unwrap();
+ assert_eq!("bazbar?", str(&d.to_vec()));
+ }
+
+ #[test]
+ fn replace_invalid_range() {
+ let mut d = Data::new(b"foo!");
+
+ assert!(d.replace_range(2..1, b"bar").is_err());
+ assert!(d.replace_range(0..3, b"bar").is_ok());
+ }
+
+ #[test]
+ fn empty_to_vec_roundtrip() {
+ let s = "";
+ assert_eq!(s.as_bytes(), Data::new(s.as_bytes()).to_vec().as_slice());
+ }
+
+ #[test]
+ fn replace_overlapping_stuff_errs() {
+ let mut d = Data::new(b"foo bar baz");
+
+ d.replace_range(4..7, b"lol").unwrap();
+ assert_eq!("foo lol baz", str(&d.to_vec()));
+
+ assert!(matches!(
+ d.replace_range(4..7, b"lol2").unwrap_err(),
+ Error::AlreadyReplaced,
+ ));
+ }
+
+ #[test]
+ fn broken_replacements() {
+ let mut d = Data::new(b"foo");
+ assert!(matches!(
+ d.replace_range(4..8, b"lol").unwrap_err(),
+ Error::DataLengthExceeded(std::ops::Range { start: 4, end: 8 }, 3),
+ ));
+ }
+
+ #[test]
+ fn replace_same_twice() {
+ let mut d = Data::new(b"foo");
+ d.replace_range(0..1, b"b").unwrap();
+ d.replace_range(0..1, b"b").unwrap();
+ assert_eq!("boo", str(&d.to_vec()));
+ }
+
+ proptest! {
+ #[test]
+ fn new_to_vec_roundtrip(ref s in "\\PC*") {
+ assert_eq!(s.as_bytes(), Data::new(s.as_bytes()).to_vec().as_slice());
+ }
+
+ #[test]
+ fn replace_random_chunks(
+ ref data in "\\PC*",
+ ref replacements in prop::collection::vec(
+ (any::<::std::ops::Range<usize>>(), any::<Vec<u8>>()),
+ 1..100,
+ )
+ ) {
+ let mut d = Data::new(data.as_bytes());
+ for &(ref range, ref bytes) in replacements {
+ let _ = d.replace_range(range.clone(), bytes);
+ }
+ }
+ }
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/edge-cases/empty.json b/src/tools/cargo/crates/rustfix/tests/edge-cases/empty.json
new file mode 100644
index 000000000..62df0b936
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/edge-cases/empty.json
@@ -0,0 +1,42 @@
+{
+ "message": "`main` function not found in crate `empty`",
+ "code": {
+ "code": "E0601",
+ "explanation": "No `main` function was found in a binary crate. To fix this error, add a\n`main` function. For example:\n\n```\nfn main() {\n // Your program will start here.\n println!(\"Hello world!\");\n}\n```\n\nIf you don't know the basics of Rust, you can go look to the Rust Book to get\nstarted: https://doc.rust-lang.org/book/\n"
+ },
+ "level": "error",
+ "spans": [
+ {
+ "file_name": "empty.rs",
+ "byte_start": 0,
+ "byte_end": 0,
+ "line_start": 0,
+ "line_end": 0,
+ "column_start": 1,
+ "column_end": 1,
+ "is_primary": true,
+ "text": [
+ {
+ "text": "",
+ "highlight_start": 1,
+ "highlight_end": 1
+ }
+ ],
+ "label": null,
+ "suggested_replacement": null,
+ "suggestion_applicability": null,
+ "expansion": null
+ }
+ ],
+ "children": [
+ {
+ "message": "consider adding a `main` function to `empty.rs`",
+ "code": null,
+ "level": "note",
+ "spans": [],
+ "children": [],
+ "rendered": null
+ }
+ ],
+ "rendered": "error[E0601]: `main` function not found in crate `empty`\n |\n = note: consider adding a `main` function to `empty.rs`\n\n"
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/edge-cases/empty.rs b/src/tools/cargo/crates/rustfix/tests/edge-cases/empty.rs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/edge-cases/empty.rs
diff --git a/src/tools/cargo/crates/rustfix/tests/edge-cases/indented_whitespace.json b/src/tools/cargo/crates/rustfix/tests/edge-cases/indented_whitespace.json
new file mode 100644
index 000000000..b25189aaf
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/edge-cases/indented_whitespace.json
@@ -0,0 +1,60 @@
+{
+ "message": "non-ASCII whitespace symbol '\\u{a0}' is not skipped",
+ "code": null,
+ "level": "warning",
+ "spans":
+ [
+ {
+ "file_name": "lib.rs",
+ "byte_start": 26,
+ "byte_end": 28,
+ "line_start": 2,
+ "line_end": 2,
+ "column_start": 1,
+ "column_end": 2,
+ "is_primary": false,
+ "text":
+ [
+ {
+ "text": " indented\";",
+ "highlight_start": 1,
+ "highlight_end": 2
+ }
+ ],
+ "label": "non-ASCII whitespace symbol '\\u{a0}' is not skipped",
+ "suggested_replacement": null,
+ "suggestion_applicability": null,
+ "expansion": null
+ },
+ {
+ "file_name": "lib.rs",
+ "byte_start": 24,
+ "byte_end": 28,
+ "line_start": 1,
+ "line_end": 2,
+ "column_start": 25,
+ "column_end": 2,
+ "is_primary": true,
+ "text":
+ [
+ {
+ "text": "pub static FOO: &str = \"\\",
+ "highlight_start": 25,
+ "highlight_end": 26
+ },
+ {
+ "text": " indented\";",
+ "highlight_start": 1,
+ "highlight_end": 2
+ }
+ ],
+ "label": null,
+ "suggested_replacement": null,
+ "suggestion_applicability": null,
+ "expansion": null
+ }
+ ],
+ "children":
+ [],
+ "rendered": "warning: non-ASCII whitespace symbol '\\u{a0}' is not skipped\n --> lib.rs:1:25\n |\n1 | pub static FOO: &str = \"\\\n | _________________________^\n2 | |  indented\";\n | | ^ non-ASCII whitespace symbol '\\u{a0}' is not skipped\n | |_|\n | \n\n"
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/edge-cases/no_main.json b/src/tools/cargo/crates/rustfix/tests/edge-cases/no_main.json
new file mode 100644
index 000000000..e4b1c8f97
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/edge-cases/no_main.json
@@ -0,0 +1,33 @@
+{
+ "message": "`main` function not found in crate `no_main`",
+ "code": {
+ "code": "E0601",
+ "explanation": "No `main` function was found in a binary crate. To fix this error, add a\n`main` function. For example:\n\n```\nfn main() {\n // Your program will start here.\n println!(\"Hello world!\");\n}\n```\n\nIf you don't know the basics of Rust, you can go look to the Rust Book to get\nstarted: https://doc.rust-lang.org/book/\n"
+ },
+ "level": "error",
+ "spans": [
+ {
+ "file_name": "no_main.rs",
+ "byte_start": 26,
+ "byte_end": 26,
+ "line_start": 1,
+ "line_end": 1,
+ "column_start": 27,
+ "column_end": 27,
+ "is_primary": true,
+ "text": [
+ {
+ "text": "// This file has no main.",
+ "highlight_start": 27,
+ "highlight_end": 27
+ }
+ ],
+ "label": "consider adding a `main` function to `no_main.rs`",
+ "suggested_replacement": null,
+ "suggestion_applicability": null,
+ "expansion": null
+ }
+ ],
+ "children": [],
+ "rendered": "error[E0601]: `main` function not found in crate `no_main`\n --> no_main.rs:1:27\n |\n1 | // This file has no main.\n | ^ consider adding a `main` function to `no_main.rs`\n\n"
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/edge-cases/no_main.rs b/src/tools/cargo/crates/rustfix/tests/edge-cases/no_main.rs
new file mode 100644
index 000000000..0147ba726
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/edge-cases/no_main.rs
@@ -0,0 +1 @@
+// This file has no main.
diff --git a/src/tools/cargo/crates/rustfix/tests/edge-cases/out_of_bounds.recorded.json b/src/tools/cargo/crates/rustfix/tests/edge-cases/out_of_bounds.recorded.json
new file mode 100644
index 000000000..147debb6c
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/edge-cases/out_of_bounds.recorded.json
@@ -0,0 +1,43 @@
+{
+ "message": "unterminated double quote string",
+ "code": null,
+ "level": "error",
+ "spans": [
+ {
+ "file_name": "./tests/everything/tab_2.rs",
+ "byte_start": 485,
+ "byte_end": 526,
+ "line_start": 12,
+ "line_end": 13,
+ "column_start": 7,
+ "column_end": 3,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " \"\"\"; //~ ERROR unterminated double quote",
+ "highlight_start": 7,
+ "highlight_end": 45
+ },
+ {
+ "text": "}",
+ "highlight_start": 1,
+ "highlight_end": 3
+ }
+ ],
+ "label": null,
+ "suggested_replacement": null,
+ "suggestion_applicability": null,
+ "expansion": null
+ }
+ ],
+ "children": [],
+ "rendered": "error: unterminated double quote string\n --> ./tests/everything/tab_2.rs:12:7\n |\n12 | \"\"\"; //~ ERROR unterminated double quote\n | _______^\n13 | | }\n | |__^\n\n"
+}
+{
+ "message": "aborting due to previous error",
+ "code": null,
+ "level": "error",
+ "spans": [],
+ "children": [],
+ "rendered": "error: aborting due to previous error\n\n"
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/edge-cases/utf8_idents.recorded.json b/src/tools/cargo/crates/rustfix/tests/edge-cases/utf8_idents.recorded.json
new file mode 100644
index 000000000..28950d694
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/edge-cases/utf8_idents.recorded.json
@@ -0,0 +1,59 @@
+{
+ "message": "expected one of `,`, `:`, `=`, or `>`, found `'β`",
+ "code": null,
+ "level": "error",
+ "spans": [
+ {
+ "file_name": "./tests/everything/utf8_idents.rs",
+ "byte_start": 14,
+ "byte_end": 14,
+ "line_start": 2,
+ "line_end": 2,
+ "column_start": 6,
+ "column_end": 6,
+ "is_primary": false,
+ "text": [
+ {
+ "text": " γ //~ ERROR non-ascii idents are not fully supported",
+ "highlight_start": 6,
+ "highlight_end": 6
+ }
+ ],
+ "label": "expected one of `,`, `:`, `=`, or `>` here",
+ "suggested_replacement": null,
+ "suggestion_applicability": null,
+ "expansion": null
+ },
+ {
+ "file_name": "./tests/everything/utf8_idents.rs",
+ "byte_start": 145,
+ "byte_end": 148,
+ "line_start": 4,
+ "line_end": 4,
+ "column_start": 5,
+ "column_end": 7,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " 'β, //~ ERROR non-ascii idents are not fully supported",
+ "highlight_start": 5,
+ "highlight_end": 7
+ }
+ ],
+ "label": "unexpected token",
+ "suggested_replacement": null,
+ "suggestion_applicability": null,
+ "expansion": null
+ }
+ ],
+ "children": [],
+ "rendered": "error: expected one of `,`, `:`, `=`, or `>`, found `'β`\n --> ./tests/everything/utf8_idents.rs:4:5\n |\n2 | γ //~ ERROR non-ascii idents are not fully supported\n | - expected one of `,`, `:`, `=`, or `>` here\n3 | //~^ WARN type parameter `γ` should have an upper camel case name\n4 | 'β, //~ ERROR non-ascii idents are not fully supported\n | ^^ unexpected token\n\n"
+}
+{
+ "message": "aborting due to previous error",
+ "code": null,
+ "level": "error",
+ "spans": [],
+ "children": [],
+ "rendered": "error: aborting due to previous error\n\n"
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/edge_cases.rs b/src/tools/cargo/crates/rustfix/tests/edge_cases.rs
new file mode 100644
index 000000000..42d1e405a
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/edge_cases.rs
@@ -0,0 +1,25 @@
+use rustfix;
+use std::collections::HashSet;
+use std::fs;
+
+macro_rules! expect_empty_json_test {
+ ($name:ident, $file:expr) => {
+ #[test]
+ fn $name() {
+ let json = fs::read_to_string(concat!("./tests/edge-cases/", $file)).unwrap();
+ let expected_suggestions = rustfix::get_suggestions_from_json(
+ &json,
+ &HashSet::new(),
+ rustfix::Filter::Everything,
+ )
+ .unwrap();
+ assert!(expected_suggestions.is_empty());
+ }
+ };
+}
+
+expect_empty_json_test! {out_of_bounds_test, "out_of_bounds.recorded.json"}
+expect_empty_json_test! {utf8_identifiers_test, "utf8_idents.recorded.json"}
+expect_empty_json_test! {empty, "empty.json"}
+expect_empty_json_test! {no_main, "no_main.json"}
+expect_empty_json_test! {indented_whitespace, "indented_whitespace.json"}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/E0178.fixed.rs b/src/tools/cargo/crates/rustfix/tests/everything/E0178.fixed.rs
new file mode 100644
index 000000000..07e611774
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/E0178.fixed.rs
@@ -0,0 +1,10 @@
+#![allow(dead_code)]
+
+trait Foo {}
+
+struct Bar<'a> {
+ w: &'a (dyn Foo + Send),
+}
+
+fn main() {
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/E0178.json b/src/tools/cargo/crates/rustfix/tests/everything/E0178.json
new file mode 100644
index 000000000..89f15b528
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/E0178.json
@@ -0,0 +1,70 @@
+{
+ "message": "expected a path on the left-hand side of `+`, not `&'a Foo`",
+ "code": {
+ "code": "E0178",
+ "explanation": "\nIn types, the `+` type operator has low precedence, so it is often necessary\nto use parentheses.\n\nFor example:\n\n```compile_fail,E0178\ntrait Foo {}\n\nstruct Bar<'a> {\n w: &'a Foo + Copy, // error, use &'a (Foo + Copy)\n x: &'a Foo + 'a, // error, use &'a (Foo + 'a)\n y: &'a mut Foo + 'a, // error, use &'a mut (Foo + 'a)\n z: fn() -> Foo + 'a, // error, use fn() -> (Foo + 'a)\n}\n```\n\nMore details can be found in [RFC 438].\n\n[RFC 438]: https://github.com/rust-lang/rfcs/pull/438\n"
+ },
+ "level": "error",
+ "spans": [
+ {
+ "file_name": "./tests/everything/E0178.rs",
+ "byte_start": 60,
+ "byte_end": 74,
+ "line_start": 6,
+ "line_end": 6,
+ "column_start": 8,
+ "column_end": 22,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " w: &'a Foo + Send,",
+ "highlight_start": 8,
+ "highlight_end": 22
+ }
+ ],
+ "label": null,
+ "suggested_replacement": null,
+ "expansion": null
+ }
+ ],
+ "children": [
+ {
+ "message": "try adding parentheses",
+ "code": null,
+ "level": "help",
+ "spans": [
+ {
+ "file_name": "./tests/everything/E0178.rs",
+ "byte_start": 60,
+ "byte_end": 74,
+ "line_start": 6,
+ "line_end": 6,
+ "column_start": 8,
+ "column_end": 22,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " w: &'a Foo + Send,",
+ "highlight_start": 8,
+ "highlight_end": 22
+ }
+ ],
+ "label": null,
+ "suggested_replacement": "&'a (Foo + Send)",
+ "expansion": null
+ }
+ ],
+ "children": [],
+ "rendered": null
+ }
+ ],
+ "rendered": "error[E0178]: expected a path on the left-hand side of `+`, not `&'a Foo`\n --> ./tests/everything/E0178.rs:6:8\n |\n6 | w: &'a Foo + Send,\n | ^^^^^^^^^^^^^^ help: try adding parentheses: `&'a (Foo + Send)`\n\nIf you want more information on this error, try using \"rustc --explain E0178\"\n"
+}
+{
+ "message": "aborting due to previous error",
+ "code": null,
+ "level": "error",
+ "spans": [],
+ "children": [],
+ "rendered": "error: aborting due to previous error\n\n"
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/E0178.rs b/src/tools/cargo/crates/rustfix/tests/everything/E0178.rs
new file mode 100644
index 000000000..24226fe0e
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/E0178.rs
@@ -0,0 +1,10 @@
+#![allow(dead_code)]
+
+trait Foo {}
+
+struct Bar<'a> {
+ w: &'a dyn Foo + Send,
+}
+
+fn main() {
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/closure-immutable-outer-variable.fixed.rs b/src/tools/cargo/crates/rustfix/tests/everything/closure-immutable-outer-variable.fixed.rs
new file mode 100644
index 000000000..e443e024b
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/closure-immutable-outer-variable.fixed.rs
@@ -0,0 +1,10 @@
+// Point at the captured immutable outer variable
+
+fn foo(mut f: Box<dyn FnMut()>) {
+ f();
+}
+
+fn main() {
+ let mut y = true;
+ foo(Box::new(move || y = false) as Box<_>); //~ ERROR cannot assign to captured outer variable
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/closure-immutable-outer-variable.json b/src/tools/cargo/crates/rustfix/tests/everything/closure-immutable-outer-variable.json
new file mode 100644
index 000000000..f7afa491d
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/closure-immutable-outer-variable.json
@@ -0,0 +1,70 @@
+{
+ "message": "cannot assign to captured outer variable in an `FnMut` closure",
+ "code": {
+ "code": "E0594",
+ "explanation": null
+ },
+ "level": "error",
+ "spans": [
+ {
+ "file_name": "./tests/everything/closure-immutable-outer-variable.rs",
+ "byte_start": 615,
+ "byte_end": 624,
+ "line_start": 19,
+ "line_end": 19,
+ "column_start": 26,
+ "column_end": 35,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " foo(Box::new(move || y = false) as Box<_>); //~ ERROR cannot assign to captured outer variable",
+ "highlight_start": 26,
+ "highlight_end": 35
+ }
+ ],
+ "label": null,
+ "suggested_replacement": null,
+ "expansion": null
+ }
+ ],
+ "children": [
+ {
+ "message": "consider making `y` mutable",
+ "code": null,
+ "level": "help",
+ "spans": [
+ {
+ "file_name": "./tests/everything/closure-immutable-outer-variable.rs",
+ "byte_start": 580,
+ "byte_end": 581,
+ "line_start": 18,
+ "line_end": 18,
+ "column_start": 9,
+ "column_end": 10,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " let y = true;",
+ "highlight_start": 9,
+ "highlight_end": 10
+ }
+ ],
+ "label": null,
+ "suggested_replacement": "mut y",
+ "expansion": null
+ }
+ ],
+ "children": [],
+ "rendered": null
+ }
+ ],
+ "rendered": "error[E0594]: cannot assign to captured outer variable in an `FnMut` closure\n --> ./tests/everything/closure-immutable-outer-variable.rs:19:26\n |\n18 | let y = true;\n | - help: consider making `y` mutable: `mut y`\n19 | foo(Box::new(move || y = false) as Box<_>); //~ ERROR cannot assign to captured outer variable\n | ^^^^^^^^^\n\nIf you want more information on this error, try using \"rustc --explain E0594\"\n"
+}
+{
+ "message": "aborting due to previous error",
+ "code": null,
+ "level": "error",
+ "spans": [],
+ "children": [],
+ "rendered": "error: aborting due to previous error\n\n"
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/closure-immutable-outer-variable.rs b/src/tools/cargo/crates/rustfix/tests/everything/closure-immutable-outer-variable.rs
new file mode 100644
index 000000000..c97ec3589
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/closure-immutable-outer-variable.rs
@@ -0,0 +1,10 @@
+// Point at the captured immutable outer variable
+
+fn foo(mut f: Box<FnMut()>) {
+ f();
+}
+
+fn main() {
+ let y = true;
+ foo(Box::new(move || y = false) as Box<_>); //~ ERROR cannot assign to captured outer variable
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/handle-insert-only.fixed.rs b/src/tools/cargo/crates/rustfix/tests/everything/handle-insert-only.fixed.rs
new file mode 100644
index 000000000..604bc8503
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/handle-insert-only.fixed.rs
@@ -0,0 +1,8 @@
+fn main() {
+ // insert only fix, adds `,` to first match arm
+ // why doesnt this replace 1 with 1,?
+ match &Some(3) {
+ &None => 1,
+ &Some(x) => x,
+ };
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/handle-insert-only.json b/src/tools/cargo/crates/rustfix/tests/everything/handle-insert-only.json
new file mode 100644
index 000000000..c32a71290
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/handle-insert-only.json
@@ -0,0 +1,68 @@
+{
+ "message": "expected one of `,`, `.`, `?`, `}`, or an operator, found `=>`",
+ "code": null,
+ "level": "error",
+ "spans": [
+ {
+ "file_name": "./tests/everything/handle-insert-only.rs",
+ "byte_start": 163,
+ "byte_end": 165,
+ "line_start": 6,
+ "line_end": 6,
+ "column_start": 18,
+ "column_end": 20,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " &Some(x) => x,",
+ "highlight_start": 18,
+ "highlight_end": 20
+ }
+ ],
+ "label": "expected one of `,`, `.`, `?`, `}`, or an operator here",
+ "suggested_replacement": null,
+ "expansion": null
+ }
+ ],
+ "children": [
+ {
+ "message": "missing a comma here to end this `match` arm",
+ "code": null,
+ "level": "help",
+ "spans": [
+ {
+ "file_name": "./tests/everything/handle-insert-only.rs",
+ "byte_start": 145,
+ "byte_end": 145,
+ "line_start": 5,
+ "line_end": 5,
+ "column_start": 19,
+ "column_end": 19,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " &None => 1",
+ "highlight_start": 19,
+ "highlight_end": 19
+ }
+ ],
+ "label": null,
+ "suggested_replacement": ",",
+ "suggestion_applicability": "Unspecified",
+ "expansion": null
+ }
+ ],
+ "children": [],
+ "rendered": null
+ }
+ ],
+ "rendered": "error: expected one of `,`, `.`, `?`, `}`, or an operator, found `=>`\n --> ./tests/everything/handle-insert-only.rs:6:18\n |\n5 | &None => 1\n | - help: missing a comma here to end this `match` arm\n6 | &Some(x) => x,\n | ^^ expected one of `,`, `.`, `?`, `}`, or an operator here\n\n"
+}
+{
+ "message": "aborting due to previous error",
+ "code": null,
+ "level": "error",
+ "spans": [],
+ "children": [],
+ "rendered": "error: aborting due to previous error\n\n"
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/handle-insert-only.rs b/src/tools/cargo/crates/rustfix/tests/everything/handle-insert-only.rs
new file mode 100644
index 000000000..d42a4caa8
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/handle-insert-only.rs
@@ -0,0 +1,8 @@
+fn main() {
+ // insert only fix, adds `,` to first match arm
+ // why doesnt this replace 1 with 1,?
+ match &Some(3) {
+ &None => 1
+ &Some(x) => x,
+ };
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/lt-generic-comp.fixed.rs b/src/tools/cargo/crates/rustfix/tests/everything/lt-generic-comp.fixed.rs
new file mode 100644
index 000000000..533c91734
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/lt-generic-comp.fixed.rs
@@ -0,0 +1,7 @@
+fn main() {
+ let x = 5i64;
+
+ if (x as u32) < 4 {
+ println!("yay");
+ }
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/lt-generic-comp.json b/src/tools/cargo/crates/rustfix/tests/everything/lt-generic-comp.json
new file mode 100644
index 000000000..0634762e3
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/lt-generic-comp.json
@@ -0,0 +1,87 @@
+{
+ "message": "`<` is interpreted as a start of generic arguments for `u32`, not a comparison",
+ "code": null,
+ "level": "error",
+ "spans": [
+ {
+ "file_name": "./tests/everything/lt-generic-comp.rs",
+ "byte_start": 49,
+ "byte_end": 50,
+ "line_start": 4,
+ "line_end": 4,
+ "column_start": 19,
+ "column_end": 20,
+ "is_primary": false,
+ "text": [
+ {
+ "text": " if x as u32 < 4 {",
+ "highlight_start": 19,
+ "highlight_end": 20
+ }
+ ],
+ "label": "interpreted as generic arguments",
+ "suggested_replacement": null,
+ "expansion": null
+ },
+ {
+ "file_name": "./tests/everything/lt-generic-comp.rs",
+ "byte_start": 47,
+ "byte_end": 48,
+ "line_start": 4,
+ "line_end": 4,
+ "column_start": 17,
+ "column_end": 18,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " if x as u32 < 4 {",
+ "highlight_start": 17,
+ "highlight_end": 18
+ }
+ ],
+ "label": "not interpreted as comparison",
+ "suggested_replacement": null,
+ "expansion": null
+ }
+ ],
+ "children": [
+ {
+ "message": "try comparing the cast value",
+ "code": null,
+ "level": "help",
+ "spans": [
+ {
+ "file_name": "./tests/everything/lt-generic-comp.rs",
+ "byte_start": 38,
+ "byte_end": 46,
+ "line_start": 4,
+ "line_end": 4,
+ "column_start": 8,
+ "column_end": 16,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " if x as u32 < 4 {",
+ "highlight_start": 8,
+ "highlight_end": 16
+ }
+ ],
+ "label": null,
+ "suggested_replacement": "(x as u32)",
+ "expansion": null
+ }
+ ],
+ "children": [],
+ "rendered": null
+ }
+ ],
+ "rendered": "error: `<` is interpreted as a start of generic arguments for `u32`, not a comparison\n --> ./tests/everything/lt-generic-comp.rs:4:17\n |\n4 | if x as u32 < 4 {\n | -------- ^ - interpreted as generic arguments\n | | |\n | | not interpreted as comparison\n | help: try comparing the cast value: `(x as u32)`\n\n"
+}
+{
+ "message": "aborting due to previous error",
+ "code": null,
+ "level": "error",
+ "spans": [],
+ "children": [],
+ "rendered": "error: aborting due to previous error\n\n"
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/lt-generic-comp.rs b/src/tools/cargo/crates/rustfix/tests/everything/lt-generic-comp.rs
new file mode 100644
index 000000000..c279b261f
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/lt-generic-comp.rs
@@ -0,0 +1,7 @@
+fn main() {
+ let x = 5i64;
+
+ if x as u32 < 4 {
+ println!("yay");
+ }
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/multiple-solutions.fixed.rs b/src/tools/cargo/crates/rustfix/tests/everything/multiple-solutions.fixed.rs
new file mode 100644
index 000000000..1a261785d
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/multiple-solutions.fixed.rs
@@ -0,0 +1,5 @@
+use std::collections::{HashSet};
+
+fn main() {
+ let _: HashSet<()>;
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/multiple-solutions.json b/src/tools/cargo/crates/rustfix/tests/everything/multiple-solutions.json
new file mode 100644
index 000000000..89b14ccc8
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/multiple-solutions.json
@@ -0,0 +1,114 @@
+{
+ "message": "unused imports: `HashMap`, `VecDeque`",
+ "code": {
+ "code": "unused_imports",
+ "explanation": null
+ },
+ "level": "warning",
+ "spans": [
+ {
+ "file_name": "src/main.rs",
+ "byte_start": 23,
+ "byte_end": 30,
+ "line_start": 1,
+ "line_end": 1,
+ "column_start": 24,
+ "column_end": 31,
+ "is_primary": true,
+ "text": [
+ {
+ "text": "use std::collections::{HashMap, HashSet, VecDeque};",
+ "highlight_start": 24,
+ "highlight_end": 31
+ }
+ ],
+ "label": null,
+ "suggested_replacement": null,
+ "suggestion_applicability": null,
+ "expansion": null
+ },
+ {
+ "file_name": "src/main.rs",
+ "byte_start": 41,
+ "byte_end": 49,
+ "line_start": 1,
+ "line_end": 1,
+ "column_start": 42,
+ "column_end": 50,
+ "is_primary": true,
+ "text": [
+ {
+ "text": "use std::collections::{HashMap, HashSet, VecDeque};",
+ "highlight_start": 42,
+ "highlight_end": 50
+ }
+ ],
+ "label": null,
+ "suggested_replacement": null,
+ "suggestion_applicability": null,
+ "expansion": null
+ }
+ ],
+ "children": [
+ {
+ "message": "#[warn(unused_imports)] on by default",
+ "code": null,
+ "level": "note",
+ "spans": [],
+ "children": [],
+ "rendered": null
+ },
+ {
+ "message": "remove the unused imports",
+ "code": null,
+ "level": "help",
+ "spans": [
+ {
+ "file_name": "src/main.rs",
+ "byte_start": 23,
+ "byte_end": 32,
+ "line_start": 1,
+ "line_end": 1,
+ "column_start": 24,
+ "column_end": 33,
+ "is_primary": true,
+ "text": [
+ {
+ "text": "use std::collections::{HashMap, HashSet, VecDeque};",
+ "highlight_start": 24,
+ "highlight_end": 33
+ }
+ ],
+ "label": null,
+ "suggested_replacement": "",
+ "suggestion_applicability": "MachineApplicable",
+ "expansion": null
+ },
+ {
+ "file_name": "src/main.rs",
+ "byte_start": 39,
+ "byte_end": 49,
+ "line_start": 1,
+ "line_end": 1,
+ "column_start": 40,
+ "column_end": 50,
+ "is_primary": true,
+ "text": [
+ {
+ "text": "use std::collections::{HashMap, HashSet, VecDeque};",
+ "highlight_start": 40,
+ "highlight_end": 50
+ }
+ ],
+ "label": null,
+ "suggested_replacement": "",
+ "suggestion_applicability": "MachineApplicable",
+ "expansion": null
+ }
+ ],
+ "children": [],
+ "rendered": null
+ }
+ ],
+ "rendered": "warning: unused imports: `HashMap`, `VecDeque`\n --> src/main.rs:1:24\n |\n1 | use std::collections::{HashMap, HashSet, VecDeque};\n | ^^^^^^^ ^^^^^^^^\n |\n = note: #[warn(unused_imports)] on by default\nhelp: remove the unused imports\n |\n1 | use std::collections::{HashSet};\n | -- --\n\n"
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/multiple-solutions.rs b/src/tools/cargo/crates/rustfix/tests/everything/multiple-solutions.rs
new file mode 100644
index 000000000..401198f7e
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/multiple-solutions.rs
@@ -0,0 +1,5 @@
+use std::collections::{HashMap, HashSet, VecDeque};
+
+fn main() {
+ let _: HashSet<()>;
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/replace-only-one-char.fixed.rs b/src/tools/cargo/crates/rustfix/tests/everything/replace-only-one-char.fixed.rs
new file mode 100644
index 000000000..1a9298d5f
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/replace-only-one-char.fixed.rs
@@ -0,0 +1,3 @@
+fn main() {
+ let _x = 42;
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/replace-only-one-char.json b/src/tools/cargo/crates/rustfix/tests/everything/replace-only-one-char.json
new file mode 100644
index 000000000..9a70c0880
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/replace-only-one-char.json
@@ -0,0 +1,70 @@
+{
+ "message": "unused variable: `x`",
+ "code": {
+ "code": "unused_variables",
+ "explanation": null
+ },
+ "level": "warning",
+ "spans": [
+ {
+ "file_name": "replace-only-one-char.rs",
+ "byte_start": 20,
+ "byte_end": 21,
+ "line_start": 2,
+ "line_end": 2,
+ "column_start": 9,
+ "column_end": 10,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " let x = 42;",
+ "highlight_start": 9,
+ "highlight_end": 10
+ }
+ ],
+ "label": null,
+ "suggested_replacement": null,
+ "expansion": null
+ }
+ ],
+ "children": [
+ {
+ "message": "#[warn(unused_variables)] on by default",
+ "code": null,
+ "level": "note",
+ "spans": [],
+ "children": [],
+ "rendered": null
+ },
+ {
+ "message": "consider using `_x` instead",
+ "code": null,
+ "level": "help",
+ "spans": [
+ {
+ "file_name": "replace-only-one-char.rs",
+ "byte_start": 20,
+ "byte_end": 21,
+ "line_start": 2,
+ "line_end": 2,
+ "column_start": 9,
+ "column_end": 10,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " let x = 42;",
+ "highlight_start": 9,
+ "highlight_end": 10
+ }
+ ],
+ "label": null,
+ "suggested_replacement": "_x",
+ "expansion": null
+ }
+ ],
+ "children": [],
+ "rendered": null
+ }
+ ],
+ "rendered": "warning: unused variable: `x`\n --> replace-only-one-char.rs:2:9\n |\n2 | let x = 42;\n | ^ help: consider using `_x` instead\n |\n = note: #[warn(unused_variables)] on by default\n\n"
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/replace-only-one-char.rs b/src/tools/cargo/crates/rustfix/tests/everything/replace-only-one-char.rs
new file mode 100644
index 000000000..36e44936f
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/replace-only-one-char.rs
@@ -0,0 +1,3 @@
+fn main() {
+ let x = 42;
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/str-lit-type-mismatch.fixed.rs b/src/tools/cargo/crates/rustfix/tests/everything/str-lit-type-mismatch.fixed.rs
new file mode 100644
index 000000000..d5a81a8a8
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/str-lit-type-mismatch.fixed.rs
@@ -0,0 +1,5 @@
+fn main() {
+ let x: &[u8] = b"foo"; //~ ERROR mismatched types
+ let y: &[u8; 4] = b"baaa"; //~ ERROR mismatched types
+ let z: &str = "foo"; //~ ERROR mismatched types
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/str-lit-type-mismatch.json b/src/tools/cargo/crates/rustfix/tests/everything/str-lit-type-mismatch.json
new file mode 100644
index 000000000..1c852513a
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/str-lit-type-mismatch.json
@@ -0,0 +1,218 @@
+{
+ "message": "mismatched types",
+ "code": {
+ "code": "E0308",
+ "explanation": "\nThis error occurs when the compiler was unable to infer the concrete type of a\nvariable. It can occur for several cases, the most common of which is a\nmismatch in the expected type that the compiler inferred for a variable's\ninitializing expression, and the actual type explicitly assigned to the\nvariable.\n\nFor example:\n\n```compile_fail,E0308\nlet x: i32 = \"I am not a number!\";\n// ~~~ ~~~~~~~~~~~~~~~~~~~~\n// | |\n// | initializing expression;\n// | compiler infers type `&str`\n// |\n// type `i32` assigned to variable `x`\n```\n"
+ },
+ "level": "error",
+ "spans": [
+ {
+ "file_name": "./tests/everything/str-lit-type-mismatch.rs",
+ "byte_start": 499,
+ "byte_end": 504,
+ "line_start": 13,
+ "line_end": 13,
+ "column_start": 20,
+ "column_end": 25,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " let x: &[u8] = \"foo\"; //~ ERROR mismatched types",
+ "highlight_start": 20,
+ "highlight_end": 25
+ }
+ ],
+ "label": "expected slice, found str",
+ "suggested_replacement": null,
+ "expansion": null
+ }
+ ],
+ "children": [
+ {
+ "message": "expected type `&[u8]`\n found type `&'static str`",
+ "code": null,
+ "level": "note",
+ "spans": [],
+ "children": [],
+ "rendered": null
+ },
+ {
+ "message": "consider adding a leading `b`",
+ "code": null,
+ "level": "help",
+ "spans": [
+ {
+ "file_name": "./tests/everything/str-lit-type-mismatch.rs",
+ "byte_start": 499,
+ "byte_end": 504,
+ "line_start": 13,
+ "line_end": 13,
+ "column_start": 20,
+ "column_end": 25,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " let x: &[u8] = \"foo\"; //~ ERROR mismatched types",
+ "highlight_start": 20,
+ "highlight_end": 25
+ }
+ ],
+ "label": null,
+ "suggested_replacement": "b\"foo\"",
+ "expansion": null
+ }
+ ],
+ "children": [],
+ "rendered": null
+ }
+ ],
+ "rendered": "error[E0308]: mismatched types\n --> ./tests/everything/str-lit-type-mismatch.rs:13:20\n |\n13 | let x: &[u8] = \"foo\"; //~ ERROR mismatched types\n | ^^^^^\n | |\n | expected slice, found str\n | help: consider adding a leading `b`: `b\"foo\"`\n |\n = note: expected type `&[u8]`\n found type `&'static str`\n\nIf you want more information on this error, try using \"rustc --explain E0308\"\n"
+}
+{
+ "message": "mismatched types",
+ "code": {
+ "code": "E0308",
+ "explanation": "\nThis error occurs when the compiler was unable to infer the concrete type of a\nvariable. It can occur for several cases, the most common of which is a\nmismatch in the expected type that the compiler inferred for a variable's\ninitializing expression, and the actual type explicitly assigned to the\nvariable.\n\nFor example:\n\n```compile_fail,E0308\nlet x: i32 = \"I am not a number!\";\n// ~~~ ~~~~~~~~~~~~~~~~~~~~\n// | |\n// | initializing expression;\n// | compiler infers type `&str`\n// |\n// type `i32` assigned to variable `x`\n```\n"
+ },
+ "level": "error",
+ "spans": [
+ {
+ "file_name": "./tests/everything/str-lit-type-mismatch.rs",
+ "byte_start": 555,
+ "byte_end": 561,
+ "line_start": 14,
+ "line_end": 14,
+ "column_start": 23,
+ "column_end": 29,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " let y: &[u8; 4] = \"baaa\"; //~ ERROR mismatched types",
+ "highlight_start": 23,
+ "highlight_end": 29
+ }
+ ],
+ "label": "expected array of 4 elements, found str",
+ "suggested_replacement": null,
+ "expansion": null
+ }
+ ],
+ "children": [
+ {
+ "message": "expected type `&[u8; 4]`\n found type `&'static str`",
+ "code": null,
+ "level": "note",
+ "spans": [],
+ "children": [],
+ "rendered": null
+ },
+ {
+ "message": "consider adding a leading `b`",
+ "code": null,
+ "level": "help",
+ "spans": [
+ {
+ "file_name": "./tests/everything/str-lit-type-mismatch.rs",
+ "byte_start": 555,
+ "byte_end": 561,
+ "line_start": 14,
+ "line_end": 14,
+ "column_start": 23,
+ "column_end": 29,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " let y: &[u8; 4] = \"baaa\"; //~ ERROR mismatched types",
+ "highlight_start": 23,
+ "highlight_end": 29
+ }
+ ],
+ "label": null,
+ "suggested_replacement": "b\"baaa\"",
+ "expansion": null
+ }
+ ],
+ "children": [],
+ "rendered": null
+ }
+ ],
+ "rendered": "error[E0308]: mismatched types\n --> ./tests/everything/str-lit-type-mismatch.rs:14:23\n |\n14 | let y: &[u8; 4] = \"baaa\"; //~ ERROR mismatched types\n | ^^^^^^\n | |\n | expected array of 4 elements, found str\n | help: consider adding a leading `b`: `b\"baaa\"`\n |\n = note: expected type `&[u8; 4]`\n found type `&'static str`\n\nIf you want more information on this error, try using \"rustc --explain E0308\"\n"
+}
+{
+ "message": "mismatched types",
+ "code": {
+ "code": "E0308",
+ "explanation": "\nThis error occurs when the compiler was unable to infer the concrete type of a\nvariable. It can occur for several cases, the most common of which is a\nmismatch in the expected type that the compiler inferred for a variable's\ninitializing expression, and the actual type explicitly assigned to the\nvariable.\n\nFor example:\n\n```compile_fail,E0308\nlet x: i32 = \"I am not a number!\";\n// ~~~ ~~~~~~~~~~~~~~~~~~~~\n// | |\n// | initializing expression;\n// | compiler infers type `&str`\n// |\n// type `i32` assigned to variable `x`\n```\n"
+ },
+ "level": "error",
+ "spans": [
+ {
+ "file_name": "./tests/everything/str-lit-type-mismatch.rs",
+ "byte_start": 608,
+ "byte_end": 614,
+ "line_start": 15,
+ "line_end": 15,
+ "column_start": 19,
+ "column_end": 25,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " let z: &str = b\"foo\"; //~ ERROR mismatched types",
+ "highlight_start": 19,
+ "highlight_end": 25
+ }
+ ],
+ "label": "expected str, found array of 3 elements",
+ "suggested_replacement": null,
+ "expansion": null
+ }
+ ],
+ "children": [
+ {
+ "message": "expected type `&str`\n found type `&'static [u8; 3]`",
+ "code": null,
+ "level": "note",
+ "spans": [],
+ "children": [],
+ "rendered": null
+ },
+ {
+ "message": "consider removing the leading `b`",
+ "code": null,
+ "level": "help",
+ "spans": [
+ {
+ "file_name": "./tests/everything/str-lit-type-mismatch.rs",
+ "byte_start": 608,
+ "byte_end": 614,
+ "line_start": 15,
+ "line_end": 15,
+ "column_start": 19,
+ "column_end": 25,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " let z: &str = b\"foo\"; //~ ERROR mismatched types",
+ "highlight_start": 19,
+ "highlight_end": 25
+ }
+ ],
+ "label": null,
+ "suggested_replacement": "\"foo\"",
+ "expansion": null
+ }
+ ],
+ "children": [],
+ "rendered": null
+ }
+ ],
+ "rendered": "error[E0308]: mismatched types\n --> ./tests/everything/str-lit-type-mismatch.rs:15:19\n |\n15 | let z: &str = b\"foo\"; //~ ERROR mismatched types\n | ^^^^^^\n | |\n | expected str, found array of 3 elements\n | help: consider removing the leading `b`: `\"foo\"`\n |\n = note: expected type `&str`\n found type `&'static [u8; 3]`\n\nIf you want more information on this error, try using \"rustc --explain E0308\"\n"
+}
+{
+ "message": "aborting due to 3 previous errors",
+ "code": null,
+ "level": "error",
+ "spans": [],
+ "children": [],
+ "rendered": "error: aborting due to 3 previous errors\n\n"
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/everything/str-lit-type-mismatch.rs b/src/tools/cargo/crates/rustfix/tests/everything/str-lit-type-mismatch.rs
new file mode 100644
index 000000000..12637c7b9
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/everything/str-lit-type-mismatch.rs
@@ -0,0 +1,5 @@
+fn main() {
+ let x: &[u8] = "foo"; //~ ERROR mismatched types
+ let y: &[u8; 4] = "baaa"; //~ ERROR mismatched types
+ let z: &str = b"foo"; //~ ERROR mismatched types
+}
diff --git a/src/tools/cargo/crates/rustfix/tests/parse_and_replace.rs b/src/tools/cargo/crates/rustfix/tests/parse_and_replace.rs
new file mode 100644
index 000000000..902275b64
--- /dev/null
+++ b/src/tools/cargo/crates/rustfix/tests/parse_and_replace.rs
@@ -0,0 +1,234 @@
+#![allow(clippy::disallowed_methods, clippy::print_stdout, clippy::print_stderr)]
+
+use anyhow::{anyhow, ensure, Context, Error};
+use rustfix::apply_suggestions;
+use std::collections::HashSet;
+use std::env;
+use std::ffi::OsString;
+use std::fs;
+use std::path::{Path, PathBuf};
+use std::process::{Command, Output};
+use tempfile::tempdir;
+use tracing::{debug, info, warn};
+
+mod fixmode {
+ pub const EVERYTHING: &str = "yolo";
+}
+
+mod settings {
+ // can be set as env var to debug
+ pub const CHECK_JSON: &str = "RUSTFIX_TEST_CHECK_JSON";
+ pub const RECORD_JSON: &str = "RUSTFIX_TEST_RECORD_JSON";
+ pub const RECORD_FIXED_RUST: &str = "RUSTFIX_TEST_RECORD_FIXED_RUST";
+}
+
+fn compile(file: &Path) -> Result<Output, Error> {
+ let tmp = tempdir()?;
+
+ let args: Vec<OsString> = vec![
+ file.into(),
+ "--error-format=json".into(),
+ "--emit=metadata".into(),
+ "--crate-name=rustfix_test".into(),
+ "--out-dir".into(),
+ tmp.path().into(),
+ ];
+
+ let res = Command::new(env::var_os("RUSTC").unwrap_or("rustc".into()))
+ .args(&args)
+ .env("CLIPPY_DISABLE_DOCS_LINKS", "true")
+ .env_remove("RUST_LOG")
+ .output()?;
+
+ Ok(res)
+}
+
+fn compile_and_get_json_errors(file: &Path) -> Result<String, Error> {
+ let res = compile(file)?;
+ let stderr = String::from_utf8(res.stderr)?;
+ if stderr.contains("is only accepted on the nightly compiler") {
+ panic!("rustfix tests require a nightly compiler");
+ }
+
+ match res.status.code() {
+ Some(0) | Some(1) | Some(101) => Ok(stderr),
+ _ => Err(anyhow!(
+ "failed with status {:?}: {}",
+ res.status.code(),
+ stderr
+ )),
+ }
+}
+
+fn compiles_without_errors(file: &Path) -> Result<(), Error> {
+ let res = compile(file)?;
+
+ match res.status.code() {
+ Some(0) => Ok(()),
+ _ => {
+ info!(
+ "file {:?} failed to compile:\n{}",
+ file,
+ String::from_utf8(res.stderr)?
+ );
+ Err(anyhow!(
+ "failed with status {:?} (`env RUST_LOG=parse_and_replace=info` for more info)",
+ res.status.code(),
+ ))
+ }
+ }
+}
+
+fn read_file(path: &Path) -> Result<String, Error> {
+ use std::io::Read;
+
+ let mut buffer = String::new();
+ let mut file = fs::File::open(path)?;
+ file.read_to_string(&mut buffer)?;
+ Ok(buffer)
+}
+
+fn diff(expected: &str, actual: &str) -> String {
+ use similar::{ChangeTag, TextDiff};
+ use std::fmt::Write;
+
+ let mut res = String::new();
+ let diff = TextDiff::from_lines(expected.trim(), actual.trim());
+
+ let mut different = false;
+ for op in diff.ops() {
+ for change in diff.iter_changes(op) {
+ let prefix = match change.tag() {
+ ChangeTag::Equal => continue,
+ ChangeTag::Insert => "+",
+ ChangeTag::Delete => "-",
+ };
+ if !different {
+ write!(
+ &mut res,
+ "differences found (+ == actual, - == expected):\n"
+ )
+ .unwrap();
+ different = true;
+ }
+ write!(&mut res, "{} {}", prefix, change.value()).unwrap();
+ }
+ }
+ if different {
+ write!(&mut res, "").unwrap();
+ }
+
+ res
+}
+
+fn test_rustfix_with_file<P: AsRef<Path>>(file: P, mode: &str) -> Result<(), Error> {
+ let file: &Path = file.as_ref();
+ let json_file = file.with_extension("json");
+ let fixed_file = file.with_extension("fixed.rs");
+
+ let filter_suggestions = if mode == fixmode::EVERYTHING {
+ rustfix::Filter::Everything
+ } else {
+ rustfix::Filter::MachineApplicableOnly
+ };
+
+ debug!("next up: {:?}", file);
+ let code = read_file(file).context(format!("could not read {}", file.display()))?;
+ let errors =
+ compile_and_get_json_errors(file).context(format!("could compile {}", file.display()))?;
+ let suggestions =
+ rustfix::get_suggestions_from_json(&errors, &HashSet::new(), filter_suggestions)
+ .context("could not load suggestions")?;
+
+ if std::env::var(settings::RECORD_JSON).is_ok() {
+ use std::io::Write;
+ let mut recorded_json = fs::File::create(&file.with_extension("recorded.json")).context(
+ format!("could not create recorded.json for {}", file.display()),
+ )?;
+ recorded_json.write_all(errors.as_bytes())?;
+ }
+
+ if std::env::var(settings::CHECK_JSON).is_ok() {
+ let expected_json = read_file(&json_file).context(format!(
+ "could not load json fixtures for {}",
+ file.display()
+ ))?;
+ let expected_suggestions =
+ rustfix::get_suggestions_from_json(&expected_json, &HashSet::new(), filter_suggestions)
+ .context("could not load expected suggestions")?;
+
+ ensure!(
+ expected_suggestions == suggestions,
+ "got unexpected suggestions from clippy:\n{}",
+ diff(
+ &format!("{:?}", expected_suggestions),
+ &format!("{:?}", suggestions)
+ )
+ );
+ }
+
+ let fixed = apply_suggestions(&code, &suggestions)
+ .context(format!("could not apply suggestions to {}", file.display()))?;
+
+ if std::env::var(settings::RECORD_FIXED_RUST).is_ok() {
+ use std::io::Write;
+ let mut recorded_rust = fs::File::create(&file.with_extension("recorded.rs"))?;
+ recorded_rust.write_all(fixed.as_bytes())?;
+ }
+
+ let expected_fixed =
+ read_file(&fixed_file).context(format!("could read fixed file for {}", file.display()))?;
+ ensure!(
+ fixed.trim() == expected_fixed.trim(),
+ "file {} doesn't look fixed:\n{}",
+ file.display(),
+ diff(fixed.trim(), expected_fixed.trim())
+ );
+
+ compiles_without_errors(&fixed_file)?;
+
+ Ok(())
+}
+
+fn get_fixture_files(p: &str) -> Result<Vec<PathBuf>, Error> {
+ Ok(fs::read_dir(&p)?
+ .into_iter()
+ .map(|e| e.unwrap().path())
+ .filter(|p| p.is_file())
+ .filter(|p| {
+ let x = p.to_string_lossy();
+ x.ends_with(".rs") && !x.ends_with(".fixed.rs") && !x.ends_with(".recorded.rs")
+ })
+ .collect())
+}
+
+fn assert_fixtures(dir: &str, mode: &str) {
+ let files = get_fixture_files(&dir)
+ .context(format!("couldn't load dir `{}`", dir))
+ .unwrap();
+ let mut failures = 0;
+
+ for file in &files {
+ if let Err(err) = test_rustfix_with_file(file, mode) {
+ println!("failed: {}", file.display());
+ warn!("{:?}", err);
+ failures += 1;
+ }
+ info!("passed: {:?}", file);
+ }
+
+ if failures > 0 {
+ panic!(
+ "{} out of {} fixture asserts failed\n\
+ (run with `env RUST_LOG=parse_and_replace=info` to get more details)",
+ failures,
+ files.len(),
+ );
+ }
+}
+
+#[test]
+fn everything() {
+ tracing_subscriber::fmt::init();
+ assert_fixtures("./tests/everything", fixmode::EVERYTHING);
+}
diff --git a/src/tools/cargo/crates/semver-check/Cargo.toml b/src/tools/cargo/crates/semver-check/Cargo.toml
index 7387c3091..ab13b5730 100644
--- a/src/tools/cargo/crates/semver-check/Cargo.toml
+++ b/src/tools/cargo/crates/semver-check/Cargo.toml
@@ -10,3 +10,6 @@ publish = false
[dependencies]
tempfile.workspace = true
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/crates/semver-check/src/main.rs b/src/tools/cargo/crates/semver-check/src/main.rs
index 9ea0d1244..edcf59957 100644
--- a/src/tools/cargo/crates/semver-check/src/main.rs
+++ b/src/tools/cargo/crates/semver-check/src/main.rs
@@ -13,6 +13,8 @@
//! - `dont-deny`: By default tests have a `#![deny(warnings)]`. This option
//! avoids this attribute. Note that `#![allow(unused)]` is always added.
+#![allow(clippy::print_stderr)]
+
use std::error::Error;
use std::fs;
use std::path::Path;
diff --git a/src/tools/cargo/crates/xtask-build-man/Cargo.toml b/src/tools/cargo/crates/xtask-build-man/Cargo.toml
index 9e92125a1..87ae8c670 100644
--- a/src/tools/cargo/crates/xtask-build-man/Cargo.toml
+++ b/src/tools/cargo/crates/xtask-build-man/Cargo.toml
@@ -6,3 +6,6 @@ edition.workspace = true
publish = false
[dependencies]
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/crates/xtask-build-man/src/main.rs b/src/tools/cargo/crates/xtask-build-man/src/main.rs
index 6680c3783..2ab3f098a 100644
--- a/src/tools/cargo/crates/xtask-build-man/src/main.rs
+++ b/src/tools/cargo/crates/xtask-build-man/src/main.rs
@@ -10,6 +10,8 @@
//! For more, read their doc comments.
//! ```
+#![allow(clippy::print_stderr)]
+
use std::fs;
use std::io;
use std::path::PathBuf;
diff --git a/src/tools/cargo/crates/xtask-bump-check/Cargo.toml b/src/tools/cargo/crates/xtask-bump-check/Cargo.toml
index c8a472adc..989ece4b7 100644
--- a/src/tools/cargo/crates/xtask-bump-check/Cargo.toml
+++ b/src/tools/cargo/crates/xtask-bump-check/Cargo.toml
@@ -14,3 +14,6 @@ git2.workspace = true
semver.workspace = true
tracing-subscriber.workspace = true
tracing.workspace = true
+
+[lints]
+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 b99ac8b32..db82fff63 100644
--- a/src/tools/cargo/crates/xtask-bump-check/src/xtask.rs
+++ b/src/tools/cargo/crates/xtask-bump-check/src/xtask.rs
@@ -41,7 +41,11 @@ pub fn cli() -> clap::Command {
.action(ArgAction::Count)
.global(true),
)
- .arg_quiet()
+ .arg(
+ flag("quiet", "Do not print cargo log messages")
+ .short('q')
+ .global(true),
+ )
.arg(
opt("color", "Coloring: auto, always, never")
.value_name("WHEN")
@@ -114,6 +118,11 @@ fn bump_check(args: &clap::ArgMatches, config: &cargo::util::Config) -> CargoRes
let changed_members = changed(&ws, &repo, &base_commit, &head_commit)?;
let status = |msg: &str| config.shell().status(STATUS, msg);
+ // Don't check against beta and stable branches,
+ // as the publish of these crates are not tied with Rust release process.
+ // See `TO_PUBLISH` in publish.py.
+ let crates_not_check_against_channels = ["home"];
+
status(&format!("base commit `{}`", base_commit.id()))?;
status(&format!("head commit `{}`", head_commit.id()))?;
@@ -125,6 +134,11 @@ fn bump_check(args: &clap::ArgMatches, config: &cargo::util::Config) -> CargoRes
status(&format!("compare against `{}`", referenced_commit.id()))?;
for referenced_member in checkout_ws(&ws, &repo, referenced_commit)?.members() {
let pkg_name = referenced_member.name().as_str();
+
+ if crates_not_check_against_channels.contains(&pkg_name) {
+ continue;
+ }
+
let Some(changed_member) = changed_members.get(pkg_name) else {
tracing::trace!("skipping {pkg_name}, may be removed or not published");
continue;
@@ -162,8 +176,12 @@ fn bump_check(args: &clap::ArgMatches, config: &cargo::util::Config) -> CargoRes
let mut cmd = ProcessBuilder::new("cargo");
cmd.arg("semver-checks")
.arg("--workspace")
+ .args(&["--exclude", "rustfix"]) // FIXME: Remove once 1.76 is stable
.arg("--baseline-rev")
.arg(referenced_commit.id().to_string());
+ for krate in crates_not_check_against_channels {
+ cmd.args(&["--exclude", krate]);
+ }
config.shell().status("Running", &cmd)?;
cmd.exec()?;
}
@@ -373,6 +391,7 @@ fn check_crates_io<'a>(
"`{name}@{current}` needs a bump because its should have a version newer than crates.io: {:?}`",
possibilities
.iter()
+ .map(|s| s.as_summary())
.map(|s| format!("{}@{}", s.name(), s.version()))
.collect::<Vec<_>>(),
);
diff --git a/src/tools/cargo/crates/xtask-stale-label/Cargo.toml b/src/tools/cargo/crates/xtask-stale-label/Cargo.toml
index 8d68536d2..aff6194b7 100644
--- a/src/tools/cargo/crates/xtask-stale-label/Cargo.toml
+++ b/src/tools/cargo/crates/xtask-stale-label/Cargo.toml
@@ -7,3 +7,6 @@ publish = false
[dependencies]
toml_edit.workspace = true
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/crates/xtask-stale-label/src/main.rs b/src/tools/cargo/crates/xtask-stale-label/src/main.rs
index 88c044b5b..efcb52d01 100644
--- a/src/tools/cargo/crates/xtask-stale-label/src/main.rs
+++ b/src/tools/cargo/crates/xtask-stale-label/src/main.rs
@@ -10,6 +10,8 @@
//! Probably autofix them in the future.
//! ```
+#![allow(clippy::print_stderr)]
+
use std::fmt::Write as _;
use std::path::PathBuf;
use std::process;
diff --git a/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml b/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml
index 9e5b1e635..3024bd497 100644
--- a/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential-1password/Cargo.toml
@@ -11,3 +11,6 @@ description = "A Cargo credential process that stores tokens in a 1password vaul
cargo-credential.workspace = true
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
+
+[lints]
+workspace = true
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 321a99c51..38b567bf2 100644
--- a/src/tools/cargo/credential/cargo-credential-1password/src/main.rs
+++ b/src/tools/cargo/credential/cargo-credential-1password/src/main.rs
@@ -1,5 +1,8 @@
//! Cargo registry 1password credential process.
+#![allow(clippy::disallowed_methods)]
+#![allow(clippy::print_stderr)]
+
use cargo_credential::{
Action, CacheControl, Credential, CredentialResponse, Error, RegistryInfo, Secret,
};
diff --git a/src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml b/src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml
index 19ef33a34..e68c9b83a 100644
--- a/src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential-libsecret/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "cargo-credential-libsecret"
-version = "0.4.1"
+version = "0.4.2"
edition.workspace = true
license.workspace = true
rust-version.workspace = true
@@ -11,3 +11,6 @@ description = "A Cargo credential process that stores tokens with GNOME libsecre
anyhow.workspace = true
cargo-credential.workspace = true
libloading.workspace = true
+
+[lints]
+workspace = true
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 4dec8def6..95ffa231f 100644
--- a/src/tools/cargo/credential/cargo-credential-macos-keychain/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential-macos-keychain/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "cargo-credential-macos-keychain"
-version = "0.4.1"
+version = "0.4.2"
edition.workspace = true
license.workspace = true
rust-version.workspace = true
@@ -12,3 +12,6 @@ cargo-credential.workspace = true
[target.'cfg(target_os = "macos")'.dependencies]
security-framework.workspace = true
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/credential/cargo-credential-macos-keychain/src/lib.rs b/src/tools/cargo/credential/cargo-credential-macos-keychain/src/lib.rs
index 9e6d55472..8a702a362 100644
--- a/src/tools/cargo/credential/cargo-credential-macos-keychain/src/lib.rs
+++ b/src/tools/cargo/credential/cargo-credential-macos-keychain/src/lib.rs
@@ -1,5 +1,7 @@
//! Cargo registry macos keychain credential process.
+#![allow(clippy::print_stderr)]
+
#[cfg(target_os = "macos")]
mod macos {
use cargo_credential::{
diff --git a/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml b/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml
index c904075bb..1ecb2a7be 100644
--- a/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential-wincred/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "cargo-credential-wincred"
-version = "0.4.1"
+version = "0.4.2"
edition.workspace = true
license.workspace = true
rust-version.workspace = true
@@ -13,3 +13,6 @@ cargo-credential.workspace = true
[target.'cfg(windows)'.dependencies.windows-sys]
features = ["Win32_Foundation", "Win32_Security_Credentials"]
workspace = true
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/credential/cargo-credential/Cargo.toml b/src/tools/cargo/credential/cargo-credential/Cargo.toml
index 8ba65b8b9..7dc37ff82 100644
--- a/src/tools/cargo/credential/cargo-credential/Cargo.toml
+++ b/src/tools/cargo/credential/cargo-credential/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "cargo-credential"
-version = "0.4.1"
+version = "0.4.2"
edition.workspace = true
license.workspace = true
rust-version = "1.70.0" # MSRV:3
@@ -20,3 +20,6 @@ windows-sys = { workspace = true, features = ["Win32_System_Console", "Win32_Fou
[dev-dependencies]
snapbox = { workspace = true, features = ["examples"] }
+
+[lints]
+workspace = true
diff --git a/src/tools/cargo/credential/cargo-credential/examples/stdout-redirected.rs b/src/tools/cargo/credential/cargo-credential/examples/stdout-redirected.rs
index 75a2d16d1..fb7c0446c 100644
--- a/src/tools/cargo/credential/cargo-credential/examples/stdout-redirected.rs
+++ b/src/tools/cargo/credential/cargo-credential/examples/stdout-redirected.rs
@@ -1,5 +1,8 @@
//! Provider used for testing redirection of stdout.
+#![allow(clippy::print_stderr)]
+#![allow(clippy::print_stdout)]
+
use cargo_credential::{Action, Credential, CredentialResponse, Error, RegistryInfo};
struct MyCredential;
diff --git a/src/tools/cargo/credential/cargo-credential/src/lib.rs b/src/tools/cargo/credential/cargo-credential/src/lib.rs
index 60bce65be..0888fb402 100644
--- a/src/tools/cargo/credential/cargo-credential/src/lib.rs
+++ b/src/tools/cargo/credential/cargo-credential/src/lib.rs
@@ -37,6 +37,9 @@
#![doc = include_str!("../examples/file-provider.rs")]
//! ```
+#![allow(clippy::print_stderr)]
+#![allow(clippy::print_stdout)]
+
use serde::{Deserialize, Serialize};
use std::{fmt::Display, io};
use time::OffsetDateTime;
diff --git a/src/tools/cargo/publish.py b/src/tools/cargo/publish.py
index 87ea0e896..114d7dbbd 100755
--- a/src/tools/cargo/publish.py
+++ b/src/tools/cargo/publish.py
@@ -21,6 +21,7 @@ TO_PUBLISH = [
'credential/cargo-credential-wincred',
'credential/cargo-credential-1password',
'credential/cargo-credential-macos-keychain',
+ 'crates/rustfix',
'crates/cargo-platform',
'crates/cargo-util',
'crates/crates-io',
diff --git a/src/tools/cargo/src/bin/cargo/cli.rs b/src/tools/cargo/src/bin/cargo/cli.rs
index a21030f01..a9f5fd308 100644
--- a/src/tools/cargo/src/bin/cargo/cli.rs
+++ b/src/tools/cargo/src/bin/cargo/cli.rs
@@ -134,9 +134,14 @@ Run with 'cargo -Z [FLAG] [COMMAND]'",
"Formats all bin and lib files of the current crate using rustfmt.",
),
]);
- drop_println!(config, "Installed Commands:");
+ drop_println!(
+ config,
+ color_print::cstr!("<green,bold>Installed Commands:</>")
+ );
for (name, command) in list_commands(config) {
let known_external_desc = known_external_command_descriptions.get(name.as_str());
+ let literal = style::LITERAL.render();
+ let reset = anstyle::Reset.render();
match command {
CommandInfo::BuiltIn { about } => {
assert!(
@@ -145,22 +150,21 @@ Run with 'cargo -Z [FLAG] [COMMAND]'",
);
let summary = about.unwrap_or_default();
let summary = summary.lines().next().unwrap_or(&summary); // display only the first line
- drop_println!(config, " {:<20} {}", name, summary);
+ drop_println!(config, " {literal}{name:<20}{reset} {summary}");
}
CommandInfo::External { path } => {
if let Some(desc) = known_external_desc {
- drop_println!(config, " {:<20} {}", name, desc);
+ drop_println!(config, " {literal}{name:<20}{reset} {desc}");
} else if is_verbose {
- drop_println!(config, " {:<20} {}", name, path.display());
+ drop_println!(config, " {literal}{name:<20}{reset} {}", path.display());
} else {
- drop_println!(config, " {}", name);
+ drop_println!(config, " {literal}{name}{reset}");
}
}
CommandInfo::Alias { target } => {
drop_println!(
config,
- " {:<20} alias: {}",
- name,
+ " {literal}{name:<20}{reset} alias: {}",
target.iter().join(" ")
);
}
@@ -589,7 +593,7 @@ See '<cyan,bold>cargo help</> <cyan><<command>></>' for more information on a sp
.action(ArgAction::Count)
.global(true),
)
- .arg_quiet()
+ .arg(flag("quiet", "Do not print cargo log messages").short('q').global(true))
.arg(
opt("color", "Coloring: auto, always, never")
.value_name("WHEN")
diff --git a/src/tools/cargo/src/bin/cargo/commands/add.rs b/src/tools/cargo/src/bin/cargo/commands/add.rs
index e1ece14b8..55a074282 100644
--- a/src/tools/cargo/src/bin/cargo/commands/add.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/add.rs
@@ -62,6 +62,19 @@ The package name will be exposed as feature of your crate.")
The package will be removed from your features.")
.conflicts_with("dev")
.overrides_with("optional"),
+ flag("public", "Mark the dependency as public")
+ .conflicts_with("dev")
+ .conflicts_with("build")
+ .long_help("Mark the dependency as public
+
+The dependency can be referenced in your library's public API."),
+ flag("no-public", "Mark the dependency as private")
+ .conflicts_with("dev")
+ .conflicts_with("build")
+ .overrides_with("public")
+ .long_help("Mark the dependency as private
+
+While you can use the crate in your implementation, it cannot be referenced in your public API."),
clap::Arg::new("rename")
.long("rename")
.action(ArgAction::Set)
@@ -80,7 +93,7 @@ Example uses:
.arg_manifest_path_without_unsupported_path_tip()
.arg_package("Package to modify")
.arg_dry_run("Don't actually write the manifest")
- .arg_quiet()
+ .arg_silent_suggestion()
.next_help_heading("Source")
.args([
clap::Arg::new("path")
@@ -235,6 +248,7 @@ fn parse_dependencies(config: &Config, matches: &ArgMatches) -> CargoResult<Vec<
};
let default_features = default_features(matches);
let optional = optional(matches);
+ let public = public(matches);
let mut crates = matches
.get_many::<String>("crates")
@@ -325,6 +339,7 @@ fn parse_dependencies(config: &Config, matches: &ArgMatches) -> CargoResult<Vec<
features,
default_features,
optional,
+ public,
registry: registry.clone(),
path: path.map(String::from),
git: git.map(String::from),
@@ -353,6 +368,10 @@ fn optional(matches: &ArgMatches) -> Option<bool> {
resolve_bool_arg(matches.flag("optional"), matches.flag("no-optional"))
}
+fn public(matches: &ArgMatches) -> Option<bool> {
+ resolve_bool_arg(matches.flag("public"), matches.flag("no-public"))
+}
+
fn resolve_bool_arg(yes: bool, no: bool) -> Option<bool> {
match (yes, no) {
(true, false) => Some(true),
diff --git a/src/tools/cargo/src/bin/cargo/commands/bench.rs b/src/tools/cargo/src/bin/cargo/commands/bench.rs
index 85c975a6c..11bcf2eb9 100644
--- a/src/tools/cargo/src/bin/cargo/commands/bench.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/bench.rs
@@ -24,7 +24,7 @@ pub fn cli() -> Command {
))
.arg_ignore_rust_version()
.arg_message_format()
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_package_spec(
"Package to run benchmarks for",
"Benchmark all packages in the workspace",
diff --git a/src/tools/cargo/src/bin/cargo/commands/build.rs b/src/tools/cargo/src/bin/cargo/commands/build.rs
index e2ed87d1b..0dde7bde9 100644
--- a/src/tools/cargo/src/bin/cargo/commands/build.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/build.rs
@@ -10,7 +10,7 @@ pub fn cli() -> Command {
.arg_ignore_rust_version()
.arg_future_incompat_report()
.arg_message_format()
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_package_spec(
"Package to build (see `cargo help pkgid`)",
"Build all packages in the workspace",
diff --git a/src/tools/cargo/src/bin/cargo/commands/check.rs b/src/tools/cargo/src/bin/cargo/commands/check.rs
index 77e2b9280..199cbf3fe 100644
--- a/src/tools/cargo/src/bin/cargo/commands/check.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/check.rs
@@ -10,7 +10,7 @@ pub fn cli() -> Command {
.arg_ignore_rust_version()
.arg_future_incompat_report()
.arg_message_format()
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_package_spec(
"Package(s) to check",
"Check all packages in the workspace",
diff --git a/src/tools/cargo/src/bin/cargo/commands/clean.rs b/src/tools/cargo/src/bin/cargo/commands/clean.rs
index 8596561c9..c7b7f98c3 100644
--- a/src/tools/cargo/src/bin/cargo/commands/clean.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/clean.rs
@@ -1,13 +1,18 @@
use crate::command_prelude::*;
-
+use crate::util::cache_lock::CacheLockMode;
+use cargo::core::gc::Gc;
+use cargo::core::gc::{parse_human_size, parse_time_span, GcOpts};
+use cargo::core::global_cache_tracker::GlobalCacheTracker;
+use cargo::ops::CleanContext;
use cargo::ops::{self, CleanOptions};
use cargo::util::print_available_packages;
+use std::time::Duration;
pub fn cli() -> Command {
subcommand("clean")
.about("Remove artifacts that cargo has generated in the past")
.arg_doc("Whether or not to clean just the documentation directory")
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_package_spec_simple("Package to clean artifacts for")
.arg_release("Whether or not to clean release artifacts")
.arg_profile("Clean artifacts of the specified profile")
@@ -15,12 +20,122 @@ pub fn cli() -> Command {
.arg_target_dir()
.arg_manifest_path()
.arg_dry_run("Display what would be deleted without deleting anything")
+ .args_conflicts_with_subcommands(true)
+ .subcommand(
+ subcommand("gc")
+ .about("Clean global caches")
+ .hide(true)
+ .arg_silent_suggestion()
+ .arg_dry_run("Display what would be deleted without deleting anything")
+ // NOTE: Not all of these options may get stabilized. Some of them are
+ // very low-level details, and may not be something typical users need.
+ .arg(
+ opt(
+ "max-src-age",
+ "Deletes source cache files that have not been used \
+ since the given age (unstable)",
+ )
+ .value_name("DURATION")
+ .value_parser(parse_time_span),
+ )
+ .arg(
+ opt(
+ "max-crate-age",
+ "Deletes crate cache files that have not been used \
+ since the given age (unstable)",
+ )
+ .value_name("DURATION")
+ .value_parser(parse_time_span),
+ )
+ .arg(
+ opt(
+ "max-index-age",
+ "Deletes registry indexes that have not been used \
+ since the given age (unstable)",
+ )
+ .value_name("DURATION")
+ .value_parser(parse_time_span),
+ )
+ .arg(
+ opt(
+ "max-git-co-age",
+ "Deletes git dependency checkouts that have not been used \
+ since the given age (unstable)",
+ )
+ .value_name("DURATION")
+ .value_parser(parse_time_span),
+ )
+ .arg(
+ opt(
+ "max-git-db-age",
+ "Deletes git dependency clones that have not been used \
+ since the given age (unstable)",
+ )
+ .value_name("DURATION")
+ .value_parser(parse_time_span),
+ )
+ .arg(
+ opt(
+ "max-download-age",
+ "Deletes any downloaded cache data that has not been used \
+ since the given age (unstable)",
+ )
+ .value_name("DURATION")
+ .value_parser(parse_time_span),
+ )
+ .arg(
+ opt(
+ "max-src-size",
+ "Deletes source cache files until the cache is under the \
+ given size (unstable)",
+ )
+ .value_name("SIZE")
+ .value_parser(parse_human_size),
+ )
+ .arg(
+ opt(
+ "max-crate-size",
+ "Deletes crate cache files until the cache is under the \
+ given size (unstable)",
+ )
+ .value_name("SIZE")
+ .value_parser(parse_human_size),
+ )
+ .arg(
+ opt(
+ "max-git-size",
+ "Deletes git dependency caches until the cache is under \
+ the given size (unstable)",
+ )
+ .value_name("SIZE")
+ .value_parser(parse_human_size),
+ )
+ .arg(
+ opt(
+ "max-download-size",
+ "Deletes downloaded cache data until the cache is under \
+ the given size (unstable)",
+ )
+ .value_name("SIZE")
+ .value_parser(parse_human_size),
+ ),
+ )
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help clean</>` for more detailed information.\n"
))
}
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
+ match args.subcommand() {
+ Some(("gc", args)) => {
+ return gc(config, args);
+ }
+ Some((cmd, _)) => {
+ unreachable!("unexpected command {}", cmd)
+ }
+ None => {}
+ }
+
let ws = args.workspace(config)?;
if args.is_present_with_zero_values("package") {
@@ -39,3 +154,44 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
ops::clean(&ws, &opts)?;
Ok(())
}
+
+fn gc(config: &Config, args: &ArgMatches) -> CliResult {
+ config.cli_unstable().fail_if_stable_command(
+ config,
+ "clean gc",
+ 12633,
+ "gc",
+ config.cli_unstable().gc,
+ )?;
+
+ let size_opt = |opt| -> Option<u64> { args.get_one::<u64>(opt).copied() };
+ let duration_opt = |opt| -> Option<Duration> { args.get_one::<Duration>(opt).copied() };
+ let mut gc_opts = GcOpts {
+ max_src_age: duration_opt("max-src-age"),
+ max_crate_age: duration_opt("max-crate-age"),
+ max_index_age: duration_opt("max-index-age"),
+ max_git_co_age: duration_opt("max-git-co-age"),
+ max_git_db_age: duration_opt("max-git-db-age"),
+ max_src_size: size_opt("max-src-size"),
+ max_crate_size: size_opt("max-crate-size"),
+ max_git_size: size_opt("max-git-size"),
+ max_download_size: size_opt("max-download-size"),
+ };
+ if let Some(age) = duration_opt("max-download-age") {
+ gc_opts.set_max_download_age(age);
+ }
+ // If the user sets any options, then only perform the options requested.
+ // If no options are set, do the default behavior.
+ if !gc_opts.is_download_cache_opt_set() {
+ gc_opts.update_for_auto_gc(config)?;
+ }
+
+ let _lock = config.acquire_package_cache_lock(CacheLockMode::MutateExclusive)?;
+ let mut cache_track = GlobalCacheTracker::new(&config)?;
+ let mut gc = Gc::new(config, &mut cache_track)?;
+ let mut clean_ctx = CleanContext::new(config);
+ clean_ctx.dry_run = args.dry_run();
+ gc.gc(&mut clean_ctx, &gc_opts)?;
+ clean_ctx.display_summary()?;
+ Ok(())
+}
diff --git a/src/tools/cargo/src/bin/cargo/commands/config.rs b/src/tools/cargo/src/bin/cargo/commands/config.rs
index 84c5e9209..feea9ed28 100644
--- a/src/tools/cargo/src/bin/cargo/commands/config.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/config.rs
@@ -31,9 +31,13 @@ pub fn cli() -> Command {
}
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
- config
- .cli_unstable()
- .fail_if_stable_command(config, "config", 9301)?;
+ config.cli_unstable().fail_if_stable_command(
+ config,
+ "config",
+ 9301,
+ "unstable-options",
+ config.cli_unstable().unstable_options,
+ )?;
match args.subcommand() {
Some(("get", args)) => {
let opts = cargo_config::GetOptions {
diff --git a/src/tools/cargo/src/bin/cargo/commands/doc.rs b/src/tools/cargo/src/bin/cargo/commands/doc.rs
index 43a6ee950..bf2e0c7ba 100644
--- a/src/tools/cargo/src/bin/cargo/commands/doc.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/doc.rs
@@ -18,7 +18,7 @@ pub fn cli() -> Command {
.arg(flag("document-private-items", "Document private items"))
.arg_ignore_rust_version()
.arg_message_format()
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_package_spec(
"Package to document",
"Document all packages in the workspace",
diff --git a/src/tools/cargo/src/bin/cargo/commands/fetch.rs b/src/tools/cargo/src/bin/cargo/commands/fetch.rs
index 794dbf9b0..1c25204e3 100644
--- a/src/tools/cargo/src/bin/cargo/commands/fetch.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/fetch.rs
@@ -6,7 +6,7 @@ use cargo::ops::FetchOptions;
pub fn cli() -> Command {
subcommand("fetch")
.about("Fetch dependencies of a package from the network")
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_target_triple("Fetch dependencies for the target triple")
.arg_manifest_path()
.after_help(color_print::cstr!(
diff --git a/src/tools/cargo/src/bin/cargo/commands/fix.rs b/src/tools/cargo/src/bin/cargo/commands/fix.rs
index bd938dbc7..93df738e1 100644
--- a/src/tools/cargo/src/bin/cargo/commands/fix.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/fix.rs
@@ -28,7 +28,7 @@ pub fn cli() -> Command {
))
.arg_ignore_rust_version()
.arg_message_format()
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_package_spec(
"Package(s) to fix",
"Fix all packages in the workspace",
diff --git a/src/tools/cargo/src/bin/cargo/commands/generate_lockfile.rs b/src/tools/cargo/src/bin/cargo/commands/generate_lockfile.rs
index 4f1382ee5..3617e38f4 100644
--- a/src/tools/cargo/src/bin/cargo/commands/generate_lockfile.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/generate_lockfile.rs
@@ -5,7 +5,7 @@ use cargo::ops;
pub fn cli() -> Command {
subcommand("generate-lockfile")
.about("Generate the lockfile for a package")
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_manifest_path()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help generate-lockfile</>` for more detailed information.\n"
diff --git a/src/tools/cargo/src/bin/cargo/commands/init.rs b/src/tools/cargo/src/bin/cargo/commands/init.rs
index 04dd7ae45..b58a6a26d 100644
--- a/src/tools/cargo/src/bin/cargo/commands/init.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/init.rs
@@ -13,7 +13,7 @@ pub fn cli() -> Command {
)
.arg_new_opts()
.arg_registry("Registry to use")
- .arg_quiet()
+ .arg_silent_suggestion()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help init</>` for more detailed information.\n"
))
diff --git a/src/tools/cargo/src/bin/cargo/commands/install.rs b/src/tools/cargo/src/bin/cargo/commands/install.rs
index cb66ba100..b3d379c41 100644
--- a/src/tools/cargo/src/bin/cargo/commands/install.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/install.rs
@@ -76,7 +76,7 @@ pub fn cli() -> Command {
))
.arg_ignore_rust_version()
.arg_message_format()
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_targets_bins_examples(
"Install only the specified binary",
"Install all binaries",
diff --git a/src/tools/cargo/src/bin/cargo/commands/locate_project.rs b/src/tools/cargo/src/bin/cargo/commands/locate_project.rs
index 217bdcac9..1f1b87e2e 100644
--- a/src/tools/cargo/src/bin/cargo/commands/locate_project.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/locate_project.rs
@@ -14,7 +14,7 @@ pub fn cli() -> Command {
)
.value_name("FMT"),
)
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_manifest_path()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help locate-project</>` for more detailed information.\n"
diff --git a/src/tools/cargo/src/bin/cargo/commands/login.rs b/src/tools/cargo/src/bin/cargo/commands/login.rs
index 877ec6aeb..d6fc6d55d 100644
--- a/src/tools/cargo/src/bin/cargo/commands/login.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/login.rs
@@ -14,7 +14,7 @@ pub fn cli() -> Command {
.num_args(0..)
.last(true),
)
- .arg_quiet()
+ .arg_silent_suggestion()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help login</>` for more detailed information.\n"
))
diff --git a/src/tools/cargo/src/bin/cargo/commands/logout.rs b/src/tools/cargo/src/bin/cargo/commands/logout.rs
index e7bacca86..cef9311a8 100644
--- a/src/tools/cargo/src/bin/cargo/commands/logout.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/logout.rs
@@ -7,7 +7,7 @@ pub fn cli() -> Command {
subcommand("logout")
.about("Remove an API token from the registry locally")
.arg_registry("Registry to use")
- .arg_quiet()
+ .arg_silent_suggestion()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help logout</>` for more detailed information.\n"
))
diff --git a/src/tools/cargo/src/bin/cargo/commands/metadata.rs b/src/tools/cargo/src/bin/cargo/commands/metadata.rs
index 09064de7d..664211420 100644
--- a/src/tools/cargo/src/bin/cargo/commands/metadata.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/metadata.rs
@@ -23,7 +23,7 @@ pub fn cli() -> Command {
.value_name("VERSION")
.value_parser(["1"]),
)
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_features()
.arg_manifest_path()
.after_help(color_print::cstr!(
diff --git a/src/tools/cargo/src/bin/cargo/commands/new.rs b/src/tools/cargo/src/bin/cargo/commands/new.rs
index 0ab093012..f2cc73621 100644
--- a/src/tools/cargo/src/bin/cargo/commands/new.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/new.rs
@@ -13,7 +13,7 @@ pub fn cli() -> Command {
)
.arg_new_opts()
.arg_registry("Registry to use")
- .arg_quiet()
+ .arg_silent_suggestion()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help new</>` for more detailed information.\n"
))
diff --git a/src/tools/cargo/src/bin/cargo/commands/owner.rs b/src/tools/cargo/src/bin/cargo/commands/owner.rs
index b787d094c..45f34bc8e 100644
--- a/src/tools/cargo/src/bin/cargo/commands/owner.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/owner.rs
@@ -27,7 +27,7 @@ pub fn cli() -> Command {
.arg_index("Registry index URL to modify owners for")
.arg_registry("Registry to modify owners for")
.arg(opt("token", "API token to use when authenticating").value_name("TOKEN"))
- .arg_quiet()
+ .arg_silent_suggestion()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help owner</>` for more detailed information.\n"
))
diff --git a/src/tools/cargo/src/bin/cargo/commands/package.rs b/src/tools/cargo/src/bin/cargo/commands/package.rs
index 0020e365e..59a3c8f66 100644
--- a/src/tools/cargo/src/bin/cargo/commands/package.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/package.rs
@@ -24,7 +24,7 @@ pub fn cli() -> Command {
"allow-dirty",
"Allow dirty working directories to be packaged",
))
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_package_spec_no_all(
"Package(s) to assemble",
"Assemble all packages in the workspace",
diff --git a/src/tools/cargo/src/bin/cargo/commands/pkgid.rs b/src/tools/cargo/src/bin/cargo/commands/pkgid.rs
index 2d1d41325..f1494af00 100644
--- a/src/tools/cargo/src/bin/cargo/commands/pkgid.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/pkgid.rs
@@ -7,7 +7,7 @@ pub fn cli() -> Command {
subcommand("pkgid")
.about("Print a fully qualified package specification")
.arg(Arg::new("spec").value_name("SPEC").action(ArgAction::Set))
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_package("Argument to get the package ID specifier for")
.arg_manifest_path()
.after_help(color_print::cstr!(
diff --git a/src/tools/cargo/src/bin/cargo/commands/publish.rs b/src/tools/cargo/src/bin/cargo/commands/publish.rs
index 8ce2ffc5b..af5bf7447 100644
--- a/src/tools/cargo/src/bin/cargo/commands/publish.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/publish.rs
@@ -17,7 +17,7 @@ pub fn cli() -> Command {
"allow-dirty",
"Allow dirty working directories to be packaged",
))
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_package("Package to publish")
.arg_features()
.arg_parallel()
diff --git a/src/tools/cargo/src/bin/cargo/commands/read_manifest.rs b/src/tools/cargo/src/bin/cargo/commands/read_manifest.rs
index 6625d60f5..8cfd9b34e 100644
--- a/src/tools/cargo/src/bin/cargo/commands/read_manifest.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/read_manifest.rs
@@ -9,7 +9,7 @@ Print a JSON representation of a Cargo.toml manifest.
Deprecated, use `<cyan,bold>cargo metadata --no-deps</>` instead.\
"
))
- .arg_quiet()
+ .arg_silent_suggestion()
.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 c115291cb..b7abb1715 100644
--- a/src/tools/cargo/src/bin/cargo/commands/remove.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/remove.rs
@@ -1,5 +1,6 @@
use cargo::core::dependency::DepKind;
use cargo::core::PackageIdSpec;
+use cargo::core::PackageIdSpecQuery;
use cargo::core::Resolve;
use cargo::core::Workspace;
use cargo::ops::cargo_remove::remove;
@@ -26,7 +27,7 @@ pub fn cli() -> clap::Command {
.value_name("DEP_ID")
.help("Dependencies to be removed")])
.arg_dry_run("Don't actually write the manifest")
- .arg_quiet()
+ .arg_silent_suggestion()
.next_help_heading("Section")
.args([
clap::Arg::new("dev")
diff --git a/src/tools/cargo/src/bin/cargo/commands/run.rs b/src/tools/cargo/src/bin/cargo/commands/run.rs
index 94396e63f..170c7ddf1 100644
--- a/src/tools/cargo/src/bin/cargo/commands/run.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/run.rs
@@ -24,7 +24,7 @@ pub fn cli() -> Command {
)
.arg_ignore_rust_version()
.arg_message_format()
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_package("Package with the target to run")
.arg_targets_bin_example(
"Name of the bin target to run",
diff --git a/src/tools/cargo/src/bin/cargo/commands/rustc.rs b/src/tools/cargo/src/bin/cargo/commands/rustc.rs
index 9b6a57577..7e5370be3 100644
--- a/src/tools/cargo/src/bin/cargo/commands/rustc.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/rustc.rs
@@ -30,7 +30,7 @@ pub fn cli() -> Command {
.arg_future_incompat_report()
.arg_ignore_rust_version()
.arg_message_format()
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_package("Package to build")
.arg_targets_all(
"Build only this package's library",
diff --git a/src/tools/cargo/src/bin/cargo/commands/rustdoc.rs b/src/tools/cargo/src/bin/cargo/commands/rustdoc.rs
index 72de57ad0..ec4e52c6d 100644
--- a/src/tools/cargo/src/bin/cargo/commands/rustdoc.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/rustdoc.rs
@@ -18,7 +18,7 @@ pub fn cli() -> Command {
))
.arg_ignore_rust_version()
.arg_message_format()
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_package("Package to document")
.arg_targets_all(
"Build only this package's library",
diff --git a/src/tools/cargo/src/bin/cargo/commands/search.rs b/src/tools/cargo/src/bin/cargo/commands/search.rs
index 377aa84e1..77394242b 100644
--- a/src/tools/cargo/src/bin/cargo/commands/search.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/search.rs
@@ -6,7 +6,7 @@ use cargo::ops;
pub fn cli() -> Command {
subcommand("search")
- .about("Search packages in crates.io")
+ .about("Search packages in the registry. Default registry is crates.io")
.arg(Arg::new("query").value_name("QUERY").num_args(0..))
.arg(
opt(
@@ -17,7 +17,7 @@ pub fn cli() -> Command {
)
.arg_index("Registry index URL to search packages in")
.arg_registry("Registry to search packages in")
- .arg_quiet()
+ .arg_silent_suggestion()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help search</>` for more detailed information.\n"
))
diff --git a/src/tools/cargo/src/bin/cargo/commands/tree.rs b/src/tools/cargo/src/bin/cargo/commands/tree.rs
index 1fe6a3a14..30cf4fe3a 100644
--- a/src/tools/cargo/src/bin/cargo/commands/tree.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/tree.rs
@@ -17,7 +17,7 @@ pub fn cli() -> Command {
.short('a')
.hide(true),
)
- .arg_quiet()
+ .arg_silent_suggestion()
.arg(flag("no-dev-dependencies", "Deprecated, use -e=no-dev instead").hide(true))
.arg(
multi_opt(
diff --git a/src/tools/cargo/src/bin/cargo/commands/uninstall.rs b/src/tools/cargo/src/bin/cargo/commands/uninstall.rs
index 30833f292..217f22ef3 100644
--- a/src/tools/cargo/src/bin/cargo/commands/uninstall.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/uninstall.rs
@@ -7,7 +7,7 @@ pub fn cli() -> Command {
.about("Remove a Rust binary")
.arg(Arg::new("spec").value_name("SPEC").num_args(0..))
.arg(opt("root", "Directory to uninstall packages from").value_name("DIR"))
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_package_spec_simple("Package to uninstall")
.arg(
multi_opt("bin", "NAME", "Only uninstall the binary NAME")
diff --git a/src/tools/cargo/src/bin/cargo/commands/update.rs b/src/tools/cargo/src/bin/cargo/commands/update.rs
index e06e8e51e..e11ac45c7 100644
--- a/src/tools/cargo/src/bin/cargo/commands/update.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/update.rs
@@ -35,7 +35,7 @@ pub fn cli() -> Command {
.value_name("PRECISE")
.requires("package-group"),
)
- .arg_quiet()
+ .arg_silent_suggestion()
.arg(
flag("workspace", "Only update the workspace packages")
.short('w')
diff --git a/src/tools/cargo/src/bin/cargo/commands/vendor.rs b/src/tools/cargo/src/bin/cargo/commands/vendor.rs
index 3f9c2dcaf..a15878484 100644
--- a/src/tools/cargo/src/bin/cargo/commands/vendor.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/vendor.rs
@@ -36,7 +36,6 @@ pub fn cli() -> Command {
.arg(unsupported("relative-path"))
.arg(unsupported("only-git-deps"))
.arg(unsupported("disallow-duplicates"))
- .arg_quiet_without_unknown_silent_arg_tip()
.arg_manifest_path()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help vendor</>` for more detailed information.\n"
diff --git a/src/tools/cargo/src/bin/cargo/commands/verify_project.rs b/src/tools/cargo/src/bin/cargo/commands/verify_project.rs
index 35bb747a4..14a5df07d 100644
--- a/src/tools/cargo/src/bin/cargo/commands/verify_project.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/verify_project.rs
@@ -6,7 +6,7 @@ use std::process;
pub fn cli() -> Command {
subcommand("verify-project")
.about("Check correctness of crate manifest")
- .arg_quiet()
+ .arg_silent_suggestion()
.arg_manifest_path()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help verify-project</>` for more detailed information.\n"
diff --git a/src/tools/cargo/src/bin/cargo/commands/version.rs b/src/tools/cargo/src/bin/cargo/commands/version.rs
index 65e1c6c47..5a6d710c3 100644
--- a/src/tools/cargo/src/bin/cargo/commands/version.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/version.rs
@@ -4,7 +4,7 @@ use crate::command_prelude::*;
pub fn cli() -> Command {
subcommand("version")
.about("Show version information")
- .arg_quiet()
+ .arg_silent_suggestion()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help version</>` for more detailed information.\n"
))
diff --git a/src/tools/cargo/src/bin/cargo/commands/yank.rs b/src/tools/cargo/src/bin/cargo/commands/yank.rs
index 75a1772ca..8a69d3eb7 100644
--- a/src/tools/cargo/src/bin/cargo/commands/yank.rs
+++ b/src/tools/cargo/src/bin/cargo/commands/yank.rs
@@ -19,7 +19,7 @@ pub fn cli() -> Command {
.arg_index("Registry index URL to yank from")
.arg_registry("Registry to yank from")
.arg(opt("token", "API token to use when authenticating").value_name("TOKEN"))
- .arg_quiet()
+ .arg_silent_suggestion()
.after_help(color_print::cstr!(
"Run `<cyan,bold>cargo help yank</>` for more detailed information.\n"
))
diff --git a/src/tools/cargo/src/bin/cargo/main.rs b/src/tools/cargo/src/bin/cargo/main.rs
index 245622b6c..14a4206d6 100644
--- a/src/tools/cargo/src/bin/cargo/main.rs
+++ b/src/tools/cargo/src/bin/cargo/main.rs
@@ -1,12 +1,10 @@
-#![warn(rust_2018_idioms)] // while we're getting used to 2018
-#![allow(clippy::all)]
-#![warn(clippy::disallowed_methods)]
+#![allow(clippy::self_named_module_files)] // false positive in `commands/build.rs`
use cargo::util::network::http::http_handle;
use cargo::util::network::http::needs_custom_http_transport;
-use cargo::util::toml::schema::StringOrVec;
use cargo::util::CliError;
use cargo::util::{self, closest_msg, command_prelude, CargoResult, CliResult, Config};
+use cargo::util_schemas::manifest::StringOrVec;
use cargo_util::{ProcessBuilder, ProcessError};
use std::collections::BTreeMap;
use std::env;
diff --git a/src/tools/cargo/src/cargo/core/compiler/fingerprint/dirty_reason.rs b/src/tools/cargo/src/cargo/core/compiler/fingerprint/dirty_reason.rs
index 363aab7e6..38b3fc441 100644
--- a/src/tools/cargo/src/cargo/core/compiler/fingerprint/dirty_reason.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/fingerprint/dirty_reason.rs
@@ -15,6 +15,10 @@ pub enum DirtyReason {
old: String,
new: String,
},
+ DeclaredFeaturesChanged {
+ old: String,
+ new: String,
+ },
TargetConfigurationChanged,
PathToSourceChanged,
ProfileConfigurationChanged,
@@ -141,6 +145,9 @@ impl DirtyReason {
DirtyReason::FeaturesChanged { .. } => {
s.dirty_because(unit, "the list of features changed")
}
+ DirtyReason::DeclaredFeaturesChanged { .. } => {
+ s.dirty_because(unit, "the list of declared features changed")
+ }
DirtyReason::TargetConfigurationChanged => {
s.dirty_because(unit, "the target configuration changed")
}
diff --git a/src/tools/cargo/src/cargo/core/compiler/fingerprint/mod.rs b/src/tools/cargo/src/cargo/core/compiler/fingerprint/mod.rs
index b1040be41..e1737a8b6 100644
--- a/src/tools/cargo/src/cargo/core/compiler/fingerprint/mod.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/fingerprint/mod.rs
@@ -65,6 +65,7 @@
//! Target Name | ✓ | ✓
//! TargetKind (bin/lib/etc.) | ✓ | ✓
//! Enabled Features | ✓ | ✓
+//! Declared Features | ✓ |
//! Immediate dependency’s hashes | ✓[^1] | ✓
//! [`CompileKind`] (host/target) | ✓ | ✓
//! __CARGO_DEFAULT_LIB_METADATA[^4] | | ✓
@@ -572,6 +573,8 @@ pub struct Fingerprint {
rustc: u64,
/// Sorted list of cfg features enabled.
features: String,
+ /// Sorted list of all the declared cfg features.
+ declared_features: String,
/// Hash of the `Target` struct, including the target name,
/// package-relative source path, edition, etc.
target: u64,
@@ -876,6 +879,7 @@ impl Fingerprint {
profile: 0,
path: 0,
features: String::new(),
+ declared_features: String::new(),
deps: Vec::new(),
local: Mutex::new(Vec::new()),
memoized_hash: Mutex::new(None),
@@ -922,6 +926,12 @@ impl Fingerprint {
new: self.features.clone(),
};
}
+ if self.declared_features != old.declared_features {
+ return DirtyReason::DeclaredFeaturesChanged {
+ old: old.declared_features.clone(),
+ new: self.declared_features.clone(),
+ };
+ }
if self.target != old.target {
return DirtyReason::TargetConfigurationChanged;
}
@@ -1200,6 +1210,7 @@ impl hash::Hash for Fingerprint {
let Fingerprint {
rustc,
ref features,
+ ref declared_features,
target,
path,
profile,
@@ -1215,6 +1226,7 @@ impl hash::Hash for Fingerprint {
(
rustc,
features,
+ declared_features,
target,
path,
profile,
@@ -1431,6 +1443,9 @@ fn calculate_normal(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Finger
allow_features.hash(&mut config);
}
let compile_kind = unit.kind.fingerprint_hash();
+ let mut declared_features = unit.pkg.summary().features().keys().collect::<Vec<_>>();
+ declared_features.sort(); // to avoid useless rebuild if the user orders it's features
+ // differently
Ok(Fingerprint {
rustc: util::hash_u64(&cx.bcx.rustc().verbose_version),
target: util::hash_u64(&unit.target),
@@ -1439,6 +1454,14 @@ fn calculate_normal(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Finger
// actually affect the output artifact so there's no need to hash it.
path: util::hash_u64(path_args(cx.bcx.ws, unit).0),
features: format!("{:?}", unit.features),
+ // Note we curently only populate `declared_features` when `-Zcheck-cfg`
+ // is passed since it's the only user-facing toggle that will make this
+ // fingerprint relevant.
+ declared_features: if cx.bcx.config.cli_unstable().check_cfg {
+ format!("{declared_features:?}")
+ } else {
+ "".to_string()
+ },
deps,
local: Mutex::new(local),
memoized_hash: Mutex::new(None),
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 af44940bd..6f59d5989 100644
--- a/src/tools/cargo/src/cargo/core/compiler/future_incompat.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/future_incompat.rs
@@ -348,7 +348,7 @@ fn get_updates(ws: &Workspace<'_>, package_ids: &BTreeSet<PackageId>) -> Option<
for (pkg_id, summaries) in summaries {
let mut updated_versions: Vec<_> = summaries
.iter()
- .map(|summary| summary.version())
+ .map(|summary| summary.as_summary().version())
.filter(|version| *version > pkg_id.version())
.collect();
updated_versions.sort();
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 e39fe184d..7c4c89e4f 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
@@ -3,7 +3,7 @@
//! ## Overview
//!
//! This module implements a job queue. A job here represents a unit of work,
-//! which is roughly a rusc invocation, a build script run, or just a no-op.
+//! which is roughly a rustc invocation, a build script run, or just a no-op.
//! The job queue primarily handles the following things:
//!
//! * Spawns concurrent jobs. Depending on its [`Freshness`], a job could be
diff --git a/src/tools/cargo/src/cargo/core/compiler/mod.rs b/src/tools/cargo/src/cargo/core/compiler/mod.rs
index ab43e9979..170b59b26 100644
--- a/src/tools/cargo/src/cargo/core/compiler/mod.rs
+++ b/src/tools/cargo/src/cargo/core/compiler/mod.rs
@@ -93,9 +93,9 @@ 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::schema::TomlDebugInfo;
-use crate::util::toml::schema::TomlTrimPaths;
use crate::util::{add_path_args, internal, iter_join_onto, profile};
+use crate::util_schemas::manifest::TomlDebugInfo;
+use crate::util_schemas::manifest::TomlTrimPaths;
use cargo_util::{paths, ProcessBuilder, ProcessError};
use rustfix::diagnostics::Applicability;
@@ -422,7 +422,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car
};
let errors = match output_options.errors_seen {
0 => String::new(),
- 1 => " due to previous error".to_string(),
+ 1 => " due to 1 previous error".to_string(),
count => format!(" due to {} previous errors", count),
};
let name = descriptive_pkg_name(&name, &target, &mode);
@@ -662,6 +662,15 @@ fn prepare_rustc(cx: &Context<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilde
let mut base = cx
.compilation
.rustc_process(unit, is_primary, is_workspace)?;
+ build_base_args(cx, &mut base, unit)?;
+
+ base.inherit_jobserver(&cx.jobserver);
+ build_deps_args(&mut base, cx, unit)?;
+ add_cap_lints(cx.bcx, unit, &mut base);
+ base.args(cx.bcx.rustflags_args(unit));
+ if cx.bcx.config.cli_unstable().binary_dep_depinfo {
+ base.arg("-Z").arg("binary-dep-depinfo");
+ }
if is_primary {
base.env("CARGO_PRIMARY_PACKAGE", "1");
@@ -671,15 +680,17 @@ fn prepare_rustc(cx: &Context<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilde
let tmp = cx.files().layout(unit.kind).prepare_tmp()?;
base.env("CARGO_TARGET_TMPDIR", tmp.display().to_string());
}
-
- base.inherit_jobserver(&cx.jobserver);
- build_base_args(cx, &mut base, unit)?;
- build_deps_args(&mut base, cx, unit)?;
- add_cap_lints(cx.bcx, unit, &mut base);
- base.args(cx.bcx.rustflags_args(unit));
- if cx.bcx.config.cli_unstable().binary_dep_depinfo {
- base.arg("-Z").arg("binary-dep-depinfo");
+ if cx.bcx.config.nightly_features_allowed {
+ // This must come after `build_base_args` (which calls `add_path_args`) so that the `cwd`
+ // is set correctly.
+ base.env(
+ "CARGO_RUSTC_CURRENT_DIR",
+ base.get_cwd()
+ .map(|c| c.display().to_string())
+ .unwrap_or(String::new()),
+ );
}
+
Ok(base)
}
@@ -732,7 +743,7 @@ fn prepare_rustdoc(cx: &Context<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuil
.arg(scrape_output_path(cx, unit)?);
// Only scrape example for items from crates in the workspace, to reduce generated file size
- for pkg in cx.bcx.ws.members() {
+ for pkg in cx.bcx.packages.packages() {
let names = pkg
.targets()
.iter()
@@ -1203,8 +1214,6 @@ fn trim_paths_args(
}
remap
};
- cmd.arg(sysroot_remap);
-
let package_remap = {
let pkg_root = unit.pkg.root();
let ws_root = cx.bcx.ws.root();
@@ -1221,7 +1230,7 @@ fn trim_paths_args(
// * 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.
+ remap.push("=."); // remap to relative rustc work dir explicitly
} else {
remap.push(pkg_root);
remap.push("=");
@@ -1231,7 +1240,11 @@ fn trim_paths_args(
}
remap
};
+
+ // Order of `--remap-path-prefix` flags is important for `-Zbuild-std`.
+ // We want to show `/rustc/<hash>/library/std` instead of `std-0.0.0`.
cmd.arg(package_remap);
+ cmd.arg(sysroot_remap);
Ok(())
}
@@ -1243,24 +1256,31 @@ fn trim_paths_args(
fn check_cfg_args(cx: &Context<'_, '_>, unit: &Unit) -> Vec<OsString> {
if cx.bcx.config.cli_unstable().check_cfg {
// This generate something like this:
- // - cfg(feature, values())
+ // - cfg()
// - 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.
+ // NOTE: Never generate a empty `values()` since it would mean that it's possible
+ // to have `cfg(feature)` without a feature name which is impossible.
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_feature.push("cfg(");
+ if !unit.pkg.summary().features().is_empty() {
+ arg_feature.push("feature, values(");
+ for (i, feature) in unit.pkg.summary().features().keys().enumerate() {
+ if i != 0 {
+ arg_feature.push(", ");
+ }
+ arg_feature.push("\"");
+ arg_feature.push(feature);
+ arg_feature.push("\"");
}
- arg_feature.push("\"");
- arg_feature.push(feature);
- arg_feature.push("\"");
+ arg_feature.push(")");
}
- arg_feature.push("))");
+ arg_feature.push(")");
vec![
OsString::from("-Zunstable-options"),
@@ -1414,6 +1434,7 @@ pub fn extern_args(
.require(Feature::public_dependency())
.is_ok()
&& !dep.public
+ && unit.target.is_lib()
{
opts.push("priv");
*unstable_opts = true;
diff --git a/src/tools/cargo/src/cargo/core/features.rs b/src/tools/cargo/src/cargo/core/features.rs
index 72a267f04..4f5b069ff 100644
--- a/src/tools/cargo/src/cargo/core/features.rs
+++ b/src/tools/cargo/src/cargo/core/features.rs
@@ -168,7 +168,7 @@ pub const SEE_CHANNELS: &str =
/// - Update [`CLI_VALUES`] to include the new edition.
/// - Set [`LATEST_UNSTABLE`] to Some with the new edition.
/// - Add an unstable feature to the [`features!`] macro invocation below for the new edition.
-/// - Gate on that new feature in [`TomlManifest::to_real_manifest`].
+/// - Gate on that new feature in [`toml::to_real_manifest`].
/// - Update the shell completion files.
/// - Update any failing tests (hopefully there are very few).
/// - Update unstable.md to add a new section for this new edition (see [this example]).
@@ -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::schema::TomlManifest::to_real_manifest
+/// [`toml::to_real_manifest`]: crate::util::toml::to_real_manifest
/// [`features!`]: macro.features.html
#[derive(Clone, Copy, Debug, Hash, PartialOrd, Ord, Eq, PartialEq, Serialize, Deserialize)]
pub enum Edition {
@@ -741,6 +741,7 @@ unstable_cli_options!(
doctest_xcompile: bool = ("Compile and run doctests for non-host target using runner config"),
dual_proc_macros: bool = ("Build proc-macros for both the host and the target"),
features: Option<Vec<String>> = (HIDDEN),
+ gc: bool = ("Track cache usage and \"garbage collect\" unused files"),
gitoxide: Option<GitoxideFeatures> = ("Use gitoxide for the given git interactions, or all of them if no argument is given"),
host_config: bool = ("Enable the [host] section in the .cargo/config.toml file"),
lints: bool = ("Pass `[lints]` to the linting tools"),
@@ -1077,6 +1078,7 @@ impl CliUnstable {
"direct-minimal-versions" => self.direct_minimal_versions = parse_empty(k, v)?,
"doctest-xcompile" => self.doctest_xcompile = parse_empty(k, v)?,
"dual-proc-macros" => self.dual_proc_macros = parse_empty(k, v)?,
+ "gc" => self.gc = parse_empty(k, v)?,
"gitoxide" => {
self.gitoxide = v.map_or_else(
|| Ok(Some(GitoxideFeatures::all())),
@@ -1114,7 +1116,17 @@ impl CliUnstable {
/// Generates an error if `-Z unstable-options` was not used for a new,
/// unstable command-line flag.
pub fn fail_if_stable_opt(&self, flag: &str, issue: u32) -> CargoResult<()> {
- if !self.unstable_options {
+ self.fail_if_stable_opt_custom_z(flag, issue, "unstable-options", self.unstable_options)
+ }
+
+ pub fn fail_if_stable_opt_custom_z(
+ &self,
+ flag: &str,
+ issue: u32,
+ z_name: &str,
+ enabled: bool,
+ ) -> CargoResult<()> {
+ if !enabled {
let see = format!(
"See https://github.com/rust-lang/cargo/issues/{issue} for more \
information about the `{flag}` flag."
@@ -1123,7 +1135,7 @@ impl CliUnstable {
let channel = channel();
if channel == "nightly" || channel == "dev" {
bail!(
- "the `{flag}` flag is unstable, pass `-Z unstable-options` to enable it\n\
+ "the `{flag}` flag is unstable, pass `-Z {z_name}` to enable it\n\
{see}"
);
} else {
@@ -1145,8 +1157,10 @@ impl CliUnstable {
config: &Config,
command: &str,
issue: u32,
+ z_name: &str,
+ enabled: bool,
) -> CargoResult<()> {
- if self.unstable_options {
+ if enabled {
return Ok(());
}
let see = format!(
@@ -1156,10 +1170,9 @@ impl CliUnstable {
);
if config.nightly_features_allowed {
bail!(
- "the `cargo {}` command is unstable, pass `-Z unstable-options` to enable it\n\
- {}",
- command,
- see
+ "the `cargo {command}` command is unstable, pass `-Z {z_name}` \
+ to enable it\n\
+ {see}",
);
} else {
bail!(
diff --git a/src/tools/cargo/src/cargo/core/gc.rs b/src/tools/cargo/src/cargo/core/gc.rs
new file mode 100644
index 000000000..565078ff0
--- /dev/null
+++ b/src/tools/cargo/src/cargo/core/gc.rs
@@ -0,0 +1,509 @@
+//! Support for garbage collecting unused files from downloaded files or
+//! artifacts from the target directory.
+//!
+//! The [`Gc`] type provides the high-level interface for the
+//! garbage-collection system.
+//!
+//! Garbage collection can be done "automatically" by cargo, which it does by
+//! default once a day when running any command that does a lot of work (like
+//! `cargo build`). The entry point for this is the [`auto_gc`] function,
+//! which handles some basic setup, creating the [`Gc`], and calling
+//! [`Gc::auto`].
+//!
+//! Garbage collection can also be done manually via the `cargo clean` command
+//! by passing any option that requests deleting unused files. That is
+//! implemented by calling the [`Gc::gc`] method.
+//!
+//! Garbage collection for the global cache is guided by the last-use tracking
+//! implemented in the [`crate::core::global_cache_tracker`] module. See that
+//! module documentation for an in-depth explanation of how global cache
+//! tracking works.
+
+use crate::core::global_cache_tracker::{self, GlobalCacheTracker};
+use crate::ops::CleanContext;
+use crate::util::cache_lock::{CacheLock, CacheLockMode};
+use crate::{CargoResult, Config};
+use anyhow::{format_err, Context};
+use serde::Deserialize;
+use std::time::Duration;
+
+/// Default max age to auto-clean extracted sources, which can be recovered
+/// without downloading anything.
+const DEFAULT_MAX_AGE_EXTRACTED: &str = "1 month";
+/// Default max ago to auto-clean cache data, which must be downloaded to
+/// recover.
+const DEFAULT_MAX_AGE_DOWNLOADED: &str = "3 months";
+/// How often auto-gc will run by default unless overridden in the config.
+const DEFAULT_AUTO_FREQUENCY: &str = "1 day";
+
+/// Performs automatic garbage collection.
+///
+/// This is called in various places in Cargo where garbage collection should
+/// be performed automatically based on the config settings. The default
+/// behavior is to only clean once a day.
+///
+/// This should only be called in code paths for commands that are already
+/// doing a lot of work. It should only be called *after* crates are
+/// downloaded so that the last-use data is updated first.
+///
+/// It should be cheap to call this multiple times (subsequent calls are
+/// ignored), but try not to abuse that.
+pub fn auto_gc(config: &Config) {
+ if !config.cli_unstable().gc {
+ return;
+ }
+ if !config.network_allowed() {
+ // As a conservative choice, auto-gc is disabled when offline. If the
+ // user is indefinitely offline, we don't want to delete things they
+ // may later depend on.
+ tracing::trace!(target: "gc", "running offline, auto gc disabled");
+ return;
+ }
+
+ if let Err(e) = auto_gc_inner(config) {
+ if global_cache_tracker::is_silent_error(&e) && !config.extra_verbose() {
+ tracing::warn!(target: "gc", "failed to auto-clean cache data: {e:?}");
+ } else {
+ crate::display_warning_with_error(
+ "failed to auto-clean cache data",
+ &e,
+ &mut config.shell(),
+ );
+ }
+ }
+}
+
+fn auto_gc_inner(config: &Config) -> CargoResult<()> {
+ let _lock = match config.try_acquire_package_cache_lock(CacheLockMode::MutateExclusive)? {
+ Some(lock) => lock,
+ None => {
+ tracing::debug!(target: "gc", "unable to acquire mutate lock, auto gc disabled");
+ return Ok(());
+ }
+ };
+ // This should not be called when there are pending deferred entries, so check that.
+ let deferred = config.deferred_global_last_use()?;
+ debug_assert!(deferred.is_empty());
+ let mut global_cache_tracker = config.global_cache_tracker()?;
+ let mut gc = Gc::new(config, &mut global_cache_tracker)?;
+ let mut clean_ctx = CleanContext::new(config);
+ gc.auto(&mut clean_ctx)?;
+ Ok(())
+}
+
+/// Automatic garbage collection settings from the `gc.auto` config table.
+///
+/// NOTE: Not all of these options may get stabilized. Some of them are very
+/// low-level details, and may not be something typical users need.
+///
+/// If any of these options are `None`, the built-in default is used.
+#[derive(Deserialize, Default)]
+#[serde(rename_all = "kebab-case")]
+struct AutoConfig {
+ /// The maximum frequency that automatic garbage collection happens.
+ frequency: Option<String>,
+ /// Anything older than this duration will be deleted in the source cache.
+ max_src_age: Option<String>,
+ /// Anything older than this duration will be deleted in the compressed crate cache.
+ max_crate_age: Option<String>,
+ /// Any index older than this duration will be deleted from the index cache.
+ max_index_age: Option<String>,
+ /// Any git checkout older than this duration will be deleted from the checkout cache.
+ max_git_co_age: Option<String>,
+ /// Any git clone older than this duration will be deleted from the git cache.
+ max_git_db_age: Option<String>,
+}
+
+/// Options to use for garbage collection.
+#[derive(Clone, Debug, Default)]
+pub struct GcOpts {
+ /// The `--max-src-age` CLI option.
+ pub max_src_age: Option<Duration>,
+ // The `--max-crate-age` CLI option.
+ pub max_crate_age: Option<Duration>,
+ /// The `--max-index-age` CLI option.
+ pub max_index_age: Option<Duration>,
+ /// The `--max-git-co-age` CLI option.
+ pub max_git_co_age: Option<Duration>,
+ /// The `--max-git-db-age` CLI option.
+ pub max_git_db_age: Option<Duration>,
+ /// The `--max-src-size` CLI option.
+ pub max_src_size: Option<u64>,
+ /// The `--max-crate-size` CLI option.
+ pub max_crate_size: Option<u64>,
+ /// The `--max-git-size` CLI option.
+ pub max_git_size: Option<u64>,
+ /// The `--max-download-size` CLI option.
+ pub max_download_size: Option<u64>,
+}
+
+impl GcOpts {
+ /// Returns whether any download cache cleaning options are set.
+ pub fn is_download_cache_opt_set(&self) -> bool {
+ self.max_src_age.is_some()
+ || self.max_crate_age.is_some()
+ || self.max_index_age.is_some()
+ || self.max_git_co_age.is_some()
+ || self.max_git_db_age.is_some()
+ || self.max_src_size.is_some()
+ || self.max_crate_size.is_some()
+ || self.max_git_size.is_some()
+ || self.max_download_size.is_some()
+ }
+
+ /// Returns whether any download cache cleaning options based on size are set.
+ pub fn is_download_cache_size_set(&self) -> bool {
+ self.max_src_size.is_some()
+ || self.max_crate_size.is_some()
+ || self.max_git_size.is_some()
+ || self.max_download_size.is_some()
+ }
+
+ /// Updates the `GcOpts` to incorporate the specified max download age.
+ ///
+ /// "Download" means any cached data that can be re-downloaded.
+ pub fn set_max_download_age(&mut self, max_download_age: Duration) {
+ self.max_src_age = Some(maybe_newer_span(max_download_age, self.max_src_age));
+ self.max_crate_age = Some(maybe_newer_span(max_download_age, self.max_crate_age));
+ self.max_index_age = Some(maybe_newer_span(max_download_age, self.max_index_age));
+ self.max_git_co_age = Some(maybe_newer_span(max_download_age, self.max_git_co_age));
+ self.max_git_db_age = Some(maybe_newer_span(max_download_age, self.max_git_db_age));
+ }
+
+ /// Updates the configuration of this [`GcOpts`] to incorporate the
+ /// settings from config.
+ pub fn update_for_auto_gc(&mut self, config: &Config) -> CargoResult<()> {
+ let auto_config = config
+ .get::<Option<AutoConfig>>("gc.auto")?
+ .unwrap_or_default();
+ self.update_for_auto_gc_config(&auto_config)
+ }
+
+ fn update_for_auto_gc_config(&mut self, auto_config: &AutoConfig) -> CargoResult<()> {
+ self.max_src_age = newer_time_span_for_config(
+ self.max_src_age,
+ "gc.auto.max-src-age",
+ auto_config
+ .max_src_age
+ .as_deref()
+ .unwrap_or(DEFAULT_MAX_AGE_EXTRACTED),
+ )?;
+ self.max_crate_age = newer_time_span_for_config(
+ self.max_crate_age,
+ "gc.auto.max-crate-age",
+ auto_config
+ .max_crate_age
+ .as_deref()
+ .unwrap_or(DEFAULT_MAX_AGE_DOWNLOADED),
+ )?;
+ self.max_index_age = newer_time_span_for_config(
+ self.max_index_age,
+ "gc.auto.max-index-age",
+ auto_config
+ .max_index_age
+ .as_deref()
+ .unwrap_or(DEFAULT_MAX_AGE_DOWNLOADED),
+ )?;
+ self.max_git_co_age = newer_time_span_for_config(
+ self.max_git_co_age,
+ "gc.auto.max-git-co-age",
+ auto_config
+ .max_git_co_age
+ .as_deref()
+ .unwrap_or(DEFAULT_MAX_AGE_EXTRACTED),
+ )?;
+ self.max_git_db_age = newer_time_span_for_config(
+ self.max_git_db_age,
+ "gc.auto.max-git-db-age",
+ auto_config
+ .max_git_db_age
+ .as_deref()
+ .unwrap_or(DEFAULT_MAX_AGE_DOWNLOADED),
+ )?;
+ Ok(())
+ }
+}
+
+/// Garbage collector.
+///
+/// See the module docs at [`crate::core::gc`] for more information on GC.
+pub struct Gc<'a, 'config> {
+ config: &'config Config,
+ global_cache_tracker: &'a mut GlobalCacheTracker,
+ /// A lock on the package cache.
+ ///
+ /// This is important to be held, since we don't want multiple cargos to
+ /// be allowed to write to the cache at the same time, or for others to
+ /// read while we are modifying the cache.
+ #[allow(dead_code)] // Held for drop.
+ lock: CacheLock<'config>,
+}
+
+impl<'a, 'config> Gc<'a, 'config> {
+ pub fn new(
+ config: &'config Config,
+ global_cache_tracker: &'a mut GlobalCacheTracker,
+ ) -> CargoResult<Gc<'a, 'config>> {
+ let lock = config.acquire_package_cache_lock(CacheLockMode::MutateExclusive)?;
+ Ok(Gc {
+ config,
+ global_cache_tracker,
+ lock,
+ })
+ }
+
+ /// Performs automatic garbage cleaning.
+ ///
+ /// This returns immediately without doing work if garbage collection has
+ /// been performed recently (since `gc.auto.frequency`).
+ fn auto(&mut self, clean_ctx: &mut CleanContext<'config>) -> CargoResult<()> {
+ if !self.config.cli_unstable().gc {
+ return Ok(());
+ }
+ let auto_config = self
+ .config
+ .get::<Option<AutoConfig>>("gc.auto")?
+ .unwrap_or_default();
+ let Some(freq) = parse_frequency(
+ auto_config
+ .frequency
+ .as_deref()
+ .unwrap_or(DEFAULT_AUTO_FREQUENCY),
+ )?
+ else {
+ tracing::trace!(target: "gc", "auto gc disabled");
+ return Ok(());
+ };
+ if !self.global_cache_tracker.should_run_auto_gc(freq)? {
+ return Ok(());
+ }
+ let mut gc_opts = GcOpts::default();
+ gc_opts.update_for_auto_gc_config(&auto_config)?;
+ self.gc(clean_ctx, &gc_opts)?;
+ if !clean_ctx.dry_run {
+ self.global_cache_tracker.set_last_auto_gc()?;
+ }
+ Ok(())
+ }
+
+ /// Performs garbage collection based on the given options.
+ pub fn gc(
+ &mut self,
+ clean_ctx: &mut CleanContext<'config>,
+ gc_opts: &GcOpts,
+ ) -> CargoResult<()> {
+ self.global_cache_tracker.clean(clean_ctx, gc_opts)?;
+ // In the future, other gc operations go here, such as target cleaning.
+ Ok(())
+ }
+}
+
+/// Returns the shorter duration from `cur_span` versus `config_span`.
+///
+/// This is used because the user may specify multiple options which overlap,
+/// and this will pick whichever one is shorter.
+///
+/// * `cur_span` is the span we are comparing against (the value from the CLI
+/// option). If None, just returns the config duration.
+/// * `config_name` is the name of the config option the span is loaded from.
+/// * `config_span` is the span value loaded from config.
+fn newer_time_span_for_config(
+ cur_span: Option<Duration>,
+ config_name: &str,
+ config_span: &str,
+) -> CargoResult<Option<Duration>> {
+ let config_span = parse_time_span_for_config(config_name, config_span)?;
+ Ok(Some(maybe_newer_span(config_span, cur_span)))
+}
+
+/// Returns whichever [`Duration`] is shorter.
+fn maybe_newer_span(a: Duration, b: Option<Duration>) -> Duration {
+ match b {
+ Some(b) => {
+ if b < a {
+ b
+ } else {
+ a
+ }
+ }
+ None => a,
+ }
+}
+
+/// Parses a frequency string.
+///
+/// Returns `Ok(None)` if the frequency is "never".
+fn parse_frequency(frequency: &str) -> CargoResult<Option<Duration>> {
+ if frequency == "always" {
+ return Ok(Some(Duration::new(0, 0)));
+ } else if frequency == "never" {
+ return Ok(None);
+ }
+ let duration = maybe_parse_time_span(frequency).ok_or_else(|| {
+ format_err!(
+ "config option `gc.auto.frequency` expected a value of \"always\", \"never\", \
+ or \"N seconds/minutes/days/weeks/months\", got: {frequency:?}"
+ )
+ })?;
+ Ok(Some(duration))
+}
+
+/// Parses a time span value fetched from config.
+///
+/// This is here to provide better error messages specific to reading from
+/// config.
+fn parse_time_span_for_config(config_name: &str, span: &str) -> CargoResult<Duration> {
+ maybe_parse_time_span(span).ok_or_else(|| {
+ format_err!(
+ "config option `{config_name}` expected a value of the form \
+ \"N seconds/minutes/days/weeks/months\", got: {span:?}"
+ )
+ })
+}
+
+/// Parses a time span string.
+///
+/// Returns None if the value is not valid. See [`parse_time_span`] if you
+/// need a variant that generates an error message.
+fn maybe_parse_time_span(span: &str) -> Option<Duration> {
+ let Some(right_i) = span.find(|c: char| !c.is_ascii_digit()) else {
+ return None;
+ };
+ let (left, mut right) = span.split_at(right_i);
+ if right.starts_with(' ') {
+ right = &right[1..];
+ }
+ let count: u64 = left.parse().ok()?;
+ let factor = match right {
+ "second" | "seconds" => 1,
+ "minute" | "minutes" => 60,
+ "hour" | "hours" => 60 * 60,
+ "day" | "days" => 24 * 60 * 60,
+ "week" | "weeks" => 7 * 24 * 60 * 60,
+ "month" | "months" => 2_629_746, // average is 30.436875 days
+ _ => return None,
+ };
+ Some(Duration::from_secs(factor * count))
+}
+
+/// Parses a time span string.
+pub fn parse_time_span(span: &str) -> CargoResult<Duration> {
+ maybe_parse_time_span(span).ok_or_else(|| {
+ format_err!(
+ "expected a value of the form \
+ \"N seconds/minutes/days/weeks/months\", got: {span:?}"
+ )
+ })
+}
+
+/// Parses a file size using metric or IEC units.
+pub fn parse_human_size(input: &str) -> CargoResult<u64> {
+ let re = regex::Regex::new(r"(?i)^([0-9]+(\.[0-9])?) ?(b|kb|mb|gb|kib|mib|gib)?$").unwrap();
+ let cap = re.captures(input).ok_or_else(|| {
+ format_err!(
+ "invalid size `{input}`, \
+ expected a number with an optional B, kB, MB, GB, kiB, MiB, or GiB suffix"
+ )
+ })?;
+ let factor = match cap.get(3) {
+ Some(suffix) => match suffix.as_str().to_lowercase().as_str() {
+ "b" => 1.0,
+ "kb" => 1_000.0,
+ "mb" => 1_000_000.0,
+ "gb" => 1_000_000_000.0,
+ "kib" => 1024.0,
+ "mib" => 1024.0 * 1024.0,
+ "gib" => 1024.0 * 1024.0 * 1024.0,
+ s => unreachable!("suffix `{s}` out of sync with regex"),
+ },
+ None => {
+ return cap[1]
+ .parse()
+ .with_context(|| format!("expected an integer size, got `{}`", &cap[1]))
+ }
+ };
+ let num = cap[1]
+ .parse::<f64>()
+ .with_context(|| format!("expected an integer or float, found `{}`", &cap[1]))?;
+ Ok((num * factor) as u64)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ #[test]
+ fn time_spans() {
+ let d = |x| Some(Duration::from_secs(x));
+ assert_eq!(maybe_parse_time_span("0 seconds"), d(0));
+ assert_eq!(maybe_parse_time_span("1second"), d(1));
+ assert_eq!(maybe_parse_time_span("23 seconds"), d(23));
+ assert_eq!(maybe_parse_time_span("5 minutes"), d(60 * 5));
+ assert_eq!(maybe_parse_time_span("2 hours"), d(60 * 60 * 2));
+ assert_eq!(maybe_parse_time_span("1 day"), d(60 * 60 * 24));
+ assert_eq!(maybe_parse_time_span("2 weeks"), d(60 * 60 * 24 * 14));
+ assert_eq!(maybe_parse_time_span("6 months"), d(2_629_746 * 6));
+
+ assert_eq!(parse_frequency("5 seconds").unwrap(), d(5));
+ assert_eq!(parse_frequency("always").unwrap(), d(0));
+ assert_eq!(parse_frequency("never").unwrap(), None);
+ }
+
+ #[test]
+ fn time_span_errors() {
+ assert_eq!(maybe_parse_time_span(""), None);
+ assert_eq!(maybe_parse_time_span("1"), None);
+ assert_eq!(maybe_parse_time_span("second"), None);
+ assert_eq!(maybe_parse_time_span("+2 seconds"), None);
+ assert_eq!(maybe_parse_time_span("day"), None);
+ assert_eq!(maybe_parse_time_span("-1 days"), None);
+ assert_eq!(maybe_parse_time_span("1.5 days"), None);
+ assert_eq!(maybe_parse_time_span("1 dayz"), None);
+ assert_eq!(maybe_parse_time_span("always"), None);
+ assert_eq!(maybe_parse_time_span("never"), None);
+ assert_eq!(maybe_parse_time_span("1 day "), None);
+ assert_eq!(maybe_parse_time_span(" 1 day"), None);
+ assert_eq!(maybe_parse_time_span("1 second"), None);
+
+ let e = parse_time_span_for_config("gc.auto.max-src-age", "-1 days").unwrap_err();
+ assert_eq!(
+ e.to_string(),
+ "config option `gc.auto.max-src-age` \
+ expected a value of the form \"N seconds/minutes/days/weeks/months\", \
+ got: \"-1 days\""
+ );
+ let e = parse_frequency("abc").unwrap_err();
+ assert_eq!(
+ e.to_string(),
+ "config option `gc.auto.frequency` \
+ expected a value of \"always\", \"never\", or \"N seconds/minutes/days/weeks/months\", \
+ got: \"abc\""
+ );
+ }
+
+ #[test]
+ fn human_sizes() {
+ assert_eq!(parse_human_size("0").unwrap(), 0);
+ assert_eq!(parse_human_size("123").unwrap(), 123);
+ assert_eq!(parse_human_size("123b").unwrap(), 123);
+ assert_eq!(parse_human_size("123B").unwrap(), 123);
+ assert_eq!(parse_human_size("123 b").unwrap(), 123);
+ assert_eq!(parse_human_size("123 B").unwrap(), 123);
+ assert_eq!(parse_human_size("1kb").unwrap(), 1_000);
+ assert_eq!(parse_human_size("5kb").unwrap(), 5_000);
+ assert_eq!(parse_human_size("1mb").unwrap(), 1_000_000);
+ assert_eq!(parse_human_size("1gb").unwrap(), 1_000_000_000);
+ assert_eq!(parse_human_size("1kib").unwrap(), 1_024);
+ assert_eq!(parse_human_size("1mib").unwrap(), 1_048_576);
+ assert_eq!(parse_human_size("1gib").unwrap(), 1_073_741_824);
+ assert_eq!(parse_human_size("1.5kb").unwrap(), 1_500);
+ assert_eq!(parse_human_size("1.7b").unwrap(), 1);
+
+ assert!(parse_human_size("").is_err());
+ assert!(parse_human_size("x").is_err());
+ assert!(parse_human_size("1x").is_err());
+ assert!(parse_human_size("1 2").is_err());
+ assert!(parse_human_size("1.5").is_err());
+ assert!(parse_human_size("+1").is_err());
+ assert!(parse_human_size("123 b").is_err());
+ }
+}
diff --git a/src/tools/cargo/src/cargo/core/global_cache_tracker.rs b/src/tools/cargo/src/cargo/core/global_cache_tracker.rs
new file mode 100644
index 000000000..79ae252d9
--- /dev/null
+++ b/src/tools/cargo/src/cargo/core/global_cache_tracker.rs
@@ -0,0 +1,1827 @@
+//! Support for tracking the last time files were used to assist with cleaning
+//! up those files if they haven't been used in a while.
+//!
+//! Tracking of cache files is stored in a sqlite database which contains a
+//! timestamp of the last time the file was used, as well as the size of the
+//! file.
+//!
+//! While cargo is running, when it detects a use of a cache file, it adds a
+//! timestamp to [`DeferredGlobalLastUse`]. This batches up a set of changes
+//! that are then flushed to the database all at once (via
+//! [`DeferredGlobalLastUse::save`]). Ideally saving would only be done once
+//! for performance reasons, but that is not really possible due to the way
+//! cargo works, since there are different ways cargo can be used (like `cargo
+//! generate-lockfile`, `cargo fetch`, and `cargo build` are all very
+//! different ways the code is used).
+//!
+//! All of the database interaction is done through the [`GlobalCacheTracker`]
+//! type.
+//!
+//! There is a single global [`GlobalCacheTracker`] and
+//! [`DeferredGlobalLastUse`] stored in [`Config`].
+//!
+//! The high-level interface for performing garbage collection is defined in
+//! the [`crate::core::gc`] module. The functions there are responsible for
+//! interacting with the [`GlobalCacheTracker`] to handle cleaning of global
+//! cache data.
+//!
+//! ## Automatic gc
+//!
+//! Some commands (primarily the build commands) will trigger an automatic
+//! deletion of files that haven't been used in a while. The high-level
+//! interface for this is the [`crate::core::gc::auto_gc`] function.
+//!
+//! The [`GlobalCacheTracker`] database tracks the last time an automatic gc
+//! was performed so that it is only done once per day for performance
+//! reasons.
+//!
+//! ## Manual gc
+//!
+//! The user can perform a manual garbage collection with the `cargo clean`
+//! command. That command has a variety of options to specify what to delete.
+//! Manual gc supports deleting based on age or size or both. From a
+//! high-level, this is done by the [`crate::core::gc::Gc::gc`] method, which
+//! calls into [`GlobalCacheTracker`] to handle all the cleaning.
+//!
+//! ## Locking
+//!
+//! Usage of the database requires that the package cache is locked to prevent
+//! concurrent access. Although sqlite has built-in locking support, we want
+//! to use cargo's locking so that the "Blocking" message gets displayed, and
+//! so that locks can block indefinitely for long-running build commands.
+//! [`rusqlite`] has a default timeout of 5 seconds, though that is
+//! configurable.
+//!
+//! When garbage collection is being performed, the package cache lock must be
+//! in [`CacheLockMode::MutateExclusive`] to ensure no other cargo process is
+//! running. See [`crate::util::cache_lock`] for more detail on locking.
+//!
+//! When performing automatic gc, [`crate::core::gc::auto_gc`] will skip the
+//! GC if the package cache lock is already held by anything else. Automatic
+//! GC is intended to be opportunistic, and should impose as little disruption
+//! to the user as possible.
+//!
+//! ## Compatibility
+//!
+//! The database must retain both forwards and backwards compatibility between
+//! different versions of cargo. For the most part, this shouldn't be too
+//! difficult to maintain. Generally sqlite doesn't change on-disk formats
+//! between versions (the introduction of WAL is one of the few examples where
+//! version 3 had a format change, but we wouldn't use it anyway since it has
+//! shared-memory requirements cargo can't depend on due to things like
+//! network mounts).
+//!
+//! Schema changes must be managed through [`migrations`] by adding new
+//! entries that make a change to the database. Changes must not break older
+//! versions of cargo. Generally, adding columns should be fine (either with a
+//! default value, or NULL). Adding tables should also be fine. Just don't do
+//! destructive things like removing a column, or changing the semantics of an
+//! existing column.
+//!
+//! Since users may run older versions of cargo that do not do cache tracking,
+//! the [`GlobalCacheTracker::sync_db_with_files`] method helps dealing with
+//! keeping the database in sync in the presence of older versions of cargo
+//! touching the cache directories.
+//!
+//! ## Performance
+//!
+//! A lot of focus on the design of this system is to minimize the performance
+//! impact. Every build command needs to save updates which we try to avoid
+//! having a noticeable impact on build times. Systems like Windows,
+//! particularly with a magnetic hard disk, can experience a fairly large
+//! impact of cargo's overhead. Cargo's benchsuite has some benchmarks to help
+//! compare different environments, or changes to the code here. Please try to
+//! keep performance in mind if making any major changes.
+//!
+//! Performance of `cargo clean` is not quite as important since it is not
+//! expected to be run often. However, it is still courteous to the user to
+//! try to not impact it too much. One part that has a performance concern is
+//! that the clean command will synchronize the database with whatever is on
+//! disk if needed (in case files were added by older versions of cargo that
+//! don't do cache tracking, or if the user manually deleted some files). This
+//! can potentially be very slow, especially if the two are very out of sync.
+//!
+//! ## Filesystems
+//!
+//! Everything here is sensitive to the kind of filesystem it is running on.
+//! People tend to run cargo in all sorts of strange environments that have
+//! limited capabilities, or on things like read-only mounts. The code here
+//! needs to gracefully handle as many situations as possible.
+//!
+//! See also the information in the [Performance](#performance) and
+//! [Locking](#locking) sections when considering different filesystems and
+//! their impact on performance and locking.
+//!
+//! There are checks for read-only filesystems, which is generally ignored.
+
+use crate::core::gc::GcOpts;
+use crate::core::Verbosity;
+use crate::ops::CleanContext;
+use crate::util::cache_lock::CacheLockMode;
+use crate::util::interning::InternedString;
+use crate::util::sqlite::{self, basic_migration, Migration};
+use crate::util::{Filesystem, Progress, ProgressStyle};
+use crate::{CargoResult, Config};
+use anyhow::{bail, Context};
+use cargo_util::paths;
+use rusqlite::{params, Connection, ErrorCode};
+use std::collections::{hash_map, HashMap};
+use std::path::{Path, PathBuf};
+use std::time::{Duration, SystemTime};
+use tracing::{debug, trace};
+
+/// The filename of the database.
+const GLOBAL_CACHE_FILENAME: &str = ".global-cache";
+
+const REGISTRY_INDEX_TABLE: &str = "registry_index";
+const REGISTRY_CRATE_TABLE: &str = "registry_crate";
+const REGISTRY_SRC_TABLE: &str = "registry_src";
+const GIT_DB_TABLE: &str = "git_db";
+const GIT_CO_TABLE: &str = "git_checkout";
+
+/// How often timestamps will be updated.
+///
+/// As an optimization timestamps are not updated unless they are older than
+/// the given number of seconds. This helps reduce the amount of disk I/O when
+/// running cargo multiple times within a short window.
+const UPDATE_RESOLUTION: u64 = 60 * 5;
+
+/// Type for timestamps as stored in the database.
+///
+/// These are seconds since the Unix epoch.
+type Timestamp = u64;
+
+/// The key for a registry index entry stored in the database.
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+pub struct RegistryIndex {
+ /// A unique name of the registry source.
+ pub encoded_registry_name: InternedString,
+}
+
+/// The key for a registry `.crate` entry stored in the database.
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+pub struct RegistryCrate {
+ /// A unique name of the registry source.
+ pub encoded_registry_name: InternedString,
+ /// The filename of the compressed crate, like `foo-1.2.3.crate`.
+ pub crate_filename: InternedString,
+ /// The size of the `.crate` file.
+ pub size: u64,
+}
+
+/// The key for a registry src directory entry stored in the database.
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+pub struct RegistrySrc {
+ /// A unique name of the registry source.
+ pub encoded_registry_name: InternedString,
+ /// The directory name of the extracted source, like `foo-1.2.3`.
+ pub package_dir: InternedString,
+ /// Total size of the src directory in bytes.
+ ///
+ /// This can be None when the size is unknown. For example, when the src
+ /// directory already exists on disk, and we just want to update the
+ /// last-use timestamp. We don't want to take the expense of computing disk
+ /// usage unless necessary. [`GlobalCacheTracker::populate_untracked`]
+ /// will handle any actual NULL values in the database, which can happen
+ /// when the src directory is created by an older version of cargo that
+ /// did not track sizes.
+ pub size: Option<u64>,
+}
+
+/// The key for a git db entry stored in the database.
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+pub struct GitDb {
+ /// A unique name of the git database.
+ pub encoded_git_name: InternedString,
+}
+
+/// The key for a git checkout entry stored in the database.
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+pub struct GitCheckout {
+ /// A unique name of the git database.
+ pub encoded_git_name: InternedString,
+ /// A unique name of the checkout without the database.
+ pub short_name: InternedString,
+ /// Total size of the checkout directory.
+ ///
+ /// This can be None when the size is unknown. See [`RegistrySrc::size`]
+ /// for an explanation.
+ pub size: Option<u64>,
+}
+
+/// Filesystem paths in the global cache.
+///
+/// Accessing these assumes a lock has already been acquired.
+struct BasePaths {
+ /// Root path to the index caches.
+ index: PathBuf,
+ /// Root path to the git DBs.
+ git_db: PathBuf,
+ /// Root path to the git checkouts.
+ git_co: PathBuf,
+ /// Root path to the `.crate` files.
+ crate_dir: PathBuf,
+ /// Root path to the `src` directories.
+ src: PathBuf,
+}
+
+/// Migrations which initialize the database, and can be used to evolve it over time.
+///
+/// See [`Migration`] for more detail.
+///
+/// **Be sure to not change the order or entries here!**
+fn migrations() -> Vec<Migration> {
+ vec![
+ // registry_index tracks the overall usage of an index cache, and tracks a
+ // numeric ID to refer to that index that is used in other tables.
+ basic_migration(
+ "CREATE TABLE registry_index (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name TEXT UNIQUE NOT NULL,
+ timestamp INTEGER NOT NULL
+ )",
+ ),
+ // .crate files
+ basic_migration(
+ "CREATE TABLE registry_crate (
+ registry_id INTEGER NOT NULL,
+ name TEXT NOT NULL,
+ size INTEGER NOT NULL,
+ timestamp INTEGER NOT NULL,
+ PRIMARY KEY (registry_id, name),
+ FOREIGN KEY (registry_id) REFERENCES registry_index (id) ON DELETE CASCADE
+ )",
+ ),
+ // Extracted src directories
+ //
+ // Note that `size` can be NULL. This will happen when marking a src
+ // directory as used that was created by an older version of cargo
+ // that didn't do size tracking.
+ basic_migration(
+ "CREATE TABLE registry_src (
+ registry_id INTEGER NOT NULL,
+ name TEXT NOT NULL,
+ size INTEGER,
+ timestamp INTEGER NOT NULL,
+ PRIMARY KEY (registry_id, name),
+ FOREIGN KEY (registry_id) REFERENCES registry_index (id) ON DELETE CASCADE
+ )",
+ ),
+ // Git db directories
+ basic_migration(
+ "CREATE TABLE git_db (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name TEXT UNIQUE NOT NULL,
+ timestamp INTEGER NOT NULL
+ )",
+ ),
+ // Git checkout directories
+ basic_migration(
+ "CREATE TABLE git_checkout (
+ git_id INTEGER NOT NULL,
+ name TEXT UNIQUE NOT NULL,
+ size INTEGER,
+ timestamp INTEGER NOT NULL,
+ PRIMARY KEY (git_id, name),
+ FOREIGN KEY (git_id) REFERENCES git_db (id) ON DELETE CASCADE
+ )",
+ ),
+ // This is a general-purpose single-row table that can store arbitrary
+ // data. Feel free to add columns (with ALTER TABLE) if necessary.
+ basic_migration(
+ "CREATE TABLE global_data (
+ last_auto_gc INTEGER NOT NULL
+ )",
+ ),
+ // last_auto_gc tracks the last time auto-gc was run (so that it only
+ // runs roughly once a day for performance reasons). Prime it with the
+ // current time to establish a baseline.
+ Box::new(|conn| {
+ conn.execute(
+ "INSERT INTO global_data (last_auto_gc) VALUES (?1)",
+ [now()],
+ )?;
+ Ok(())
+ }),
+ ]
+}
+
+/// Type for SQL columns that refer to the primary key of their parent table.
+///
+/// For example, `registry_crate.registry_id` refers to its parent `registry_index.id`.
+#[derive(Copy, Clone, Debug, PartialEq)]
+struct ParentId(i64);
+
+impl rusqlite::types::FromSql for ParentId {
+ fn column_result(value: rusqlite::types::ValueRef<'_>) -> rusqlite::types::FromSqlResult<Self> {
+ let i = i64::column_result(value)?;
+ Ok(ParentId(i))
+ }
+}
+
+impl rusqlite::types::ToSql for ParentId {
+ fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
+ Ok(rusqlite::types::ToSqlOutput::from(self.0))
+ }
+}
+
+/// Tracking for the global shared cache (registry files, etc.).
+///
+/// This is the interface to the global cache database, used for tracking and
+/// cleaning. See the [`crate::core::global_cache_tracker`] module docs for
+/// details.
+#[derive(Debug)]
+pub struct GlobalCacheTracker {
+ /// Connection to the SQLite database.
+ conn: Connection,
+ /// This is an optimization used to make sure cargo only checks if gc
+ /// needs to run once per session. This starts as `false`, and then the
+ /// first time it checks if automatic gc needs to run, it will be set to
+ /// `true`.
+ auto_gc_checked_this_session: bool,
+}
+
+impl GlobalCacheTracker {
+ /// Creates a new [`GlobalCacheTracker`].
+ ///
+ /// The caller is responsible for locking the package cache with
+ /// [`CacheLockMode::DownloadExclusive`] before calling this.
+ pub fn new(config: &Config) -> CargoResult<GlobalCacheTracker> {
+ let db_path = Self::db_path(config);
+ // A package cache lock is required to ensure only one cargo is
+ // accessing at the same time. If there is concurrent access, we
+ // want to rely on cargo's own "Blocking" system (which can
+ // provide user feedback) rather than blocking inside sqlite
+ // (which by default has a short timeout).
+ let db_path =
+ config.assert_package_cache_locked(CacheLockMode::DownloadExclusive, &db_path);
+ let mut conn = if config.cli_unstable().gc {
+ Connection::open(db_path)?
+ } else {
+ // To simplify things (so there aren't checks everywhere for being
+ // enabled), just process everything in memory.
+ Connection::open_in_memory()?
+ };
+ conn.pragma_update(None, "foreign_keys", true)?;
+ sqlite::migrate(&mut conn, &migrations())?;
+ Ok(GlobalCacheTracker {
+ conn,
+ auto_gc_checked_this_session: false,
+ })
+ }
+
+ /// The path to the database.
+ pub fn db_path(config: &Config) -> Filesystem {
+ config.home().join(GLOBAL_CACHE_FILENAME)
+ }
+
+ /// Given an encoded registry name, returns its ID.
+ ///
+ /// Returns None if the given name isn't in the database.
+ fn id_from_name(
+ conn: &Connection,
+ table_name: &str,
+ encoded_name: &str,
+ ) -> CargoResult<Option<ParentId>> {
+ let mut stmt =
+ conn.prepare_cached(&format!("SELECT id FROM {table_name} WHERE name = ?"))?;
+ match stmt.query_row([encoded_name], |row| row.get(0)) {
+ Ok(id) => Ok(Some(id)),
+ Err(rusqlite::Error::QueryReturnedNoRows) => Ok(None),
+ Err(e) => Err(e.into()),
+ }
+ }
+
+ /// Returns a map of ID to path for the given ids in the given table.
+ ///
+ /// For example, given `registry_index` IDs, it returns filenames of the
+ /// form "index.crates.io-6f17d22bba15001f".
+ fn get_id_map(
+ conn: &Connection,
+ table_name: &str,
+ ids: &[i64],
+ ) -> CargoResult<HashMap<i64, PathBuf>> {
+ let mut stmt =
+ conn.prepare_cached(&format!("SELECT name FROM {table_name} WHERE id = ?1"))?;
+ ids.iter()
+ .map(|id| {
+ let name = stmt.query_row(params![id], |row| {
+ Ok(PathBuf::from(row.get::<_, String>(0)?))
+ })?;
+ Ok((*id, name))
+ })
+ .collect()
+ }
+
+ /// Returns all index cache timestamps.
+ pub fn registry_index_all(&self) -> CargoResult<Vec<(RegistryIndex, Timestamp)>> {
+ let mut stmt = self
+ .conn
+ .prepare_cached("SELECT name, timestamp FROM registry_index")?;
+ let rows = stmt
+ .query_map([], |row| {
+ let encoded_registry_name = row.get_unwrap(0);
+ let timestamp = row.get_unwrap(1);
+ let kind = RegistryIndex {
+ encoded_registry_name,
+ };
+ Ok((kind, timestamp))
+ })?
+ .collect::<Result<Vec<_>, _>>()?;
+ Ok(rows)
+ }
+
+ /// Returns all registry crate cache timestamps.
+ pub fn registry_crate_all(&self) -> CargoResult<Vec<(RegistryCrate, Timestamp)>> {
+ let mut stmt = self.conn.prepare_cached(
+ "SELECT registry_index.name, registry_crate.name, registry_crate.size, registry_crate.timestamp
+ FROM registry_index, registry_crate
+ WHERE registry_crate.registry_id = registry_index.id",
+ )?;
+ let rows = stmt
+ .query_map([], |row| {
+ let encoded_registry_name = row.get_unwrap(0);
+ let crate_filename = row.get_unwrap(1);
+ let size = row.get_unwrap(2);
+ let timestamp = row.get_unwrap(3);
+ let kind = RegistryCrate {
+ encoded_registry_name,
+ crate_filename,
+ size,
+ };
+ Ok((kind, timestamp))
+ })?
+ .collect::<Result<Vec<_>, _>>()?;
+ Ok(rows)
+ }
+
+ /// Returns all registry source cache timestamps.
+ pub fn registry_src_all(&self) -> CargoResult<Vec<(RegistrySrc, Timestamp)>> {
+ let mut stmt = self.conn.prepare_cached(
+ "SELECT registry_index.name, registry_src.name, registry_src.size, registry_src.timestamp
+ FROM registry_index, registry_src
+ WHERE registry_src.registry_id = registry_index.id",
+ )?;
+ let rows = stmt
+ .query_map([], |row| {
+ let encoded_registry_name = row.get_unwrap(0);
+ let package_dir = row.get_unwrap(1);
+ let size = row.get_unwrap(2);
+ let timestamp = row.get_unwrap(3);
+ let kind = RegistrySrc {
+ encoded_registry_name,
+ package_dir,
+ size,
+ };
+ Ok((kind, timestamp))
+ })?
+ .collect::<Result<Vec<_>, _>>()?;
+ Ok(rows)
+ }
+
+ /// Returns all git db timestamps.
+ pub fn git_db_all(&self) -> CargoResult<Vec<(GitDb, Timestamp)>> {
+ let mut stmt = self
+ .conn
+ .prepare_cached("SELECT name, timestamp FROM git_db")?;
+ let rows = stmt
+ .query_map([], |row| {
+ let encoded_git_name = row.get_unwrap(0);
+ let timestamp = row.get_unwrap(1);
+ let kind = GitDb { encoded_git_name };
+ Ok((kind, timestamp))
+ })?
+ .collect::<Result<Vec<_>, _>>()?;
+ Ok(rows)
+ }
+
+ /// Returns all git checkout timestamps.
+ pub fn git_checkout_all(&self) -> CargoResult<Vec<(GitCheckout, Timestamp)>> {
+ let mut stmt = self.conn.prepare_cached(
+ "SELECT git_db.name, git_checkout.name, git_checkout.size, git_checkout.timestamp
+ FROM git_db, git_checkout
+ WHERE git_checkout.registry_id = git_db.id",
+ )?;
+ let rows = stmt
+ .query_map([], |row| {
+ let encoded_git_name = row.get_unwrap(0);
+ let short_name = row.get_unwrap(1);
+ let size = row.get_unwrap(2);
+ let timestamp = row.get_unwrap(3);
+ let kind = GitCheckout {
+ encoded_git_name,
+ short_name,
+ size,
+ };
+ Ok((kind, timestamp))
+ })?
+ .collect::<Result<Vec<_>, _>>()?;
+ Ok(rows)
+ }
+
+ /// Returns whether or not an auto GC should be performed, compared to the
+ /// last time it was recorded in the database.
+ pub fn should_run_auto_gc(&mut self, frequency: Duration) -> CargoResult<bool> {
+ trace!(target: "gc", "should_run_auto_gc");
+ if self.auto_gc_checked_this_session {
+ return Ok(false);
+ }
+ let last_auto_gc: Timestamp =
+ self.conn
+ .query_row("SELECT last_auto_gc FROM global_data", [], |row| row.get(0))?;
+ let should_run = last_auto_gc + frequency.as_secs() < now();
+ trace!(target: "gc",
+ "last auto gc was {}, {}",
+ last_auto_gc,
+ if should_run { "running" } else { "skipping" }
+ );
+ self.auto_gc_checked_this_session = true;
+ Ok(should_run)
+ }
+
+ /// Writes to the database to indicate that an automatic GC has just been
+ /// completed.
+ pub fn set_last_auto_gc(&self) -> CargoResult<()> {
+ self.conn
+ .execute("UPDATE global_data SET last_auto_gc = ?1", [now()])?;
+ Ok(())
+ }
+
+ /// Deletes files from the global cache based on the given options.
+ pub fn clean(&mut self, clean_ctx: &mut CleanContext<'_>, gc_opts: &GcOpts) -> CargoResult<()> {
+ self.clean_inner(clean_ctx, gc_opts)
+ .with_context(|| "failed to clean entries from the global cache")
+ }
+
+ fn clean_inner(
+ &mut self,
+ clean_ctx: &mut CleanContext<'_>,
+ gc_opts: &GcOpts,
+ ) -> CargoResult<()> {
+ let _p = crate::util::profile::start("cleaning global cache files");
+ let config = clean_ctx.config;
+ let base_git_path = config.git_path().into_path_unlocked();
+ let base = BasePaths {
+ index: config.registry_index_path().into_path_unlocked(),
+ git_db: base_git_path.join("db"),
+ git_co: base_git_path.join("checkouts"),
+ crate_dir: config.registry_cache_path().into_path_unlocked(),
+ src: config.registry_source_path().into_path_unlocked(),
+ };
+ let now = now();
+ trace!(target: "gc", "cleaning {gc_opts:?}");
+ let tx = self.conn.transaction()?;
+ let mut delete_paths = Vec::new();
+ // This can be an expensive operation, so only perform it if necessary.
+ if gc_opts.is_download_cache_opt_set() {
+ // TODO: Investigate how slow this might be.
+ Self::sync_db_with_files(
+ &tx,
+ now,
+ config,
+ &base,
+ gc_opts.is_download_cache_size_set(),
+ &mut delete_paths,
+ )
+ .with_context(|| "failed to sync tracking database")?
+ }
+ if let Some(max_age) = gc_opts.max_index_age {
+ let max_age = now - max_age.as_secs();
+ Self::get_registry_index_to_clean(&tx, max_age, &base, &mut delete_paths)?;
+ }
+ if let Some(max_age) = gc_opts.max_src_age {
+ let max_age = now - max_age.as_secs();
+ Self::get_registry_items_to_clean_age(
+ &tx,
+ max_age,
+ REGISTRY_SRC_TABLE,
+ &base.src,
+ &mut delete_paths,
+ )?;
+ }
+ if let Some(max_age) = gc_opts.max_crate_age {
+ let max_age = now - max_age.as_secs();
+ Self::get_registry_items_to_clean_age(
+ &tx,
+ max_age,
+ REGISTRY_CRATE_TABLE,
+ &base.crate_dir,
+ &mut delete_paths,
+ )?;
+ }
+ if let Some(max_age) = gc_opts.max_git_db_age {
+ let max_age = now - max_age.as_secs();
+ Self::get_git_db_items_to_clean(&tx, max_age, &base, &mut delete_paths)?;
+ }
+ if let Some(max_age) = gc_opts.max_git_co_age {
+ let max_age = now - max_age.as_secs();
+ Self::get_git_co_items_to_clean(&tx, max_age, &base.git_co, &mut delete_paths)?;
+ }
+ // Size collection must happen after date collection so that dates
+ // have precedence, since size constraints are a more blunt
+ // instrument.
+ //
+ // These are also complicated by the `--max-download-size` option
+ // overlapping with `--max-crate-size` and `--max-src-size`, which
+ // requires some coordination between those options which isn't
+ // necessary with the age-based options. An item's age is either older
+ // or it isn't, but contrast that with size which is based on the sum
+ // of all tracked items. Also, `--max-download-size` is summed against
+ // both the crate and src tracking, which requires combining them to
+ // compute the size, and then separating them to calculate the correct
+ // paths.
+ if let Some(max_size) = gc_opts.max_crate_size {
+ Self::get_registry_items_to_clean_size(
+ &tx,
+ max_size,
+ REGISTRY_CRATE_TABLE,
+ &base.crate_dir,
+ &mut delete_paths,
+ )?;
+ }
+ if let Some(max_size) = gc_opts.max_src_size {
+ Self::get_registry_items_to_clean_size(
+ &tx,
+ max_size,
+ REGISTRY_SRC_TABLE,
+ &base.src,
+ &mut delete_paths,
+ )?;
+ }
+ if let Some(max_size) = gc_opts.max_git_size {
+ Self::get_git_items_to_clean_size(&tx, max_size, &base, &mut delete_paths)?;
+ }
+ if let Some(max_size) = gc_opts.max_download_size {
+ Self::get_registry_items_to_clean_size_both(&tx, max_size, &base, &mut delete_paths)?;
+ }
+
+ clean_ctx.remove_paths(&delete_paths)?;
+
+ if clean_ctx.dry_run {
+ tx.rollback()?;
+ } else {
+ tx.commit()?;
+ }
+ Ok(())
+ }
+
+ /// Returns a list of directory entries in the given path.
+ fn names_from(path: &Path) -> CargoResult<Vec<String>> {
+ let entries = match path.read_dir() {
+ Ok(e) => e,
+ Err(e) => {
+ if e.kind() == std::io::ErrorKind::NotFound {
+ return Ok(Vec::new());
+ } else {
+ return Err(
+ anyhow::Error::new(e).context(format!("failed to read path `{path:?}`"))
+ );
+ }
+ }
+ };
+ let names = entries
+ .filter_map(|entry| entry.ok()?.file_name().into_string().ok())
+ .collect();
+ Ok(names)
+ }
+
+ /// Synchronizes the database to match the files on disk.
+ ///
+ /// This performs the following cleanups:
+ ///
+ /// 1. Remove entries from the database that are missing on disk.
+ /// 2. Adds missing entries to the database that are on disk (such as when
+ /// files are added by older versions of cargo).
+ /// 3. Fills in the `size` column where it is NULL (such as when something
+ /// is added to disk by an older version of cargo, and one of the mark
+ /// functions marked it without knowing the size).
+ ///
+ /// Size computations are only done if `sync_size` is set since it can
+ /// be a very expensive operation. This should only be set if the user
+ /// requested to clean based on the cache size.
+ /// 4. Checks for orphaned files. For example, if there are `.crate` files
+ /// associated with an index that does not exist.
+ ///
+ /// These orphaned files will be added to `delete_paths` so that the
+ /// caller can delete them.
+ fn sync_db_with_files(
+ conn: &Connection,
+ now: Timestamp,
+ config: &Config,
+ base: &BasePaths,
+ sync_size: bool,
+ delete_paths: &mut Vec<PathBuf>,
+ ) -> CargoResult<()> {
+ let _p = crate::util::profile::start("global cache db sync");
+ debug!(target: "gc", "starting db sync");
+ // For registry_index and git_db, add anything that is missing in the db.
+ Self::update_parent_for_missing_from_db(conn, now, REGISTRY_INDEX_TABLE, &base.index)?;
+ Self::update_parent_for_missing_from_db(conn, now, GIT_DB_TABLE, &base.git_db)?;
+
+ // For registry_crate, registry_src, and git_checkout, remove anything
+ // from the db that isn't on disk.
+ Self::update_db_for_removed(
+ conn,
+ REGISTRY_INDEX_TABLE,
+ "registry_id",
+ REGISTRY_CRATE_TABLE,
+ &base.crate_dir,
+ )?;
+ Self::update_db_for_removed(
+ conn,
+ REGISTRY_INDEX_TABLE,
+ "registry_id",
+ REGISTRY_SRC_TABLE,
+ &base.src,
+ )?;
+ Self::update_db_for_removed(conn, GIT_DB_TABLE, "git_id", GIT_CO_TABLE, &base.git_co)?;
+
+ // For registry_index and git_db, remove anything from the db that
+ // isn't on disk.
+ //
+ // This also collects paths for any child files that don't have their
+ // respective parent on disk.
+ Self::update_db_parent_for_removed_from_disk(
+ conn,
+ REGISTRY_INDEX_TABLE,
+ &base.index,
+ &[&base.crate_dir, &base.src],
+ delete_paths,
+ )?;
+ Self::update_db_parent_for_removed_from_disk(
+ conn,
+ GIT_DB_TABLE,
+ &base.git_db,
+ &[&base.git_co],
+ delete_paths,
+ )?;
+
+ // For registry_crate, registry_src, and git_checkout, add anything
+ // that is missing in the db.
+ Self::populate_untracked_crate(conn, now, &base.crate_dir)?;
+ Self::populate_untracked(
+ conn,
+ now,
+ config,
+ REGISTRY_INDEX_TABLE,
+ "registry_id",
+ REGISTRY_SRC_TABLE,
+ &base.src,
+ sync_size,
+ )?;
+ Self::populate_untracked(
+ conn,
+ now,
+ config,
+ GIT_DB_TABLE,
+ "git_id",
+ GIT_CO_TABLE,
+ &base.git_co,
+ sync_size,
+ )?;
+
+ // Update any NULL sizes if needed.
+ if sync_size {
+ Self::update_null_sizes(
+ conn,
+ config,
+ REGISTRY_INDEX_TABLE,
+ "registry_id",
+ REGISTRY_SRC_TABLE,
+ &base.src,
+ )?;
+ Self::update_null_sizes(
+ conn,
+ config,
+ GIT_DB_TABLE,
+ "git_id",
+ GIT_CO_TABLE,
+ &base.git_co,
+ )?;
+ }
+ Ok(())
+ }
+
+ /// For parent tables, add any entries that are on disk but aren't tracked in the db.
+ fn update_parent_for_missing_from_db(
+ conn: &Connection,
+ now: Timestamp,
+ parent_table_name: &str,
+ base_path: &Path,
+ ) -> CargoResult<()> {
+ let _p = crate::util::profile::start(format!(
+ "update parent db for missing from db {parent_table_name}"
+ ));
+ trace!(target: "gc", "checking for untracked parent to add to {parent_table_name}");
+ let names = Self::names_from(base_path)?;
+
+ let mut stmt = conn.prepare_cached(&format!(
+ "INSERT INTO {parent_table_name} (name, timestamp)
+ VALUES (?1, ?2)
+ ON CONFLICT DO NOTHING",
+ ))?;
+ for name in names {
+ stmt.execute(params![name, now])?;
+ }
+ Ok(())
+ }
+
+ /// Removes database entries for any files that are not on disk for the child tables.
+ ///
+ /// This could happen for example if the user manually deleted the file or
+ /// any such scenario where the filesystem and db are out of sync.
+ fn update_db_for_removed(
+ conn: &Connection,
+ parent_table_name: &str,
+ id_column_name: &str,
+ table_name: &str,
+ base_path: &Path,
+ ) -> CargoResult<()> {
+ let _p = crate::util::profile::start(format!("update db for removed {table_name}"));
+ trace!(target: "gc", "checking for db entries to remove from {table_name}");
+ let mut select_stmt = conn.prepare_cached(&format!(
+ "SELECT {table_name}.rowid, {parent_table_name}.name, {table_name}.name
+ FROM {parent_table_name}, {table_name}
+ WHERE {table_name}.{id_column_name} = {parent_table_name}.id",
+ ))?;
+ let mut delete_stmt =
+ conn.prepare_cached(&format!("DELETE FROM {table_name} WHERE rowid = ?1"))?;
+ let mut rows = select_stmt.query([])?;
+ while let Some(row) = rows.next()? {
+ let rowid: i64 = row.get_unwrap(0);
+ let id_name: String = row.get_unwrap(1);
+ let name: String = row.get_unwrap(2);
+ if !base_path.join(id_name).join(name).exists() {
+ delete_stmt.execute([rowid])?;
+ }
+ }
+ Ok(())
+ }
+
+ /// Removes database entries for any files that are not on disk for the parent tables.
+ fn update_db_parent_for_removed_from_disk(
+ conn: &Connection,
+ parent_table_name: &str,
+ base_path: &Path,
+ child_base_paths: &[&Path],
+ delete_paths: &mut Vec<PathBuf>,
+ ) -> CargoResult<()> {
+ let _p = crate::util::profile::start(format!(
+ "update db parent for removed from disk {parent_table_name}"
+ ));
+ trace!(target: "gc", "checking for db entries to remove from {parent_table_name}");
+ let mut select_stmt =
+ conn.prepare_cached(&format!("SELECT rowid, name FROM {parent_table_name}"))?;
+ let mut delete_stmt =
+ conn.prepare_cached(&format!("DELETE FROM {parent_table_name} WHERE rowid = ?1"))?;
+ let mut rows = select_stmt.query([])?;
+ while let Some(row) = rows.next()? {
+ let rowid: i64 = row.get_unwrap(0);
+ let id_name: String = row.get_unwrap(1);
+ if !base_path.join(&id_name).exists() {
+ delete_stmt.execute([rowid])?;
+ // Make sure any child data is also cleaned up.
+ for child_base in child_base_paths {
+ let child_path = child_base.join(&id_name);
+ if child_path.exists() {
+ debug!(target: "gc", "removing orphaned path {child_path:?}");
+ delete_paths.push(child_path);
+ }
+ }
+ }
+ }
+ Ok(())
+ }
+
+ /// Updates the database to add any `.crate` files that are currently
+ /// not tracked (such as when they are downloaded by an older version of
+ /// cargo).
+ fn populate_untracked_crate(
+ conn: &Connection,
+ now: Timestamp,
+ base_path: &Path,
+ ) -> CargoResult<()> {
+ let _p = crate::util::profile::start("populate untracked crate");
+ trace!(target: "gc", "populating untracked crate files");
+ let mut insert_stmt = conn.prepare_cached(
+ "INSERT INTO registry_crate (registry_id, name, size, timestamp)
+ VALUES (?1, ?2, ?3, ?4)
+ ON CONFLICT DO NOTHING",
+ )?;
+ let index_names = Self::names_from(&base_path)?;
+ for index_name in index_names {
+ let Some(id) = Self::id_from_name(conn, REGISTRY_INDEX_TABLE, &index_name)? else {
+ // The id is missing from the database. This should be resolved
+ // via update_db_parent_for_removed_from_disk.
+ continue;
+ };
+ let index_path = base_path.join(index_name);
+ for crate_name in Self::names_from(&index_path)? {
+ if crate_name.ends_with(".crate") {
+ // Missing files should have already been taken care of by
+ // update_db_for_removed.
+ let size = paths::metadata(index_path.join(&crate_name))?.len();
+ insert_stmt.execute(params![id, crate_name, size, now])?;
+ }
+ }
+ }
+ Ok(())
+ }
+
+ /// Updates the database to add any files that are currently not tracked
+ /// (such as when they are downloaded by an older version of cargo).
+ fn populate_untracked(
+ conn: &Connection,
+ now: Timestamp,
+ config: &Config,
+ id_table_name: &str,
+ id_column_name: &str,
+ table_name: &str,
+ base_path: &Path,
+ populate_size: bool,
+ ) -> CargoResult<()> {
+ let _p = crate::util::profile::start(format!("populate untracked {table_name}"));
+ trace!(target: "gc", "populating untracked files for {table_name}");
+ // Gather names (and make sure they are in the database).
+ let id_names = Self::names_from(&base_path)?;
+
+ // This SELECT is used to determine if the directory is already
+ // tracked. We don't want to do the expensive size computation unless
+ // necessary.
+ let mut select_stmt = conn.prepare_cached(&format!(
+ "SELECT 1 FROM {table_name}
+ WHERE {id_column_name} = ?1 AND name = ?2",
+ ))?;
+ let mut insert_stmt = conn.prepare_cached(&format!(
+ "INSERT INTO {table_name} ({id_column_name}, name, size, timestamp)
+ VALUES (?1, ?2, ?3, ?4)
+ ON CONFLICT DO NOTHING",
+ ))?;
+ let mut progress = Progress::with_style("Scanning", ProgressStyle::Ratio, config);
+ // Compute the size of any directory not in the database.
+ for id_name in id_names {
+ let Some(id) = Self::id_from_name(conn, id_table_name, &id_name)? else {
+ // The id is missing from the database. This should be resolved
+ // via update_db_parent_for_removed_from_disk.
+ continue;
+ };
+ let index_path = base_path.join(id_name);
+ let names = Self::names_from(&index_path)?;
+ let max = names.len();
+ for (i, name) in names.iter().enumerate() {
+ if select_stmt.exists(params![id, name])? {
+ continue;
+ }
+ let dir_path = index_path.join(name);
+ if !dir_path.is_dir() {
+ continue;
+ }
+ progress.tick(i, max, "")?;
+ let size = if populate_size {
+ Some(du(&dir_path, table_name)?)
+ } else {
+ None
+ };
+ insert_stmt.execute(params![id, name, size, now])?;
+ }
+ }
+ Ok(())
+ }
+
+ /// Fills in the `size` column where it is NULL.
+ ///
+ /// This can happen when something is added to disk by an older version of
+ /// cargo, and one of the mark functions marked it without knowing the
+ /// size.
+ ///
+ /// `update_db_for_removed` should be called before this is called.
+ fn update_null_sizes(
+ conn: &Connection,
+ config: &Config,
+ parent_table_name: &str,
+ id_column_name: &str,
+ table_name: &str,
+ base_path: &Path,
+ ) -> CargoResult<()> {
+ let _p = crate::util::profile::start(format!("update NULL sizes {table_name}"));
+ trace!(target: "gc", "updating NULL size information in {table_name}");
+ let mut null_stmt = conn.prepare_cached(&format!(
+ "SELECT {table_name}.rowid, {table_name}.name, {parent_table_name}.name
+ FROM {table_name}, {parent_table_name}
+ WHERE {table_name}.size IS NULL AND {table_name}.{id_column_name} = {parent_table_name}.id",
+ ))?;
+ let mut update_stmt = conn.prepare_cached(&format!(
+ "UPDATE {table_name} SET size = ?1 WHERE rowid = ?2"
+ ))?;
+ let mut progress = Progress::with_style("Scanning", ProgressStyle::Ratio, config);
+ let rows: Vec<_> = null_stmt
+ .query_map([], |row| {
+ Ok((row.get_unwrap(0), row.get_unwrap(1), row.get_unwrap(2)))
+ })?
+ .collect();
+ let max = rows.len();
+ for (i, row) in rows.into_iter().enumerate() {
+ let (rowid, name, id_name): (i64, String, String) = row?;
+ let path = base_path.join(id_name).join(name);
+ progress.tick(i, max, "")?;
+ // Missing files should have already been taken care of by
+ // update_db_for_removed.
+ let size = du(&path, table_name)?;
+ update_stmt.execute(params![size, rowid])?;
+ }
+ Ok(())
+ }
+
+ /// Adds paths to delete from either registry_crate or registry_src whose
+ /// last use is older than the given timestamp.
+ fn get_registry_items_to_clean_age(
+ conn: &Connection,
+ max_age: Timestamp,
+ table_name: &str,
+ base_path: &Path,
+ delete_paths: &mut Vec<PathBuf>,
+ ) -> CargoResult<()> {
+ debug!(target: "gc", "cleaning {table_name} since {max_age:?}");
+ let mut stmt = conn.prepare_cached(&format!(
+ "DELETE FROM {table_name} WHERE timestamp < ?1
+ RETURNING registry_id, name"
+ ))?;
+ let rows = stmt
+ .query_map(params![max_age], |row| {
+ let registry_id = row.get_unwrap(0);
+ let name: String = row.get_unwrap(1);
+ Ok((registry_id, name))
+ })?
+ .collect::<Result<Vec<_>, _>>()?;
+ let ids: Vec<_> = rows.iter().map(|r| r.0).collect();
+ let id_map = Self::get_id_map(conn, REGISTRY_INDEX_TABLE, &ids)?;
+ for (id, name) in rows {
+ let encoded_registry_name = &id_map[&id];
+ delete_paths.push(base_path.join(encoded_registry_name).join(name));
+ }
+ Ok(())
+ }
+
+ /// Adds paths to delete from either `registry_crate` or `registry_src` in
+ /// order to keep the total size under the given max size.
+ fn get_registry_items_to_clean_size(
+ conn: &Connection,
+ max_size: u64,
+ table_name: &str,
+ base_path: &Path,
+ delete_paths: &mut Vec<PathBuf>,
+ ) -> CargoResult<()> {
+ debug!(target: "gc", "cleaning {table_name} till under {max_size:?}");
+ let total_size: u64 = conn.query_row(
+ &format!("SELECT coalesce(SUM(size), 0) FROM {table_name}"),
+ [],
+ |row| row.get(0),
+ )?;
+ if total_size <= max_size {
+ return Ok(());
+ }
+ // This SQL statement selects all of the rows ordered by timestamp,
+ // and then uses a window function to keep a running total of the
+ // size. It selects all rows until the running total exceeds the
+ // threshold of the total number of bytes that we want to delete.
+ //
+ // The window function essentially computes an aggregate over all
+ // previous rows as it goes along. As long as the running size is
+ // below the total amount that we need to delete, it keeps picking
+ // more rows.
+ //
+ // The ORDER BY includes `name` mainly for test purposes so that
+ // entries with the same timestamp have deterministic behavior.
+ //
+ // The coalesce helps convert NULL to 0.
+ let mut stmt = conn.prepare(&format!(
+ "DELETE FROM {table_name} WHERE rowid IN \
+ (SELECT x.rowid FROM \
+ (SELECT rowid, size, SUM(size) OVER \
+ (ORDER BY timestamp, name ROWS UNBOUNDED PRECEDING) AS running_amount \
+ FROM {table_name}) x \
+ WHERE coalesce(x.running_amount, 0) - x.size < ?1) \
+ RETURNING registry_id, name;"
+ ))?;
+ let rows = stmt
+ .query_map(params![total_size - max_size], |row| {
+ let id = row.get_unwrap(0);
+ let name: String = row.get_unwrap(1);
+ Ok((id, name))
+ })?
+ .collect::<Result<Vec<_>, _>>()?;
+ // Convert registry_id to the encoded registry name, and join those.
+ let ids: Vec<_> = rows.iter().map(|r| r.0).collect();
+ let id_map = Self::get_id_map(conn, REGISTRY_INDEX_TABLE, &ids)?;
+ for (id, name) in rows {
+ let encoded_name = &id_map[&id];
+ delete_paths.push(base_path.join(encoded_name).join(name));
+ }
+ Ok(())
+ }
+
+ /// Adds paths to delete from both `registry_crate` and `registry_src` in
+ /// order to keep the total size under the given max size.
+ fn get_registry_items_to_clean_size_both(
+ conn: &Connection,
+ max_size: u64,
+ base: &BasePaths,
+ delete_paths: &mut Vec<PathBuf>,
+ ) -> CargoResult<()> {
+ debug!(target: "gc", "cleaning download till under {max_size:?}");
+
+ // This SQL statement selects from both registry_src and
+ // registry_crate so that sorting of timestamps incorporates both of
+ // them at the same time. It uses a const value of 1 or 2 as the first
+ // column so that the code below can determine which table the value
+ // came from.
+ let mut stmt = conn.prepare_cached(
+ "SELECT 1, registry_src.rowid, registry_src.name AS name, registry_index.name,
+ registry_src.size, registry_src.timestamp AS timestamp
+ FROM registry_src, registry_index
+ WHERE registry_src.registry_id = registry_index.id AND registry_src.size NOT NULL
+
+ UNION
+
+ SELECT 2, registry_crate.rowid, registry_crate.name AS name, registry_index.name,
+ registry_crate.size, registry_crate.timestamp AS timestamp
+ FROM registry_crate, registry_index
+ WHERE registry_crate.registry_id = registry_index.id
+
+ ORDER BY timestamp, name",
+ )?;
+ let mut delete_src_stmt =
+ conn.prepare_cached("DELETE FROM registry_src WHERE rowid = ?1")?;
+ let mut delete_crate_stmt =
+ conn.prepare_cached("DELETE FROM registry_crate WHERE rowid = ?1")?;
+ let rows = stmt
+ .query_map([], |row| {
+ Ok((
+ row.get_unwrap(0),
+ row.get_unwrap(1),
+ row.get_unwrap(2),
+ row.get_unwrap(3),
+ row.get_unwrap(4),
+ ))
+ })?
+ .collect::<Result<Vec<(i64, i64, String, String, u64)>, _>>()?;
+ let mut total_size: u64 = rows.iter().map(|r| r.4).sum();
+ debug!(target: "gc", "total download cache size appears to be {total_size}");
+ for (table, rowid, name, index_name, size) in rows {
+ if total_size <= max_size {
+ break;
+ }
+ if table == 1 {
+ delete_paths.push(base.src.join(index_name).join(name));
+ delete_src_stmt.execute([rowid])?;
+ } else {
+ delete_paths.push(base.crate_dir.join(index_name).join(name));
+ delete_crate_stmt.execute([rowid])?;
+ }
+ // TODO: If delete crate, ensure src is also deleted.
+ total_size -= size;
+ }
+ Ok(())
+ }
+
+ /// Adds paths to delete from the git cache, keeping the total size under
+ /// the give value.
+ ///
+ /// Paths are relative to the `git` directory in the cache directory.
+ fn get_git_items_to_clean_size(
+ conn: &Connection,
+ max_size: u64,
+ base: &BasePaths,
+ delete_paths: &mut Vec<PathBuf>,
+ ) -> CargoResult<()> {
+ debug!(target: "gc", "cleaning git till under {max_size:?}");
+
+ // Collect all the sizes from git_db and git_checkouts, and then sort them by timestamp.
+ let mut stmt = conn.prepare_cached("SELECT rowid, name, timestamp FROM git_db")?;
+ let mut git_info = stmt
+ .query_map([], |row| {
+ let rowid: i64 = row.get_unwrap(0);
+ let name: String = row.get_unwrap(1);
+ let timestamp: Timestamp = row.get_unwrap(2);
+ // Size is added below so that the error doesn't need to be
+ // converted to a rusqlite error.
+ Ok((timestamp, rowid, None, name, 0))
+ })?
+ .collect::<Result<Vec<_>, _>>()?;
+ for info in &mut git_info {
+ let size = cargo_util::du(&base.git_db.join(&info.3), &[])?;
+ info.4 = size;
+ }
+
+ let mut stmt = conn.prepare_cached(
+ "SELECT git_checkout.rowid, git_db.name, git_checkout.name,
+ git_checkout.size, git_checkout.timestamp
+ FROM git_checkout, git_db
+ WHERE git_checkout.git_id = git_db.id AND git_checkout.size NOT NULL",
+ )?;
+ let git_co_rows = stmt
+ .query_map([], |row| {
+ let rowid = row.get_unwrap(0);
+ let db_name: String = row.get_unwrap(1);
+ let name = row.get_unwrap(2);
+ let size = row.get_unwrap(3);
+ let timestamp = row.get_unwrap(4);
+ Ok((timestamp, rowid, Some(db_name), name, size))
+ })?
+ .collect::<Result<Vec<_>, _>>()?;
+ git_info.extend(git_co_rows);
+
+ // Sort by timestamp, and name. The name is included mostly for test
+ // purposes so that entries with the same timestamp have deterministic
+ // behavior.
+ git_info.sort_by(|a, b| (b.0, &b.3).cmp(&(a.0, &a.3)));
+
+ // Collect paths to delete.
+ let mut delete_db_stmt = conn.prepare_cached("DELETE FROM git_db WHERE rowid = ?1")?;
+ let mut delete_co_stmt =
+ conn.prepare_cached("DELETE FROM git_checkout WHERE rowid = ?1")?;
+ let mut total_size: u64 = git_info.iter().map(|r| r.4).sum();
+ debug!(target: "gc", "total git cache size appears to be {total_size}");
+ while let Some((_timestamp, rowid, db_name, name, size)) = git_info.pop() {
+ if total_size <= max_size {
+ break;
+ }
+ if let Some(db_name) = db_name {
+ delete_paths.push(base.git_co.join(db_name).join(name));
+ delete_co_stmt.execute([rowid])?;
+ total_size -= size;
+ } else {
+ total_size -= size;
+ delete_paths.push(base.git_db.join(&name));
+ delete_db_stmt.execute([rowid])?;
+ // If the db is deleted, then all the checkouts must be deleted.
+ let mut i = 0;
+ while i < git_info.len() {
+ if git_info[i].2.as_deref() == Some(name.as_ref()) {
+ let (_, rowid, db_name, name, size) = git_info.remove(i);
+ delete_paths.push(base.git_co.join(db_name.unwrap()).join(name));
+ delete_co_stmt.execute([rowid])?;
+ total_size -= size;
+ } else {
+ i += 1;
+ }
+ }
+ }
+ }
+ Ok(())
+ }
+
+ /// Adds paths to delete from `registry_index` whose last use is older
+ /// than the given timestamp.
+ fn get_registry_index_to_clean(
+ conn: &Connection,
+ max_age: Timestamp,
+ base: &BasePaths,
+ delete_paths: &mut Vec<PathBuf>,
+ ) -> CargoResult<()> {
+ debug!(target: "gc", "cleaning index since {max_age:?}");
+ let mut stmt = conn.prepare_cached(
+ "DELETE FROM registry_index WHERE timestamp < ?1
+ RETURNING name",
+ )?;
+ let mut rows = stmt.query([max_age])?;
+ while let Some(row) = rows.next()? {
+ let name: String = row.get_unwrap(0);
+ delete_paths.push(base.index.join(&name));
+ // Also delete .crate and src directories, since by definition
+ // they cannot be used without their index.
+ delete_paths.push(base.src.join(&name));
+ delete_paths.push(base.crate_dir.join(&name));
+ }
+ Ok(())
+ }
+
+ /// Adds paths to delete from `git_checkout` whose last use is
+ /// older than the given timestamp.
+ fn get_git_co_items_to_clean(
+ conn: &Connection,
+ max_age: Timestamp,
+ base_path: &Path,
+ delete_paths: &mut Vec<PathBuf>,
+ ) -> CargoResult<()> {
+ debug!(target: "gc", "cleaning git co since {max_age:?}");
+ let mut stmt = conn.prepare_cached(
+ "DELETE FROM git_checkout WHERE timestamp < ?1
+ RETURNING git_id, name",
+ )?;
+ let rows = stmt
+ .query_map(params![max_age], |row| {
+ let git_id = row.get_unwrap(0);
+ let name: String = row.get_unwrap(1);
+ Ok((git_id, name))
+ })?
+ .collect::<Result<Vec<_>, _>>()?;
+ let ids: Vec<_> = rows.iter().map(|r| r.0).collect();
+ let id_map = Self::get_id_map(conn, GIT_DB_TABLE, &ids)?;
+ for (id, name) in rows {
+ let encoded_git_name = &id_map[&id];
+ delete_paths.push(base_path.join(encoded_git_name).join(name));
+ }
+ Ok(())
+ }
+
+ /// Adds paths to delete from `git_db` in order to keep the total size
+ /// under the given max size.
+ fn get_git_db_items_to_clean(
+ conn: &Connection,
+ max_age: Timestamp,
+ base: &BasePaths,
+ delete_paths: &mut Vec<PathBuf>,
+ ) -> CargoResult<()> {
+ debug!(target: "gc", "cleaning git db since {max_age:?}");
+ let mut stmt = conn.prepare_cached(
+ "DELETE FROM git_db WHERE timestamp < ?1
+ RETURNING name",
+ )?;
+ let mut rows = stmt.query([max_age])?;
+ while let Some(row) = rows.next()? {
+ let name: String = row.get_unwrap(0);
+ delete_paths.push(base.git_db.join(&name));
+ // Also delete checkout directories, since by definition they
+ // cannot be used without their db.
+ delete_paths.push(base.git_co.join(&name));
+ }
+ Ok(())
+ }
+}
+
+/// Helper to generate the upsert for the parent tables.
+///
+/// This handles checking if the row already exists, and only updates the
+/// timestamp it if it hasn't been updated recently. This also handles keeping
+/// a cached map of the `id` value.
+///
+/// Unfortunately it is a bit tricky to share this code without a macro.
+macro_rules! insert_or_update_parent {
+ ($self:expr, $conn:expr, $table_name:expr, $timestamps_field:ident, $keys_field:ident, $encoded_name:ident) => {
+ let mut select_stmt = $conn.prepare_cached(concat!(
+ "SELECT id, timestamp FROM ",
+ $table_name,
+ " WHERE name = ?1"
+ ))?;
+ let mut insert_stmt = $conn.prepare_cached(concat!(
+ "INSERT INTO ",
+ $table_name,
+ " (name, timestamp)
+ VALUES (?1, ?2)
+ ON CONFLICT DO UPDATE SET timestamp=excluded.timestamp
+ RETURNING id",
+ ))?;
+ let mut update_stmt = $conn.prepare_cached(concat!(
+ "UPDATE ",
+ $table_name,
+ " SET timestamp = ?1 WHERE id = ?2"
+ ))?;
+ for (parent, new_timestamp) in std::mem::take(&mut $self.$timestamps_field) {
+ trace!(target: "gc",
+ concat!("insert ", $table_name, " {:?} {}"),
+ parent,
+ new_timestamp
+ );
+ let mut rows = select_stmt.query([parent.$encoded_name])?;
+ let id = if let Some(row) = rows.next()? {
+ let id: ParentId = row.get_unwrap(0);
+ let timestamp: Timestamp = row.get_unwrap(1);
+ if timestamp < new_timestamp - UPDATE_RESOLUTION {
+ update_stmt.execute(params![new_timestamp, id])?;
+ }
+ id
+ } else {
+ insert_stmt.query_row(params![parent.$encoded_name, new_timestamp], |row| {
+ row.get(0)
+ })?
+ };
+ match $self.$keys_field.entry(parent.$encoded_name) {
+ hash_map::Entry::Occupied(o) => {
+ assert_eq!(*o.get(), id);
+ }
+ hash_map::Entry::Vacant(v) => {
+ v.insert(id);
+ }
+ }
+ }
+ return Ok(());
+ };
+}
+
+/// This is a cache of modifications that will be saved to disk all at once
+/// via the [`DeferredGlobalLastUse::save`] method.
+///
+/// This is here to improve performance.
+#[derive(Debug)]
+pub struct DeferredGlobalLastUse {
+ /// Cache of registry keys, used for faster fetching.
+ ///
+ /// The key is the registry name (which is its directory name) and the
+ /// value is the `id` in the `registry_index` table.
+ registry_keys: HashMap<InternedString, ParentId>,
+ /// Cache of git keys, used for faster fetching.
+ ///
+ /// The key is the git db name (which is its directory name) and the value
+ /// is the `id` in the `git_db` table.
+ git_keys: HashMap<InternedString, ParentId>,
+
+ /// New registry index entries to insert.
+ registry_index_timestamps: HashMap<RegistryIndex, Timestamp>,
+ /// New registry `.crate` entries to insert.
+ registry_crate_timestamps: HashMap<RegistryCrate, Timestamp>,
+ /// New registry src directory entries to insert.
+ registry_src_timestamps: HashMap<RegistrySrc, Timestamp>,
+ /// New git db entries to insert.
+ git_db_timestamps: HashMap<GitDb, Timestamp>,
+ /// New git checkout entries to insert.
+ git_checkout_timestamps: HashMap<GitCheckout, Timestamp>,
+ /// This is used so that a warning about failing to update the database is
+ /// only displayed once.
+ save_err_has_warned: bool,
+ /// The current time, used to improve performance to avoid accessing the
+ /// clock hundreds of times.
+ now: Timestamp,
+}
+
+impl DeferredGlobalLastUse {
+ pub fn new() -> DeferredGlobalLastUse {
+ DeferredGlobalLastUse {
+ registry_keys: HashMap::new(),
+ git_keys: HashMap::new(),
+ registry_index_timestamps: HashMap::new(),
+ registry_crate_timestamps: HashMap::new(),
+ registry_src_timestamps: HashMap::new(),
+ git_db_timestamps: HashMap::new(),
+ git_checkout_timestamps: HashMap::new(),
+ save_err_has_warned: false,
+ now: now(),
+ }
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.registry_index_timestamps.is_empty()
+ && self.registry_crate_timestamps.is_empty()
+ && self.registry_src_timestamps.is_empty()
+ && self.git_db_timestamps.is_empty()
+ && self.git_checkout_timestamps.is_empty()
+ }
+
+ fn clear(&mut self) {
+ self.registry_index_timestamps.clear();
+ self.registry_crate_timestamps.clear();
+ self.registry_src_timestamps.clear();
+ self.git_db_timestamps.clear();
+ self.git_checkout_timestamps.clear();
+ }
+
+ /// Indicates the given [`RegistryIndex`] has been used right now.
+ pub fn mark_registry_index_used(&mut self, registry_index: RegistryIndex) {
+ self.mark_registry_index_used_stamp(registry_index, None);
+ }
+
+ /// Indicates the given [`RegistryCrate`] has been used right now.
+ ///
+ /// Also implicitly marks the index used, too.
+ pub fn mark_registry_crate_used(&mut self, registry_crate: RegistryCrate) {
+ self.mark_registry_crate_used_stamp(registry_crate, None);
+ }
+
+ /// Indicates the given [`RegistrySrc`] has been used right now.
+ ///
+ /// Also implicitly marks the index used, too.
+ pub fn mark_registry_src_used(&mut self, registry_src: RegistrySrc) {
+ self.mark_registry_src_used_stamp(registry_src, None);
+ }
+
+ /// Indicates the given [`GitCheckout`] has been used right now.
+ ///
+ /// Also implicitly marks the git db used, too.
+ pub fn mark_git_checkout_used(&mut self, git_checkout: GitCheckout) {
+ self.mark_git_checkout_used_stamp(git_checkout, None);
+ }
+
+ /// Indicates the given [`RegistryIndex`] has been used with the given
+ /// time (or "now" if `None`).
+ pub fn mark_registry_index_used_stamp(
+ &mut self,
+ registry_index: RegistryIndex,
+ timestamp: Option<&SystemTime>,
+ ) {
+ let timestamp = timestamp.map_or(self.now, to_timestamp);
+ self.registry_index_timestamps
+ .insert(registry_index, timestamp);
+ }
+
+ /// Indicates the given [`RegistryCrate`] has been used with the given
+ /// time (or "now" if `None`).
+ ///
+ /// Also implicitly marks the index used, too.
+ pub fn mark_registry_crate_used_stamp(
+ &mut self,
+ registry_crate: RegistryCrate,
+ timestamp: Option<&SystemTime>,
+ ) {
+ let timestamp = timestamp.map_or(self.now, to_timestamp);
+ let index = RegistryIndex {
+ encoded_registry_name: registry_crate.encoded_registry_name,
+ };
+ self.registry_index_timestamps.insert(index, timestamp);
+ self.registry_crate_timestamps
+ .insert(registry_crate, timestamp);
+ }
+
+ /// Indicates the given [`RegistrySrc`] has been used with the given
+ /// time (or "now" if `None`).
+ ///
+ /// Also implicitly marks the index used, too.
+ pub fn mark_registry_src_used_stamp(
+ &mut self,
+ registry_src: RegistrySrc,
+ timestamp: Option<&SystemTime>,
+ ) {
+ let timestamp = timestamp.map_or(self.now, to_timestamp);
+ let index = RegistryIndex {
+ encoded_registry_name: registry_src.encoded_registry_name,
+ };
+ self.registry_index_timestamps.insert(index, timestamp);
+ self.registry_src_timestamps.insert(registry_src, timestamp);
+ }
+
+ /// Indicates the given [`GitCheckout`] has been used with the given
+ /// time (or "now" if `None`).
+ ///
+ /// Also implicitly marks the git db used, too.
+ pub fn mark_git_checkout_used_stamp(
+ &mut self,
+ git_checkout: GitCheckout,
+ timestamp: Option<&SystemTime>,
+ ) {
+ let timestamp = timestamp.map_or(self.now, to_timestamp);
+ let db = GitDb {
+ encoded_git_name: git_checkout.encoded_git_name,
+ };
+ self.git_db_timestamps.insert(db, timestamp);
+ self.git_checkout_timestamps.insert(git_checkout, timestamp);
+ }
+
+ /// Saves all of the deferred information to the database.
+ ///
+ /// This will also clear the state of `self`.
+ pub fn save(&mut self, tracker: &mut GlobalCacheTracker) -> CargoResult<()> {
+ let _p = crate::util::profile::start("saving last-use data");
+ trace!(target: "gc", "saving last-use data");
+ if self.is_empty() {
+ return Ok(());
+ }
+ let tx = tracker.conn.transaction()?;
+ // These must run before the ones that refer to their IDs.
+ self.insert_registry_index_from_cache(&tx)?;
+ self.insert_git_db_from_cache(&tx)?;
+ self.insert_registry_crate_from_cache(&tx)?;
+ self.insert_registry_src_from_cache(&tx)?;
+ self.insert_git_checkout_from_cache(&tx)?;
+ tx.commit()?;
+ trace!(target: "gc", "last-use save complete");
+ Ok(())
+ }
+
+ /// Variant of [`DeferredGlobalLastUse::save`] that does not return an
+ /// error.
+ ///
+ /// This will log or display a warning to the user.
+ pub fn save_no_error(&mut self, config: &Config) {
+ if let Err(e) = self.save_with_config(config) {
+ // Because there is an assertion in auto-gc that checks if this is
+ // empty, be sure to clear it so that assertion doesn't fail.
+ self.clear();
+ if !self.save_err_has_warned {
+ if is_silent_error(&e) && config.shell().verbosity() != Verbosity::Verbose {
+ tracing::warn!("failed to save last-use data: {e:?}");
+ } else {
+ crate::display_warning_with_error(
+ "failed to save last-use data\n\
+ This may prevent cargo from accurately tracking what is being \
+ used in its global cache. This information is used for \
+ automatically removing unused data in the cache.",
+ &e,
+ &mut config.shell(),
+ );
+ self.save_err_has_warned = true;
+ }
+ }
+ }
+ }
+
+ fn save_with_config(&mut self, config: &Config) -> CargoResult<()> {
+ let mut tracker = config.global_cache_tracker()?;
+ self.save(&mut tracker)
+ }
+
+ /// Flushes all of the `registry_index_timestamps` to the database,
+ /// clearing `registry_index_timestamps`.
+ fn insert_registry_index_from_cache(&mut self, conn: &Connection) -> CargoResult<()> {
+ insert_or_update_parent!(
+ self,
+ conn,
+ "registry_index",
+ registry_index_timestamps,
+ registry_keys,
+ encoded_registry_name
+ );
+ }
+
+ /// Flushes all of the `git_db_timestamps` to the database,
+ /// clearing `registry_index_timestamps`.
+ fn insert_git_db_from_cache(&mut self, conn: &Connection) -> CargoResult<()> {
+ insert_or_update_parent!(
+ self,
+ conn,
+ "git_db",
+ git_db_timestamps,
+ git_keys,
+ encoded_git_name
+ );
+ }
+
+ /// Flushes all of the `registry_crate_timestamps` to the database,
+ /// clearing `registry_index_timestamps`.
+ fn insert_registry_crate_from_cache(&mut self, conn: &Connection) -> CargoResult<()> {
+ let registry_crate_timestamps = std::mem::take(&mut self.registry_crate_timestamps);
+ for (registry_crate, timestamp) in registry_crate_timestamps {
+ trace!(target: "gc", "insert registry crate {registry_crate:?} {timestamp}");
+ let registry_id = self.registry_id(conn, registry_crate.encoded_registry_name)?;
+ let mut stmt = conn.prepare_cached(
+ "INSERT INTO registry_crate (registry_id, name, size, timestamp)
+ VALUES (?1, ?2, ?3, ?4)
+ ON CONFLICT DO UPDATE SET timestamp=excluded.timestamp
+ WHERE timestamp < ?5
+ ",
+ )?;
+ stmt.execute(params![
+ registry_id,
+ registry_crate.crate_filename,
+ registry_crate.size,
+ timestamp,
+ timestamp - UPDATE_RESOLUTION
+ ])?;
+ }
+ Ok(())
+ }
+
+ /// Flushes all of the `registry_src_timestamps` to the database,
+ /// clearing `registry_index_timestamps`.
+ fn insert_registry_src_from_cache(&mut self, conn: &Connection) -> CargoResult<()> {
+ let registry_src_timestamps = std::mem::take(&mut self.registry_src_timestamps);
+ for (registry_src, timestamp) in registry_src_timestamps {
+ trace!(target: "gc", "insert registry src {registry_src:?} {timestamp}");
+ let registry_id = self.registry_id(conn, registry_src.encoded_registry_name)?;
+ let mut stmt = conn.prepare_cached(
+ "INSERT INTO registry_src (registry_id, name, size, timestamp)
+ VALUES (?1, ?2, ?3, ?4)
+ ON CONFLICT DO UPDATE SET timestamp=excluded.timestamp
+ WHERE timestamp < ?5
+ ",
+ )?;
+ stmt.execute(params![
+ registry_id,
+ registry_src.package_dir,
+ registry_src.size,
+ timestamp,
+ timestamp - UPDATE_RESOLUTION
+ ])?;
+ }
+
+ Ok(())
+ }
+
+ /// Flushes all of the `git_checkout_timestamps` to the database,
+ /// clearing `registry_index_timestamps`.
+ fn insert_git_checkout_from_cache(&mut self, conn: &Connection) -> CargoResult<()> {
+ let git_checkout_timestamps = std::mem::take(&mut self.git_checkout_timestamps);
+ for (git_checkout, timestamp) in git_checkout_timestamps {
+ let git_id = self.git_id(conn, git_checkout.encoded_git_name)?;
+ let mut stmt = conn.prepare_cached(
+ "INSERT INTO git_checkout (git_id, name, size, timestamp)
+ VALUES (?1, ?2, ?3, ?4)
+ ON CONFLICT DO UPDATE SET timestamp=excluded.timestamp
+ WHERE timestamp < ?5",
+ )?;
+ stmt.execute(params![
+ git_id,
+ git_checkout.short_name,
+ git_checkout.size,
+ timestamp,
+ timestamp - UPDATE_RESOLUTION
+ ])?;
+ }
+
+ Ok(())
+ }
+
+ /// Returns the numeric ID of the registry, either fetching from the local
+ /// cache, or getting it from the database.
+ ///
+ /// It is an error if the registry does not exist.
+ fn registry_id(
+ &mut self,
+ conn: &Connection,
+ encoded_registry_name: InternedString,
+ ) -> CargoResult<ParentId> {
+ match self.registry_keys.get(&encoded_registry_name) {
+ Some(i) => Ok(*i),
+ None => {
+ let Some(id) = GlobalCacheTracker::id_from_name(
+ conn,
+ REGISTRY_INDEX_TABLE,
+ &encoded_registry_name,
+ )?
+ else {
+ bail!("expected registry_index {encoded_registry_name} to exist, but wasn't found");
+ };
+ self.registry_keys.insert(encoded_registry_name, id);
+ Ok(id)
+ }
+ }
+ }
+
+ /// Returns the numeric ID of the git db, either fetching from the local
+ /// cache, or getting it from the database.
+ ///
+ /// It is an error if the git db does not exist.
+ fn git_id(
+ &mut self,
+ conn: &Connection,
+ encoded_git_name: InternedString,
+ ) -> CargoResult<ParentId> {
+ match self.git_keys.get(&encoded_git_name) {
+ Some(i) => Ok(*i),
+ None => {
+ let Some(id) =
+ GlobalCacheTracker::id_from_name(conn, GIT_DB_TABLE, &encoded_git_name)?
+ else {
+ bail!("expected git_db {encoded_git_name} to exist, but wasn't found")
+ };
+ self.git_keys.insert(encoded_git_name, id);
+ Ok(id)
+ }
+ }
+ }
+}
+
+/// Converts a [`SystemTime`] to a [`Timestamp`] which can be stored in the database.
+fn to_timestamp(t: &SystemTime) -> Timestamp {
+ t.duration_since(SystemTime::UNIX_EPOCH)
+ .expect("invalid clock")
+ .as_secs()
+}
+
+/// Returns the current time.
+///
+/// This supports pretending that the time is different for testing using an
+/// environment variable.
+///
+/// If possible, try to avoid calling this too often since accessing clocks
+/// can be a little slow on some systems.
+#[allow(clippy::disallowed_methods)]
+fn now() -> Timestamp {
+ match std::env::var("__CARGO_TEST_LAST_USE_NOW") {
+ Ok(now) => now.parse().unwrap(),
+ Err(_) => to_timestamp(&SystemTime::now()),
+ }
+}
+
+/// Returns whether or not the given error should cause a warning to be
+/// displayed to the user.
+///
+/// In some situations, like a read-only global cache, we don't want to spam
+/// the user with a warning. I think once cargo has controllable lints, I
+/// think we should consider changing this to always warn, but give the user
+/// an option to silence the warning.
+pub fn is_silent_error(e: &anyhow::Error) -> bool {
+ if let Some(e) = e.downcast_ref::<rusqlite::Error>() {
+ if matches!(
+ e.sqlite_error_code(),
+ Some(ErrorCode::CannotOpen | ErrorCode::ReadOnly)
+ ) {
+ return true;
+ }
+ }
+ false
+}
+
+/// Returns the disk usage for a git checkout directory.
+pub fn du_git_checkout(path: &Path) -> CargoResult<u64> {
+ // !.git is used because clones typically use hardlinks for the git
+ // contents. TODO: Verify behavior on Windows.
+ // TODO: Or even better, switch to worktrees, and remove this.
+ cargo_util::du(&path, &["!.git"])
+}
+
+fn du(path: &Path, table_name: &str) -> CargoResult<u64> {
+ if table_name == GIT_CO_TABLE {
+ du_git_checkout(path)
+ } else {
+ cargo_util::du(&path, &[])
+ }
+}
diff --git a/src/tools/cargo/src/cargo/core/manifest.rs b/src/tools/cargo/src/cargo/core/manifest.rs
index 66af40c10..0468caa96 100644
--- a/src/tools/cargo/src/cargo/core/manifest.rs
+++ b/src/tools/cargo/src/cargo/core/manifest.rs
@@ -18,8 +18,9 @@ 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::schema::{TomlManifest, TomlProfiles};
-use crate::util::{short_hash, Config, Filesystem, RustVersion};
+use crate::util::{short_hash, Config, Filesystem};
+use crate::util_schemas::manifest::RustVersion;
+use crate::util_schemas::manifest::{TomlManifest, TomlProfiles};
pub enum EitherManifest {
Real(Manifest),
diff --git a/src/tools/cargo/src/cargo/core/mod.rs b/src/tools/cargo/src/cargo/core/mod.rs
index 2add52d5c..f3b3142fa 100644
--- a/src/tools/cargo/src/cargo/core/mod.rs
+++ b/src/tools/cargo/src/cargo/core/mod.rs
@@ -4,21 +4,23 @@ pub use self::manifest::{EitherManifest, VirtualManifest};
pub use self::manifest::{Manifest, Target, TargetKind};
pub use self::package::{Package, PackageSet};
pub use self::package_id::PackageId;
-pub use self::package_id_spec::PackageIdSpec;
+pub use self::package_id_spec::PackageIdSpecQuery;
pub use self::registry::Registry;
pub use self::resolver::{Resolve, ResolveVersion};
pub use self::shell::{Shell, Verbosity};
-pub use self::source_id::{GitReference, SourceId};
+pub use self::source_id::SourceId;
pub use self::summary::{FeatureMap, FeatureValue, Summary};
pub use self::workspace::{
find_workspace_root, resolve_relative_path, MaybePackage, Workspace, WorkspaceConfig,
WorkspaceRootConfig,
};
-pub use crate::util::toml::schema::InheritableFields;
+pub use crate::util_schemas::core::{GitReference, PackageIdSpec, SourceKind};
pub mod compiler;
pub mod dependency;
pub mod features;
+pub mod gc;
+pub mod global_cache_tracker;
pub mod manifest;
pub mod package;
pub mod package_id;
diff --git a/src/tools/cargo/src/cargo/core/package.rs b/src/tools/cargo/src/cargo/core/package.rs
index 274798474..082544882 100644
--- a/src/tools/cargo/src/cargo/core/package.rs
+++ b/src/tools/cargo/src/cargo/core/package.rs
@@ -31,8 +31,9 @@ use crate::util::network::http::http_handle_and_timeout;
use crate::util::network::http::HttpTimeout;
use crate::util::network::retry::{Retry, RetryResult};
use crate::util::network::sleep::SleepTracker;
-use crate::util::RustVersion;
+use crate::util::toml::prepare_for_publish;
use crate::util::{self, internal, Config, Progress, ProgressStyle};
+use crate::util_schemas::manifest::RustVersion;
pub const MANIFEST_PREAMBLE: &str = "\
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
@@ -197,10 +198,7 @@ impl Package {
}
pub fn to_registry_toml(&self, ws: &Workspace<'_>) -> CargoResult<String> {
- let manifest = self
- .manifest()
- .original()
- .prepare_for_publish(ws, self.root())?;
+ let manifest = prepare_for_publish(self.manifest().original(), ws, self.root())?;
let toml = toml::to_string_pretty(&manifest)?;
Ok(format!("{}\n{}", MANIFEST_PREAMBLE, toml))
}
@@ -491,6 +489,10 @@ impl<'cfg> PackageSet<'cfg> {
pkgs.push(downloads.wait()?);
}
downloads.success = true;
+ drop(downloads);
+
+ let mut deferred = self.config.deferred_global_last_use()?;
+ deferred.save_no_error(self.config);
Ok(pkgs)
}
diff --git a/src/tools/cargo/src/cargo/core/package_id.rs b/src/tools/cargo/src/cargo/core/package_id.rs
index 3e9c03a47..37b367218 100644
--- a/src/tools/cargo/src/cargo/core/package_id.rs
+++ b/src/tools/cargo/src/cargo/core/package_id.rs
@@ -10,6 +10,7 @@ use std::sync::OnceLock;
use serde::de;
use serde::ser;
+use crate::core::PackageIdSpec;
use crate::core::SourceId;
use crate::util::interning::InternedString;
use crate::util::CargoResult;
@@ -88,7 +89,7 @@ impl<'de> de::Deserialize<'de> for PackageId {
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))
+ Ok(PackageId::new(name, version, source_id))
}
}
@@ -123,16 +124,16 @@ impl Hash for PackageId {
}
impl PackageId {
- pub fn new(
+ pub fn try_new(
name: impl Into<InternedString>,
version: &str,
sid: SourceId,
) -> CargoResult<PackageId> {
let v = version.parse()?;
- Ok(PackageId::pure(name.into(), v, sid))
+ Ok(PackageId::new(name.into(), v, sid))
}
- pub fn pure(name: InternedString, version: semver::Version, source_id: SourceId) -> PackageId {
+ pub fn new(name: InternedString, version: semver::Version, source_id: SourceId) -> PackageId {
let inner = PackageIdInner {
name,
version,
@@ -161,7 +162,7 @@ impl PackageId {
}
pub fn with_source_id(self, source: SourceId) -> PackageId {
- PackageId::pure(self.inner.name, self.inner.version.clone(), source)
+ PackageId::new(self.inner.name, self.inner.version.clone(), source)
}
pub fn map_source(self, to_replace: SourceId, replace_with: SourceId) -> Self {
@@ -186,6 +187,15 @@ impl PackageId {
pub fn tarball_name(&self) -> String {
format!("{}-{}.crate", self.name(), self.version())
}
+
+ /// Convert a `PackageId` to a `PackageIdSpec`, which will have both the `PartialVersion` and `Url`
+ /// fields filled in.
+ pub fn to_spec(&self) -> PackageIdSpec {
+ PackageIdSpec::new(String::from(self.name().as_str()))
+ .with_version(self.version().clone().into())
+ .with_url(self.source_id().url().clone())
+ .with_kind(self.source_id().kind().clone())
+ }
}
pub struct PackageIdStableHash<'a>(PackageId, &'a Path);
@@ -242,16 +252,17 @@ mod tests {
let loc = CRATES_IO_INDEX.into_url().unwrap();
let repo = SourceId::for_registry(&loc).unwrap();
- assert!(PackageId::new("foo", "1.0", repo).is_err());
- assert!(PackageId::new("foo", "1", repo).is_err());
- assert!(PackageId::new("foo", "bar", repo).is_err());
- assert!(PackageId::new("foo", "", repo).is_err());
+ assert!(PackageId::try_new("foo", "1.0", repo).is_err());
+ assert!(PackageId::try_new("foo", "1", repo).is_err());
+ assert!(PackageId::try_new("foo", "bar", repo).is_err());
+ assert!(PackageId::try_new("foo", "", repo).is_err());
}
#[test]
fn display() {
let loc = CRATES_IO_INDEX.into_url().unwrap();
- let pkg_id = PackageId::new("foo", "1.0.0", SourceId::for_registry(&loc).unwrap()).unwrap();
+ let pkg_id =
+ PackageId::try_new("foo", "1.0.0", SourceId::for_registry(&loc).unwrap()).unwrap();
assert_eq!("foo v1.0.0", pkg_id.to_string());
}
@@ -259,8 +270,8 @@ mod tests {
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();
+ let first = PackageId::try_new("foo", "0.0.1+first", repo).unwrap();
+ let second = PackageId::try_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 c617c1f7a..35c5437ae 100644
--- a/src/tools/cargo/src/cargo/core/package_id_spec.rs
+++ b/src/tools/cargo/src/cargo/core/package_id_spec.rs
@@ -1,88 +1,30 @@
use std::collections::HashMap;
-use std::fmt;
use anyhow::{bail, Context as _};
-use semver::Version;
-use serde::{de, ser};
-use url::Url;
use crate::core::PackageId;
+use crate::core::PackageIdSpec;
use crate::util::edit_distance;
use crate::util::errors::CargoResult;
-use crate::util::{validate_package_name, IntoUrl};
-use crate::util_semver::PartialVersion;
-/// Some or all of the data required to identify a package:
-///
-/// 1. the package name (a `String`, required)
-/// 2. the package version (a `Version`, optional)
-/// 3. the package source (a `Url`, optional)
-///
-/// If any of the optional fields are omitted, then the package ID may be ambiguous, there may be
-/// more than one package/version/url combo that will match. However, often just the name is
-/// sufficient to uniquely define a package ID.
-#[derive(Clone, PartialEq, Eq, Debug, Hash, Ord, PartialOrd)]
-pub struct PackageIdSpec {
- name: String,
- version: Option<PartialVersion>,
- url: Option<Url>,
-}
+pub trait PackageIdSpecQuery {
+ /// Roughly equivalent to `PackageIdSpec::parse(spec)?.query(i)`
+ fn query_str<I>(spec: &str, i: I) -> CargoResult<PackageId>
+ where
+ I: IntoIterator<Item = PackageId>;
-impl PackageIdSpec {
- /// Parses a spec string and returns a `PackageIdSpec` if the string was valid.
- ///
- /// # Examples
- /// Some examples of valid strings
- ///
- /// ```
- /// use cargo::core::PackageIdSpec;
- ///
- /// let specs = vec![
- /// "https://crates.io/foo",
- /// "https://crates.io/foo#1.2.3",
- /// "https://crates.io/foo#bar:1.2.3",
- /// "https://crates.io/foo#bar@1.2.3",
- /// "foo",
- /// "foo:1.2.3",
- /// "foo@1.2.3",
- /// ];
- /// for spec in specs {
- /// assert!(PackageIdSpec::parse(spec).is_ok());
- /// }
- pub fn parse(spec: &str) -> CargoResult<PackageIdSpec> {
- if spec.contains("://") {
- if let Ok(url) = spec.into_url() {
- return PackageIdSpec::from_url(url);
- }
- } else if spec.contains('/') || spec.contains('\\') {
- let abs = std::env::current_dir().unwrap_or_default().join(spec);
- if abs.exists() {
- let maybe_url = Url::from_file_path(abs)
- .map_or_else(|_| "a file:// URL".to_string(), |url| url.to_string());
- bail!(
- "package ID specification `{}` looks like a file path, \
- maybe try {}",
- spec,
- maybe_url
- );
- }
- }
- let mut parts = spec.splitn(2, [':', '@']);
- let name = parts.next().unwrap();
- let version = match parts.next() {
- Some(version) => Some(version.parse::<PartialVersion>()?),
- None => None,
- };
- validate_package_name(name, "pkgid", "")?;
- Ok(PackageIdSpec {
- name: String::from(name),
- version,
- url: None,
- })
- }
+ /// Checks whether the given `PackageId` matches the `PackageIdSpec`.
+ fn matches(&self, package_id: PackageId) -> bool;
- /// Roughly equivalent to `PackageIdSpec::parse(spec)?.query(i)`
- pub fn query_str<I>(spec: &str, i: I) -> CargoResult<PackageId>
+ /// Checks a list of `PackageId`s to find 1 that matches this `PackageIdSpec`. If 0, 2, or
+ /// more are found, then this returns an error.
+ fn query<I>(&self, i: I) -> CargoResult<PackageId>
+ where
+ I: IntoIterator<Item = PackageId>;
+}
+
+impl PackageIdSpecQuery for PackageIdSpec {
+ fn query_str<I>(spec: &str, i: I) -> CargoResult<PackageId>
where
I: IntoIterator<Item = PackageId>,
{
@@ -94,94 +36,25 @@ impl PackageIdSpec {
spec.query(i)
}
- /// Convert a `PackageId` to a `PackageIdSpec`, which will have both the `PartialVersion` and `Url`
- /// fields filled in.
- pub fn from_package_id(package_id: PackageId) -> PackageIdSpec {
- PackageIdSpec {
- name: String::from(package_id.name().as_str()),
- version: Some(package_id.version().clone().into()),
- url: Some(package_id.source_id().url().clone()),
- }
- }
-
- /// Tries to convert a valid `Url` to a `PackageIdSpec`.
- fn from_url(mut url: Url) -> CargoResult<PackageIdSpec> {
- if url.query().is_some() {
- bail!("cannot have a query string in a pkgid: {}", url)
- }
- let frag = url.fragment().map(|s| s.to_owned());
- url.set_fragment(None);
- let (name, version) = {
- let mut path = url
- .path_segments()
- .ok_or_else(|| anyhow::format_err!("pkgid urls must have a path: {}", url))?;
- let path_name = path.next_back().ok_or_else(|| {
- anyhow::format_err!(
- "pkgid urls must have at least one path \
- component: {}",
- url
- )
- })?;
- match frag {
- Some(fragment) => match fragment.split_once([':', '@']) {
- Some((name, part)) => {
- let version = part.parse::<PartialVersion>()?;
- (String::from(name), Some(version))
- }
- None => {
- if fragment.chars().next().unwrap().is_alphabetic() {
- (String::from(fragment.as_str()), None)
- } else {
- let version = fragment.parse::<PartialVersion>()?;
- (String::from(path_name), Some(version))
- }
- }
- },
- None => (String::from(path_name), None),
- }
- };
- Ok(PackageIdSpec {
- name,
- version,
- url: Some(url),
- })
- }
-
- 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.to_version())
- }
-
- pub fn partial_version(&self) -> Option<&PartialVersion> {
- self.version.as_ref()
- }
-
- pub fn url(&self) -> Option<&Url> {
- self.url.as_ref()
- }
-
- pub fn set_url(&mut self, url: Url) {
- self.url = Some(url);
- }
-
- /// Checks whether the given `PackageId` matches the `PackageIdSpec`.
- pub fn matches(&self, package_id: PackageId) -> bool {
+ fn matches(&self, package_id: PackageId) -> bool {
if self.name() != package_id.name().as_str() {
return false;
}
- if let Some(ref v) = self.version {
+ if let Some(ref v) = self.partial_version() {
if !v.matches(package_id.version()) {
return false;
}
}
- if let Some(u) = &self.url {
- if u != package_id.source_id().url() {
+ if let Some(u) = &self.url() {
+ if *u != package_id.source_id().url() {
+ return false;
+ }
+ }
+
+ if let Some(k) = &self.kind() {
+ if *k != package_id.source_id().kind() {
return false;
}
}
@@ -189,9 +62,7 @@ impl PackageIdSpec {
true
}
- /// Checks a list of `PackageId`s to find 1 that matches this `PackageIdSpec`. If 0, 2, or
- /// more are found, then this returns an error.
- pub fn query<I>(&self, i: I) -> CargoResult<PackageId>
+ fn query<I>(&self, i: I) -> CargoResult<PackageId>
where
I: IntoIterator<Item = PackageId>,
{
@@ -210,29 +81,21 @@ impl PackageIdSpec {
minimize(suggestion, &try_matches, self);
}
};
- if self.url.is_some() {
- try_spec(
- PackageIdSpec {
- name: self.name.clone(),
- version: self.version.clone(),
- url: None,
- },
- &mut suggestion,
- );
+ if self.url().is_some() {
+ let spec = PackageIdSpec::new(self.name().to_owned());
+ let spec = if let Some(version) = self.partial_version().cloned() {
+ spec.with_version(version)
+ } else {
+ spec
+ };
+ try_spec(spec, &mut suggestion);
}
- if suggestion.is_empty() && self.version.is_some() {
- try_spec(
- PackageIdSpec {
- name: self.name.clone(),
- version: None,
- url: None,
- },
- &mut suggestion,
- );
+ if suggestion.is_empty() && self.version().is_some() {
+ try_spec(PackageIdSpec::new(self.name().to_owned()), &mut suggestion);
}
if suggestion.is_empty() {
suggestion.push_str(&edit_distance::closest_msg(
- &self.name,
+ self.name(),
all_ids.iter(),
|id| id.name().as_str(),
));
@@ -273,274 +136,26 @@ impl PackageIdSpec {
if version_cnt[id.version()] == 1 {
msg.push_str(&format!("\n {}@{}", spec.name(), id.version()));
} else {
- msg.push_str(&format!("\n {}", PackageIdSpec::from_package_id(*id)));
+ msg.push_str(&format!("\n {}", id.to_spec()));
}
}
}
}
}
-impl fmt::Display for PackageIdSpec {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut printed_name = false;
- match self.url {
- Some(ref url) => {
- write!(f, "{}", url)?;
- if url.path_segments().unwrap().next_back().unwrap() != &*self.name {
- printed_name = true;
- write!(f, "#{}", self.name)?;
- }
- }
- None => {
- printed_name = true;
- write!(f, "{}", self.name)?;
- }
- }
- if let Some(ref v) = self.version {
- write!(f, "{}{}", if printed_name { "@" } else { "#" }, v)?;
- }
- Ok(())
- }
-}
-
-impl ser::Serialize for PackageIdSpec {
- 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 PackageIdSpec {
- fn deserialize<D>(d: D) -> Result<PackageIdSpec, D::Error>
- where
- D: de::Deserializer<'de>,
- {
- let string = String::deserialize(d)?;
- PackageIdSpec::parse(&string).map_err(de::Error::custom)
- }
-}
-
#[cfg(test)]
mod tests {
use super::PackageIdSpec;
+ use super::PackageIdSpecQuery;
use crate::core::{PackageId, SourceId};
use url::Url;
#[test]
- fn good_parsing() {
- #[track_caller]
- fn ok(spec: &str, expected: PackageIdSpec, expected_rendered: &str) {
- let parsed = PackageIdSpec::parse(spec).unwrap();
- assert_eq!(parsed, expected);
- 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: String::from("foo"),
- version: None,
- url: Some(Url::parse("https://crates.io/foo").unwrap()),
- },
- "https://crates.io/foo",
- );
- ok(
- "https://crates.io/foo#1.2.3",
- PackageIdSpec {
- name: String::from("foo"),
- version: Some("1.2.3".parse().unwrap()),
- url: Some(Url::parse("https://crates.io/foo").unwrap()),
- },
- "https://crates.io/foo#1.2.3",
- );
- ok(
- "https://crates.io/foo#1.2",
- PackageIdSpec {
- name: String::from("foo"),
- version: Some("1.2".parse().unwrap()),
- url: Some(Url::parse("https://crates.io/foo").unwrap()),
- },
- "https://crates.io/foo#1.2",
- );
- ok(
- "https://crates.io/foo#bar:1.2.3",
- PackageIdSpec {
- name: String::from("bar"),
- version: Some("1.2.3".parse().unwrap()),
- url: Some(Url::parse("https://crates.io/foo").unwrap()),
- },
- "https://crates.io/foo#bar@1.2.3",
- );
- ok(
- "https://crates.io/foo#bar@1.2.3",
- PackageIdSpec {
- name: String::from("bar"),
- version: Some("1.2.3".parse().unwrap()),
- url: Some(Url::parse("https://crates.io/foo").unwrap()),
- },
- "https://crates.io/foo#bar@1.2.3",
- );
- ok(
- "https://crates.io/foo#bar@1.2",
- PackageIdSpec {
- name: String::from("bar"),
- version: Some("1.2".parse().unwrap()),
- url: Some(Url::parse("https://crates.io/foo").unwrap()),
- },
- "https://crates.io/foo#bar@1.2",
- );
- ok(
- "foo",
- PackageIdSpec {
- name: String::from("foo"),
- version: None,
- url: None,
- },
- "foo",
- );
- ok(
- "foo:1.2.3",
- PackageIdSpec {
- name: String::from("foo"),
- version: Some("1.2.3".parse().unwrap()),
- url: None,
- },
- "foo@1.2.3",
- );
- ok(
- "foo@1.2.3",
- PackageIdSpec {
- name: String::from("foo"),
- version: Some("1.2.3".parse().unwrap()),
- url: None,
- },
- "foo@1.2.3",
- );
- ok(
- "foo@1.2",
- PackageIdSpec {
- 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]
- fn bad_parsing() {
- assert!(PackageIdSpec::parse("baz:").is_err());
- assert!(PackageIdSpec::parse("baz:*").is_err());
- assert!(PackageIdSpec::parse("baz@").is_err());
- assert!(PackageIdSpec::parse("baz@*").is_err());
- assert!(PackageIdSpec::parse("baz@^1.0").is_err());
- assert!(PackageIdSpec::parse("https://baz:1.0").is_err());
- assert!(PackageIdSpec::parse("https://#baz:1.0").is_err());
- }
-
- #[test]
fn matching() {
let url = Url::parse("https://example.com").unwrap();
let sid = SourceId::for_registry(&url).unwrap();
- let foo = PackageId::new("foo", "1.2.3", sid).unwrap();
+ let foo = PackageId::try_new("foo", "1.2.3", sid).unwrap();
assert!(PackageIdSpec::parse("foo").unwrap().matches(foo));
assert!(!PackageIdSpec::parse("bar").unwrap().matches(foo));
assert!(PackageIdSpec::parse("foo:1.2.3").unwrap().matches(foo));
@@ -554,8 +169,14 @@ mod tests {
assert!(!PackageIdSpec::parse("https://bob.com#foo@1.2")
.unwrap()
.matches(foo));
+ assert!(PackageIdSpec::parse("registry+https://example.com#foo@1.2")
+ .unwrap()
+ .matches(foo));
+ assert!(!PackageIdSpec::parse("git+https://example.com#foo@1.2")
+ .unwrap()
+ .matches(foo));
- let meta = PackageId::new("meta", "1.2.3+hello", sid).unwrap();
+ let meta = PackageId::try_new("meta", "1.2.3+hello", sid).unwrap();
assert!(PackageIdSpec::parse("meta").unwrap().matches(meta));
assert!(PackageIdSpec::parse("meta@1").unwrap().matches(meta));
assert!(PackageIdSpec::parse("meta@1.2").unwrap().matches(meta));
@@ -570,7 +191,7 @@ mod tests {
.unwrap()
.matches(meta));
- let pre = PackageId::new("pre", "1.2.3-alpha.0", sid).unwrap();
+ let pre = PackageId::try_new("pre", "1.2.3-alpha.0", sid).unwrap();
assert!(PackageIdSpec::parse("pre").unwrap().matches(pre));
assert!(!PackageIdSpec::parse("pre@1").unwrap().matches(pre));
assert!(!PackageIdSpec::parse("pre@1.2").unwrap().matches(pre));
diff --git a/src/tools/cargo/src/cargo/core/profiles.rs b/src/tools/cargo/src/cargo/core/profiles.rs
index ec53dbae5..4d2a23f50 100644
--- a/src/tools/cargo/src/cargo/core/profiles.rs
+++ b/src/tools/cargo/src/cargo/core/profiles.rs
@@ -25,14 +25,17 @@ 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::core::{
+ PackageId, PackageIdSpec, PackageIdSpecQuery, Resolve, Shell, Target, Workspace,
+};
use crate::util::interning::InternedString;
-use crate::util::toml::schema::TomlTrimPaths;
-use crate::util::toml::schema::TomlTrimPathsValue;
-use crate::util::toml::schema::{
+use crate::util::toml::validate_profile;
+use crate::util::{closest_msg, config, CargoResult, Config};
+use crate::util_schemas::manifest::TomlTrimPaths;
+use crate::util_schemas::manifest::TomlTrimPathsValue;
+use crate::util_schemas::manifest::{
ProfilePackageSpec, StringOrBool, TomlDebugInfo, TomlProfile, TomlProfiles,
};
-use crate::util::{closest_msg, config, CargoResult, Config};
use anyhow::{bail, Context as _};
use std::collections::{BTreeMap, HashMap, HashSet};
use std::hash::Hash;
@@ -1235,20 +1238,19 @@ fn get_config_profile(ws: &Workspace<'_>, name: &str) -> CargoResult<Option<Toml
return Ok(None);
};
let mut warnings = Vec::new();
- profile
- .val
- .validate(
- name,
- ws.config().cli_unstable(),
- ws.unstable_features(),
- &mut warnings,
+ validate_profile(
+ &profile.val,
+ name,
+ ws.config().cli_unstable(),
+ ws.unstable_features(),
+ &mut warnings,
+ )
+ .with_context(|| {
+ format!(
+ "config profile `{}` is not valid (defined in `{}`)",
+ name, profile.definition
)
- .with_context(|| {
- format!(
- "config profile `{}` is not valid (defined in `{}`)",
- name, profile.definition
- )
- })?;
+ })?;
for warning in warnings {
ws.config().shell().warn(warning)?;
}
diff --git a/src/tools/cargo/src/cargo/core/registry.rs b/src/tools/cargo/src/cargo/core/registry.rs
index a91f2986a..a1fb1054d 100644
--- a/src/tools/cargo/src/cargo/core/registry.rs
+++ b/src/tools/cargo/src/cargo/core/registry.rs
@@ -7,6 +7,7 @@ use crate::sources::config::SourceConfigMap;
use crate::sources::source::QueryKind;
use crate::sources::source::Source;
use crate::sources::source::SourceMap;
+use crate::sources::IndexSummary;
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::{CanonicalUrl, Config};
@@ -23,10 +24,14 @@ pub trait Registry {
&mut self,
dep: &Dependency,
kind: QueryKind,
- f: &mut dyn FnMut(Summary),
+ f: &mut dyn FnMut(IndexSummary),
) -> Poll<CargoResult<()>>;
- fn query_vec(&mut self, dep: &Dependency, kind: QueryKind) -> Poll<CargoResult<Vec<Summary>>> {
+ fn query_vec(
+ &mut self,
+ dep: &Dependency,
+ kind: QueryKind,
+ ) -> Poll<CargoResult<Vec<IndexSummary>>> {
let mut ret = Vec::new();
self.query(dep, kind, &mut |s| ret.push(s)).map_ok(|()| ret)
}
@@ -337,6 +342,8 @@ impl<'cfg> PackageRegistry<'cfg> {
}
};
+ let summaries = summaries.into_iter().map(|s| s.into_summary()).collect();
+
let (summary, should_unlock) =
match summary_for_patch(orig_patch, &locked, summaries, source) {
Poll::Ready(x) => x,
@@ -481,13 +488,15 @@ impl<'cfg> PackageRegistry<'cfg> {
Ok(())
}
- fn query_overrides(&mut self, dep: &Dependency) -> Poll<CargoResult<Option<Summary>>> {
+ fn query_overrides(&mut self, dep: &Dependency) -> Poll<CargoResult<Option<IndexSummary>>> {
for &s in self.overrides.iter() {
let src = self.sources.get_mut(s).unwrap();
let dep = Dependency::new_override(dep.package_name(), s);
- let mut results = ready!(src.query_vec(&dep, QueryKind::Exact))?;
- if !results.is_empty() {
- return Poll::Ready(Ok(Some(results.remove(0))));
+
+ let mut results = None;
+ ready!(src.query(&dep, QueryKind::Exact, &mut |s| results = Some(s)))?;
+ if results.is_some() {
+ return Poll::Ready(Ok(results));
}
}
Poll::Ready(Ok(None))
@@ -575,7 +584,7 @@ impl<'cfg> Registry for PackageRegistry<'cfg> {
&mut self,
dep: &Dependency,
kind: QueryKind,
- f: &mut dyn FnMut(Summary),
+ f: &mut dyn FnMut(IndexSummary),
) -> Poll<CargoResult<()>> {
assert!(self.patches_locked);
let (override_summary, n, to_warn) = {
@@ -607,9 +616,9 @@ impl<'cfg> Registry for PackageRegistry<'cfg> {
if patches.len() == 1 && dep.is_locked() {
let patch = patches.remove(0);
match override_summary {
- Some(summary) => (summary, 1, Some(patch)),
+ Some(summary) => (summary, 1, Some(IndexSummary::Candidate(patch))),
None => {
- f(patch);
+ f(IndexSummary::Candidate(patch));
return Poll::Ready(Ok(()));
}
}
@@ -646,7 +655,7 @@ impl<'cfg> Registry for PackageRegistry<'cfg> {
// everything upstairs after locking the summary
(None, Some(source)) => {
for patch in patches.iter() {
- f(patch.clone());
+ f(IndexSummary::Candidate(patch.clone()));
}
// Our sources shouldn't ever come back to us with two
@@ -658,14 +667,18 @@ impl<'cfg> Registry for PackageRegistry<'cfg> {
// already selected, then we skip this `summary`.
let locked = &self.locked;
let all_patches = &self.patches_available;
- let callback = &mut |summary: Summary| {
+ let callback = &mut |summary: IndexSummary| {
for patch in patches.iter() {
let patch = patch.package_id().version();
if summary.package_id().version() == patch {
return;
}
}
- f(lock(locked, all_patches, summary))
+ f(IndexSummary::Candidate(lock(
+ locked,
+ all_patches,
+ summary.into_summary(),
+ )))
};
return source.query(dep, kind, callback);
}
@@ -702,9 +715,12 @@ impl<'cfg> Registry for PackageRegistry<'cfg> {
"found an override with a non-locked list"
)));
} else if let Some(summary) = to_warn {
- self.warn_bad_override(&override_summary, &summary)?;
+ self.warn_bad_override(override_summary.as_summary(), summary.as_summary())?;
}
- f(self.lock(override_summary));
+ f(IndexSummary::Candidate(
+ self.lock(override_summary.into_summary()),
+ ));
+
Poll::Ready(Ok(()))
}
@@ -887,6 +903,8 @@ fn summary_for_patch(
Vec::new()
});
+ let orig_matches = orig_matches.into_iter().map(|s| s.into_summary()).collect();
+
let summary = ready!(summary_for_patch(orig_patch, &None, orig_matches, source))?;
// The unlocked version found a match. This returns a value to
@@ -907,7 +925,7 @@ fn summary_for_patch(
});
let mut vers = name_summaries
.iter()
- .map(|summary| summary.version())
+ .map(|summary| summary.as_summary().version())
.collect::<Vec<_>>();
let found = match vers.len() {
0 => format!(""),
diff --git a/src/tools/cargo/src/cargo/core/resolver/context.rs b/src/tools/cargo/src/cargo/core/resolver/context.rs
index 09b16b39c..cfeea209a 100644
--- a/src/tools/cargo/src/cargo/core/resolver/context.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/context.rs
@@ -22,9 +22,6 @@ pub struct Context {
pub resolve_features: im_rc::HashMap<PackageId, FeaturesSet>,
/// get the package that will be linking to a native library by its links attribute
pub links: im_rc::HashMap<InternedString, PackageId>,
- /// for each package the list of names it can see,
- /// then for each name the exact version that name represents and whether the name is public.
- pub public_dependency: Option<PublicDependency>,
/// a way to look up for a package in activations what packages required it
/// and all of the exact deps that it fulfilled.
@@ -74,16 +71,11 @@ impl PackageId {
}
impl Context {
- pub fn new(check_public_visible_dependencies: bool) -> Context {
+ pub fn new() -> Context {
Context {
age: 0,
resolve_features: im_rc::HashMap::new(),
links: im_rc::HashMap::new(),
- public_dependency: if check_public_visible_dependencies {
- Some(PublicDependency::new())
- } else {
- None
- },
parents: Graph::new(),
activations: im_rc::HashMap::new(),
}
@@ -192,42 +184,6 @@ impl Context {
.and_then(|(s, l)| if s.package_id() == id { Some(*l) } else { None })
}
- /// If the conflict reason on the package still applies returns the `ContextAge` when it was added
- pub fn still_applies(&self, id: PackageId, reason: &ConflictReason) -> Option<ContextAge> {
- self.is_active(id).and_then(|mut max| {
- match reason {
- ConflictReason::PublicDependency(name) => {
- if &id == name {
- return Some(max);
- }
- max = std::cmp::max(max, self.is_active(*name)?);
- max = std::cmp::max(
- max,
- self.public_dependency
- .as_ref()
- .unwrap()
- .can_see_item(*name, id)?,
- );
- }
- ConflictReason::PubliclyExports(name) => {
- if &id == name {
- return Some(max);
- }
- max = std::cmp::max(max, self.is_active(*name)?);
- max = std::cmp::max(
- max,
- self.public_dependency
- .as_ref()
- .unwrap()
- .publicly_exports_item(*name, id)?,
- );
- }
- _ => {}
- }
- Some(max)
- })
- }
-
/// Checks whether all of `parent` and the keys of `conflicting activations`
/// are still active.
/// If so returns the `ContextAge` when the newest one was added.
@@ -241,8 +197,8 @@ impl Context {
max = std::cmp::max(max, self.is_active(parent)?);
}
- for (id, reason) in conflicting_activations.iter() {
- max = std::cmp::max(max, self.still_applies(*id, reason)?);
+ for id in conflicting_activations.keys() {
+ max = std::cmp::max(max, self.is_active(*id)?);
}
Some(max)
}
@@ -280,158 +236,3 @@ impl Graph<PackageId, im_rc::HashSet<Dependency>> {
.map(|(grand, d)| (*grand, d.iter().any(|x| x.is_public())))
}
}
-
-#[derive(Clone, Debug, Default)]
-pub struct PublicDependency {
- /// For each active package the set of all the names it can see,
- /// for each name the exact package that name resolves to,
- /// the `ContextAge` when it was first visible,
- /// and the `ContextAge` when it was first exported.
- inner: im_rc::HashMap<
- PackageId,
- im_rc::HashMap<InternedString, (PackageId, ContextAge, Option<ContextAge>)>,
- >,
-}
-
-impl PublicDependency {
- fn new() -> Self {
- PublicDependency {
- inner: im_rc::HashMap::new(),
- }
- }
- fn publicly_exports(&self, candidate_pid: PackageId) -> Vec<PackageId> {
- self.inner
- .get(&candidate_pid) // if we have seen it before
- .iter()
- .flat_map(|x| x.values()) // all the things we have stored
- .filter(|x| x.2.is_some()) // as publicly exported
- .map(|x| x.0)
- .chain(Some(candidate_pid)) // but even if not we know that everything exports itself
- .collect()
- }
- fn publicly_exports_item(
- &self,
- candidate_pid: PackageId,
- target: PackageId,
- ) -> Option<ContextAge> {
- debug_assert_ne!(candidate_pid, target);
- let out = self
- .inner
- .get(&candidate_pid)
- .and_then(|names| names.get(&target.name()))
- .filter(|(p, _, _)| *p == target)
- .and_then(|(_, _, age)| *age);
- debug_assert_eq!(
- out.is_some(),
- self.publicly_exports(candidate_pid).contains(&target)
- );
- out
- }
- pub fn can_see_item(&self, candidate_pid: PackageId, target: PackageId) -> Option<ContextAge> {
- self.inner
- .get(&candidate_pid)
- .and_then(|names| names.get(&target.name()))
- .filter(|(p, _, _)| *p == target)
- .map(|(_, age, _)| *age)
- }
- pub fn add_edge(
- &mut self,
- candidate_pid: PackageId,
- parent_pid: PackageId,
- is_public: bool,
- age: ContextAge,
- parents: &Graph<PackageId, im_rc::HashSet<Dependency>>,
- ) {
- // one tricky part is that `candidate_pid` may already be active and
- // have public dependencies of its own. So we not only need to mark
- // `candidate_pid` as visible to its parents but also all of its existing
- // publicly exported dependencies.
- for c in self.publicly_exports(candidate_pid) {
- // for each (transitive) parent that can newly see `t`
- let mut stack = vec![(parent_pid, is_public)];
- while let Some((p, public)) = stack.pop() {
- match self.inner.entry(p).or_default().entry(c.name()) {
- im_rc::hashmap::Entry::Occupied(mut o) => {
- // the (transitive) parent can already see something by `c`s name, it had better be `c`.
- assert_eq!(o.get().0, c);
- if o.get().2.is_some() {
- // The previous time the parent saw `c`, it was a public dependency.
- // So all of its parents already know about `c`
- // and we can save some time by stopping now.
- continue;
- }
- if public {
- // Mark that `c` has now bean seen publicly
- let old_age = o.get().1;
- o.insert((c, old_age, if public { Some(age) } else { None }));
- }
- }
- im_rc::hashmap::Entry::Vacant(v) => {
- // The (transitive) parent does not have anything by `c`s name,
- // so we add `c`.
- v.insert((c, age, if public { Some(age) } else { None }));
- }
- }
- // if `candidate_pid` was a private dependency of `p` then `p` parents can't see `c` thru `p`
- if public {
- // if it was public, then we add all of `p`s parents to be checked
- stack.extend(parents.parents_of(p));
- }
- }
- }
- }
- pub fn can_add_edge(
- &self,
- b_id: PackageId,
- parent: PackageId,
- is_public: bool,
- parents: &Graph<PackageId, im_rc::HashSet<Dependency>>,
- ) -> Result<
- (),
- (
- ((PackageId, ConflictReason), (PackageId, ConflictReason)),
- Option<(PackageId, ConflictReason)>,
- ),
- > {
- // one tricky part is that `candidate_pid` may already be active and
- // have public dependencies of its own. So we not only need to check
- // `b_id` as visible to its parents but also all of its existing
- // publicly exported dependencies.
- for t in self.publicly_exports(b_id) {
- // for each (transitive) parent that can newly see `t`
- let mut stack = vec![(parent, is_public)];
- while let Some((p, public)) = stack.pop() {
- // TODO: don't look at the same thing more than once
- if let Some(o) = self.inner.get(&p).and_then(|x| x.get(&t.name())) {
- if o.0 != t {
- // the (transitive) parent can already see a different version by `t`s name.
- // So, adding `b` will cause `p` to have a public dependency conflict on `t`.
- return Err((
- (o.0, ConflictReason::PublicDependency(p)), // p can see the other version and
- (parent, ConflictReason::PublicDependency(p)), // p can see us
- ))
- .map_err(|e| {
- if t == b_id {
- (e, None)
- } else {
- (e, Some((t, ConflictReason::PubliclyExports(b_id))))
- }
- });
- }
- if o.2.is_some() {
- // The previous time the parent saw `t`, it was a public dependency.
- // So all of its parents already know about `t`
- // and we can save some time by stopping now.
- continue;
- }
- }
- // if `b` was a private dependency of `p` then `p` parents can't see `t` thru `p`
- if public {
- // if it was public, then we add all of `p`s parents to be checked
- stack.extend(parents.parents_of(p));
- }
- }
- }
- Ok(())
- }
-}
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 6c904c148..9e8ffd351 100644
--- a/src/tools/cargo/src/cargo/core/resolver/dep_cache.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/dep_cache.rs
@@ -16,7 +16,9 @@ use crate::core::resolver::{
ActivateError, ActivateResult, CliFeatures, RequestedFeatures, ResolveOpts, VersionOrdering,
VersionPreferences,
};
-use crate::core::{Dependency, FeatureValue, PackageId, PackageIdSpec, Registry, Summary};
+use crate::core::{
+ Dependency, FeatureValue, PackageId, PackageIdSpec, PackageIdSpecQuery, Registry, Summary,
+};
use crate::sources::source::QueryKind;
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
@@ -105,7 +107,7 @@ impl<'a> RegistryQueryer<'a> {
let mut ret = Vec::new();
let ready = self.registry.query(dep, QueryKind::Exact, &mut |s| {
- ret.push(s);
+ ret.push(s.into_summary());
})?;
if ready.is_pending() {
self.registry_cache
@@ -135,16 +137,19 @@ impl<'a> RegistryQueryer<'a> {
return Poll::Pending;
}
};
- let s = summaries.next().ok_or_else(|| {
- anyhow::format_err!(
- "no matching package for override `{}` found\n\
+ let s = summaries
+ .next()
+ .ok_or_else(|| {
+ anyhow::format_err!(
+ "no matching package for override `{}` found\n\
location searched: {}\n\
version required: {}",
- spec,
- dep.source_id(),
- dep.version_req()
- )
- })?;
+ spec,
+ dep.source_id(),
+ dep.version_req()
+ )
+ })?
+ .into_summary();
let summaries = summaries.collect::<Vec<_>>();
if !summaries.is_empty() {
let bullets = summaries
diff --git a/src/tools/cargo/src/cargo/core/resolver/encode.rs b/src/tools/cargo/src/cargo/core/resolver/encode.rs
index fcef1578a..34bfbe820 100644
--- a/src/tools/cargo/src/cargo/core/resolver/encode.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/encode.rs
@@ -209,7 +209,7 @@ impl EncodableResolve {
debug!("path dependency now missing {} v{}", pkg.name, pkg.version);
continue;
}
- Some(&source) => PackageId::new(&pkg.name, &pkg.version, source)?,
+ Some(&source) => PackageId::try_new(&pkg.name, &pkg.version, source)?,
};
// If a package has a checksum listed directly on it then record
@@ -365,7 +365,7 @@ impl EncodableResolve {
let mut unused_patches = Vec::new();
for pkg in self.patch.unused {
let id = match pkg.source.as_deref().or_else(|| path_deps.get(&pkg.name)) {
- Some(&src) => PackageId::new(&pkg.name, &pkg.version, src)?,
+ Some(&src) => PackageId::try_new(&pkg.name, &pkg.version, src)?,
None => continue,
};
unused_patches.push(id);
diff --git a/src/tools/cargo/src/cargo/core/resolver/errors.rs b/src/tools/cargo/src/cargo/core/resolver/errors.rs
index 15a006ffb..f3e72c51b 100644
--- a/src/tools/cargo/src/cargo/core/resolver/errors.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/errors.rs
@@ -140,9 +140,9 @@ pub(super) fn activation_error(
msg.push_str("` as well:\n");
msg.push_str(&describe_path_in_context(cx, p));
msg.push_str("\nOnly one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. ");
- msg.push_str("Try to adjust your dependencies so that only one package uses the links ='");
- msg.push_str(&*dep.package_name());
- msg.push_str("' value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links.");
+ msg.push_str("Try to adjust your dependencies so that only one package uses the `links = \"");
+ msg.push_str(link);
+ msg.push_str("\"` value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links.");
}
ConflictReason::MissingFeatures(features) => {
msg.push_str("\n\nthe package `");
@@ -228,7 +228,7 @@ pub(super) fn activation_error(
let mut new_dep = dep.clone();
new_dep.set_version_req(OptVersionReq::Any);
- let mut candidates = loop {
+ let candidates = loop {
match registry.query_vec(&new_dep, QueryKind::Exact) {
Poll::Ready(Ok(candidates)) => break candidates,
Poll::Ready(Err(e)) => return to_resolve_err(e),
@@ -239,6 +239,8 @@ pub(super) fn activation_error(
}
};
+ let mut candidates: Vec<_> = candidates.into_iter().map(|s| s.into_summary()).collect();
+
candidates.sort_unstable_by(|a, b| b.version().cmp(a.version()));
let mut msg = if !candidates.is_empty() {
@@ -303,7 +305,7 @@ pub(super) fn activation_error(
} else {
// Maybe the user mistyped the name? Like `dep-thing` when `Dep_Thing`
// was meant. So we try asking the registry for a `fuzzy` search for suggestions.
- let mut candidates = loop {
+ let candidates = loop {
match registry.query_vec(&new_dep, QueryKind::Fuzzy) {
Poll::Ready(Ok(candidates)) => break candidates,
Poll::Ready(Err(e)) => return to_resolve_err(e),
@@ -314,6 +316,8 @@ pub(super) fn activation_error(
}
};
+ let mut candidates: Vec<_> = candidates.into_iter().map(|s| s.into_summary()).collect();
+
candidates.sort_unstable_by_key(|a| a.name());
candidates.dedup_by(|a, b| a.name() == b.name());
let mut candidates: Vec<_> = candidates
diff --git a/src/tools/cargo/src/cargo/core/resolver/mod.rs b/src/tools/cargo/src/cargo/core/resolver/mod.rs
index ecb6f36e6..4b12f2cf3 100644
--- a/src/tools/cargo/src/cargo/core/resolver/mod.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/mod.rs
@@ -120,24 +120,12 @@ mod version_prefs;
///
/// * `config` - a location to print warnings and such, or `None` if no warnings
/// should be printed
-///
-/// * `check_public_visible_dependencies` - a flag for whether to enforce the restrictions
-/// introduced in the "public & private dependencies" RFC (1977). The current implementation
-/// makes sure that there is only one version of each name visible to each package.
-///
-/// But there are 2 stable ways to directly depend on different versions of the same name.
-/// 1. Use the renamed dependencies functionality
-/// 2. Use 'cfg({})' dependencies functionality
-///
-/// When we have a decision for how to implement is without breaking existing functionality
-/// this flag can be removed.
pub fn resolve(
summaries: &[(Summary, ResolveOpts)],
replacements: &[(PackageIdSpec, Dependency)],
registry: &mut dyn Registry,
version_prefs: &VersionPreferences,
config: Option<&Config>,
- check_public_visible_dependencies: bool,
) -> CargoResult<Resolve> {
let _p = profile::start("resolving");
let first_version = match config {
@@ -148,7 +136,7 @@ pub fn resolve(
};
let mut registry = RegistryQueryer::new(registry, replacements, version_prefs);
let cx = loop {
- let cx = Context::new(check_public_visible_dependencies);
+ let cx = Context::new();
let cx = activate_deps_loop(cx, &mut registry, summaries, first_version, config)?;
if registry.reset_pending() {
break cx;
@@ -286,12 +274,7 @@ fn activate_deps_loop(
let mut backtracked = false;
loop {
- let next = remaining_candidates.next(
- &mut conflicting_activations,
- &cx,
- &dep,
- parent.package_id(),
- );
+ let next = remaining_candidates.next(&mut conflicting_activations, &cx);
let (candidate, has_another) = next.ok_or(()).or_else(|_| {
// If we get here then our `remaining_candidates` was just
@@ -649,15 +632,6 @@ fn activate(
.link(candidate_pid, parent_pid)
// and associate dep with that edge
.insert(dep.clone());
- if let Some(public_dependency) = cx.public_dependency.as_mut() {
- public_dependency.add_edge(
- candidate_pid,
- parent_pid,
- dep.is_public(),
- cx.age,
- &cx.parents,
- );
- }
}
let activated = cx.flag_activated(&candidate, opts, parent)?;
@@ -772,8 +746,6 @@ impl RemainingCandidates {
&mut self,
conflicting_prev_active: &mut ConflictMap,
cx: &Context,
- dep: &Dependency,
- parent: PackageId,
) -> Option<(Summary, bool)> {
for b in self.remaining.by_ref() {
let b_id = b.package_id();
@@ -808,23 +780,6 @@ impl RemainingCandidates {
continue;
}
}
- // We may still have to reject do to a public dependency conflict. If one of any of our
- // ancestors that can see us already knows about a different crate with this name then
- // we have to reject this candidate. Additionally this candidate may already have been
- // activated and have public dependants of its own,
- // all of witch also need to be checked the same way.
- if let Some(public_dependency) = cx.public_dependency.as_ref() {
- if let Err(((c1, c2), c3)) =
- public_dependency.can_add_edge(b_id, parent, dep.is_public(), &cx.parents)
- {
- conflicting_prev_active.insert(c1.0, c1.1);
- conflicting_prev_active.insert(c2.0, c2.1);
- if let Some(c3) = c3 {
- conflicting_prev_active.insert(c3.0, c3.1);
- }
- continue;
- }
- }
// Well if we made it this far then we've got a valid dependency. We
// want this iterator to be inherently "peekable" so we don't
@@ -1001,12 +956,9 @@ fn find_candidate(
};
while let Some(mut frame) = backtrack_stack.pop() {
- let next = frame.remaining_candidates.next(
- &mut frame.conflicting_activations,
- &frame.context,
- &frame.dep,
- frame.parent.package_id(),
- );
+ let next = frame
+ .remaining_candidates
+ .next(&mut frame.conflicting_activations, &frame.context);
let Some((candidate, has_another)) = next else {
continue;
};
diff --git a/src/tools/cargo/src/cargo/core/resolver/resolve.rs b/src/tools/cargo/src/cargo/core/resolver/resolve.rs
index b401e9232..02f112166 100644
--- a/src/tools/cargo/src/cargo/core/resolver/resolve.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/resolve.rs
@@ -1,6 +1,6 @@
use super::encode::Metadata;
use crate::core::dependency::DepKind;
-use crate::core::{Dependency, PackageId, PackageIdSpec, Summary, Target};
+use crate::core::{Dependency, PackageId, PackageIdSpec, PackageIdSpecQuery, Summary, Target};
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::Graph;
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 0deef5565..1e4eacb59 100644
--- a/src/tools/cargo/src/cargo/core/resolver/version_prefs.rs
+++ b/src/tools/cargo/src/cargo/core/resolver/version_prefs.rs
@@ -6,7 +6,7 @@ use std::collections::{HashMap, HashSet};
use crate::core::{Dependency, PackageId, Summary};
use crate::util::interning::InternedString;
-use crate::util::RustVersion;
+use crate::util_schemas::manifest::RustVersion;
/// A collection of preferences for particular package versions.
///
@@ -58,10 +58,10 @@ impl VersionPreferences {
///
/// Sort order:
/// 1. Preferred packages
- /// 2. `first_version`, falling back to [`VersionPreferences::version_ordering`] when `None`
+ /// 2. [`VersionPreferences::max_rust_version`]
+ /// 3. `first_version`, falling back to [`VersionPreferences::version_ordering`] when `None`
///
/// Filtering:
- /// - [`VersionPreferences::max_rust_version`]
/// - `first_version`
pub fn sort_summaries(
&self,
@@ -76,9 +76,6 @@ 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());
@@ -87,6 +84,40 @@ impl VersionPreferences {
return previous_cmp;
}
+ if let Some(max_rust_version) = &self.max_rust_version {
+ match (a.rust_version(), b.rust_version()) {
+ // Fallback
+ (None, None) => {}
+ (Some(a), Some(b)) if a == b => {}
+ // Primary comparison
+ (Some(a), Some(b)) => {
+ let a_is_compat = a <= max_rust_version;
+ let b_is_compat = b <= max_rust_version;
+ match (a_is_compat, b_is_compat) {
+ (true, true) => {} // fallback
+ (false, false) => {} // fallback
+ (true, false) => return Ordering::Less,
+ (false, true) => return Ordering::Greater,
+ }
+ }
+ // Prioritize `None` over incompatible
+ (None, Some(b)) => {
+ if b <= max_rust_version {
+ return Ordering::Greater;
+ } else {
+ return Ordering::Less;
+ }
+ }
+ (Some(a), None) => {
+ if a <= max_rust_version {
+ return Ordering::Less;
+ } else {
+ return Ordering::Greater;
+ }
+ }
+ }
+ }
+
let cmp = a.version().cmp(b.version());
match first_version.unwrap_or(self.version_ordering) {
VersionOrdering::MaximumVersionsFirst => cmp.reverse(),
@@ -108,7 +139,7 @@ mod test {
fn pkgid(name: &str, version: &str) -> PackageId {
let src_id =
SourceId::from_url("registry+https://github.com/rust-lang/crates.io-index").unwrap();
- PackageId::new(name, version, src_id).unwrap()
+ PackageId::try_new(name, version, src_id).unwrap()
}
fn dep(name: &str, version: &str) -> Dependency {
@@ -226,8 +257,11 @@ mod test {
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.2.4", None),
+ summ("foo", "1.2.3", Some("1.60")),
+ summ("foo", "1.2.2", None),
+ summ("foo", "1.2.1", Some("1.50")),
+ summ("foo", "1.2.0", None),
summ("foo", "1.1.0", Some("1.40")),
summ("foo", "1.0.9", None),
];
@@ -236,14 +270,16 @@ mod test {
vp.sort_summaries(&mut summaries, None);
assert_eq!(
describe(&summaries),
- "foo/1.2.3, foo/1.1.0, foo/1.0.9".to_string()
+ "foo/1.2.1, foo/1.1.0, foo/1.2.4, foo/1.2.2, foo/1.2.0, foo/1.0.9, foo/1.2.3"
+ .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()
+ "foo/1.1.0, foo/1.2.1, foo/1.0.9, foo/1.2.0, foo/1.2.2, foo/1.2.4, foo/1.2.3"
+ .to_string()
);
}
}
diff --git a/src/tools/cargo/src/cargo/core/source_id.rs b/src/tools/cargo/src/cargo/core/source_id.rs
index e53b1704d..3b1cad942 100644
--- a/src/tools/cargo/src/cargo/core/source_id.rs
+++ b/src/tools/cargo/src/cargo/core/source_id.rs
@@ -1,4 +1,6 @@
+use crate::core::GitReference;
use crate::core::PackageId;
+use crate::core::SourceKind;
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};
@@ -82,38 +84,6 @@ impl fmt::Display for Precise {
}
}
-/// The possible kinds of code source.
-/// Along with [`SourceIdInner`], this fully defines the source.
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-enum SourceKind {
- /// A git repository.
- Git(GitReference),
- /// A local path.
- Path,
- /// A remote registry.
- Registry,
- /// A sparse registry.
- SparseRegistry,
- /// A local filesystem-based registry.
- LocalRegistry,
- /// A directory-based registry.
- Directory,
-}
-
-/// Information to find a specific commit in a Git repository.
-#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub enum GitReference {
- /// From a tag.
- Tag(String),
- /// From a branch.
- Branch(String),
- /// From a specific revision. Can be a commit hash (either short or full),
- /// or a named reference like `refs/pull/493/head`.
- Rev(String),
- /// The default branch of the repository, the reference named `HEAD`.
- DefaultBranch,
-}
-
/// Where the remote source key is defined.
///
/// The purpose of this is to provide better diagnostics for different sources of keys.
@@ -373,6 +343,10 @@ impl SourceId {
Some(self.inner.url.to_file_path().unwrap())
}
+ pub fn kind(&self) -> &SourceKind {
+ &self.inner.kind
+ }
+
/// Returns `true` if this source is from a registry (either local or not).
pub fn is_registry(self) -> bool {
matches!(
@@ -742,108 +716,6 @@ 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> {
- Some(self.cmp(other))
- }
-}
-
-/// Note that this is specifically not derived on `SourceKind` although the
-/// implementation here is very similar to what it might look like if it were
-/// otherwise derived.
-///
-/// The reason for this is somewhat obtuse. First of all the hash value of
-/// `SourceKind` makes its way into `~/.cargo/registry/index/github.com-XXXX`
-/// which means that changes to the hash means that all Rust users need to
-/// redownload the crates.io index and all their crates. If possible we strive
-/// to not change this to make this redownloading behavior happen as little as
-/// possible. How is this connected to `Ord` you might ask? That's a good
-/// question!
-///
-/// Since the beginning of time `SourceKind` has had `#[derive(Hash)]`. It for
-/// the longest time *also* derived the `Ord` and `PartialOrd` traits. In #8522,
-/// however, the implementation of `Ord` changed. This handwritten implementation
-/// forgot to sync itself with the originally derived implementation, namely
-/// placing git dependencies as sorted after all other dependencies instead of
-/// first as before.
-///
-/// This regression in #8522 (Rust 1.47) went unnoticed. When we switched back
-/// to a derived implementation in #9133 (Rust 1.52 beta) we only then ironically
-/// saw an issue (#9334). In #9334 it was observed that stable Rust at the time
-/// (1.51) was sorting git dependencies last, whereas Rust 1.52 beta would sort
-/// git dependencies first. This is because the `PartialOrd` implementation in
-/// 1.51 used #8522, the buggy implementation, which put git deps last. In 1.52
-/// it was (unknowingly) restored to the pre-1.47 behavior with git dependencies
-/// first.
-///
-/// Because the breakage was only witnessed after the original breakage, this
-/// trait implementation is preserving the "broken" behavior. Put a different way:
-///
-/// * Rust pre-1.47 sorted git deps first.
-/// * Rust 1.47 to Rust 1.51 sorted git deps last, a breaking change (#8522) that
-/// was never noticed.
-/// * Rust 1.52 restored the pre-1.47 behavior (#9133, without knowing it did
-/// so), and breakage was witnessed by actual users due to difference with
-/// 1.51.
-/// * Rust 1.52 (the source as it lives now) was fixed to match the 1.47-1.51
-/// behavior (#9383), which is now considered intentionally breaking from the
-/// pre-1.47 behavior.
-///
-/// Note that this was all discovered when Rust 1.53 was in nightly and 1.52 was
-/// in beta. #9133 was in both beta and nightly at the time of discovery. For
-/// 1.52 #9383 reverted #9133, meaning 1.52 is the same as 1.51. On nightly
-/// (1.53) #9397 was created to fix the regression introduced by #9133 relative
-/// to the current stable (1.51).
-///
-/// That's all a long winded way of saying "it's weird that git deps hash first
-/// and are sorted last, but it's the way it is right now". The author of this
-/// comment chose to handwrite the `Ord` implementation instead of the `Hash`
-/// implementation, but it's only required that at most one of them is
-/// hand-written because the other can be derived. Perhaps one day in
-/// the future someone can figure out how to remove this behavior.
-impl Ord for SourceKind {
- fn cmp(&self, other: &SourceKind) -> Ordering {
- match (self, other) {
- (SourceKind::Path, SourceKind::Path) => Ordering::Equal,
- (SourceKind::Path, _) => Ordering::Less,
- (_, SourceKind::Path) => Ordering::Greater,
-
- (SourceKind::Registry, SourceKind::Registry) => Ordering::Equal,
- (SourceKind::Registry, _) => Ordering::Less,
- (_, SourceKind::Registry) => Ordering::Greater,
-
- (SourceKind::SparseRegistry, SourceKind::SparseRegistry) => Ordering::Equal,
- (SourceKind::SparseRegistry, _) => Ordering::Less,
- (_, SourceKind::SparseRegistry) => Ordering::Greater,
-
- (SourceKind::LocalRegistry, SourceKind::LocalRegistry) => Ordering::Equal,
- (SourceKind::LocalRegistry, _) => Ordering::Less,
- (_, SourceKind::LocalRegistry) => Ordering::Greater,
-
- (SourceKind::Directory, SourceKind::Directory) => Ordering::Equal,
- (SourceKind::Directory, _) => Ordering::Less,
- (_, SourceKind::Directory) => Ordering::Greater,
-
- (SourceKind::Git(a), SourceKind::Git(b)) => a.cmp(b),
- }
- }
-}
-
/// A `Display`able view into a `SourceId` that will write it as a url
pub struct SourceIdAsUrl<'a> {
inner: &'a SourceIdInner,
@@ -873,73 +745,6 @@ impl<'a> fmt::Display for SourceIdAsUrl<'a> {
}
}
-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<'_>> {
- match self {
- GitReference::DefaultBranch => None,
- _ => Some(PrettyRef {
- inner: self,
- url_encoded,
- }),
- }
- }
-}
-
-/// A git reference that can be `Display`ed
-pub struct PrettyRef<'a> {
- inner: &'a GitReference,
- url_encoded: bool,
-}
-
-impl<'a> fmt::Display for PrettyRef<'a> {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let value: &str;
- match self.inner {
- GitReference::Branch(s) => {
- write!(f, "branch=")?;
- value = s;
- }
- GitReference::Tag(s) => {
- write!(f, "tag=")?;
- value = s;
- }
- GitReference::Rev(s) => {
- write!(f, "rev=")?;
- value = s;
- }
- GitReference::DefaultBranch => unreachable!(),
- }
- if self.url_encoded {
- for value in url::form_urlencoded::byte_serialize(value.as_bytes()) {
- write!(f, "{value}")?;
- }
- } else {
- write!(f, "{value}")?;
- }
- Ok(())
- }
-}
-
impl KeyOf {
/// Gets the underlying key.
fn key(&self) -> &str {
diff --git a/src/tools/cargo/src/cargo/core/summary.rs b/src/tools/cargo/src/cargo/core/summary.rs
index 243f6b398..2137d6332 100644
--- a/src/tools/cargo/src/cargo/core/summary.rs
+++ b/src/tools/cargo/src/cargo/core/summary.rs
@@ -1,7 +1,8 @@
use crate::core::{Dependency, PackageId, SourceId};
use crate::util::interning::InternedString;
use crate::util::CargoResult;
-use crate::util::RustVersion;
+use crate::util_schemas::manifest::FeatureName;
+use crate::util_schemas::manifest::RustVersion;
use anyhow::bail;
use semver::Version;
use std::collections::{BTreeMap, HashMap, HashSet};
@@ -49,7 +50,7 @@ impl Summary {
)
}
}
- let feature_map = build_feature_map(pkg_id, features, &dependencies)?;
+ let feature_map = build_feature_map(features, &dependencies)?;
Ok(Summary {
inner: Rc::new(Inner {
package_id: pkg_id,
@@ -140,7 +141,6 @@ impl Hash for Summary {
/// Checks features for errors, bailing out a CargoResult:Err if invalid,
/// and creates FeatureValues for each feature.
fn build_feature_map(
- pkg_id: PackageId,
features: &BTreeMap<InternedString, Vec<InternedString>>,
dependencies: &[Dependency],
) -> CargoResult<FeatureMap> {
@@ -191,19 +191,7 @@ fn build_feature_map(
// Validate features are listed properly.
for (feature, fvs) in &map {
- if feature.starts_with("dep:") {
- bail!(
- "feature named `{}` is not allowed to start with `dep:`",
- feature
- );
- }
- if feature.contains('/') {
- bail!(
- "feature named `{}` is not allowed to contain slashes",
- feature
- );
- }
- validate_feature_name(pkg_id, feature)?;
+ FeatureName::new(feature)?;
for fv in fvs {
// Find data for the referenced dependency...
let dep_data = {
@@ -429,68 +417,3 @@ 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)) {
- bail!(
- "invalid character `{}` in feature `{}` in package {}, \
- the first character must be a Unicode XID start character or digit \
- (most letters or `_` or `0` to `9`)",
- ch,
- name,
- pkg_id
- );
- }
- }
- for ch in chars {
- 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 `.` \
- (numbers, `+`, `-`, `_`, `.`, or most letters)",
- ch,
- name,
- pkg_id
- );
- }
- }
- Ok(())
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
- use crate::sources::CRATES_IO_INDEX;
- use crate::util::into_url::IntoUrl;
-
- use crate::core::SourceId;
-
- #[test]
- fn valid_feature_names() {
- let loc = CRATES_IO_INDEX.into_url().unwrap();
- let source_id = SourceId::for_registry(&loc).unwrap();
- let pkg_id = PackageId::new("foo", "1.0.0", source_id).unwrap();
-
- assert!(validate_feature_name(pkg_id, "c++17").is_ok());
- assert!(validate_feature_name(pkg_id, "128bit").is_ok());
- assert!(validate_feature_name(pkg_id, "_foo").is_ok());
- assert!(validate_feature_name(pkg_id, "feat-name").is_ok());
- assert!(validate_feature_name(pkg_id, "feat_name").is_ok());
- assert!(validate_feature_name(pkg_id, "foo.bar").is_ok());
-
- assert!(validate_feature_name(pkg_id, "+foo").is_err());
- assert!(validate_feature_name(pkg_id, "-foo").is_err());
- assert!(validate_feature_name(pkg_id, ".foo").is_err());
- assert!(validate_feature_name(pkg_id, "foo:bar").is_err());
- assert!(validate_feature_name(pkg_id, "foo?").is_err());
- 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 4667c8029..b933c6173 100644
--- a/src/tools/cargo/src/cargo/core/workspace.rs
+++ b/src/tools/cargo/src/cargo/core/workspace.rs
@@ -15,18 +15,19 @@ use crate::core::features::Features;
use crate::core::registry::PackageRegistry;
use crate::core::resolver::features::CliFeatures;
use crate::core::resolver::ResolveBehavior;
-use crate::core::{Dependency, Edition, FeatureValue, PackageId, PackageIdSpec};
+use crate::core::{
+ Dependency, Edition, FeatureValue, PackageId, PackageIdSpec, PackageIdSpecQuery,
+};
use crate::core::{EitherManifest, Package, SourceId, VirtualManifest};
use crate::ops;
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, schema::InheritableFields, schema::TomlDependency, schema::TomlProfiles,
-};
-use crate::util::RustVersion;
+use crate::util::toml::{read_manifest, InheritableFields};
use crate::util::{config::ConfigRelativePath, Config, Filesystem, IntoUrl};
+use crate::util_schemas::manifest::RustVersion;
+use crate::util_schemas::manifest::{TomlDependency, TomlProfiles};
use cargo_util::paths;
use cargo_util::paths::normalize_path;
use pathdiff::diff_paths;
@@ -437,7 +438,8 @@ impl<'cfg> Workspace<'cfg> {
url,
deps.iter()
.map(|(name, dep)| {
- dep.to_dependency_split(
+ crate::util::toml::to_dependency(
+ dep,
name,
source,
&mut nested_paths,
diff --git a/src/tools/cargo/src/cargo/lib.rs b/src/tools/cargo/src/cargo/lib.rs
index 6947642c9..6d7468ca3 100644
--- a/src/tools/cargo/src/cargo/lib.rs
+++ b/src/tools/cargo/src/cargo/lib.rs
@@ -70,6 +70,11 @@
//! This is not directly depended upon with a `path` dependency; cargo uses the version from crates.io.
//! It is intended to be versioned and published independently of Rust's release system.
//! Whenever a change needs to be made, bump the version in Cargo.toml and `cargo publish` it manually, and then update cargo's `Cargo.toml` to depend on the new version.
+//! - [`rustfix`](https://crates.io/crates/rustfix)
+//! ([nightly docs](https://doc.rust-lang.org/nightly/nightly-rustc/rustfix)):
+//! This defines structures that represent fix suggestions from rustc,
+//! as well as generates "fixed" code from suggestions.
+//! Operations in `rustfix` are all in memory and won't write to disks.
//! - [`cargo-test-support`](https://github.com/rust-lang/cargo/tree/master/crates/cargo-test-support)
//! ([nightly docs](https://doc.rust-lang.org/nightly/nightly-rustc/cargo_test_support/index.html)):
//! This contains a variety of code to support writing tests
@@ -93,7 +98,7 @@
//! Files that interact with cargo include
//!
//! - Package
-//! - `Cargo.toml`: User-written project manifest, loaded with [`util::toml::schema::TomlManifest`] and then
+//! - `Cargo.toml`: User-written project manifest, loaded with [`util_schemas::manifest::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`]
@@ -129,20 +134,6 @@
//! [The Cargo Book]: https://doc.rust-lang.org/cargo/
//! [Cargo Contributor Guide]: https://doc.crates.io/contrib/
-// TODO: consider removing these lint attributes when `-Zlints` hits stable.
-// For various reasons, some idioms are still allow'ed, but we would like to
-// test and enforce them.
-#![warn(rust_2018_idioms)]
-// Due to some of the default clippy lints being somewhat subjective and not
-// necessarily an improvement, we prefer to not use them at this time.
-#![allow(clippy::all)]
-#![warn(clippy::disallowed_methods)]
-#![warn(clippy::self_named_module_files)]
-#![warn(clippy::print_stdout)]
-#![warn(clippy::print_stderr)]
-#![warn(clippy::dbg_macro)]
-#![allow(rustdoc::private_intra_doc_links)]
-
use crate::core::shell::Verbosity::Verbose;
use crate::core::Shell;
use anyhow::Error;
@@ -161,6 +152,7 @@ pub mod core;
pub mod ops;
pub mod sources;
pub mod util;
+pub mod util_schemas;
pub mod util_semver;
mod version;
diff --git a/src/tools/cargo/src/cargo/ops/cargo_add/crate_spec.rs b/src/tools/cargo/src/cargo/ops/cargo_add/crate_spec.rs
index f07e2fae5..65c58314f 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_add/crate_spec.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_add/crate_spec.rs
@@ -4,7 +4,7 @@ use anyhow::Context as _;
use super::Dependency;
use crate::util::toml_mut::dependency::RegistrySource;
-use crate::util::validate_package_name;
+use crate::util_schemas::manifest::PackageName;
use crate::CargoResult;
/// User-specified crate
@@ -12,7 +12,6 @@ use crate::CargoResult;
/// This can be a
/// - Name (e.g. `docopt`)
/// - Name and a version req (e.g. `docopt@^0.8`)
-/// - Path
#[derive(Debug)]
pub struct CrateSpec {
/// Crate name
@@ -29,7 +28,7 @@ impl CrateSpec {
.map(|(n, v)| (n, Some(v)))
.unwrap_or((pkg_id, None));
- validate_package_name(name, "dependency name", "")?;
+ PackageName::new(name)?;
if let Some(version) = version {
semver::VersionReq::parse(version)
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 39e37b156..9be290b48 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_add/mod.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_add/mod.rs
@@ -7,6 +7,7 @@ use std::collections::BTreeSet;
use std::collections::VecDeque;
use std::fmt::Write;
use std::path::Path;
+use std::str::FromStr;
use anyhow::Context as _;
use cargo_util::paths;
@@ -34,7 +35,7 @@ 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;
+use crate::util_schemas::manifest::RustVersion;
use crate::CargoResult;
use crate::Config;
use crate_spec::CrateSpec;
@@ -196,6 +197,20 @@ pub fn add(workspace: &Workspace<'_>, options: &AddOptions<'_>) -> CargoResult<(
print_dep_table_msg(&mut options.config.shell(), &dep)?;
manifest.insert_into_table(&dep_table, &dep)?;
+ if dep.optional == Some(true) {
+ let is_namespaced_features_supported =
+ check_rust_version_for_optional_dependency(options.spec.rust_version())?;
+ if is_namespaced_features_supported {
+ let dep_key = dep.toml_key();
+ if !manifest.is_explicit_dep_activation(dep_key) {
+ let table = manifest.get_table_mut(&[String::from("features")])?;
+ let dep_name = dep.rename.as_deref().unwrap_or(&dep.name);
+ let new_feature: toml_edit::Value =
+ [format!("dep:{dep_name}")].iter().collect();
+ table[dep_key] = toml_edit::value(new_feature);
+ }
+ }
+ }
manifest.gc_dep(dep.toml_key());
}
@@ -244,6 +259,9 @@ pub struct DepOp {
/// Whether dependency is optional
pub optional: Option<bool>,
+ /// Whether dependency is public
+ pub public: Option<bool>,
+
/// Registry for looking up dependency version
pub registry: Option<String>,
@@ -469,6 +487,26 @@ fn check_invalid_ws_keys(toml_key: &str, arg: &DepOp) -> CargoResult<()> {
Ok(())
}
+/// When the `--optional` option is added using `cargo add`, we need to
+/// check the current rust-version. As the `dep:` syntax is only avaliable
+/// starting with Rust 1.60.0
+///
+/// `true` means that the rust-version is None or the rust-version is higher
+/// than the version needed.
+///
+/// Note: Previous versions can only use the implicit feature name.
+fn check_rust_version_for_optional_dependency(
+ rust_version: Option<&RustVersion>,
+) -> CargoResult<bool> {
+ match rust_version {
+ Some(version) => {
+ let syntax_support_version = RustVersion::from_str("1.60.0")?;
+ Ok(&syntax_support_version <= version)
+ }
+ None => Ok(true),
+ }
+}
+
/// Provide the existing dependency for the target table
///
/// If it doesn't exist but exists in another table, let's use that as most likely users
@@ -545,7 +583,7 @@ fn get_latest_dependency(
unreachable!("registry dependencies required, found a workspace dependency");
}
MaybeWorkspace::Other(query) => {
- let mut possibilities = loop {
+ let possibilities = loop {
match registry.query_vec(&query, QueryKind::Fuzzy) {
std::task::Poll::Ready(res) => {
break res?;
@@ -554,6 +592,11 @@ fn get_latest_dependency(
}
};
+ let mut possibilities: Vec<_> = possibilities
+ .into_iter()
+ .map(|s| s.into_summary())
+ .collect();
+
possibilities.sort_by_key(|s| {
// Fallback to a pre-release if no official release is available by sorting them as
// less.
@@ -671,6 +714,12 @@ fn select_package(
std::task::Poll::Pending => registry.block_until_ready()?,
}
};
+
+ let possibilities: Vec<_> = possibilities
+ .into_iter()
+ .map(|s| s.into_summary())
+ .collect();
+
match possibilities.len() {
0 => {
let source = dependency
@@ -747,6 +796,13 @@ fn populate_dependency(mut dependency: Dependency, arg: &DepOp) -> Dependency {
dependency.optional = None;
}
}
+ if let Some(value) = arg.public {
+ if value {
+ dependency.public = Some(true);
+ } else {
+ dependency.public = None;
+ }
+ }
if let Some(value) = arg.default_features {
if value {
dependency.default_features = None;
@@ -889,6 +945,7 @@ fn populate_available_features(
// in the lock file for a given version requirement.
let lowest_common_denominator = possibilities
.iter()
+ .map(|s| s.as_summary())
.min_by_key(|s| {
// Fallback to a pre-release if no official release is available by sorting them as
// more.
@@ -933,6 +990,9 @@ fn print_action_msg(shell: &mut Shell, dep: &DependencyUI, section: &[String]) -
if dep.optional().unwrap_or(false) {
write!(message, " optional")?;
}
+ if dep.public().unwrap_or(false) {
+ write!(message, " public")?;
+ }
let section = if section.len() == 1 {
section[0].clone()
} else {
diff --git a/src/tools/cargo/src/cargo/ops/cargo_clean.rs b/src/tools/cargo/src/cargo/ops/cargo_clean.rs
index 6f58b8bdc..4add5d863 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_clean.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_clean.rs
@@ -1,6 +1,6 @@
use crate::core::compiler::{CompileKind, CompileMode, Layout, RustcTargetData};
use crate::core::profiles::Profiles;
-use crate::core::{PackageIdSpec, TargetKind, Workspace};
+use crate::core::{PackageIdSpec, PackageIdSpecQuery, TargetKind, Workspace};
use crate::ops;
use crate::util::edit_distance;
use crate::util::errors::CargoResult;
@@ -389,7 +389,7 @@ impl<'cfg> CleanContext<'cfg> {
Ok(())
}
- fn display_summary(&self) -> CargoResult<()> {
+ pub fn display_summary(&self) -> CargoResult<()> {
let status = if self.dry_run { "Summary" } else { "Removed" };
let byte_count = if self.total_bytes_removed == 0 {
String::new()
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 94c6cf9de..3522ef9d3 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs
@@ -153,6 +153,7 @@ pub fn compile_ws<'a>(
unit_graph::emit_serialized_unit_graph(&bcx.roots, &bcx.unit_graph, ws.config())?;
return Compilation::new(&bcx);
}
+ crate::core::gc::auto_gc(bcx.config);
let _p = profile::start("compiling");
let cx = Context::new(&bcx)?;
cx.compile(exec)
diff --git a/src/tools/cargo/src/cargo/ops/cargo_compile/packages.rs b/src/tools/cargo/src/cargo/ops/cargo_compile/packages.rs
index 2d14d60a6..439b87111 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_compile/packages.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_compile/packages.rs
@@ -47,7 +47,7 @@ impl Packages {
Packages::All => ws
.members()
.map(Package::package_id)
- .map(PackageIdSpec::from_package_id)
+ .map(|id| id.to_spec())
.collect(),
Packages::OptOut(opt_out) => {
let (mut patterns, mut names) = opt_patterns_and_names(opt_out)?;
@@ -57,7 +57,7 @@ impl Packages {
!names.remove(pkg.name().as_str()) && !match_patterns(pkg, &mut patterns)
})
.map(Package::package_id)
- .map(PackageIdSpec::from_package_id)
+ .map(|id| id.to_spec())
.collect();
let warn = |e| ws.config().shell().warn(e);
emit_package_not_found(ws, names, true).or_else(warn)?;
@@ -65,7 +65,7 @@ impl Packages {
specs
}
Packages::Packages(packages) if packages.is_empty() => {
- vec![PackageIdSpec::from_package_id(ws.current()?.package_id())]
+ vec![ws.current()?.package_id().to_spec()]
}
Packages::Packages(opt_in) => {
let (mut patterns, packages) = opt_patterns_and_names(opt_in)?;
@@ -78,7 +78,7 @@ impl Packages {
.members()
.filter(|pkg| match_patterns(pkg, &mut patterns))
.map(Package::package_id)
- .map(PackageIdSpec::from_package_id);
+ .map(|id| id.to_spec());
specs.extend(matched_pkgs);
}
emit_pattern_not_found(ws, patterns, false)?;
@@ -87,7 +87,7 @@ impl Packages {
Packages::Default => ws
.default_members()
.map(Package::package_id)
- .map(PackageIdSpec::from_package_id)
+ .map(|id| id.to_spec())
.collect(),
};
if specs.is_empty() {
@@ -195,7 +195,7 @@ fn opt_patterns_and_names(
let mut opt_patterns = Vec::new();
let mut opt_names = BTreeSet::new();
for x in opt.iter() {
- if is_glob_pattern(x) {
+ if PackageIdSpec::parse(x).is_err() && is_glob_pattern(x) {
opt_patterns.push((build_glob(x)?, false));
} else {
opt_names.insert(String::as_str(x));
diff --git a/src/tools/cargo/src/cargo/ops/cargo_fetch.rs b/src/tools/cargo/src/cargo/ops/cargo_fetch.rs
index 6acdbddef..ac2b60aab 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_fetch.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_fetch.rs
@@ -76,6 +76,7 @@ pub fn fetch<'a>(
}
packages.get_many(to_download)?;
+ crate::core::gc::auto_gc(config);
Ok((resolve, packages))
}
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 a16d6d403..1bba64925 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_generate_lockfile.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_generate_lockfile.rs
@@ -1,6 +1,6 @@
use crate::core::registry::PackageRegistry;
use crate::core::resolver::features::{CliFeatures, HasDevUnits};
-use crate::core::{PackageId, PackageIdSpec};
+use crate::core::{PackageId, PackageIdSpec, PackageIdSpecQuery};
use crate::core::{Resolve, SourceId, Workspace};
use crate::ops;
use crate::util::cache_lock::CacheLockMode;
@@ -122,6 +122,26 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
}
}
+ // Mirror `--workspace` and never avoid workspace members.
+ // Filtering them out here so the above processes them normally
+ // so their dependencies can be updated as requested
+ to_avoid = to_avoid
+ .into_iter()
+ .filter(|id| {
+ for package in ws.members() {
+ let member_id = package.package_id();
+ // Skip checking the `version` because `previous_resolve` might have a stale
+ // value.
+ // When dealing with workspace members, the other fields should be a
+ // sufficiently unique match.
+ if id.name() == member_id.name() && id.source_id() == member_id.source_id() {
+ return false;
+ }
+ }
+ true
+ })
+ .collect();
+
registry.add_sources(sources)?;
}
diff --git a/src/tools/cargo/src/cargo/ops/cargo_install.rs b/src/tools/cargo/src/cargo/ops/cargo_install.rs
index 16027233e..0d7836bc2 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_install.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_install.rs
@@ -4,9 +4,7 @@ use std::sync::Arc;
use std::{env, fs};
use crate::core::compiler::{CompileKind, DefaultExecutor, Executor, UnitOutput};
-use crate::core::{
- Dependency, Edition, Package, PackageId, PackageIdSpec, SourceId, Target, Workspace,
-};
+use crate::core::{Dependency, Edition, Package, PackageId, SourceId, Target, Workspace};
use crate::ops::{common_for_install_and_uninstall::*, FilterRule};
use crate::ops::{CompileFilter, Packages};
use crate::sources::source::Source;
@@ -206,7 +204,7 @@ impl<'cfg> InstallablePackage<'cfg> {
// For cargo install tracking, we retain the source git url in `pkg`, but for the build spec
// we need to unconditionally use `ws.current()` to correctly address the path where we
// locally cloned that repo.
- let pkgidspec = PackageIdSpec::from_package_id(ws.current()?.package_id());
+ let pkgidspec = ws.current()?.package_id().to_spec();
opts.spec = Packages::Packages(vec![pkgidspec.to_string()]);
if from_cwd {
diff --git a/src/tools/cargo/src/cargo/ops/cargo_new.rs b/src/tools/cargo/src/cargo/ops/cargo_new.rs
index 1c06b5f82..57c7e268e 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_new.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_new.rs
@@ -4,6 +4,7 @@ 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 crate::util_schemas::manifest::PackageName;
use anyhow::{anyhow, Context};
use cargo_util::paths::{self, write_atomic};
use serde::de;
@@ -180,7 +181,7 @@ fn check_name(
};
let bin_help = || {
let mut help = String::from(name_help);
- if has_bin {
+ if has_bin && !name.is_empty() {
help.push_str(&format!(
"\n\
If you need a binary with the name \"{name}\", use a valid package \
@@ -197,7 +198,10 @@ fn check_name(
}
help
};
- restricted_names::validate_package_name(name, "package name", &bin_help())?;
+ PackageName::new(name).map_err(|err| {
+ let help = bin_help();
+ anyhow::anyhow!("{err}{help}")
+ })?;
if restricted_names::is_keyword(name) {
anyhow::bail!(
diff --git a/src/tools/cargo/src/cargo/ops/cargo_package.rs b/src/tools/cargo/src/cargo/ops/cargo_package.rs
index 6ac09dc77..2ff0187fe 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_package.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_package.rs
@@ -7,6 +7,7 @@ use std::sync::Arc;
use std::task::Poll;
use crate::core::compiler::{BuildConfig, CompileMode, DefaultExecutor, Executor};
+use crate::core::manifest::Target;
use crate::core::resolver::CliFeatures;
use crate::core::{registry::PackageRegistry, resolver::HasDevUnits};
use crate::core::{Feature, Shell, Verbosity, Workspace};
@@ -15,7 +16,7 @@ use crate::sources::PathSource;
use crate::util::cache_lock::CacheLockMode;
use crate::util::config::JobsConfig;
use crate::util::errors::CargoResult;
-use crate::util::toml::schema::TomlManifest;
+use crate::util::toml::{prepare_for_publish, to_real_manifest};
use crate::util::{self, human_readable_bytes, restricted_names, Config, FileLock};
use crate::{drop_println, ops};
use anyhow::Context as _;
@@ -331,6 +332,23 @@ fn build_ar_list(
warn_on_nonexistent_file(&pkg, &readme_path, "readme", &ws)?;
}
}
+
+ for t in pkg
+ .manifest()
+ .targets()
+ .iter()
+ .filter(|t| t.is_custom_build())
+ {
+ if let Some(custome_build_path) = t.src_path().path() {
+ let abs_custome_build_path =
+ paths::normalize_path(&pkg.root().join(custome_build_path));
+ if !abs_custome_build_path.is_file() || !abs_custome_build_path.starts_with(pkg.root())
+ {
+ error_custom_build_file_not_in_package(pkg, &abs_custome_build_path, t)?;
+ }
+ }
+ }
+
result.sort_unstable_by(|a, b| a.rel_path.cmp(&b.rel_path));
Ok(result)
@@ -405,20 +423,42 @@ fn warn_on_nonexistent_file(
))
}
+fn error_custom_build_file_not_in_package(
+ pkg: &Package,
+ path: &Path,
+ target: &Target,
+) -> CargoResult<Vec<ArchiveFile>> {
+ let tip = {
+ let description_name = target.description_named();
+ if path.is_file() {
+ format!("the source file of {description_name} doesn't appear to be a path inside of the package.\n\
+ It is at `{}`, whereas the root the package is `{}`.\n",
+ path.display(), pkg.root().display()
+ )
+ } else {
+ format!("the source file of {description_name} doesn't appear to exist.\n",)
+ }
+ };
+ let msg = format!(
+ "{}\
+ This may cause issue during packaging, as modules resolution and resources included via macros are often relative to the path of source files.\n\
+ Please update the `build` setting in the manifest at `{}` and point to a path inside the root of the package.",
+ tip, pkg.manifest_path().display()
+ );
+ anyhow::bail!(msg)
+}
+
/// Construct `Cargo.lock` for the package to be published.
fn build_lock(ws: &Workspace<'_>, orig_pkg: &Package) -> CargoResult<String> {
let config = ws.config();
let orig_resolve = ops::load_pkg_lockfile(ws)?;
// Convert Package -> TomlManifest -> Manifest -> Package
- let toml_manifest = orig_pkg
- .manifest()
- .original()
- .prepare_for_publish(ws, orig_pkg.root())?;
+ let toml_manifest = prepare_for_publish(orig_pkg.manifest().original(), 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)?;
+ 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();
diff --git a/src/tools/cargo/src/cargo/ops/cargo_pkgid.rs b/src/tools/cargo/src/cargo/ops/cargo_pkgid.rs
index bbae154a7..4e81e741f 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_pkgid.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_pkgid.rs
@@ -1,4 +1,4 @@
-use crate::core::{PackageIdSpec, Workspace};
+use crate::core::{PackageIdSpec, PackageIdSpecQuery, Workspace};
use crate::ops;
use crate::util::CargoResult;
@@ -11,5 +11,5 @@ pub fn pkgid(ws: &Workspace<'_>, spec: Option<&str>) -> CargoResult<PackageIdSpe
Some(spec) => PackageIdSpec::query_str(spec, resolve.iter())?,
None => ws.current()?.package_id(),
};
- Ok(PackageIdSpec::from_package_id(pkgid))
+ Ok(pkgid.to_spec())
}
diff --git a/src/tools/cargo/src/cargo/ops/cargo_uninstall.rs b/src/tools/cargo/src/cargo/ops/cargo_uninstall.rs
index 1f22e191e..7b45a69b4 100644
--- a/src/tools/cargo/src/cargo/ops/cargo_uninstall.rs
+++ b/src/tools/cargo/src/cargo/ops/cargo_uninstall.rs
@@ -1,12 +1,11 @@
use crate::core::PackageId;
-use crate::core::{PackageIdSpec, SourceId};
+use crate::core::{PackageIdSpec, PackageIdSpecQuery, SourceId};
use crate::ops::common_for_install_and_uninstall::*;
use crate::sources::PathSource;
use crate::util::errors::CargoResult;
use crate::util::Config;
use crate::util::Filesystem;
use anyhow::bail;
-use cargo_util::paths;
use std::collections::BTreeSet;
use std::env;
@@ -103,7 +102,6 @@ fn uninstall_pkgid(
bins: &[String],
config: &Config,
) -> CargoResult<()> {
- let mut to_remove = Vec::new();
let installed = match tracker.installed_bins(pkgid) {
Some(bins) => bins.clone(),
None => bail!("package `{}` is not installed", pkgid),
@@ -137,19 +135,18 @@ fn uninstall_pkgid(
}
}
- if bins.is_empty() {
- to_remove.extend(installed.iter().map(|b| dst.join(b)));
- tracker.remove(pkgid, &installed);
- } else {
- for bin in bins.iter() {
- to_remove.push(dst.join(bin));
+ let to_remove = {
+ if bins.is_empty() {
+ installed
+ } else {
+ bins
}
- tracker.remove(pkgid, &bins);
- }
- tracker.save()?;
+ };
+
for bin in to_remove {
- config.shell().status("Removing", bin.display())?;
- paths::remove_file(bin)?;
+ let bin_path = dst.join(&bin);
+ config.shell().status("Removing", bin_path.display())?;
+ tracker.remove_bin_then_save(pkgid, &bin, &bin_path)?;
}
Ok(())
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 d1f9152be..e678a64df 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
@@ -7,6 +7,7 @@ use std::rc::Rc;
use std::task::Poll;
use anyhow::{bail, format_err, Context as _};
+use cargo_util::paths;
use ops::FilterRule;
use serde::{Deserialize, Serialize};
@@ -319,6 +320,20 @@ impl InstallTracker {
self.v1.remove(pkg_id, bins);
self.v2.remove(pkg_id, bins);
}
+
+ /// Remove a bin after it successfully had been removed in disk and then save the tracker at last.
+ pub fn remove_bin_then_save(
+ &mut self,
+ pkg_id: PackageId,
+ bin: &str,
+ bin_path: &PathBuf,
+ ) -> CargoResult<()> {
+ paths::remove_file(bin_path)?;
+ self.v1.remove_bin(pkg_id, bin);
+ self.v2.remove_bin(pkg_id, bin);
+ self.save()?;
+ Ok(())
+ }
}
impl CrateListingV1 {
@@ -359,6 +374,17 @@ impl CrateListingV1 {
}
}
+ fn remove_bin(&mut self, pkg_id: PackageId, bin: &str) {
+ let mut installed = match self.v1.entry(pkg_id) {
+ btree_map::Entry::Occupied(e) => e,
+ btree_map::Entry::Vacant(..) => panic!("v1 unexpected missing `{}`", pkg_id),
+ };
+ installed.get_mut().remove(bin);
+ if installed.get().is_empty() {
+ installed.remove();
+ }
+ }
+
fn save(&self, lock: &FileLock) -> CargoResult<()> {
let mut file = lock.file();
file.seek(SeekFrom::Start(0))?;
@@ -468,6 +494,17 @@ impl CrateListingV2 {
}
}
+ fn remove_bin(&mut self, pkg_id: PackageId, bin: &str) {
+ let mut info_entry = match self.installs.entry(pkg_id) {
+ btree_map::Entry::Occupied(e) => e,
+ btree_map::Entry::Vacant(..) => panic!("v1 unexpected missing `{}`", pkg_id),
+ };
+ info_entry.get_mut().bins.remove(bin);
+ if info_entry.get().bins.is_empty() {
+ info_entry.remove();
+ }
+ }
+
fn save(&self, lock: &FileLock) -> CargoResult<()> {
let mut file = lock.file();
file.seek(SeekFrom::Start(0))?;
@@ -552,7 +589,11 @@ where
Poll::Pending => source.block_until_ready()?,
}
};
- match deps.iter().max_by_key(|p| p.package_id()) {
+ match deps
+ .iter()
+ .map(|s| s.as_summary())
+ .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();
@@ -571,6 +612,7 @@ where
};
if let Some(alt) = msrv_deps
.iter()
+ .map(|s| s.as_summary())
.filter(|summary| {
summary
.rust_version()
@@ -608,7 +650,7 @@ cannot install package `{name} {ver}`, it requires rustc {msrv} or newer, while
let is_yanked: bool = if dep.version_req().is_exact() {
let version: String = dep.version_req().to_string();
if let Ok(pkg_id) =
- PackageId::new(dep.package_name(), &version[1..], source.source_id())
+ PackageId::try_new(dep.package_name(), &version[1..], source.source_id())
{
source.invalidate_cache();
loop {
diff --git a/src/tools/cargo/src/cargo/ops/mod.rs b/src/tools/cargo/src/cargo/ops/mod.rs
index 13613eaf6..76fa91d25 100644
--- a/src/tools/cargo/src/cargo/ops/mod.rs
+++ b/src/tools/cargo/src/cargo/ops/mod.rs
@@ -1,6 +1,6 @@
use crate::sources::CRATES_IO_DOMAIN;
-pub use self::cargo_clean::{clean, CleanOptions};
+pub use self::cargo_clean::{clean, CleanContext, CleanOptions};
pub use self::cargo_compile::{
compile, compile_with_exec, compile_ws, create_bcx, print, resolve_all_features, CompileOptions,
};
diff --git a/src/tools/cargo/src/cargo/ops/registry/publish.rs b/src/tools/cargo/src/cargo/ops/registry/publish.rs
index 201907bb2..2313792c8 100644
--- a/src/tools/cargo/src/cargo/ops/registry/publish.rs
+++ b/src/tools/cargo/src/cargo/ops/registry/publish.rs
@@ -21,6 +21,7 @@ use crate::core::manifest::ManifestMetadata;
use crate::core::resolver::CliFeatures;
use crate::core::Dependency;
use crate::core::Package;
+use crate::core::PackageIdSpecQuery;
use crate::core::SourceId;
use crate::core::Workspace;
use crate::ops;
diff --git a/src/tools/cargo/src/cargo/ops/resolve.rs b/src/tools/cargo/src/cargo/ops/resolve.rs
index 8ca72f77c..5421d0572 100644
--- a/src/tools/cargo/src/cargo/ops/resolve.rs
+++ b/src/tools/cargo/src/cargo/ops/resolve.rs
@@ -64,14 +64,15 @@ use crate::core::resolver::{
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::core::{
+ GitReference, PackageId, PackageIdSpec, PackageIdSpecQuery, 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};
+use crate::util_schemas::manifest::RustVersion;
use anyhow::Context as _;
use std::collections::{HashMap, HashSet};
use tracing::{debug, trace};
@@ -512,9 +513,6 @@ pub fn resolve_with_previous<'cfg>(
registry,
&version_prefs,
Some(ws.config()),
- ws.unstable_features()
- .require(Feature::public_dependency())
- .is_ok(),
)?;
let patches: Vec<_> = registry
.patches()
@@ -530,6 +528,9 @@ pub fn resolve_with_previous<'cfg>(
if let Some(previous) = previous {
resolved.merge_from(previous)?;
}
+ let config = ws.config();
+ let mut deferred = config.deferred_global_last_use()?;
+ deferred.save_no_error(config);
Ok(resolved)
}
diff --git a/src/tools/cargo/src/cargo/ops/tree/mod.rs b/src/tools/cargo/src/cargo/ops/tree/mod.rs
index ce3bae8cc..6928ec5f9 100644
--- a/src/tools/cargo/src/cargo/ops/tree/mod.rs
+++ b/src/tools/cargo/src/cargo/ops/tree/mod.rs
@@ -4,7 +4,7 @@ use self::format::Pattern;
use crate::core::compiler::{CompileKind, RustcTargetData};
use crate::core::dependency::DepKind;
use crate::core::resolver::{features::CliFeatures, ForceAllTargets, HasDevUnits};
-use crate::core::{Package, PackageId, PackageIdSpec, Workspace};
+use crate::core::{Package, PackageId, PackageIdSpec, PackageIdSpecQuery, Workspace};
use crate::ops::{self, Packages};
use crate::util::{CargoResult, Config};
use crate::{drop_print, drop_println};
diff --git a/src/tools/cargo/src/cargo/sources/directory.rs b/src/tools/cargo/src/cargo/sources/directory.rs
index 7195fd72d..01c3c4330 100644
--- a/src/tools/cargo/src/cargo/sources/directory.rs
+++ b/src/tools/cargo/src/cargo/sources/directory.rs
@@ -3,10 +3,11 @@ use std::fmt::{self, Debug, Formatter};
use std::path::{Path, PathBuf};
use std::task::Poll;
-use crate::core::{Dependency, Package, PackageId, SourceId, Summary};
+use crate::core::{Dependency, Package, PackageId, SourceId};
use crate::sources::source::MaybePackage;
use crate::sources::source::QueryKind;
use crate::sources::source::Source;
+use crate::sources::IndexSummary;
use crate::sources::PathSource;
use crate::util::errors::CargoResult;
use crate::util::Config;
@@ -99,7 +100,7 @@ impl<'cfg> Source for DirectorySource<'cfg> {
&mut self,
dep: &Dependency,
kind: QueryKind,
- f: &mut dyn FnMut(Summary),
+ f: &mut dyn FnMut(IndexSummary),
) -> Poll<CargoResult<()>> {
if !self.updated {
return Poll::Pending;
@@ -110,7 +111,7 @@ impl<'cfg> Source for DirectorySource<'cfg> {
QueryKind::Fuzzy => true,
});
for summary in matches.map(|pkg| pkg.summary().clone()) {
- f(summary);
+ f(IndexSummary::Candidate(summary));
}
Poll::Ready(Ok(()))
}
diff --git a/src/tools/cargo/src/cargo/sources/git/known_hosts.rs b/src/tools/cargo/src/cargo/sources/git/known_hosts.rs
index 0b0dd3208..f316cc253 100644
--- a/src/tools/cargo/src/cargo/sources/git/known_hosts.rs
+++ b/src/tools/cargo/src/cargo/sources/git/known_hosts.rs
@@ -538,7 +538,7 @@ fn user_known_host_location() -> Option<PathBuf> {
// - OpenSSH (most unix platforms): Uses `pw->pw_dir` from `getpwuid()`.
//
// This doesn't do anything close to that. home_dir's behavior is:
- // - Windows: $USERPROFILE, or SHGetFolderPathW()
+ // - Windows: $USERPROFILE, or SHGetKnownFolderPath()
// - Unix: $HOME, or getpwuid_r()
//
// Since there is a mismatch here, the location returned here might be
diff --git a/src/tools/cargo/src/cargo/sources/git/source.rs b/src/tools/cargo/src/cargo/sources/git/source.rs
index a75c1ec6d..664c64bfe 100644
--- a/src/tools/cargo/src/cargo/sources/git/source.rs
+++ b/src/tools/cargo/src/cargo/sources/git/source.rs
@@ -1,16 +1,19 @@
//! See [GitSource].
+use crate::core::global_cache_tracker;
use crate::core::GitReference;
use crate::core::SourceId;
-use crate::core::{Dependency, Package, PackageId, Summary};
+use crate::core::{Dependency, Package, PackageId};
use crate::sources::git::utils::GitRemote;
use crate::sources::source::MaybePackage;
use crate::sources::source::QueryKind;
use crate::sources::source::Source;
+use crate::sources::IndexSummary;
use crate::sources::PathSource;
use crate::util::cache_lock::CacheLockMode;
use crate::util::errors::CargoResult;
use crate::util::hex::short_hash;
+use crate::util::interning::InternedString;
use crate::util::Config;
use anyhow::Context;
use cargo_util::paths::exclude_from_backups_and_indexing;
@@ -73,10 +76,21 @@ pub struct GitSource<'cfg> {
/// The unique identifier of this source.
source_id: SourceId,
/// The underlying path source to discover packages inside the Git repository.
+ ///
+ /// This gets set to `Some` after the git repo has been checked out
+ /// (automatically handled via [`GitSource::block_until_ready`]).
path_source: Option<PathSource<'cfg>>,
+ /// A short string that uniquely identifies the version of the checkout.
+ ///
+ /// This is typically a 7-character string of the OID hash, automatically
+ /// increasing in size if it is ambiguous.
+ ///
+ /// This is set to `Some` after the git repo has been checked out
+ /// (automatically handled via [`GitSource::block_until_ready`]).
+ short_id: Option<InternedString>,
/// The identifier of this source for Cargo's Git cache directory.
/// See [`ident`] for more.
- ident: String,
+ ident: InternedString,
config: &'cfg Config,
/// Disables status messages.
quiet: bool,
@@ -104,7 +118,8 @@ impl<'cfg> GitSource<'cfg> {
locked_rev,
source_id,
path_source: None,
- ident,
+ short_id: None,
+ ident: ident.into(),
config,
quiet: false,
};
@@ -127,6 +142,17 @@ impl<'cfg> GitSource<'cfg> {
}
self.path_source.as_mut().unwrap().read_packages()
}
+
+ fn mark_used(&self, size: Option<u64>) -> CargoResult<()> {
+ self.config
+ .deferred_global_last_use()?
+ .mark_git_checkout_used(global_cache_tracker::GitCheckout {
+ encoded_git_name: self.ident,
+ short_name: self.short_id.expect("update before download"),
+ size,
+ });
+ Ok(())
+ }
}
/// Create an identifier from a URL,
@@ -177,7 +203,7 @@ impl<'cfg> Source for GitSource<'cfg> {
&mut self,
dep: &Dependency,
kind: QueryKind,
- f: &mut dyn FnMut(Summary),
+ f: &mut dyn FnMut(IndexSummary),
) -> Poll<CargoResult<()>> {
if let Some(src) = self.path_source.as_mut() {
src.query(dep, kind, f)
@@ -200,6 +226,7 @@ impl<'cfg> Source for GitSource<'cfg> {
fn block_until_ready(&mut self) -> CargoResult<()> {
if self.path_source.is_some() {
+ self.mark_used(None)?;
return Ok(());
}
@@ -290,8 +317,16 @@ impl<'cfg> Source for GitSource<'cfg> {
let path_source = PathSource::new_recursive(&checkout_path, source_id, self.config);
self.path_source = Some(path_source);
+ self.short_id = Some(short_id.as_str().into());
self.locked_rev = Some(actual_rev);
- self.path_source.as_mut().unwrap().update()
+ self.path_source.as_mut().unwrap().update()?;
+
+ // Hopefully this shouldn't incur too much of a performance hit since
+ // most of this should already be in cache since it was just
+ // extracted.
+ let size = global_cache_tracker::du_git_checkout(&checkout_path)?;
+ self.mark_used(Some(size))?;
+ Ok(())
}
fn download(&mut self, id: PackageId) -> CargoResult<MaybePackage> {
@@ -300,6 +335,7 @@ impl<'cfg> Source for GitSource<'cfg> {
id,
self.remote
);
+ self.mark_used(None)?;
self.path_source
.as_mut()
.expect("BUG: `update()` must be called before `get()`")
diff --git a/src/tools/cargo/src/cargo/sources/mod.rs b/src/tools/cargo/src/cargo/sources/mod.rs
index 7da1d652d..1d2f51a37 100644
--- a/src/tools/cargo/src/cargo/sources/mod.rs
+++ b/src/tools/cargo/src/cargo/sources/mod.rs
@@ -30,7 +30,9 @@ pub use self::config::SourceConfigMap;
pub use self::directory::DirectorySource;
pub use self::git::GitSource;
pub use self::path::PathSource;
-pub use self::registry::{RegistrySource, CRATES_IO_DOMAIN, CRATES_IO_INDEX, CRATES_IO_REGISTRY};
+pub use self::registry::{
+ IndexSummary, RegistrySource, CRATES_IO_DOMAIN, CRATES_IO_INDEX, CRATES_IO_REGISTRY,
+};
pub use self::replaced::ReplacedSource;
pub mod config;
diff --git a/src/tools/cargo/src/cargo/sources/path.rs b/src/tools/cargo/src/cargo/sources/path.rs
index 0cc639976..bbf6f056b 100644
--- a/src/tools/cargo/src/cargo/sources/path.rs
+++ b/src/tools/cargo/src/cargo/sources/path.rs
@@ -3,11 +3,12 @@ use std::fmt::{self, Debug, Formatter};
use std::path::{Path, PathBuf};
use std::task::Poll;
-use crate::core::{Dependency, Package, PackageId, SourceId, Summary};
+use crate::core::{Dependency, Package, PackageId, SourceId};
use crate::ops;
use crate::sources::source::MaybePackage;
use crate::sources::source::QueryKind;
use crate::sources::source::Source;
+use crate::sources::IndexSummary;
use crate::util::{internal, CargoResult, Config};
use anyhow::Context as _;
use cargo_util::paths;
@@ -327,7 +328,12 @@ impl<'cfg> PathSource<'cfg> {
match file_path.file_name().and_then(|s| s.to_str()) {
// The `target` directory is never included.
- Some("target") => continue,
+ Some("target") => {
+ // Only filter out target if its in the package root.
+ if file_path.parent().unwrap() == pkg_path {
+ continue;
+ }
+ }
// Keep track of all sub-packages found and also strip out all
// matches we've found so far. Note, though, that if we find
@@ -542,7 +548,7 @@ impl<'cfg> Source for PathSource<'cfg> {
&mut self,
dep: &Dependency,
kind: QueryKind,
- f: &mut dyn FnMut(Summary),
+ f: &mut dyn FnMut(IndexSummary),
) -> Poll<CargoResult<()>> {
self.update()?;
for s in self.packages.iter().map(|p| p.summary()) {
@@ -551,7 +557,7 @@ impl<'cfg> Source for PathSource<'cfg> {
QueryKind::Fuzzy => true,
};
if matched {
- f(s.clone())
+ f(IndexSummary::Candidate(s.clone()))
}
}
Poll::Ready(Ok(()))
diff --git a/src/tools/cargo/src/cargo/sources/registry/download.rs b/src/tools/cargo/src/cargo/sources/registry/download.rs
index 786432835..daf1d0537 100644
--- a/src/tools/cargo/src/cargo/sources/registry/download.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/download.rs
@@ -3,11 +3,13 @@
//! [`HttpRegistry`]: super::http_remote::HttpRegistry
//! [`RemoteRegistry`]: super::remote::RemoteRegistry
+use crate::util::interning::InternedString;
use anyhow::Context;
use cargo_credential::Operation;
use cargo_util::registry::make_dep_path;
use cargo_util::Sha256;
+use crate::core::global_cache_tracker;
use crate::core::PackageId;
use crate::sources::registry::MaybeLock;
use crate::sources::registry::RegistryConfig;
@@ -34,6 +36,7 @@ const CHECKSUM_TEMPLATE: &str = "{sha256-checksum}";
pub(super) fn download(
cache_path: &Filesystem,
config: &Config,
+ encoded_registry_name: InternedString,
pkg: PackageId,
checksum: &str,
registry_config: RegistryConfig,
@@ -50,6 +53,13 @@ pub(super) fn download(
if let Ok(dst) = File::open(path) {
let meta = dst.metadata()?;
if meta.len() > 0 {
+ config.deferred_global_last_use()?.mark_registry_crate_used(
+ global_cache_tracker::RegistryCrate {
+ encoded_registry_name,
+ crate_filename: pkg.tarball_name().into(),
+ size: meta.len(),
+ },
+ );
return Ok(MaybeLock::Ready(dst));
}
}
@@ -106,6 +116,7 @@ pub(super) fn download(
pub(super) fn finish_download(
cache_path: &Filesystem,
config: &Config,
+ encoded_registry_name: InternedString,
pkg: PackageId,
checksum: &str,
data: &[u8],
@@ -115,6 +126,13 @@ pub(super) fn finish_download(
if actual != checksum {
anyhow::bail!("failed to verify the checksum of `{}`", pkg)
}
+ config.deferred_global_last_use()?.mark_registry_crate_used(
+ global_cache_tracker::RegistryCrate {
+ encoded_registry_name,
+ crate_filename: pkg.tarball_name().into(),
+ size: data.len() as u64,
+ },
+ );
cache_path.create_dir()?;
let path = cache_path.join(&pkg.tarball_name());
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 3d31110c3..821cf3cd1 100644
--- a/src/tools/cargo/src/cargo/sources/registry/http_remote.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/http_remote.rs
@@ -1,11 +1,13 @@
//! Access to a HTTP-based crate registry. See [`HttpRegistry`] for details.
+use crate::core::global_cache_tracker;
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::interning::InternedString;
use crate::util::network::http::http_handle;
use crate::util::network::retry::{Retry, RetryResult};
use crate::util::network::sleep::SleepTracker;
@@ -52,6 +54,9 @@ const UNKNOWN: &'static str = "Unknown";
///
/// [RFC 2789]: https://github.com/rust-lang/rfcs/pull/2789
pub struct HttpRegistry<'cfg> {
+ /// The name of this source, a unique string (across all sources) used as
+ /// the directory name where its cached content is stored.
+ name: InternedString,
/// Path to the registry index (`$CARGO_HOME/registry/index/$REG-HASH`).
///
/// To be fair, `HttpRegistry` doesn't store the registry index it
@@ -199,6 +204,7 @@ impl<'cfg> HttpRegistry<'cfg> {
.expect("a url with the sparse+ stripped should still be valid");
Ok(HttpRegistry {
+ name: name.into(),
index_path: config.registry_index_path().join(name),
cache_path: config.registry_cache_path().join(name),
source_id,
@@ -454,6 +460,11 @@ impl<'cfg> HttpRegistry<'cfg> {
impl<'cfg> RegistryData for HttpRegistry<'cfg> {
fn prepare(&self) -> CargoResult<()> {
+ self.config
+ .deferred_global_last_use()?
+ .mark_registry_index_used(global_cache_tracker::RegistryIndex {
+ encoded_registry_name: self.name,
+ });
Ok(())
}
@@ -750,6 +761,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
download::download(
&self.cache_path,
&self.config,
+ self.name.clone(),
pkg,
checksum,
registry_config,
@@ -762,7 +774,14 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> {
checksum: &str,
data: &[u8],
) -> CargoResult<File> {
- download::finish_download(&self.cache_path, &self.config, pkg, checksum, data)
+ download::finish_download(
+ &self.cache_path,
+ &self.config,
+ self.name.clone(),
+ pkg,
+ checksum,
+ data,
+ )
}
fn is_crate_downloaded(&self, pkg: PackageId) -> bool {
diff --git a/src/tools/cargo/src/cargo/sources/registry/index.rs b/src/tools/cargo/src/cargo/sources/registry/index.rs
index 00f21d669..aed8fc813 100644
--- a/src/tools/cargo/src/cargo/sources/registry/index.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/index.rs
@@ -92,7 +92,8 @@ 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};
+use crate::util::{internal, CargoResult, Config, Filesystem, OptVersionReq};
+use crate::util_schemas::manifest::RustVersion;
use anyhow::bail;
use cargo_util::{paths, registry::make_dep_path};
use semver::Version;
@@ -223,6 +224,15 @@ impl IndexSummary {
}
}
+ pub fn map_summary(self, f: impl Fn(Summary) -> Summary) -> Self {
+ match self {
+ IndexSummary::Candidate(s) => IndexSummary::Candidate(f(s)),
+ IndexSummary::Yanked(s) => IndexSummary::Yanked(f(s)),
+ IndexSummary::Offline(s) => IndexSummary::Offline(f(s)),
+ IndexSummary::Unsupported(s, v) => IndexSummary::Unsupported(f(s), v.clone()),
+ }
+ }
+
/// Extract the package id from any variant
pub fn package_id(&self) -> PackageId {
match self {
@@ -935,7 +945,7 @@ impl IndexSummary {
} = serde_json::from_slice(line)?;
let v = v.unwrap_or(1);
tracing::trace!("json parsed registry {}/{}", name, vers);
- let pkgid = PackageId::pure(name.into(), vers.clone(), source_id);
+ let pkgid = PackageId::new(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 7ee461edd..5bdd71e7d 100644
--- a/src/tools/cargo/src/cargo/sources/registry/mod.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/mod.rs
@@ -201,7 +201,8 @@ use tar::Archive;
use tracing::debug;
use crate::core::dependency::Dependency;
-use crate::core::{Package, PackageId, SourceId, Summary};
+use crate::core::global_cache_tracker;
+use crate::core::{Package, PackageId, SourceId};
use crate::sources::source::MaybePackage;
use crate::sources::source::QueryKind;
use crate::sources::source::Source;
@@ -239,6 +240,9 @@ struct LockMetadata {
///
/// For general concepts of registries, see the [module-level documentation](crate::sources::registry).
pub struct RegistrySource<'cfg> {
+ /// A unique name of the source (typically used as the directory name
+ /// where its cached content is stored).
+ name: InternedString,
/// The unique identifier of this source.
source_id: SourceId,
/// The path where crate files are extracted (`$CARGO_HOME/registry/src/$REG-HASH`).
@@ -435,12 +439,18 @@ pub enum MaybeLock {
mod download;
mod http_remote;
mod index;
+pub use index::IndexSummary;
mod local;
mod remote;
/// Generates a unique name for [`SourceId`] to have a unique path to put their
/// index files.
fn short_name(id: SourceId, is_shallow: bool) -> String {
+ // CAUTION: This should not change between versions. If you change how
+ // this is computed, it will orphan previously cached data, forcing the
+ // cache to be rebuilt and potentially wasting significant disk space. If
+ // you change it, be cautious of the impact. See `test_cratesio_hash` for
+ // a similar discussion.
let hash = hex::short_hash(&id);
let ident = id.url().host_str().unwrap_or("").to_string();
let mut name = format!("{}-{}", ident, hash);
@@ -514,6 +524,7 @@ impl<'cfg> RegistrySource<'cfg> {
yanked_whitelist: &HashSet<PackageId>,
) -> RegistrySource<'cfg> {
RegistrySource {
+ name: name.into(),
src_path: config.registry_source_path().join(name),
config,
source_id,
@@ -589,6 +600,13 @@ impl<'cfg> RegistrySource<'cfg> {
match fs::read_to_string(path) {
Ok(ok) => match serde_json::from_str::<LockMetadata>(&ok) {
Ok(lock_meta) if lock_meta.v == 1 => {
+ self.config
+ .deferred_global_last_use()?
+ .mark_registry_src_used(global_cache_tracker::RegistrySrc {
+ encoded_registry_name: self.name,
+ package_dir: package_dir.into(),
+ size: None,
+ });
return Ok(unpack_dir.to_path_buf());
}
_ => {
@@ -613,6 +631,7 @@ impl<'cfg> RegistrySource<'cfg> {
set_mask(&mut tar);
tar
};
+ let mut bytes_written = 0;
let prefix = unpack_dir.file_name().unwrap();
let parent = unpack_dir.parent().unwrap();
for entry in tar.entries()? {
@@ -644,6 +663,7 @@ impl<'cfg> RegistrySource<'cfg> {
continue;
}
// Unpacking failed
+ bytes_written += entry.size();
let mut result = entry.unpack_in(parent).map_err(anyhow::Error::from);
if cfg!(windows) && restricted_names::is_windows_reserved_path(&entry_path) {
result = result.with_context(|| {
@@ -670,6 +690,14 @@ impl<'cfg> RegistrySource<'cfg> {
let lock_meta = LockMetadata { v: 1 };
write!(ok, "{}", serde_json::to_string(&lock_meta).unwrap())?;
+ self.config
+ .deferred_global_last_use()?
+ .mark_registry_src_used(global_cache_tracker::RegistrySrc {
+ encoded_registry_name: self.name,
+ package_dir: package_dir.into(),
+ size: Some(bytes_written),
+ });
+
Ok(unpack_dir.to_path_buf())
}
@@ -710,7 +738,7 @@ impl<'cfg> Source for RegistrySource<'cfg> {
&mut self,
dep: &Dependency,
kind: QueryKind,
- f: &mut dyn FnMut(Summary),
+ f: &mut dyn FnMut(IndexSummary),
) -> Poll<CargoResult<()>> {
let mut req = dep.version_req().clone();
@@ -736,7 +764,7 @@ impl<'cfg> Source for RegistrySource<'cfg> {
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.into_summary());
+ f(s);
}
},))?;
if called {
@@ -761,7 +789,7 @@ impl<'cfg> Source for RegistrySource<'cfg> {
if matched
&& (!s.is_yanked() || self.yanked_whitelist.contains(&s.package_id()))
{
- f(s.into_summary());
+ f(s);
called = true;
}
}))?;
@@ -786,9 +814,7 @@ impl<'cfg> Source for RegistrySource<'cfg> {
}
any_pending |= self
.index
- .query_inner(name_permutation, &req, &mut *self.ops, &mut |s| {
- f(s.into_summary());
- })?
+ .query_inner(name_permutation, &req, &mut *self.ops, f)?
.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 ba171eac3..841ee3683 100644
--- a/src/tools/cargo/src/cargo/sources/registry/remote.rs
+++ b/src/tools/cargo/src/cargo/sources/registry/remote.rs
@@ -1,5 +1,6 @@
//! Access to a Git index based registry. See [`RemoteRegistry`] for details.
+use crate::core::global_cache_tracker;
use crate::core::{GitReference, PackageId, SourceId};
use crate::sources::git;
use crate::sources::git::fetch::RemoteKind;
@@ -47,6 +48,9 @@ use tracing::{debug, trace};
///
/// [`HttpRegistry`]: super::http_remote::HttpRegistry
pub struct RemoteRegistry<'cfg> {
+ /// The name of this source, a unique string (across all sources) used as
+ /// the directory name where its cached content is stored.
+ name: InternedString,
/// Path to the registry index (`$CARGO_HOME/registry/index/$REG-HASH`).
index_path: Filesystem,
/// Path to the cache of `.crate` files (`$CARGO_HOME/registry/cache/$REG-HASH`).
@@ -87,6 +91,7 @@ impl<'cfg> RemoteRegistry<'cfg> {
/// registry index are stored. Expect to be unique.
pub fn new(source_id: SourceId, config: &'cfg Config, name: &str) -> RemoteRegistry<'cfg> {
RemoteRegistry {
+ name: name.into(),
index_path: config.registry_index_path().join(name),
cache_path: config.registry_cache_path().join(name),
source_id,
@@ -211,6 +216,11 @@ impl<'cfg> RemoteRegistry<'cfg> {
impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
fn prepare(&self) -> CargoResult<()> {
self.repo()?;
+ self.config
+ .deferred_global_last_use()?
+ .mark_registry_index_used(global_cache_tracker::RegistryIndex {
+ encoded_registry_name: self.name,
+ });
Ok(())
}
@@ -403,6 +413,7 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
download::download(
&self.cache_path,
&self.config,
+ self.name,
pkg,
checksum,
registry_config,
@@ -415,7 +426,14 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> {
checksum: &str,
data: &[u8],
) -> CargoResult<File> {
- download::finish_download(&self.cache_path, &self.config, pkg, checksum, data)
+ download::finish_download(
+ &self.cache_path,
+ &self.config,
+ self.name.clone(),
+ pkg,
+ checksum,
+ data,
+ )
}
fn is_crate_downloaded(&self, pkg: PackageId) -> bool {
diff --git a/src/tools/cargo/src/cargo/sources/replaced.rs b/src/tools/cargo/src/cargo/sources/replaced.rs
index f224e073d..8c38a5241 100644
--- a/src/tools/cargo/src/cargo/sources/replaced.rs
+++ b/src/tools/cargo/src/cargo/sources/replaced.rs
@@ -1,7 +1,8 @@
-use crate::core::{Dependency, Package, PackageId, SourceId, Summary};
+use crate::core::{Dependency, Package, PackageId, SourceId};
use crate::sources::source::MaybePackage;
use crate::sources::source::QueryKind;
use crate::sources::source::Source;
+use crate::sources::IndexSummary;
use crate::util::errors::CargoResult;
use std::task::Poll;
@@ -59,14 +60,14 @@ impl<'cfg> Source for ReplacedSource<'cfg> {
&mut self,
dep: &Dependency,
kind: QueryKind,
- f: &mut dyn FnMut(Summary),
+ f: &mut dyn FnMut(IndexSummary),
) -> Poll<CargoResult<()>> {
let (replace_with, to_replace) = (self.replace_with, self.to_replace);
let dep = dep.clone().map_source(to_replace, replace_with);
self.inner
.query(&dep, kind, &mut |summary| {
- f(summary.map_source(replace_with, to_replace))
+ f(summary.map_summary(|s| s.map_source(replace_with, to_replace)))
})
.map_err(|e| {
e.context(format!(
diff --git a/src/tools/cargo/src/cargo/sources/source.rs b/src/tools/cargo/src/cargo/sources/source.rs
index 5a3439e5d..dd6619e59 100644
--- a/src/tools/cargo/src/cargo/sources/source.rs
+++ b/src/tools/cargo/src/cargo/sources/source.rs
@@ -6,7 +6,8 @@ use std::task::Poll;
use crate::core::package::PackageSet;
use crate::core::SourceId;
-use crate::core::{Dependency, Package, PackageId, Summary};
+use crate::core::{Dependency, Package, PackageId};
+use crate::sources::IndexSummary;
use crate::util::{CargoResult, Config};
/// An abstraction of different sources of Cargo packages.
@@ -36,11 +37,11 @@ pub trait Source {
self.source_id()
}
- /// Returns whether or not this source will return [`Summary`] items with
+ /// Returns whether or not this source will return [`IndexSummary`] items with
/// checksums listed.
fn supports_checksums(&self) -> bool;
- /// Returns whether or not this source will return [`Summary`] items with
+ /// Returns whether or not this source will return [`IndexSummary`] items with
/// the `precise` field in the [`SourceId`] listed.
fn requires_precise(&self) -> bool;
@@ -50,17 +51,21 @@ pub trait Source {
/// wait until package information become available. Otherwise any query
/// may return a [`Poll::Pending`].
///
- /// The `f` argument is expected to get called when any [`Summary`] becomes available.
+ /// The `f` argument is expected to get called when any [`IndexSummary`] becomes available.
fn query(
&mut self,
dep: &Dependency,
kind: QueryKind,
- f: &mut dyn FnMut(Summary),
+ f: &mut dyn FnMut(IndexSummary),
) -> Poll<CargoResult<()>>;
- /// Gathers the result from [`Source::query`] as a list of [`Summary`] items
+ /// Gathers the result from [`Source::query`] as a list of [`IndexSummary`] items
/// when they become available.
- fn query_vec(&mut self, dep: &Dependency, kind: QueryKind) -> Poll<CargoResult<Vec<Summary>>> {
+ fn query_vec(
+ &mut self,
+ dep: &Dependency,
+ kind: QueryKind,
+ ) -> Poll<CargoResult<Vec<IndexSummary>>> {
let mut ret = Vec::new();
self.query(dep, kind, &mut |s| ret.push(s)).map_ok(|_| ret)
}
@@ -215,7 +220,7 @@ impl<'a, T: Source + ?Sized + 'a> Source for Box<T> {
&mut self,
dep: &Dependency,
kind: QueryKind,
- f: &mut dyn FnMut(Summary),
+ f: &mut dyn FnMut(IndexSummary),
) -> Poll<CargoResult<()>> {
(**self).query(dep, kind, f)
}
@@ -287,7 +292,7 @@ impl<'a, T: Source + ?Sized + 'a> Source for &'a mut T {
&mut self,
dep: &Dependency,
kind: QueryKind,
- f: &mut dyn FnMut(Summary),
+ f: &mut dyn FnMut(IndexSummary),
) -> Poll<CargoResult<()>> {
(**self).query(dep, kind, f)
}
diff --git a/src/tools/cargo/src/cargo/util/command_prelude.rs b/src/tools/cargo/src/cargo/util/command_prelude.rs
index 3888b80c4..24c8c098d 100644
--- a/src/tools/cargo/src/cargo/util/command_prelude.rs
+++ b/src/tools/cargo/src/cargo/util/command_prelude.rs
@@ -7,11 +7,13 @@ 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;
-use crate::util::toml::schema::StringOrVec;
use crate::util::{
print_available_benches, print_available_binaries, print_available_examples,
print_available_packages, print_available_tests,
};
+use crate::util_schemas::manifest::ProfileName;
+use crate::util_schemas::manifest::RegistryName;
+use crate::util_schemas::manifest::StringOrVec;
use crate::CargoResult;
use anyhow::bail;
use cargo_util::paths;
@@ -363,20 +365,19 @@ pub trait CommandExt: Sized {
))
}
- fn arg_quiet(self) -> Self {
- let unsupported_silent_arg = {
- let value_parser = UnknownArgumentValueParser::suggest_arg("--quiet");
+ /// Adds a suggestion for the `--silent` or `-s` flags to use the
+ /// `--quiet` flag instead. This is to help with people familiar with
+ /// other tools that use `-s`.
+ ///
+ /// Every command should call this, unless it has its own `-s` short flag.
+ fn arg_silent_suggestion(self) -> Self {
+ let value_parser = UnknownArgumentValueParser::suggest_arg("--quiet");
+ self._arg(
flag("silent", "")
.short('s')
.value_parser(value_parser)
- .hide(true)
- };
- self.arg_quiet_without_unknown_silent_arg_tip()
- ._arg(unsupported_silent_arg)
- }
-
- fn arg_quiet_without_unknown_silent_arg_tip(self) -> Self {
- self._arg(flag("quiet", "Do not print cargo log messages").short('q'))
+ .hide(true),
+ )
}
fn arg_timings(self) -> Self {
@@ -606,7 +607,7 @@ Run `{cmd}` to see possible targets."
bail!("profile `doc` is reserved and not allowed to be explicitly specified")
}
(_, _, Some(name)) => {
- restricted_names::validate_profile_name(name)?;
+ ProfileName::new(name)?;
name
}
};
@@ -834,7 +835,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) => {
- restricted_names::validate_package_name(r, "registry name", "")?;
+ RegistryName::new(r)?;
Some(RegistryOrIndex::Registry(r.to_string()))
}
(Some(_), Some(_)) => {
@@ -849,7 +850,7 @@ Run `{cmd}` to see possible targets."
match self._value_of("registry").map(|s| s.to_string()) {
None => config.default_registry(),
Some(registry) => {
- restricted_names::validate_package_name(&registry, "registry name", "")?;
+ RegistryName::new(&registry)?;
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 50153466b..1c1b949a7 100644
--- a/src/tools/cargo/src/cargo/util/config/mod.rs
+++ b/src/tools/cargo/src/cargo/util/config/mod.rs
@@ -68,6 +68,7 @@ use std::time::Instant;
use self::ConfigValue as CV;
use crate::core::compiler::rustdoc::RustdocExternMap;
+use crate::core::global_cache_tracker::{DeferredGlobalLastUse, GlobalCacheTracker};
use crate::core::shell::Verbosity;
use crate::core::{features, CliUnstable, Shell, SourceId, Workspace, WorkspaceRootConfig};
use crate::ops::RegistryCredentialConfig;
@@ -76,9 +77,10 @@ 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::try_canonicalize;
use crate::util::{internal, CanonicalUrl};
-use crate::util::{try_canonicalize, validate_package_name};
use crate::util::{Filesystem, IntoUrl, IntoUrlWithBase, Rustc};
+use crate::util_schemas::manifest::RegistryName;
use anyhow::{anyhow, bail, format_err, Context as _};
use cargo_credential::Secret;
use cargo_util::paths;
@@ -244,6 +246,11 @@ pub struct Config {
pub nightly_features_allowed: bool,
/// WorkspaceRootConfigs that have been found
pub ws_roots: RefCell<HashMap<PathBuf, WorkspaceRootConfig>>,
+ /// The global cache tracker is a database used to track disk cache usage.
+ global_cache_tracker: LazyCell<RefCell<GlobalCacheTracker>>,
+ /// A cache of modifications to make to [`Config::global_cache_tracker`],
+ /// saved to disk in a batch to improve performance.
+ deferred_global_last_use: LazyCell<RefCell<DeferredGlobalLastUse>>,
}
impl Config {
@@ -317,6 +324,8 @@ impl Config {
env_config: LazyCell::new(),
nightly_features_allowed: matches!(&*features::channel(), "nightly" | "dev"),
ws_roots: RefCell::new(HashMap::new()),
+ global_cache_tracker: LazyCell::new(),
+ deferred_global_last_use: LazyCell::new(),
}
}
@@ -1195,6 +1204,8 @@ impl Config {
path.display()
);
}
+ tracing::debug!(?path, ?why_load, includes, "load config from file");
+
let contents = fs::read_to_string(path)
.with_context(|| format!("failed to read configuration file `{}`", path.display()))?;
let toml = parse_document(&contents, path, self).with_context(|| {
@@ -1541,7 +1552,7 @@ impl Config {
/// Gets the index for a registry.
pub fn get_registry_index(&self, registry: &str) -> CargoResult<Url> {
- validate_package_name(registry, "registry name", "")?;
+ RegistryName::new(registry)?;
if let Some(index) = self.get_string(&format!("registries.{}.index", registry))? {
self.resolve_registry_index(&index).with_context(|| {
format!(
@@ -1919,6 +1930,25 @@ impl Config {
) -> CargoResult<Option<CacheLock<'_>>> {
self.package_cache_lock.try_lock(self, mode)
}
+
+ /// Returns a reference to the shared [`GlobalCacheTracker`].
+ ///
+ /// The package cache lock must be held to call this function (and to use
+ /// it in general).
+ pub fn global_cache_tracker(&self) -> CargoResult<RefMut<'_, GlobalCacheTracker>> {
+ let tracker = self.global_cache_tracker.try_borrow_with(|| {
+ Ok::<_, anyhow::Error>(RefCell::new(GlobalCacheTracker::new(self)?))
+ })?;
+ Ok(tracker.borrow_mut())
+ }
+
+ /// Returns a reference to the shared [`DeferredGlobalLastUse`].
+ pub fn deferred_global_last_use(&self) -> CargoResult<RefMut<'_, DeferredGlobalLastUse>> {
+ let deferred = self.deferred_global_last_use.try_borrow_with(|| {
+ Ok::<_, anyhow::Error>(RefCell::new(DeferredGlobalLastUse::new()))
+ })?;
+ Ok(deferred.borrow_mut())
+ }
}
/// Internal error for serde errors.
diff --git a/src/tools/cargo/src/cargo/util/errors.rs b/src/tools/cargo/src/cargo/util/errors.rs
index 9589e1ae3..5752f2370 100644
--- a/src/tools/cargo/src/cargo/util/errors.rs
+++ b/src/tools/cargo/src/cargo/util/errors.rs
@@ -1,5 +1,3 @@
-#![allow(unknown_lints)]
-
use anyhow::Error;
use curl::easy::Easy;
use std::fmt::{self, Write};
diff --git a/src/tools/cargo/src/cargo/util/graph.rs b/src/tools/cargo/src/cargo/util/graph.rs
index 8c4a593ea..eed3ad4c1 100644
--- a/src/tools/cargo/src/cargo/util/graph.rs
+++ b/src/tools/cargo/src/cargo/util/graph.rs
@@ -128,30 +128,32 @@ impl<'s, N: Eq + Ord + Clone + 's, E: Default + Clone + 's> Graph<N, E> {
{
let mut back_link = BTreeMap::new();
let mut queue = VecDeque::from([pkg]);
- let mut bottom = None;
+ let mut last = pkg;
while let Some(p) = queue.pop_front() {
- bottom = Some(p);
+ last = p;
+ let mut out_edges = true;
for (child, edge) in fn_edge(&self, p) {
- bottom = None;
+ out_edges = false;
back_link.entry(child).or_insert_with(|| {
queue.push_back(child);
(p, edge)
});
}
- if bottom.is_some() {
+ if out_edges {
break;
}
}
let mut result = Vec::new();
- let mut next =
- bottom.expect("the only path was a cycle, no dependency graph has this shape");
+ let mut next = last;
while let Some((p, e)) = back_link.remove(&next) {
result.push((next, Some(e)));
next = p;
}
- result.push((next, None));
+ if result.iter().all(|(n, _)| n != &next) {
+ result.push((next, None));
+ }
result.reverse();
#[cfg(debug_assertions)]
{
@@ -165,8 +167,12 @@ impl<'s, N: Eq + Ord + Clone + 's, E: Default + Clone + 's> Graph<N, E> {
));
}
let last = result.last().unwrap().0;
- // fixme: this may sometimes be wrong when there are cycles.
- if !fn_edge(&self, last).next().is_none() {
+ let set: Vec<_> = result.iter().map(|(k, _)| k).collect();
+ if !fn_edge(&self, last)
+ .filter(|(e, _)| !set.contains(&e))
+ .next()
+ .is_none()
+ {
self.print_for_test();
unreachable!("The last element in the path should not have outgoing edges");
}
@@ -188,6 +194,14 @@ fn path_to_case() {
);
}
+#[test]
+fn path_to_self() {
+ // Extracted from #12941
+ let mut new: Graph<i32, ()> = Graph::new();
+ new.link(0, 0);
+ assert_eq!(new.path_to_bottom(&0), vec![(&0, Some(&()))]);
+}
+
impl<N: Eq + Ord + Clone, E: Default + Clone> Default for Graph<N, E> {
fn default() -> Graph<N, E> {
Graph::new()
diff --git a/src/tools/cargo/src/cargo/util/mod.rs b/src/tools/cargo/src/cargo/util/mod.rs
index fb4c4b39c..e5ecd077f 100644
--- a/src/tools/cargo/src/cargo/util/mod.rs
+++ b/src/tools/cargo/src/cargo/util/mod.rs
@@ -21,9 +21,8 @@ pub(crate) use self::io::LimitErrorReader;
pub use self::lockserver::{LockServer, LockServerClient, LockServerStarted};
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, RustVersion};
+pub use self::semver_ext::OptVersionReq;
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,
@@ -62,6 +61,7 @@ mod queue;
pub mod restricted_names;
pub mod rustc;
mod semver_ext;
+pub mod sqlite;
pub mod style;
pub mod toml;
pub mod toml_mut;
diff --git a/src/tools/cargo/src/cargo/util/restricted_names.rs b/src/tools/cargo/src/cargo/util/restricted_names.rs
index f61249775..f047b0d66 100644
--- a/src/tools/cargo/src/cargo/util/restricted_names.rs
+++ b/src/tools/cargo/src/cargo/util/restricted_names.rs
@@ -1,7 +1,5 @@
//! Helpers for validating and checking names like package and crate names.
-use crate::util::CargoResult;
-use anyhow::bail;
use std::path::Path;
/// Returns `true` if the name contains non-ASCII characters.
@@ -36,76 +34,6 @@ pub fn is_conflicting_artifact_name(name: &str) -> bool {
["deps", "examples", "build", "incremental"].contains(&name)
}
-/// Check the base requirements for a package name.
-///
-/// This can be used for other things than package names, to enforce some
-/// level of sanity. Note that package names have other restrictions
-/// elsewhere. `cargo new` has a few restrictions, such as checking for
-/// reserved names. crates.io has even more restrictions.
-pub fn validate_package_name(name: &str, what: &str, help: &str) -> CargoResult<()> {
- let mut chars = name.chars();
- if let Some(ch) = chars.next() {
- if ch.is_digit(10) {
- // A specific error for a potentially common case.
- bail!(
- "the name `{}` cannot be used as a {}, \
- the name cannot start with a digit{}",
- name,
- what,
- help
- );
- }
- if !(unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_') {
- bail!(
- "invalid character `{}` in {}: `{}`, \
- the first character must be a Unicode XID start character \
- (most letters or `_`){}",
- ch,
- what,
- name,
- help
- );
- }
- }
- for ch in chars {
- if !(unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-') {
- bail!(
- "invalid character `{}` in {}: `{}`, \
- characters must be Unicode XID characters \
- (numbers, `-`, `_`, or most letters){}",
- ch,
- what,
- name,
- help
- );
- }
- }
- Ok(())
-}
-
-/// Ensure a package name is [valid][validate_package_name]
-pub fn sanitize_package_name(name: &str, placeholder: char) -> String {
- let mut slug = String::new();
- let mut chars = name.chars();
- while let Some(ch) = chars.next() {
- if (unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_') && !ch.is_digit(10) {
- slug.push(ch);
- break;
- }
- }
- while let Some(ch) = chars.next() {
- if unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-' {
- slug.push(ch);
- } else {
- slug.push(placeholder);
- }
- }
- if slug.is_empty() {
- slug.push_str("package");
- }
- slug
-}
-
/// Check the entire path for names reserved in Windows.
pub fn is_windows_reserved_path(path: &Path) -> bool {
path.iter()
@@ -120,82 +48,3 @@ 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 f51580f29..80a9fe802 100644
--- a/src/tools/cargo/src/cargo/util/rustc.rs
+++ b/src/tools/cargo/src/cargo/util/rustc.rs
@@ -83,14 +83,20 @@ impl Rustc {
)
})?;
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"
- );
+ // Possible commit-hash values from rustc are SHA hex string and "unknown". See:
+ // * https://github.com/rust-lang/rust/blob/531cb83fc/src/bootstrap/src/utils/channel.rs#L73
+ // * https://github.com/rust-lang/rust/blob/531cb83fc/compiler/rustc_driver_impl/src/lib.rs#L911-L913
+ #[cfg(debug_assertions)]
+ if hash != "unknown" {
+ debug_assert!(
+ hash.chars().all(|ch| ch.is_ascii_hexdigit()),
+ "commit hash must be a hex string, got: {hash:?}"
+ );
+ debug_assert!(
+ hash.len() == 40 || hash.len() == 64,
+ "hex string must be generated from sha1 or sha256 (i.e., it must be 40 or 64 characters long)\ngot: {hash:?}"
+ );
+ }
hash.to_string()
});
diff --git a/src/tools/cargo/src/cargo/util/semver_ext.rs b/src/tools/cargo/src/cargo/util/semver_ext.rs
index 561cf140e..854fab6c8 100644
--- a/src/tools/cargo/src/cargo/util/semver_ext.rs
+++ b/src/tools/cargo/src/cargo/util/semver_ext.rs
@@ -1,9 +1,7 @@
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)]
@@ -125,48 +123,3 @@ impl From<VersionReq> for OptVersionReq {
OptVersionReq::Req(req)
}
}
-
-#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Debug, serde::Serialize)]
-#[serde(transparent)]
-pub struct RustVersion(PartialVersion);
-
-impl std::ops::Deref for RustVersion {
- type Target = PartialVersion;
-
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-impl std::str::FromStr for RustVersion {
- type Err = anyhow::Error;
-
- fn from_str(value: &str) -> Result<Self, Self::Err> {
- let partial = value.parse::<PartialVersion>()?;
- if partial.pre.is_some() {
- anyhow::bail!("unexpected prerelease field, expected a version like \"1.32\"")
- }
- if partial.build.is_some() {
- anyhow::bail!("unexpected prerelease field, expected a version like \"1.32\"")
- }
- Ok(Self(partial))
- }
-}
-
-impl<'de> serde::Deserialize<'de> for RustVersion {
- 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)
- }
-}
-
-impl Display for RustVersion {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
- }
-}
diff --git a/src/tools/cargo/src/cargo/util/sqlite.rs b/src/tools/cargo/src/cargo/util/sqlite.rs
new file mode 100644
index 000000000..b391cc6db
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util/sqlite.rs
@@ -0,0 +1,118 @@
+//! Utilities to help with working with sqlite.
+
+use crate::util::interning::InternedString;
+use crate::CargoResult;
+use rusqlite::types::{FromSql, FromSqlError, ToSql, ToSqlOutput};
+use rusqlite::{Connection, TransactionBehavior};
+
+impl FromSql for InternedString {
+ fn column_result(value: rusqlite::types::ValueRef<'_>) -> Result<Self, FromSqlError> {
+ value.as_str().map(InternedString::new)
+ }
+}
+
+impl ToSql for InternedString {
+ fn to_sql(&self) -> Result<ToSqlOutput<'_>, rusqlite::Error> {
+ Ok(ToSqlOutput::from(self.as_str()))
+ }
+}
+
+/// A function or closure representing a database migration.
+///
+/// Migrations support evolving the schema and contents of the database across
+/// new versions of cargo. The [`migrate`] function should be called
+/// immediately after opening a connection to a database in order to configure
+/// the schema. Whether or not a migration has been done is tracked by the
+/// `pragma_user_version` value in the database. Typically you include the
+/// initial `CREATE TABLE` statements in the initial list, but as time goes on
+/// you can add new tables or `ALTER TABLE` statements. The migration code
+/// will only execute statements that haven't previously been run.
+///
+/// Important things to note about how you define migrations:
+///
+/// * Never remove a migration entry from the list. Migrations are tracked by
+/// the index number in the list.
+/// * Never perform any schema modifications that would be backwards
+/// incompatible. For example, don't drop tables or columns.
+///
+/// The [`basic_migration`] function is a convenience function for specifying
+/// migrations that are simple SQL statements. If you need to do something
+/// more complex, then you can specify a closure that takes a [`Connection`]
+/// and does whatever is needed.
+///
+/// For example:
+///
+/// ```rust
+/// # use cargo::util::sqlite::*;
+/// # use rusqlite::Connection;
+/// # let mut conn = Connection::open_in_memory()?;
+/// # fn generate_name() -> String { "example".to_string() };
+/// migrate(
+/// &mut conn,
+/// &[
+/// basic_migration(
+/// "CREATE TABLE foo (
+/// id INTEGER PRIMARY KEY AUTOINCREMENT,
+/// name STRING NOT NULL
+/// )",
+/// ),
+/// Box::new(|conn| {
+/// conn.execute("INSERT INTO foo (name) VALUES (?1)", [generate_name()])?;
+/// Ok(())
+/// }),
+/// basic_migration("ALTER TABLE foo ADD COLUMN size INTEGER"),
+/// ],
+/// )?;
+/// # Ok::<(), anyhow::Error>(())
+/// ```
+pub type Migration = Box<dyn Fn(&Connection) -> CargoResult<()>>;
+
+/// A basic migration that is a single static SQL statement.
+///
+/// See [`Migration`] for more information.
+pub fn basic_migration(stmt: &'static str) -> Migration {
+ Box::new(|conn| {
+ conn.execute(stmt, [])?;
+ Ok(())
+ })
+}
+
+/// Perform one-time SQL migrations.
+///
+/// See [`Migration`] for more information.
+pub fn migrate(conn: &mut Connection, migrations: &[Migration]) -> CargoResult<()> {
+ // EXCLUSIVE ensures that it starts with an exclusive write lock. No other
+ // readers will be allowed. This generally shouldn't be needed if there is
+ // a file lock, but might be helpful in cases where cargo's `FileLock`
+ // failed.
+ let tx = conn.transaction_with_behavior(TransactionBehavior::Exclusive)?;
+ let user_version = tx.query_row("SELECT user_version FROM pragma_user_version", [], |row| {
+ row.get(0)
+ })?;
+ if user_version < migrations.len() {
+ for migration in &migrations[user_version..] {
+ migration(&tx)?;
+ }
+ tx.pragma_update(None, "user_version", &migrations.len())?;
+ }
+ tx.commit()?;
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn migrate_twice() -> CargoResult<()> {
+ // Check that a second migration will apply.
+ let mut conn = Connection::open_in_memory()?;
+ let mut migrations = vec![basic_migration("CREATE TABLE foo (a, b, c)")];
+ migrate(&mut conn, &migrations)?;
+ conn.execute("INSERT INTO foo VALUES (1,2,3)", [])?;
+ migrations.push(basic_migration("ALTER TABLE foo ADD COLUMN d"));
+ migrate(&mut conn, &migrations)?;
+ conn.execute("INSERT INTO foo VALUES (1,2,3,4)", [])?;
+ Ok(())
+ }
+}
diff --git a/src/tools/cargo/src/cargo/util/toml/embedded.rs b/src/tools/cargo/src/cargo/util/toml/embedded.rs
index 4c57195d4..cad7abc38 100644
--- a/src/tools/cargo/src/cargo/util/toml/embedded.rs
+++ b/src/tools/cargo/src/cargo/util/toml/embedded.rs
@@ -1,6 +1,7 @@
use anyhow::Context as _;
use crate::util::restricted_names;
+use crate::util_schemas::manifest::PackageName;
use crate::CargoResult;
use crate::Config;
@@ -171,7 +172,7 @@ fn sanitize_name(name: &str) -> String {
'-'
};
- let mut name = restricted_names::sanitize_package_name(name, placeholder);
+ let mut name = PackageName::sanitize(name, placeholder).into_inner();
loop {
if restricted_names::is_keyword(&name) {
diff --git a/src/tools/cargo/src/cargo/util/toml/mod.rs b/src/tools/cargo/src/cargo/util/toml/mod.rs
index cb841476b..8affc69a4 100644
--- a/src/tools/cargo/src/cargo/util/toml/mod.rs
+++ b/src/tools/cargo/src/cargo/util/toml/mod.rs
@@ -23,14 +23,11 @@ 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,
-};
+use crate::util::{self, config::ConfigRelativePath, Config, IntoUrl, OptVersionReq};
+use crate::util_schemas::manifest;
+use crate::util_schemas::manifest::RustVersion;
mod embedded;
-pub mod schema;
mod targets;
use self::targets::targets;
@@ -97,7 +94,7 @@ fn read_manifest_from_str(
let mut unused = BTreeSet::new();
let deserializer = toml::de::Deserializer::new(contents);
- let manifest: schema::TomlManifest = serde_ignored::deserialize(deserializer, |path| {
+ let manifest: manifest::TomlManifest = serde_ignored::deserialize(deserializer, |path| {
let mut key = String::new();
stringify(&mut key, &path);
unused.insert(key);
@@ -118,21 +115,16 @@ fn read_manifest_from_str(
{
for (name, dep) in deps {
if dep.is_optional() {
- bail!(
- "{} is optional, but workspace dependencies cannot be optional",
- name
- );
+ bail!("{name} is optional, but workspace dependencies cannot be optional",);
+ }
+ if dep.is_public() {
+ bail!("{name} is public, but workspace dependencies cannot be public",);
}
}
}
return if manifest.project.is_some() || manifest.package.is_some() {
- let (mut manifest, paths) = schema::TomlManifest::to_real_manifest(
- manifest,
- embedded,
- source_id,
- package_root,
- config,
- )?;
+ let (mut manifest, paths) =
+ 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,8 +135,7 @@ fn read_manifest_from_str(
}
Ok((EitherManifest::Real(manifest), paths))
} else {
- let (mut m, paths) =
- schema::TomlManifest::to_virtual_manifest(manifest, source_id, package_root, config)?;
+ let (mut m, paths) = to_virtual_manifest(manifest, source_id, package_root, config)?;
add_unused(m.warnings_mut());
Ok((EitherManifest::Virtual(m), paths))
};
@@ -184,890 +175,877 @@ fn warn_on_deprecated(new_path: &str, name: &str, kind: &str, warnings: &mut Vec
))
}
-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.
- pub fn prepare_for_publish(
- &self,
- ws: &Workspace<'_>,
- package_root: &Path,
- ) -> CargoResult<schema::TomlManifest> {
- let config = ws.config();
- let mut package = self
- .package
- .as_ref()
- .or_else(|| self.project.as_ref())
- .unwrap()
- .clone();
- package.workspace = None;
- let current_resolver = package
- .resolver
- .as_ref()
- .map(|r| ResolveBehavior::from_manifest(r))
- .unwrap_or_else(|| {
- package
- .edition
- .as_ref()
- .and_then(|e| e.as_defined())
- .map(|e| Edition::from_str(e))
- .unwrap_or(Ok(Edition::Edition2015))
- .map(|e| e.default_resolve_behavior())
- })?;
- if ws.resolve_behavior() != current_resolver {
- // This ensures the published crate if built as a root (e.g. `cargo install`) will
- // use the same resolver behavior it was tested with in the workspace.
- // To avoid forcing a higher MSRV we don't explicitly set this if it would implicitly
- // result in the same thing.
- package.resolver = Some(ws.resolve_behavior().to_manifest());
- }
- if let Some(license_file) = &package.license_file {
- let license_file = license_file
- .as_defined()
- .context("license file should have been resolved before `prepare_for_publish()`")?;
- let license_path = Path::new(&license_file);
- let abs_license_path = paths::normalize_path(&package_root.join(license_path));
- 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(schema::MaybeWorkspace::Defined(
- license_path
- .file_name()
- .unwrap()
- .to_str()
- .unwrap()
- .to_string(),
- ));
- }
+/// Prepares the manifest for publishing.
+// - Path and git components of dependency specifications are removed.
+// - License path is updated to point within the package.
+pub fn prepare_for_publish(
+ me: &manifest::TomlManifest,
+ ws: &Workspace<'_>,
+ package_root: &Path,
+) -> CargoResult<manifest::TomlManifest> {
+ let config = ws.config();
+ let mut package = me.package().unwrap().clone();
+ package.workspace = None;
+ let current_resolver = package
+ .resolver
+ .as_ref()
+ .map(|r| ResolveBehavior::from_manifest(r))
+ .unwrap_or_else(|| {
+ package
+ .edition
+ .as_ref()
+ .and_then(|e| e.as_value())
+ .map(|e| Edition::from_str(e))
+ .unwrap_or(Ok(Edition::Edition2015))
+ .map(|e| e.default_resolve_behavior())
+ })?;
+ if ws.resolve_behavior() != current_resolver {
+ // This ensures the published crate if built as a root (e.g. `cargo install`) will
+ // use the same resolver behavior it was tested with in the workspace.
+ // To avoid forcing a higher MSRV we don't explicitly set this if it would implicitly
+ // result in the same thing.
+ package.resolver = Some(ws.resolve_behavior().to_manifest());
+ }
+ if let Some(license_file) = &package.license_file {
+ let license_file = license_file
+ .as_value()
+ .context("license file should have been resolved before `prepare_for_publish()`")?;
+ let license_path = Path::new(&license_file);
+ let abs_license_path = paths::normalize_path(&package_root.join(license_path));
+ 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(manifest::InheritableField::Value(
+ license_path
+ .file_name()
+ .unwrap()
+ .to_str()
+ .unwrap()
+ .to_string(),
+ ));
}
+ }
- if let Some(readme) = &package.readme {
- let readme = readme
- .as_defined()
- .context("readme should have been resolved before `prepare_for_publish()`")?;
- match 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(schema::MaybeWorkspace::Defined(
- schema::StringOrBool::String(
- readme_path
- .file_name()
- .unwrap()
- .to_str()
- .unwrap()
- .to_string(),
- ),
- ));
- }
+ if let Some(readme) = &package.readme {
+ let readme = readme
+ .as_value()
+ .context("readme should have been resolved before `prepare_for_publish()`")?;
+ match readme {
+ manifest::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(manifest::InheritableField::Value(
+ manifest::StringOrBool::String(
+ readme_path
+ .file_name()
+ .unwrap()
+ .to_str()
+ .unwrap()
+ .to_string(),
+ ),
+ ));
}
- schema::StringOrBool::Bool(_) => {}
}
+ manifest::StringOrBool::Bool(_) => {}
}
- let all = |_d: &schema::TomlDependency| true;
- return Ok(schema::TomlManifest {
- package: Some(package),
- project: None,
- profile: self.profile.clone(),
- lib: self.lib.clone(),
- bin: self.bin.clone(),
- example: self.example.clone(),
- test: self.test.clone(),
- bench: self.bench.clone(),
- dependencies: map_deps(config, self.dependencies.as_ref(), all)?,
- dev_dependencies: map_deps(
- config,
- self.dev_dependencies(),
- schema::TomlDependency::is_version_specified,
- )?,
- dev_dependencies2: None,
- 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| {
- target_map
- .iter()
- .map(|(k, v)| {
- Ok((
- k.clone(),
- schema::TomlPlatform {
- dependencies: map_deps(config, v.dependencies.as_ref(), all)?,
- dev_dependencies: map_deps(
- config,
- v.dev_dependencies(),
- schema::TomlDependency::is_version_specified,
- )?,
- dev_dependencies2: None,
- build_dependencies: map_deps(config, v.build_dependencies(), all)?,
- build_dependencies2: None,
- },
- ))
- })
- .collect()
- }) {
- Some(Ok(v)) => Some(v),
- Some(Err(e)) => return Err(e),
- None => None,
- },
- replace: None,
- patch: None,
- workspace: None,
- badges: self.badges.clone(),
- cargo_features: self.cargo_features.clone(),
- lints: self.lints.clone(),
- });
-
- fn map_deps(
- config: &Config,
- 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
+ }
+ let all = |_d: &manifest::TomlDependency| true;
+ return Ok(manifest::TomlManifest {
+ package: Some(package),
+ project: None,
+ profile: me.profile.clone(),
+ lib: me.lib.clone(),
+ bin: me.bin.clone(),
+ example: me.example.clone(),
+ test: me.test.clone(),
+ bench: me.bench.clone(),
+ dependencies: map_deps(config, me.dependencies.as_ref(), all)?,
+ dev_dependencies: map_deps(
+ config,
+ me.dev_dependencies(),
+ manifest::TomlDependency::is_version_specified,
+ )?,
+ dev_dependencies2: None,
+ build_dependencies: map_deps(config, me.build_dependencies(), all)?,
+ build_dependencies2: None,
+ features: me.features.clone(),
+ target: match me.target.as_ref().map(|target_map| {
+ target_map
.iter()
- .filter(|(_k, v)| {
- if let schema::MaybeWorkspace::Defined(def) = v {
- filter(def)
- } else {
- false
- }
+ .map(|(k, v)| {
+ Ok((
+ k.clone(),
+ manifest::TomlPlatform {
+ dependencies: map_deps(config, v.dependencies.as_ref(), all)?,
+ dev_dependencies: map_deps(
+ config,
+ v.dev_dependencies(),
+ manifest::TomlDependency::is_version_specified,
+ )?,
+ dev_dependencies2: None,
+ build_dependencies: map_deps(config, v.build_dependencies(), all)?,
+ build_dependencies2: None,
+ },
+ ))
})
- .map(|(k, v)| Ok((k.clone(), map_dependency(config, v)?)))
- .collect::<CargoResult<BTreeMap<_, _>>>()?;
- Ok(Some(deps))
- }
-
- fn map_dependency(
- config: &Config,
- dep: &schema::MaybeWorkspaceDependency,
- ) -> CargoResult<schema::MaybeWorkspaceDependency> {
- let dep = match dep {
- schema::MaybeWorkspace::Defined(schema::TomlDependency::Detailed(d)) => {
- let mut d = d.clone();
- // Path dependencies become crates.io deps.
- d.path.take();
- // Same with git dependencies.
- d.git.take();
- d.branch.take();
- d.tag.take();
- d.rev.take();
- // registry specifications are elaborated to the index URL
- if let Some(registry) = d.registry.take() {
- d.registry_index = Some(config.get_registry_index(&registry)?.to_string());
- }
- Ok(d)
- }
- schema::MaybeWorkspace::Defined(schema::TomlDependency::Simple(s)) => {
- Ok(schema::DetailedTomlDependency {
- version: Some(s.clone()),
- ..Default::default()
- })
+ .collect()
+ }) {
+ Some(Ok(v)) => Some(v),
+ Some(Err(e)) => return Err(e),
+ None => None,
+ },
+ replace: None,
+ patch: None,
+ workspace: None,
+ badges: me.badges.clone(),
+ cargo_features: me.cargo_features.clone(),
+ lints: me.lints.clone(),
+ });
+
+ fn map_deps(
+ config: &Config,
+ deps: Option<&BTreeMap<manifest::PackageName, manifest::InheritableDependency>>,
+ filter: impl Fn(&manifest::TomlDependency) -> bool,
+ ) -> CargoResult<Option<BTreeMap<manifest::PackageName, manifest::InheritableDependency>>> {
+ let Some(deps) = deps else { return Ok(None) };
+ let deps = deps
+ .iter()
+ .filter(|(_k, v)| {
+ if let manifest::InheritableDependency::Value(def) = v {
+ filter(def)
+ } else {
+ false
}
- _ => unreachable!(),
- };
- dep.map(schema::TomlDependency::Detailed)
- .map(schema::MaybeWorkspace::Defined)
- }
+ })
+ .map(|(k, v)| Ok((k.clone(), map_dependency(config, v)?)))
+ .collect::<CargoResult<BTreeMap<_, _>>>()?;
+ Ok(Some(deps))
}
- pub fn to_real_manifest(
- me: schema::TomlManifest,
- embedded: bool,
- source_id: SourceId,
- package_root: &Path,
+ fn map_dependency(
config: &Config,
- ) -> CargoResult<(Manifest, Vec<PathBuf>)> {
- fn get_ws(
- config: &Config,
- resolved_path: &Path,
- workspace_config: &WorkspaceConfig,
- ) -> CargoResult<schema::InheritableFields> {
- match workspace_config {
- WorkspaceConfig::Root(root) => Ok(root.inheritable().clone()),
- WorkspaceConfig::Member {
- root: Some(ref path_to_root),
- } => {
- let path = resolved_path
- .parent()
- .unwrap()
- .join(path_to_root)
- .join("Cargo.toml");
- let root_path = paths::normalize_path(&path);
- inheritable_from_path(config, root_path)
+ dep: &manifest::InheritableDependency,
+ ) -> CargoResult<manifest::InheritableDependency> {
+ let dep = match dep {
+ manifest::InheritableDependency::Value(manifest::TomlDependency::Detailed(d)) => {
+ let mut d = d.clone();
+ // Path dependencies become crates.io deps.
+ d.path.take();
+ // Same with git dependencies.
+ d.git.take();
+ d.branch.take();
+ d.tag.take();
+ d.rev.take();
+ // registry specifications are elaborated to the index URL
+ if let Some(registry) = d.registry.take() {
+ d.registry_index = Some(config.get_registry_index(&registry)?.to_string());
}
- WorkspaceConfig::Member { root: None } => {
- match find_workspace_root(&resolved_path, config)? {
- Some(path_to_root) => inheritable_from_path(config, path_to_root),
- None => Err(anyhow!("failed to find a workspace root")),
- }
+ Ok(d)
+ }
+ manifest::InheritableDependency::Value(manifest::TomlDependency::Simple(s)) => {
+ Ok(manifest::TomlDetailedDependency {
+ version: Some(s.clone()),
+ ..Default::default()
+ })
+ }
+ _ => unreachable!(),
+ };
+ dep.map(manifest::TomlDependency::Detailed)
+ .map(manifest::InheritableDependency::Value)
+ }
+}
+
+pub fn to_real_manifest(
+ me: manifest::TomlManifest,
+ embedded: bool,
+ source_id: SourceId,
+ package_root: &Path,
+ config: &Config,
+) -> CargoResult<(Manifest, Vec<PathBuf>)> {
+ fn get_ws(
+ config: &Config,
+ resolved_path: &Path,
+ workspace_config: &WorkspaceConfig,
+ ) -> CargoResult<InheritableFields> {
+ match workspace_config {
+ WorkspaceConfig::Root(root) => Ok(root.inheritable().clone()),
+ WorkspaceConfig::Member {
+ root: Some(ref path_to_root),
+ } => {
+ let path = resolved_path
+ .parent()
+ .unwrap()
+ .join(path_to_root)
+ .join("Cargo.toml");
+ let root_path = paths::normalize_path(&path);
+ inheritable_from_path(config, root_path)
+ }
+ WorkspaceConfig::Member { root: None } => {
+ match find_workspace_root(&resolved_path, config)? {
+ Some(path_to_root) => inheritable_from_path(config, path_to_root),
+ None => Err(anyhow!("failed to find a workspace root")),
}
}
}
+ }
- if !package_root.is_dir() {
- bail!(
- "package root '{}' is not a directory",
- package_root.display()
- );
- };
+ if !package_root.is_dir() {
+ bail!(
+ "package root '{}' is not a directory",
+ package_root.display()
+ );
+ };
- let mut nested_paths = vec![];
- let mut warnings = vec![];
- let mut errors = vec![];
+ let mut nested_paths = vec![];
+ let mut warnings = vec![];
+ let mut errors = vec![];
- // Parse features first so they will be available when parsing other parts of the TOML.
- let empty = Vec::new();
- let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty);
- let features = Features::new(cargo_features, config, &mut warnings, source_id.is_path())?;
+ // Parse features first so they will be available when parsing other parts of the TOML.
+ let empty = Vec::new();
+ let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty);
+ let features = Features::new(cargo_features, config, &mut warnings, source_id.is_path())?;
- let mut package = match (&me.package, &me.project) {
- (Some(_), Some(project)) => {
- if source_id.is_path() {
- config.shell().warn(format!(
- "manifest at `{}` contains both `project` and `package`, \
+ let mut package = match (&me.package, &me.project) {
+ (Some(_), Some(project)) => {
+ if source_id.is_path() {
+ config.shell().warn(format!(
+ "manifest at `{}` contains both `project` and `package`, \
this could become a hard error in the future",
- package_root.display()
- ))?;
- }
- project.clone()
+ package_root.display()
+ ))?;
}
- (Some(package), None) => package.clone(),
- (None, Some(project)) => {
- if source_id.is_path() {
- config.shell().warn(format!(
- "manifest at `{}` contains `[project]` instead of `[package]`, \
+ project.clone()
+ }
+ (Some(package), None) => package.clone(),
+ (None, Some(project)) => {
+ if source_id.is_path() {
+ config.shell().warn(format!(
+ "manifest at `{}` contains `[project]` instead of `[package]`, \
this could become a hard error in the future",
- package_root.display()
- ))?;
- }
- project.clone()
+ package_root.display()
+ ))?;
}
- (None, None) => bail!("no `package` section found"),
- };
+ project.clone()
+ }
+ (None, None) => bail!("no `package` section found"),
+ };
- let workspace_config = match (me.workspace.as_ref(), package.workspace.as_ref()) {
- (Some(toml_config), None) => {
- let mut inheritable = toml_config.package.clone().unwrap_or_default();
- inheritable.update_ws_path(package_root.to_path_buf());
- inheritable.update_deps(toml_config.dependencies.clone());
- let lints = toml_config.lints.clone();
- let lints = verify_lints(lints)?;
- inheritable.update_lints(lints);
- if let Some(ws_deps) = &inheritable.dependencies {
- for (name, dep) in ws_deps {
- unused_dep_keys(
- name,
- "workspace.dependencies",
- dep.unused_keys(),
- &mut warnings,
- );
- }
+ let workspace_config = match (me.workspace.as_ref(), package.workspace.as_ref()) {
+ (Some(toml_config), None) => {
+ let lints = toml_config.lints.clone();
+ let lints = verify_lints(lints)?;
+ let inheritable = InheritableFields {
+ package: toml_config.package.clone(),
+ dependencies: toml_config.dependencies.clone(),
+ lints,
+ _ws_root: package_root.to_path_buf(),
+ };
+ if let Some(ws_deps) = &inheritable.dependencies {
+ for (name, dep) in ws_deps {
+ unused_dep_keys(
+ name,
+ "workspace.dependencies",
+ dep.unused_keys(),
+ &mut warnings,
+ );
}
- let ws_root_config = WorkspaceRootConfig::new(
- package_root,
- &toml_config.members,
- &toml_config.default_members,
- &toml_config.exclude,
- &Some(inheritable),
- &toml_config.metadata,
- );
- config
- .ws_roots
- .borrow_mut()
- .insert(package_root.to_path_buf(), ws_root_config.clone());
- WorkspaceConfig::Root(ws_root_config)
}
- (None, root) => WorkspaceConfig::Member {
- root: root.cloned(),
- },
- (Some(..), Some(..)) => bail!(
- "cannot configure both `package.workspace` and \
- `[workspace]`, only one can be specified"
- ),
- };
-
- let package_name = package.name.trim();
- if package_name.is_empty() {
- bail!("package name cannot be an empty string")
+ let ws_root_config = WorkspaceRootConfig::new(
+ package_root,
+ &toml_config.members,
+ &toml_config.default_members,
+ &toml_config.exclude,
+ &Some(inheritable),
+ &toml_config.metadata,
+ );
+ config
+ .ws_roots
+ .borrow_mut()
+ .insert(package_root.to_path_buf(), ws_root_config.clone());
+ WorkspaceConfig::Root(ws_root_config)
}
+ (None, root) => WorkspaceConfig::Member {
+ root: root.cloned(),
+ },
+ (Some(..), Some(..)) => bail!(
+ "cannot configure both `package.workspace` and \
+ `[workspace]`, only one can be specified"
+ ),
+ };
- validate_package_name(package_name, "package name", "")?;
+ let package_name = package.name.trim();
- let resolved_path = package_root.join("Cargo.toml");
+ let resolved_path = package_root.join("Cargo.toml");
- let inherit_cell: LazyCell<schema::InheritableFields> = LazyCell::new();
- let inherit =
- || inherit_cell.try_borrow_with(|| get_ws(config, &resolved_path, &workspace_config));
+ let inherit_cell: LazyCell<InheritableFields> = LazyCell::new();
+ let inherit =
+ || inherit_cell.try_borrow_with(|| get_ws(config, &resolved_path, &workspace_config));
- let version = package
- .version
- .clone()
- .map(|version| version.resolve("version", || inherit()?.version()))
- .transpose()?;
+ let version = package
+ .version
+ .clone()
+ .map(|version| field_inherit_with(version, "version", || inherit()?.version()))
+ .transpose()?;
- package.version = version.clone().map(schema::MaybeWorkspace::Defined);
+ package.version = version.clone().map(manifest::InheritableField::Value);
- let pkgid = package.to_package_id(
- source_id,
- version
- .clone()
- .unwrap_or_else(|| semver::Version::new(0, 0, 0)),
- );
+ let pkgid = PackageId::new(
+ package.name.as_str().into(),
+ version
+ .clone()
+ .unwrap_or_else(|| semver::Version::new(0, 0, 0)),
+ source_id,
+ );
- 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(schema::MaybeWorkspace::Defined(edition.to_string()));
+ let edition = if let Some(edition) = package.edition.clone() {
+ let edition: Edition = field_inherit_with(edition, "edition", || inherit()?.edition())?
+ .parse()
+ .with_context(|| "failed to parse the `edition` key")?;
+ package.edition = Some(manifest::InheritableField::Value(edition.to_string()));
+ edition
+ } else {
+ Edition::Edition2015
+ };
+ // Add these lines if start a new unstable edition.
+ // ```
+ // if edition == Edition::Edition20xx {
+ // features.require(Feature::edition20xx())?;
+ // }
+ // ```
+ 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",
edition
- } else {
- Edition::Edition2015
- };
- // Add these lines if start a new unstable edition.
- // ```
- // if edition == Edition::Edition20xx {
- // features.require(Feature::edition20xx())?;
- // }
- // ```
- 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",
- edition
- )));
- }
+ )));
+ }
- let rust_version = if let Some(rust_version) = &package.rust_version {
- let rust_version = rust_version
- .clone()
- .resolve("rust_version", || inherit()?.rust_version())?;
- 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);
- if req.matches(&unsupported) {
- bail!(
- "rust-version {} is older than first version ({}) required by \
+ let rust_version = if let Some(rust_version) = &package.rust_version {
+ let rust_version = field_inherit_with(rust_version.clone(), "rust_version", || {
+ inherit()?.rust_version()
+ })?;
+ 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);
+ if req.matches(&unsupported) {
+ bail!(
+ "rust-version {} is older than first version ({}) required by \
the specified edition ({})",
- rust_version,
- first_version,
- edition,
- )
- }
+ rust_version,
+ first_version,
+ edition,
+ )
}
- Some(rust_version)
- } else {
- None
- };
-
- if package.metabuild.is_some() {
- features.require(Feature::metabuild())?;
}
+ Some(rust_version)
+ } else {
+ None
+ };
- let resolve_behavior = match (
- package.resolver.as_ref(),
- me.workspace.as_ref().and_then(|ws| ws.resolver.as_ref()),
- ) {
- (None, None) => None,
- (Some(s), None) | (None, Some(s)) => Some(ResolveBehavior::from_manifest(s)?),
- (Some(_), Some(_)) => {
- bail!("cannot specify `resolver` field in both `[workspace]` and `[package]`")
- }
- };
-
- // If we have no lib at all, use the inferred lib, if available.
- // If we have a lib with a path, we're done.
- // If we have a lib with no path, use the inferred lib or else the package name.
- let targets = targets(
- &features,
- &me,
- package_name,
- package_root,
- edition,
- &package.build,
- &package.metabuild,
- &mut warnings,
- &mut errors,
- )?;
+ if package.metabuild.is_some() {
+ features.require(Feature::metabuild())?;
+ }
- if targets.is_empty() {
- debug!("manifest has no build targets");
+ let resolve_behavior = match (
+ package.resolver.as_ref(),
+ me.workspace.as_ref().and_then(|ws| ws.resolver.as_ref()),
+ ) {
+ (None, None) => None,
+ (Some(s), None) | (None, Some(s)) => Some(ResolveBehavior::from_manifest(s)?),
+ (Some(_), Some(_)) => {
+ bail!("cannot specify `resolver` field in both `[workspace]` and `[package]`")
}
+ };
- if let Err(conflict_targets) = unique_build_targets(&targets, package_root) {
- conflict_targets
- .iter()
- .for_each(|(target_path, conflicts)| {
- warnings.push(format!(
- "file `{}` found to be present in multiple \
+ // If we have no lib at all, use the inferred lib, if available.
+ // If we have a lib with a path, we're done.
+ // If we have a lib with no path, use the inferred lib or else the package name.
+ let targets = targets(
+ &features,
+ &me,
+ package_name,
+ package_root,
+ edition,
+ &package.build,
+ &package.metabuild,
+ &mut warnings,
+ &mut errors,
+ )?;
+
+ if targets.is_empty() {
+ debug!("manifest has no build targets");
+ }
+
+ if let Err(conflict_targets) = unique_build_targets(&targets, package_root) {
+ conflict_targets
+ .iter()
+ .for_each(|(target_path, conflicts)| {
+ warnings.push(format!(
+ "file `{}` found to be present in multiple \
build targets:\n{}",
- target_path.display().to_string(),
- conflicts
- .iter()
- .map(|t| format!(
- " * `{}` target `{}`",
- t.kind().description(),
- t.name(),
- ))
- .join("\n")
- ));
- })
- }
+ target_path.display().to_string(),
+ conflicts
+ .iter()
+ .map(|t| format!(" * `{}` target `{}`", t.kind().description(), t.name(),))
+ .join("\n")
+ ));
+ })
+ }
- if let Some(links) = &package.links {
- if !targets.iter().any(|t| t.is_custom_build()) {
- bail!(
- "package `{}` specifies that it links to `{}` but does not \
+ if let Some(links) = &package.links {
+ if !targets.iter().any(|t| t.is_custom_build()) {
+ bail!(
+ "package `{}` specifies that it links to `{}` but does not \
have a custom build script",
- pkgid,
- links
- )
- }
+ pkgid,
+ links
+ )
}
+ }
- let mut deps = Vec::new();
+ let mut deps = Vec::new();
+
+ let mut cx = Context {
+ deps: &mut deps,
+ source_id,
+ nested_paths: &mut nested_paths,
+ config,
+ warnings: &mut warnings,
+ features: &features,
+ platform: None,
+ root: package_root,
+ };
- let mut cx = Context {
- deps: &mut deps,
- source_id,
- nested_paths: &mut nested_paths,
- config,
- warnings: &mut warnings,
- features: &features,
- platform: None,
- root: package_root,
+ fn process_dependencies(
+ cx: &mut Context<'_, '_>,
+ new_deps: Option<&BTreeMap<manifest::PackageName, manifest::InheritableDependency>>,
+ kind: Option<DepKind>,
+ workspace_config: &WorkspaceConfig,
+ inherit_cell: &LazyCell<InheritableFields>,
+ ) -> CargoResult<Option<BTreeMap<manifest::PackageName, manifest::InheritableDependency>>> {
+ let Some(dependencies) = new_deps else {
+ return Ok(None);
};
- fn process_dependencies(
- cx: &mut Context<'_, '_>,
- new_deps: Option<&BTreeMap<String, schema::MaybeWorkspaceDependency>>,
- kind: Option<DepKind>,
- workspace_config: &WorkspaceConfig,
- inherit_cell: &LazyCell<schema::InheritableFields>,
- ) -> CargoResult<Option<BTreeMap<String, schema::MaybeWorkspaceDependency>>> {
- let Some(dependencies) = new_deps else {
- return Ok(None);
- };
+ let inheritable = || {
+ inherit_cell.try_borrow_with(|| {
+ get_ws(cx.config, &cx.root.join("Cargo.toml"), &workspace_config)
+ })
+ };
- let inheritable = || {
- inherit_cell.try_borrow_with(|| {
- get_ws(cx.config, &cx.root.join("Cargo.toml"), &workspace_config)
- })
+ let mut deps: BTreeMap<manifest::PackageName, manifest::InheritableDependency> =
+ BTreeMap::new();
+ for (n, v) in dependencies.iter() {
+ let resolved = dependency_inherit_with(v.clone(), n, inheritable, cx)?;
+ let dep = dep_to_dependency(&resolved, n, cx, kind)?;
+ let name_in_toml = dep.name_in_toml().as_str();
+ let kind_name = match kind {
+ Some(k) => k.kind_table(),
+ None => "dependencies",
};
-
- let mut deps: BTreeMap<String, schema::MaybeWorkspaceDependency> = BTreeMap::new();
- for (n, v) in dependencies.iter() {
- let resolved = v
- .clone()
- .resolve_with_self(n, |dep| dep.resolve(n, inheritable, cx))?;
- let dep = resolved.to_dependency(n, cx, kind)?;
- let name_in_toml = dep.name_in_toml().as_str();
- validate_package_name(name_in_toml, "dependency name", "")?;
- let kind_name = match kind {
- Some(k) => k.kind_table(),
- None => "dependencies",
- };
- let table_in_toml = if let Some(platform) = &cx.platform {
- format!("target.{}.{kind_name}", platform.to_string())
- } else {
- kind_name.to_string()
- };
- unused_dep_keys(name_in_toml, &table_in_toml, v.unused_keys(), cx.warnings);
- cx.deps.push(dep);
- deps.insert(
- n.to_string(),
- schema::MaybeWorkspace::Defined(resolved.clone()),
- );
- }
- Ok(Some(deps))
+ let table_in_toml = if let Some(platform) = &cx.platform {
+ format!("target.{}.{kind_name}", platform.to_string())
+ } else {
+ kind_name.to_string()
+ };
+ unused_dep_keys(name_in_toml, &table_in_toml, v.unused_keys(), cx.warnings);
+ cx.deps.push(dep);
+ deps.insert(
+ n.clone(),
+ manifest::InheritableDependency::Value(resolved.clone()),
+ );
}
+ Ok(Some(deps))
+ }
- // Collect the dependencies.
- let dependencies = process_dependencies(
+ // Collect the dependencies.
+ let dependencies = process_dependencies(
+ &mut cx,
+ me.dependencies.as_ref(),
+ None,
+ &workspace_config,
+ &inherit_cell,
+ )?;
+ 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();
+ let dev_deps = process_dependencies(
+ &mut cx,
+ dev_deps,
+ Some(DepKind::Development),
+ &workspace_config,
+ &inherit_cell,
+ )?;
+ 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();
+ let build_deps = process_dependencies(
+ &mut cx,
+ build_deps,
+ Some(DepKind::Build),
+ &workspace_config,
+ &inherit_cell,
+ )?;
+
+ let lints = me
+ .lints
+ .clone()
+ .map(|mw| lints_inherit_with(mw, || inherit()?.lints()))
+ .transpose()?;
+ let lints = verify_lints(lints)?;
+ let default = manifest::TomlLints::default();
+ let rustflags = lints_to_rustflags(lints.as_ref().unwrap_or(&default));
+
+ let mut target: BTreeMap<String, manifest::TomlPlatform> = BTreeMap::new();
+ for (name, platform) in me.target.iter().flatten() {
+ cx.platform = {
+ let platform: Platform = name.parse()?;
+ platform.check_cfg_attributes(cx.warnings);
+ Some(platform)
+ };
+ let deps = process_dependencies(
&mut cx,
- me.dependencies.as_ref(),
+ platform.dependencies.as_ref(),
None,
&workspace_config,
&inherit_cell,
)?;
- if me.dev_dependencies.is_some() && me.dev_dependencies2.is_some() {
- warn_on_deprecated("dev-dependencies", package_name, "package", cx.warnings);
+ if platform.build_dependencies.is_some() && platform.build_dependencies2.is_some() {
+ warn_on_deprecated("build-dependencies", name, "platform target", cx.warnings);
}
- let dev_deps = me.dev_dependencies();
- let dev_deps = process_dependencies(
+ let build_deps = platform.build_dependencies();
+ let build_deps = process_dependencies(
&mut cx,
- dev_deps,
- Some(DepKind::Development),
+ build_deps,
+ Some(DepKind::Build),
&workspace_config,
&inherit_cell,
)?;
- if me.build_dependencies.is_some() && me.build_dependencies2.is_some() {
- warn_on_deprecated("build-dependencies", package_name, "package", cx.warnings);
+ if platform.dev_dependencies.is_some() && platform.dev_dependencies2.is_some() {
+ warn_on_deprecated("dev-dependencies", name, "platform target", cx.warnings);
}
- let build_deps = me.build_dependencies();
- let build_deps = process_dependencies(
+ let dev_deps = platform.dev_dependencies();
+ let dev_deps = process_dependencies(
&mut cx,
- build_deps,
- Some(DepKind::Build),
+ dev_deps,
+ Some(DepKind::Development),
&workspace_config,
&inherit_cell,
)?;
+ target.insert(
+ name.clone(),
+ manifest::TomlPlatform {
+ dependencies: deps,
+ build_dependencies: build_deps,
+ build_dependencies2: None,
+ dev_dependencies: dev_deps,
+ dev_dependencies2: None,
+ },
+ );
+ }
- let lints = me
- .lints
- .clone()
- .map(|mw| mw.resolve(|| inherit()?.lints()))
- .transpose()?;
- let lints = verify_lints(lints)?;
- let default = schema::TomlLints::default();
- let rustflags = lints_to_rustflags(lints.as_ref().unwrap_or(&default));
-
- let mut target: BTreeMap<String, schema::TomlPlatform> = BTreeMap::new();
- for (name, platform) in me.target.iter().flatten() {
- cx.platform = {
- let platform: Platform = name.parse()?;
- platform.check_cfg_attributes(cx.warnings);
- Some(platform)
- };
- let deps = process_dependencies(
- &mut cx,
- platform.dependencies.as_ref(),
- None,
- &workspace_config,
- &inherit_cell,
- )?;
- 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();
- let build_deps = process_dependencies(
- &mut cx,
- build_deps,
- Some(DepKind::Build),
- &workspace_config,
- &inherit_cell,
- )?;
- 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();
- let dev_deps = process_dependencies(
- &mut cx,
- dev_deps,
- Some(DepKind::Development),
- &workspace_config,
- &inherit_cell,
- )?;
- target.insert(
- name.clone(),
- schema::TomlPlatform {
- dependencies: deps,
- build_dependencies: build_deps,
- build_dependencies2: None,
- dev_dependencies: dev_deps,
- dev_dependencies2: None,
- },
- );
- }
-
- let target = if target.is_empty() {
- None
- } else {
- Some(target)
- };
- let replace = me.replace(&mut cx)?;
- let patch = me.patch(&mut cx)?;
+ let target = if target.is_empty() {
+ None
+ } else {
+ Some(target)
+ };
+ let replace = replace(&me, &mut cx)?;
+ let patch = patch(&me, &mut cx)?;
- {
- let mut names_sources = BTreeMap::new();
- for dep in &deps {
- let name = dep.name_in_toml();
- let prev = names_sources.insert(name, dep.source_id());
- if prev.is_some() && prev != Some(dep.source_id()) {
- bail!(
- "Dependency '{}' has different source paths depending on the build \
+ {
+ let mut names_sources = BTreeMap::new();
+ for dep in &deps {
+ let name = dep.name_in_toml();
+ let prev = names_sources.insert(name, dep.source_id());
+ if prev.is_some() && prev != Some(dep.source_id()) {
+ bail!(
+ "Dependency '{}' has different source paths depending on the build \
target. Each dependency must have a single canonical source path \
irrespective of build target.",
- name
- );
- }
+ name
+ );
}
}
+ }
- let exclude = package
- .exclude
- .clone()
- .map(|mw| mw.resolve("exclude", || inherit()?.exclude()))
- .transpose()?
- .unwrap_or_default();
- let include = package
- .include
- .clone()
- .map(|mw| mw.resolve("include", || inherit()?.include()))
- .transpose()?
- .unwrap_or_default();
- let empty_features = BTreeMap::new();
-
- let summary = Summary::new(
- pkgid,
- deps,
- &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(),
- )?;
+ let exclude = package
+ .exclude
+ .clone()
+ .map(|mw| field_inherit_with(mw, "exclude", || inherit()?.exclude()))
+ .transpose()?
+ .unwrap_or_default();
+ let include = package
+ .include
+ .clone()
+ .map(|mw| field_inherit_with(mw, "include", || inherit()?.include()))
+ .transpose()?
+ .unwrap_or_default();
+ let empty_features = BTreeMap::new();
+
+ let summary = Summary::new(
+ pkgid,
+ deps,
+ &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(),
+ )?;
- let metadata = ManifestMetadata {
- description: package
- .description
- .clone()
- .map(|mw| mw.resolve("description", || inherit()?.description()))
- .transpose()?,
- homepage: package
- .homepage
- .clone()
- .map(|mw| mw.resolve("homepage", || inherit()?.homepage()))
- .transpose()?,
- documentation: package
- .documentation
- .clone()
- .map(|mw| mw.resolve("documentation", || inherit()?.documentation()))
- .transpose()?,
- readme: readme_for_package(
- package_root,
- package
- .readme
- .clone()
- .map(|mw| mw.resolve("readme", || inherit()?.readme(package_root)))
- .transpose()?
- .as_ref(),
- ),
- authors: package
- .authors
- .clone()
- .map(|mw| mw.resolve("authors", || inherit()?.authors()))
- .transpose()?
- .unwrap_or_default(),
- license: package
- .license
- .clone()
- .map(|mw| mw.resolve("license", || inherit()?.license()))
- .transpose()?,
- license_file: package
- .license_file
- .clone()
- .map(|mw| mw.resolve("license", || inherit()?.license_file(package_root)))
- .transpose()?,
- repository: package
- .repository
- .clone()
- .map(|mw| mw.resolve("repository", || inherit()?.repository()))
- .transpose()?,
- keywords: package
- .keywords
- .clone()
- .map(|mw| mw.resolve("keywords", || inherit()?.keywords()))
- .transpose()?
- .unwrap_or_default(),
- categories: package
- .categories
- .clone()
- .map(|mw| mw.resolve("categories", || inherit()?.categories()))
- .transpose()?
- .unwrap_or_default(),
- badges: me
- .badges
- .clone()
- .map(|mw| mw.resolve("badges", || inherit()?.badges()))
- .transpose()?
- .unwrap_or_default(),
- links: package.links.clone(),
- rust_version: package
- .rust_version
- .map(|mw| mw.resolve("rust-version", || inherit()?.rust_version()))
- .transpose()?,
- };
- package.description = metadata
+ let metadata = ManifestMetadata {
+ description: package
.description
.clone()
- .map(|description| schema::MaybeWorkspace::Defined(description));
- package.homepage = metadata
+ .map(|mw| field_inherit_with(mw, "description", || inherit()?.description()))
+ .transpose()?,
+ homepage: package
.homepage
.clone()
- .map(|homepage| schema::MaybeWorkspace::Defined(homepage));
- package.documentation = metadata
+ .map(|mw| field_inherit_with(mw, "homepage", || inherit()?.homepage()))
+ .transpose()?,
+ documentation: package
.documentation
.clone()
- .map(|documentation| schema::MaybeWorkspace::Defined(documentation));
- package.readme = metadata
- .readme
- .clone()
- .map(|readme| schema::MaybeWorkspace::Defined(schema::StringOrBool::String(readme)));
- package.authors = package
+ .map(|mw| field_inherit_with(mw, "documentation", || inherit()?.documentation()))
+ .transpose()?,
+ readme: readme_for_package(
+ package_root,
+ package
+ .readme
+ .clone()
+ .map(|mw| field_inherit_with(mw, "readme", || inherit()?.readme(package_root)))
+ .transpose()?
+ .as_ref(),
+ ),
+ authors: package
.authors
- .as_ref()
- .map(|_| schema::MaybeWorkspace::Defined(metadata.authors.clone()));
- package.license = metadata
+ .clone()
+ .map(|mw| field_inherit_with(mw, "authors", || inherit()?.authors()))
+ .transpose()?
+ .unwrap_or_default(),
+ license: package
.license
.clone()
- .map(|license| schema::MaybeWorkspace::Defined(license));
- package.license_file = metadata
+ .map(|mw| field_inherit_with(mw, "license", || inherit()?.license()))
+ .transpose()?,
+ license_file: package
.license_file
.clone()
- .map(|license_file| schema::MaybeWorkspace::Defined(license_file));
- package.repository = metadata
+ .map(|mw| field_inherit_with(mw, "license", || inherit()?.license_file(package_root)))
+ .transpose()?,
+ repository: package
.repository
.clone()
- .map(|repository| schema::MaybeWorkspace::Defined(repository));
- package.keywords = package
+ .map(|mw| field_inherit_with(mw, "repository", || inherit()?.repository()))
+ .transpose()?,
+ keywords: package
.keywords
- .as_ref()
- .map(|_| schema::MaybeWorkspace::Defined(metadata.keywords.clone()));
- package.categories = package
+ .clone()
+ .map(|mw| field_inherit_with(mw, "keywords", || inherit()?.keywords()))
+ .transpose()?
+ .unwrap_or_default(),
+ categories: package
.categories
- .as_ref()
- .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(|_| schema::MaybeWorkspace::Defined(exclude.clone()));
- package.include = package
- .include
- .as_ref()
- .map(|_| schema::MaybeWorkspace::Defined(include.clone()));
+ .map(|mw| field_inherit_with(mw, "categories", || inherit()?.categories()))
+ .transpose()?
+ .unwrap_or_default(),
+ badges: me
+ .badges
+ .clone()
+ .map(|mw| field_inherit_with(mw, "badges", || inherit()?.badges()))
+ .transpose()?
+ .unwrap_or_default(),
+ links: package.links.clone(),
+ rust_version: package
+ .rust_version
+ .map(|mw| field_inherit_with(mw, "rust-version", || inherit()?.rust_version()))
+ .transpose()?,
+ };
+ package.description = metadata
+ .description
+ .clone()
+ .map(|description| manifest::InheritableField::Value(description));
+ package.homepage = metadata
+ .homepage
+ .clone()
+ .map(|homepage| manifest::InheritableField::Value(homepage));
+ package.documentation = metadata
+ .documentation
+ .clone()
+ .map(|documentation| manifest::InheritableField::Value(documentation));
+ package.readme = metadata
+ .readme
+ .clone()
+ .map(|readme| manifest::InheritableField::Value(manifest::StringOrBool::String(readme)));
+ package.authors = package
+ .authors
+ .as_ref()
+ .map(|_| manifest::InheritableField::Value(metadata.authors.clone()));
+ package.license = metadata
+ .license
+ .clone()
+ .map(|license| manifest::InheritableField::Value(license));
+ package.license_file = metadata
+ .license_file
+ .clone()
+ .map(|license_file| manifest::InheritableField::Value(license_file));
+ package.repository = metadata
+ .repository
+ .clone()
+ .map(|repository| manifest::InheritableField::Value(repository));
+ package.keywords = package
+ .keywords
+ .as_ref()
+ .map(|_| manifest::InheritableField::Value(metadata.keywords.clone()));
+ package.categories = package
+ .categories
+ .as_ref()
+ .map(|_| manifest::InheritableField::Value(metadata.categories.clone()));
+ package.rust_version = rust_version
+ .clone()
+ .map(|rv| manifest::InheritableField::Value(rv));
+ package.exclude = package
+ .exclude
+ .as_ref()
+ .map(|_| manifest::InheritableField::Value(exclude.clone()));
+ package.include = package
+ .include
+ .as_ref()
+ .map(|_| manifest::InheritableField::Value(include.clone()));
- let profiles = me.profile.clone();
- if let Some(profiles) = &profiles {
- let cli_unstable = config.cli_unstable();
- profiles.validate(cli_unstable, &features, &mut warnings)?;
- }
+ let profiles = me.profile.clone();
+ if let Some(profiles) = &profiles {
+ let cli_unstable = config.cli_unstable();
+ validate_profiles(profiles, cli_unstable, &features, &mut warnings)?;
+ }
- let publish = package
- .publish
- .clone()
- .map(|publish| publish.resolve("publish", || inherit()?.publish()).unwrap());
+ let publish = package
+ .publish
+ .clone()
+ .map(|publish| field_inherit_with(publish, "publish", || inherit()?.publish()).unwrap());
- package.publish = publish.clone().map(|p| schema::MaybeWorkspace::Defined(p));
+ package.publish = publish
+ .clone()
+ .map(|p| manifest::InheritableField::Value(p));
- let publish = match publish {
- 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![]),
- };
+ let publish = match publish {
+ Some(manifest::VecStringOrBool::VecString(ref vecstring)) => Some(vecstring.clone()),
+ Some(manifest::VecStringOrBool::Bool(false)) => Some(vec![]),
+ Some(manifest::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 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]. \
+ if summary.features().contains_key("default-features") {
+ warnings.push(
+ "`default-features = [\"..\"]` was found in [features]. \
Did you mean to use `default = [\"..\"]`?"
- .to_string(),
- )
- }
+ .to_string(),
+ )
+ }
- if let Some(run) = &package.default_run {
- if !targets
- .iter()
- .filter(|t| t.is_bin())
- .any(|t| t.name() == run)
- {
- let suggestion =
- util::closest_msg(run, targets.iter().filter(|t| t.is_bin()), |t| t.name());
- bail!("default-run target `{}` not found{}", run, suggestion);
- }
+ if let Some(run) = &package.default_run {
+ if !targets
+ .iter()
+ .filter(|t| t.is_bin())
+ .any(|t| t.name() == run)
+ {
+ let suggestion =
+ util::closest_msg(run, targets.iter().filter(|t| t.is_bin()), |t| t.name());
+ bail!("default-run target `{}` not found{}", run, suggestion);
}
+ }
- let default_kind = package
- .default_target
- .as_ref()
- .map(|t| CompileTarget::new(&*t))
- .transpose()?
- .map(CompileKind::Target);
- let forced_kind = package
- .forced_target
+ let default_kind = package
+ .default_target
+ .as_ref()
+ .map(|t| CompileTarget::new(&*t))
+ .transpose()?
+ .map(CompileKind::Target);
+ let forced_kind = package
+ .forced_target
+ .as_ref()
+ .map(|t| CompileTarget::new(&*t))
+ .transpose()?
+ .map(CompileKind::Target);
+ let custom_metadata = package.metadata.clone();
+ let resolved_toml = manifest::TomlManifest {
+ cargo_features: me.cargo_features.clone(),
+ package: Some(package.clone()),
+ project: None,
+ profile: me.profile.clone(),
+ lib: me.lib.clone(),
+ bin: me.bin.clone(),
+ example: me.example.clone(),
+ test: me.test.clone(),
+ bench: me.bench.clone(),
+ dependencies,
+ dev_dependencies: dev_deps,
+ dev_dependencies2: None,
+ build_dependencies: build_deps,
+ build_dependencies2: None,
+ features: me.features.clone(),
+ target,
+ replace: me.replace.clone(),
+ patch: me.patch.clone(),
+ workspace: me.workspace.clone(),
+ badges: me
+ .badges
.as_ref()
- .map(|t| CompileTarget::new(&*t))
- .transpose()?
- .map(CompileKind::Target);
- let custom_metadata = package.metadata.clone();
- let resolved_toml = schema::TomlManifest {
- cargo_features: me.cargo_features.clone(),
- package: Some(package.clone()),
- project: None,
- profile: me.profile.clone(),
- lib: me.lib.clone(),
- bin: me.bin.clone(),
- example: me.example.clone(),
- test: me.test.clone(),
- bench: me.bench.clone(),
- dependencies,
- dev_dependencies: dev_deps,
- dev_dependencies2: None,
- build_dependencies: build_deps,
- build_dependencies2: None,
- features: me.features.clone(),
- target,
- replace: me.replace.clone(),
- patch: me.patch.clone(),
- workspace: me.workspace.clone(),
- badges: me
- .badges
- .as_ref()
- .map(|_| schema::MaybeWorkspace::Defined(metadata.badges.clone())),
- lints: lints.map(|lints| schema::MaybeWorkspaceLints {
- workspace: false,
- lints,
- }),
- };
- let mut manifest = Manifest::new(
- summary,
- default_kind,
- forced_kind,
- targets,
- exclude,
- include,
- package.links.clone(),
- metadata,
- custom_metadata,
- profiles,
- publish,
- replace,
- patch,
- workspace_config,
- features,
- edition,
- rust_version,
- package.im_a_teapot,
- package.default_run.clone(),
- Rc::new(resolved_toml),
- package.metabuild.clone().map(|sov| sov.0),
- resolve_behavior,
- rustflags,
- embedded,
- );
- if package.license_file.is_some() && package.license.is_some() {
- manifest.warnings_mut().add_warning(
+ .map(|_| manifest::InheritableField::Value(metadata.badges.clone())),
+ lints: lints.map(|lints| manifest::InheritableLints {
+ workspace: false,
+ lints,
+ }),
+ };
+ let mut manifest = Manifest::new(
+ summary,
+ default_kind,
+ forced_kind,
+ targets,
+ exclude,
+ include,
+ package.links.clone(),
+ metadata,
+ custom_metadata,
+ profiles,
+ publish,
+ replace,
+ patch,
+ workspace_config,
+ features,
+ edition,
+ rust_version,
+ package.im_a_teapot,
+ package.default_run.clone(),
+ Rc::new(resolved_toml),
+ package.metabuild.clone().map(|sov| sov.0),
+ resolve_behavior,
+ rustflags,
+ embedded,
+ );
+ if package.license_file.is_some() && package.license.is_some() {
+ manifest.warnings_mut().add_warning(
"only one of `license` or `license-file` is necessary\n\
`license` should be used if the package license can be expressed \
with a standard SPDX expression.\n\
@@ -1076,212 +1054,224 @@ impl schema::TomlManifest {
for more information."
.to_string(),
);
- }
- for warning in warnings {
- manifest.warnings_mut().add_warning(warning);
- }
- for error in errors {
- manifest.warnings_mut().add_critical_warning(error);
- }
+ }
+ for warning in warnings {
+ manifest.warnings_mut().add_warning(warning);
+ }
+ for error in errors {
+ manifest.warnings_mut().add_critical_warning(error);
+ }
- manifest.feature_gate()?;
+ manifest.feature_gate()?;
- Ok((manifest, nested_paths))
+ Ok((manifest, nested_paths))
+}
+
+fn to_virtual_manifest(
+ me: manifest::TomlManifest,
+ source_id: SourceId,
+ root: &Path,
+ config: &Config,
+) -> CargoResult<(VirtualManifest, Vec<PathBuf>)> {
+ if me.project.is_some() {
+ bail!("this virtual manifest specifies a [project] section, which is not allowed");
+ }
+ if me.package.is_some() {
+ bail!("this virtual manifest specifies a [package] section, which is not allowed");
+ }
+ if me.lib.is_some() {
+ bail!("this virtual manifest specifies a [lib] section, which is not allowed");
+ }
+ if me.bin.is_some() {
+ bail!("this virtual manifest specifies a [[bin]] section, which is not allowed");
+ }
+ if me.example.is_some() {
+ bail!("this virtual manifest specifies a [[example]] section, which is not allowed");
+ }
+ if me.test.is_some() {
+ bail!("this virtual manifest specifies a [[test]] section, which is not allowed");
+ }
+ if me.bench.is_some() {
+ bail!("this virtual manifest specifies a [[bench]] section, which is not allowed");
+ }
+ if me.dependencies.is_some() {
+ bail!("this virtual manifest specifies a [dependencies] section, which is not allowed");
+ }
+ 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() {
+ bail!(
+ "this virtual manifest specifies a [build-dependencies] section, which is not allowed"
+ );
+ }
+ if me.features.is_some() {
+ bail!("this virtual manifest specifies a [features] section, which is not allowed");
+ }
+ if me.target.is_some() {
+ bail!("this virtual manifest specifies a [target] section, which is not allowed");
+ }
+ if me.badges.is_some() {
+ bail!("this virtual manifest specifies a [badges] section, which is not allowed");
+ }
+ if me.lints.is_some() {
+ bail!("this virtual manifest specifies a [lints] section, which is not allowed");
}
- fn to_virtual_manifest(
- me: schema::TomlManifest,
- source_id: SourceId,
- root: &Path,
- config: &Config,
- ) -> CargoResult<(VirtualManifest, Vec<PathBuf>)> {
- if me.project.is_some() {
- bail!("this virtual manifest specifies a [project] section, which is not allowed");
- }
- if me.package.is_some() {
- bail!("this virtual manifest specifies a [package] section, which is not allowed");
- }
- if me.lib.is_some() {
- bail!("this virtual manifest specifies a [lib] section, which is not allowed");
- }
- if me.bin.is_some() {
- bail!("this virtual manifest specifies a [[bin]] section, which is not allowed");
- }
- if me.example.is_some() {
- bail!("this virtual manifest specifies a [[example]] section, which is not allowed");
- }
- if me.test.is_some() {
- bail!("this virtual manifest specifies a [[test]] section, which is not allowed");
- }
- if me.bench.is_some() {
- bail!("this virtual manifest specifies a [[bench]] section, which is not allowed");
- }
- if me.dependencies.is_some() {
- bail!("this virtual manifest specifies a [dependencies] section, which is not allowed");
- }
- 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() {
- bail!("this virtual manifest specifies a [build-dependencies] section, which is not allowed");
- }
- if me.features.is_some() {
- bail!("this virtual manifest specifies a [features] section, which is not allowed");
- }
- if me.target.is_some() {
- bail!("this virtual manifest specifies a [target] section, which is not allowed");
- }
- if me.badges.is_some() {
- bail!("this virtual manifest specifies a [badges] section, which is not allowed");
- }
+ let mut nested_paths = Vec::new();
+ let mut warnings = Vec::new();
+ let mut deps = Vec::new();
+ let empty = Vec::new();
+ let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty);
+ let features = Features::new(cargo_features, config, &mut warnings, source_id.is_path())?;
- let mut nested_paths = Vec::new();
- let mut warnings = Vec::new();
- let mut deps = Vec::new();
- let empty = Vec::new();
- let cargo_features = me.cargo_features.as_ref().unwrap_or(&empty);
- let features = Features::new(cargo_features, config, &mut warnings, source_id.is_path())?;
-
- let (replace, patch) = {
- let mut cx = Context {
- deps: &mut deps,
- source_id,
- nested_paths: &mut nested_paths,
- config,
- warnings: &mut warnings,
- platform: None,
- features: &features,
- root,
- };
- (me.replace(&mut cx)?, me.patch(&mut cx)?)
- };
- let profiles = me.profile.clone();
- if let Some(profiles) = &profiles {
- profiles.validate(config.cli_unstable(), &features, &mut warnings)?;
- }
- let resolve_behavior = me
- .workspace
- .as_ref()
- .and_then(|ws| ws.resolver.as_deref())
- .map(|r| ResolveBehavior::from_manifest(r))
- .transpose()?;
- let workspace_config = match me.workspace {
- Some(ref toml_config) => {
- let mut inheritable = toml_config.package.clone().unwrap_or_default();
- inheritable.update_ws_path(root.to_path_buf());
- inheritable.update_deps(toml_config.dependencies.clone());
- let lints = toml_config.lints.clone();
- let lints = verify_lints(lints)?;
- inheritable.update_lints(lints);
- let ws_root_config = WorkspaceRootConfig::new(
- root,
- &toml_config.members,
- &toml_config.default_members,
- &toml_config.exclude,
- &Some(inheritable),
- &toml_config.metadata,
- );
- config
- .ws_roots
- .borrow_mut()
- .insert(root.to_path_buf(), ws_root_config.clone());
- WorkspaceConfig::Root(ws_root_config)
- }
- None => {
- bail!("virtual manifests must be configured with [workspace]");
- }
+ let (replace, patch) = {
+ let mut cx = Context {
+ deps: &mut deps,
+ source_id,
+ nested_paths: &mut nested_paths,
+ config,
+ warnings: &mut warnings,
+ platform: None,
+ features: &features,
+ root,
};
- Ok((
- VirtualManifest::new(
- replace,
- patch,
- workspace_config,
- profiles,
- features,
- resolve_behavior,
- ),
- nested_paths,
- ))
+ (replace(&me, &mut cx)?, patch(&me, &mut cx)?)
+ };
+ let profiles = me.profile.clone();
+ if let Some(profiles) = &profiles {
+ validate_profiles(profiles, config.cli_unstable(), &features, &mut warnings)?;
}
-
- fn replace(&self, cx: &mut Context<'_, '_>) -> CargoResult<Vec<(PackageIdSpec, Dependency)>> {
- if self.patch.is_some() && self.replace.is_some() {
- bail!("cannot specify both [replace] and [patch]");
+ let resolve_behavior = me
+ .workspace
+ .as_ref()
+ .and_then(|ws| ws.resolver.as_deref())
+ .map(|r| ResolveBehavior::from_manifest(r))
+ .transpose()?;
+ let workspace_config = match me.workspace {
+ Some(ref toml_config) => {
+ let lints = toml_config.lints.clone();
+ let lints = verify_lints(lints)?;
+ let inheritable = InheritableFields {
+ package: toml_config.package.clone(),
+ dependencies: toml_config.dependencies.clone(),
+ lints,
+ _ws_root: root.to_path_buf(),
+ };
+ let ws_root_config = WorkspaceRootConfig::new(
+ root,
+ &toml_config.members,
+ &toml_config.default_members,
+ &toml_config.exclude,
+ &Some(inheritable),
+ &toml_config.metadata,
+ );
+ config
+ .ws_roots
+ .borrow_mut()
+ .insert(root.to_path_buf(), ws_root_config.clone());
+ WorkspaceConfig::Root(ws_root_config)
}
- let mut replace = Vec::new();
- for (spec, replacement) in self.replace.iter().flatten() {
- let mut spec = PackageIdSpec::parse(spec).with_context(|| {
- format!(
- "replacements must specify a valid semver \
+ None => {
+ bail!("virtual manifests must be configured with [workspace]");
+ }
+ };
+ Ok((
+ VirtualManifest::new(
+ replace,
+ patch,
+ workspace_config,
+ profiles,
+ features,
+ resolve_behavior,
+ ),
+ nested_paths,
+ ))
+}
+
+fn replace(
+ me: &manifest::TomlManifest,
+ cx: &mut Context<'_, '_>,
+) -> CargoResult<Vec<(PackageIdSpec, Dependency)>> {
+ if me.patch.is_some() && me.replace.is_some() {
+ bail!("cannot specify both [replace] and [patch]");
+ }
+ let mut replace = Vec::new();
+ for (spec, replacement) in me.replace.iter().flatten() {
+ let mut spec = PackageIdSpec::parse(spec).with_context(|| {
+ format!(
+ "replacements must specify a valid semver \
version to replace, but `{}` does not",
- spec
- )
- })?;
- if spec.url().is_none() {
- spec.set_url(CRATES_IO_INDEX.parse().unwrap());
- }
+ spec
+ )
+ })?;
+ if spec.url().is_none() {
+ spec.set_url(CRATES_IO_INDEX.parse().unwrap());
+ }
- if replacement.is_version_specified() {
- bail!(
- "replacements cannot specify a version \
+ if replacement.is_version_specified() {
+ bail!(
+ "replacements cannot specify a version \
requirement, but found one for `{}`",
- spec
- );
- }
-
- let mut dep = replacement.to_dependency(spec.name(), cx, None)?;
- let version = spec.version().ok_or_else(|| {
- anyhow!(
- "replacements must specify a version \
- to replace, but `{}` does not",
- spec
- )
- })?;
- unused_dep_keys(
- dep.name_in_toml().as_str(),
- "replace",
- replacement.unused_keys(),
- &mut cx.warnings,
+ spec
);
- dep.set_version_req(OptVersionReq::exact(&version));
- replace.push((spec, dep));
}
- Ok(replace)
- }
-
- fn patch(&self, cx: &mut Context<'_, '_>) -> CargoResult<HashMap<Url, Vec<Dependency>>> {
- let mut patch = HashMap::new();
- for (toml_url, deps) in self.patch.iter().flatten() {
- let url = match &toml_url[..] {
- CRATES_IO_REGISTRY => CRATES_IO_INDEX.parse().unwrap(),
- _ => cx
- .config
- .get_registry_index(toml_url)
- .or_else(|_| toml_url.into_url())
- .with_context(|| {
- format!(
- "[patch] entry `{}` should be a URL or registry name",
- toml_url
- )
- })?,
- };
- patch.insert(
- url,
- deps.iter()
- .map(|(name, dep)| {
- unused_dep_keys(
- name,
- &format!("patch.{toml_url}",),
- dep.unused_keys(),
- &mut cx.warnings,
- );
- dep.to_dependency(name, cx, None)
- })
- .collect::<CargoResult<Vec<_>>>()?,
- );
- }
- Ok(patch)
+
+ let mut dep = dep_to_dependency(replacement, spec.name(), cx, None)?;
+ let version = spec.version().ok_or_else(|| {
+ anyhow!(
+ "replacements must specify a version \
+ to replace, but `{}` does not",
+ spec
+ )
+ })?;
+ unused_dep_keys(
+ dep.name_in_toml().as_str(),
+ "replace",
+ replacement.unused_keys(),
+ &mut cx.warnings,
+ );
+ dep.set_version_req(OptVersionReq::exact(&version));
+ replace.push((spec, dep));
+ }
+ Ok(replace)
+}
+
+fn patch(
+ me: &manifest::TomlManifest,
+ cx: &mut Context<'_, '_>,
+) -> CargoResult<HashMap<Url, Vec<Dependency>>> {
+ let mut patch = HashMap::new();
+ for (toml_url, deps) in me.patch.iter().flatten() {
+ let url = match &toml_url[..] {
+ CRATES_IO_REGISTRY => CRATES_IO_INDEX.parse().unwrap(),
+ _ => cx
+ .config
+ .get_registry_index(toml_url)
+ .or_else(|_| toml_url.into_url())
+ .with_context(|| {
+ format!(
+ "[patch] entry `{}` should be a URL or registry name",
+ toml_url
+ )
+ })?,
+ };
+ patch.insert(
+ url,
+ deps.iter()
+ .map(|(name, dep)| {
+ unused_dep_keys(
+ name,
+ &format!("patch.{toml_url}",),
+ dep.unused_keys(),
+ &mut cx.warnings,
+ );
+ dep_to_dependency(dep, name, cx, None)
+ })
+ .collect::<CargoResult<Vec<_>>>()?,
+ );
}
+ Ok(patch)
}
struct Context<'a, 'b> {
@@ -1295,7 +1285,7 @@ struct Context<'a, 'b> {
features: &'a Features,
}
-fn verify_lints(lints: Option<schema::TomlLints>) -> CargoResult<Option<schema::TomlLints>> {
+fn verify_lints(lints: Option<manifest::TomlLints>) -> CargoResult<Option<manifest::TomlLints>> {
let Some(lints) = lints else {
return Ok(None);
};
@@ -1326,12 +1316,18 @@ fn verify_lints(lints: Option<schema::TomlLints>) -> CargoResult<Option<schema::
Ok(Some(lints))
}
-fn lints_to_rustflags(lints: &schema::TomlLints) -> Vec<String> {
+fn lints_to_rustflags(lints: &manifest::TomlLints) -> Vec<String> {
let mut rustflags = lints
.iter()
.flat_map(|(tool, lints)| {
lints.iter().map(move |(name, config)| {
- let flag = config.level().flag();
+ let flag = match config.level() {
+ manifest::TomlLintLevel::Forbid => "--forbid",
+ manifest::TomlLintLevel::Deny => "--deny",
+ manifest::TomlLintLevel::Warn => "--warn",
+ manifest::TomlLintLevel::Allow => "--allow",
+ };
+
let option = if tool == "rust" {
format!("{flag}={name}")
} else {
@@ -1366,7 +1362,7 @@ fn unused_dep_keys(
fn inheritable_from_path(
config: &Config,
workspace_path: PathBuf,
-) -> CargoResult<schema::InheritableFields> {
+) -> CargoResult<InheritableFields> {
// Workspace path should have Cargo.toml at the end
let workspace_path_root = workspace_path.parent().unwrap();
@@ -1393,17 +1389,17 @@ fn inheritable_from_path(
}
}
-/// Returns the name of the README file for a [`schema::TomlPackage`].
+/// Returns the name of the README file for a [`manifest::TomlPackage`].
fn readme_for_package(
package_root: &Path,
- readme: Option<&schema::StringOrBool>,
+ readme: Option<&manifest::StringOrBool>,
) -> Option<String> {
match &readme {
None => default_readme_from_package_root(package_root),
Some(value) => match value {
- schema::StringOrBool::Bool(false) => None,
- schema::StringOrBool::Bool(true) => Some("README.md".to_string()),
- schema::StringOrBool::String(v) => Some(v.clone()),
+ manifest::StringOrBool::Bool(false) => None,
+ manifest::StringOrBool::Bool(true) => Some("README.md".to_string()),
+ manifest::StringOrBool::String(v) => Some(v.clone()),
},
}
}
@@ -1449,13 +1445,13 @@ fn unique_build_targets(
}
/// Defines simple getter methods for inheritable fields.
-macro_rules! inheritable_field_getter {
+macro_rules! package_field_getter {
( $(($key:literal, $field:ident -> $ret:ty),)* ) => (
$(
- #[doc = concat!("Gets the field `workspace.", $key, "`.")]
+ #[doc = concat!("Gets the field `workspace.package", $key, "`.")]
fn $field(&self) -> CargoResult<$ret> {
- let Some(val) = &self.$field else {
- bail!("`workspace.{}` was not defined", $key);
+ let Some(val) = self.package.as_ref().and_then(|p| p.$field.as_ref()) else {
+ bail!("`workspace.package.{}` was not defined", $key);
};
Ok(val.clone())
}
@@ -1463,25 +1459,35 @@ macro_rules! inheritable_field_getter {
)
}
-impl schema::InheritableFields {
- inheritable_field_getter! {
+/// A group of fields that are inheritable by members of the workspace
+#[derive(Clone, Debug, Default)]
+pub struct InheritableFields {
+ package: Option<manifest::InheritablePackage>,
+ dependencies: Option<BTreeMap<manifest::PackageName, manifest::TomlDependency>>,
+ lints: Option<manifest::TomlLints>,
+
+ // Bookkeeping to help when resolving values from above
+ _ws_root: PathBuf,
+}
+
+impl InheritableFields {
+ package_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),
+ ("authors", authors -> Vec<String>),
+ ("badges", badges -> BTreeMap<String, BTreeMap<String, String>>),
+ ("categories", categories -> Vec<String>),
+ ("description", description -> String),
+ ("documentation", documentation -> String),
+ ("edition", edition -> String),
+ ("exclude", exclude -> Vec<String>),
+ ("homepage", homepage -> String),
+ ("include", include -> Vec<String>),
+ ("keywords", keywords -> Vec<String>),
+ ("license", license -> String),
+ ("publish", publish -> manifest::VecStringOrBool),
+ ("repository", repository -> String),
+ ("rust-version", rust_version -> RustVersion),
+ ("version", version -> semver::Version),
}
/// Gets a workspace dependency with the `name`.
@@ -1489,7 +1495,7 @@ impl schema::InheritableFields {
&self,
name: &str,
package_root: &Path,
- ) -> CargoResult<schema::TomlDependency> {
+ ) -> CargoResult<manifest::TomlDependency> {
let Some(deps) = &self.dependencies else {
bail!("`workspace.dependencies` was not defined");
};
@@ -1497,815 +1503,640 @@ impl schema::InheritableFields {
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)?;
+ if let manifest::TomlDependency::Detailed(detailed) = &mut dep {
+ if let Some(rel_path) = &detailed.path {
+ detailed.path = Some(resolve_relative_path(
+ name,
+ self.ws_root(),
+ package_root,
+ rel_path,
+ )?);
+ }
}
Ok(dep)
}
+ /// Gets the field `workspace.lint`.
+ fn lints(&self) -> CargoResult<manifest::TomlLints> {
+ let Some(val) = &self.lints else {
+ bail!("`workspace.lints` was not defined");
+ };
+ Ok(val.clone())
+ }
+
/// Gets the field `workspace.package.license-file`.
fn license_file(&self, package_root: &Path) -> CargoResult<String> {
- let Some(license_file) = &self.license_file else {
+ let Some(license_file) = self.package.as_ref().and_then(|p| p.license_file.as_ref()) else {
bail!("`workspace.package.license-file` was not defined");
};
- resolve_relative_path("license-file", &self.ws_root, package_root, license_file)
+ 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 {
+ fn readme(&self, package_root: &Path) -> CargoResult<manifest::StringOrBool> {
+ let Some(readme) = readme_for_package(
+ self._ws_root.as_path(),
+ self.package.as_ref().and_then(|p| p.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)
+ resolve_relative_path("readme", &self._ws_root, package_root, &readme)
+ .map(manifest::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)
+ &self._ws_root
}
}
-/// 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(|| {
+fn field_inherit_with<'a, T>(
+ field: manifest::InheritableField<T>,
+ label: &str,
+ get_ws_inheritable: impl FnOnce() -> CargoResult<T>,
+) -> CargoResult<T> {
+ match field {
+ manifest::InheritableField::Value(value) => Ok(value),
+ manifest::InheritableField::Inherit(_) => get_ws_inheritable().with_context(|| {
format!(
- "error inheriting `{label}` from workspace root manifest's `workspace.{}.{label}`",
- w.inherit_toml_table(),
+ "error inheriting `{label}` from workspace root manifest's `workspace.package.{label}`",
)
}),
}
- }
-
- 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),
+fn lints_inherit_with(
+ lints: manifest::InheritableLints,
+ get_ws_inheritable: impl FnOnce() -> CargoResult<manifest::TomlLints>,
+) -> CargoResult<manifest::TomlLints> {
+ if lints.workspace {
+ if !lints.lints.is_empty() {
+ anyhow::bail!("cannot override `workspace.lints` in `lints`, either remove the overrides or `lints.workspace = true` and manually specify the lints");
}
+ get_ws_inheritable().with_context(|| {
+ "error inheriting `lints` from workspace root manifest's `workspace.lints`"
+ })
+ } else {
+ Ok(lints.lints)
}
}
-impl WorkspaceInherit for schema::TomlWorkspaceField {
- fn inherit_toml_table(&self) -> &str {
- "package"
- }
-
- fn workspace(&self) -> bool {
- self.workspace
- }
+fn dependency_inherit_with<'a>(
+ dependency: manifest::InheritableDependency,
+ name: &str,
+ inheritable: impl FnOnce() -> CargoResult<&'a InheritableFields>,
+ cx: &mut Context<'_, '_>,
+) -> CargoResult<manifest::TomlDependency> {
+ match dependency {
+ manifest::InheritableDependency::Value(value) => Ok(value),
+ manifest::InheritableDependency::Inherit(w) => {
+ inner_dependency_inherit_with(w, name, inheritable, cx).with_context(|| {
+ format!(
+ "error inheriting `{name}` from workspace root manifest's `workspace.dependencies.{name}`",
+ )
+ })
+ }
+ }
}
-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 \
+fn inner_dependency_inherit_with<'a>(
+ dependency: manifest::TomlInheritedDependency,
+ name: &str,
+ inheritable: impl FnOnce() -> CargoResult<&'a InheritableFields>,
+ cx: &mut Context<'_, '_>,
+) -> CargoResult<manifest::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)
- }
+ ))
+ }
+ if dependency.default_features.is_some() && dependency.default_features2.is_some() {
+ warn_on_deprecated("default-features", name, "dependency", cx.warnings);
+ }
+ inheritable()?.get_dependency(name, cx.root).map(|d| {
+ match d {
+ manifest::TomlDependency::Simple(s) => {
+ if let Some(false) = dependency.default_features() {
+ default_features_msg(name, None, cx);
+ }
+ if dependency.optional.is_some()
+ || dependency.features.is_some()
+ || dependency.public.is_some()
+ {
+ manifest::TomlDependency::Detailed(manifest::TomlDetailedDependency {
+ version: Some(s),
+ optional: dependency.optional,
+ features: dependency.features.clone(),
+ public: dependency.public,
+ ..Default::default()
+ })
+ } else {
+ manifest::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);
- }
- _ => {}
+ }
+ manifest::TomlDependency::Detailed(d) => {
+ let mut d = d.clone();
+ match (dependency.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);
}
- // Inherit the workspace configuration for `public` unless
- // it's explicitly specified for this dependency.
- if let Some(public) = self.public {
- d.public = Some(public);
+ // 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);
- schema::TomlDependency::Detailed(d)
+ _ => {}
}
+ d.features = match (d.features.clone(), dependency.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,
+ };
+ d.optional = dependency.optional;
+ manifest::TomlDependency::Detailed(d)
}
- })
- }
+ }
+ })
}
-impl WorkspaceInherit for schema::TomlWorkspaceDependency {
- fn inherit_toml_table(&self) -> &str {
- "dependencies"
- }
-
- fn workspace(&self) -> bool {
- self.workspace
- }
+pub(crate) fn to_dependency<P: ResolveToPath + Clone>(
+ dep: &manifest::TomlDependency<P>,
+ name: &str,
+ source_id: SourceId,
+ nested_paths: &mut Vec<PathBuf>,
+ config: &Config,
+ warnings: &mut Vec<String>,
+ platform: Option<Platform>,
+ root: &Path,
+ features: &Features,
+ kind: Option<DepKind>,
+) -> CargoResult<Dependency> {
+ dep_to_dependency(
+ dep,
+ name,
+ &mut Context {
+ deps: &mut Vec::new(),
+ source_id,
+ nested_paths,
+ config,
+ warnings,
+ platform,
+ root,
+ features,
+ },
+ kind,
+ )
}
-impl<P: ResolveToPath + Clone> schema::TomlDependency<P> {
- pub(crate) fn to_dependency_split(
- &self,
- name: &str,
- source_id: SourceId,
- nested_paths: &mut Vec<PathBuf>,
- config: &Config,
- warnings: &mut Vec<String>,
- platform: Option<Platform>,
- root: &Path,
- features: &Features,
- kind: Option<DepKind>,
- ) -> CargoResult<Dependency> {
- self.to_dependency(
- name,
- &mut Context {
- deps: &mut Vec::new(),
- source_id,
- nested_paths,
- config,
- warnings,
- platform,
- root,
- features,
- },
- kind,
- )
- }
-
- fn to_dependency(
- &self,
- name: &str,
- cx: &mut Context<'_, '_>,
- kind: Option<DepKind>,
- ) -> CargoResult<Dependency> {
- match *self {
- schema::TomlDependency::Simple(ref version) => schema::DetailedTomlDependency::<P> {
+fn dep_to_dependency<P: ResolveToPath + Clone>(
+ orig: &manifest::TomlDependency<P>,
+ name: &str,
+ cx: &mut Context<'_, '_>,
+ kind: Option<DepKind>,
+) -> CargoResult<Dependency> {
+ match *orig {
+ manifest::TomlDependency::Simple(ref version) => detailed_dep_to_dependency(
+ &manifest::TomlDetailedDependency::<P> {
version: Some(version.clone()),
..Default::default()
- }
- .to_dependency(name, cx, kind),
- schema::TomlDependency::Detailed(ref details) => details.to_dependency(name, cx, kind),
- }
- }
-}
-
-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 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,
- )?)
+ },
+ name,
+ cx,
+ kind,
+ ),
+ manifest::TomlDependency::Detailed(ref details) => {
+ detailed_dep_to_dependency(details, name, cx, kind)
}
- Ok(())
}
}
-impl<P: ResolveToPath + Clone> schema::DetailedTomlDependency<P> {
- fn to_dependency(
- &self,
- name_in_toml: &str,
- cx: &mut Context<'_, '_>,
- kind: Option<DepKind>,
- ) -> CargoResult<Dependency> {
- if self.version.is_none() && self.path.is_none() && self.git.is_none() {
- let msg = format!(
- "dependency ({}) specified without \
+fn detailed_dep_to_dependency<P: ResolveToPath + Clone>(
+ orig: &manifest::TomlDetailedDependency<P>,
+ name_in_toml: &str,
+ cx: &mut Context<'_, '_>,
+ kind: Option<DepKind>,
+) -> CargoResult<Dependency> {
+ if orig.version.is_none() && orig.path.is_none() && orig.git.is_none() {
+ let msg = format!(
+ "dependency ({}) specified without \
providing a local path, Git repository, version, or \
workspace dependency to use. This will be considered an \
error in future versions",
- name_in_toml
- );
- cx.warnings.push(msg);
- }
+ name_in_toml
+ );
+ cx.warnings.push(msg);
+ }
- if let Some(version) = &self.version {
- if version.contains('+') {
- cx.warnings.push(format!(
- "version requirement `{}` for dependency `{}` \
+ if let Some(version) = &orig.version {
+ if version.contains('+') {
+ cx.warnings.push(format!(
+ "version requirement `{}` for dependency `{}` \
includes semver metadata which will be ignored, removing the \
metadata is recommended to avoid confusion",
- version, name_in_toml
- ));
- }
+ version, name_in_toml
+ ));
}
+ }
- if self.git.is_none() {
- let git_only_keys = [
- (&self.branch, "branch"),
- (&self.tag, "tag"),
- (&self.rev, "rev"),
- ];
-
- for &(key, key_name) in &git_only_keys {
- if key.is_some() {
- bail!(
- "key `{}` is ignored for dependency ({}).",
- key_name,
- name_in_toml
- );
- }
+ if orig.git.is_none() {
+ let git_only_keys = [
+ (&orig.branch, "branch"),
+ (&orig.tag, "tag"),
+ (&orig.rev, "rev"),
+ ];
+
+ for &(key, key_name) in &git_only_keys {
+ if key.is_some() {
+ bail!(
+ "key `{}` is ignored for dependency ({}).",
+ key_name,
+ name_in_toml
+ );
}
}
+ }
- // Early detection of potentially misused feature syntax
- // instead of generating a "feature not found" error.
- if let Some(features) = &self.features {
- for feature in features {
- if feature.contains('/') {
- bail!(
- "feature `{}` in dependency `{}` is not allowed to contain slashes\n\
+ // Early detection of potentially misused feature syntax
+ // instead of generating a "feature not found" error.
+ if let Some(features) = &orig.features {
+ for feature in features {
+ if feature.contains('/') {
+ bail!(
+ "feature `{}` in dependency `{}` is not allowed to contain slashes\n\
If you want to enable features of a transitive dependency, \
the direct dependency needs to re-export those features from \
the `[features]` table.",
- feature,
- name_in_toml
- );
- }
- if feature.starts_with("dep:") {
- bail!(
- "feature `{}` in dependency `{}` is not allowed to use explicit \
+ feature,
+ name_in_toml
+ );
+ }
+ if feature.starts_with("dep:") {
+ bail!(
+ "feature `{}` in dependency `{}` is not allowed to use explicit \
`dep:` syntax\n\
If you want to enable an optional dependency, specify the name \
of the optional dependency without the `dep:` prefix, or specify \
a feature from the dependency's `[features]` table that enables \
the optional dependency.",
- feature,
- name_in_toml
- );
- }
+ feature,
+ name_in_toml
+ );
}
}
+ }
- let new_source_id = match (
- self.git.as_ref(),
- self.path.as_ref(),
- self.registry.as_ref(),
- self.registry_index.as_ref(),
- ) {
- (Some(_), _, Some(_), _) | (Some(_), _, _, Some(_)) => bail!(
- "dependency ({}) specification is ambiguous. \
+ let new_source_id = match (
+ orig.git.as_ref(),
+ orig.path.as_ref(),
+ orig.registry.as_ref(),
+ orig.registry_index.as_ref(),
+ ) {
+ (Some(_), _, Some(_), _) | (Some(_), _, _, Some(_)) => bail!(
+ "dependency ({}) specification is ambiguous. \
Only one of `git` or `registry` is allowed.",
- name_in_toml
- ),
- (_, _, Some(_), Some(_)) => bail!(
- "dependency ({}) specification is ambiguous. \
+ name_in_toml
+ ),
+ (_, _, Some(_), Some(_)) => bail!(
+ "dependency ({}) specification is ambiguous. \
Only one of `registry` or `registry-index` is allowed.",
- name_in_toml
- ),
- (Some(git), maybe_path, _, _) => {
- if maybe_path.is_some() {
- bail!(
- "dependency ({}) specification is ambiguous. \
+ name_in_toml
+ ),
+ (Some(git), maybe_path, _, _) => {
+ if maybe_path.is_some() {
+ bail!(
+ "dependency ({}) specification is ambiguous. \
Only one of `git` or `path` is allowed.",
- name_in_toml
- );
- }
+ name_in_toml
+ );
+ }
- let n_details = [&self.branch, &self.tag, &self.rev]
- .iter()
- .filter(|d| d.is_some())
- .count();
+ let n_details = [&orig.branch, &orig.tag, &orig.rev]
+ .iter()
+ .filter(|d| d.is_some())
+ .count();
- if n_details > 1 {
- bail!(
- "dependency ({}) specification is ambiguous. \
+ if n_details > 1 {
+ bail!(
+ "dependency ({}) specification is ambiguous. \
Only one of `branch`, `tag` or `rev` is allowed.",
- name_in_toml
- );
- }
+ name_in_toml
+ );
+ }
- let reference = self
- .branch
- .clone()
- .map(GitReference::Branch)
- .or_else(|| self.tag.clone().map(GitReference::Tag))
- .or_else(|| self.rev.clone().map(GitReference::Rev))
- .unwrap_or(GitReference::DefaultBranch);
- let loc = git.into_url()?;
-
- if let Some(fragment) = loc.fragment() {
- let msg = format!(
- "URL fragment `#{}` in git URL is ignored for dependency ({}). \
+ let reference = orig
+ .branch
+ .clone()
+ .map(GitReference::Branch)
+ .or_else(|| orig.tag.clone().map(GitReference::Tag))
+ .or_else(|| orig.rev.clone().map(GitReference::Rev))
+ .unwrap_or(GitReference::DefaultBranch);
+ let loc = git.into_url()?;
+
+ if let Some(fragment) = loc.fragment() {
+ let msg = format!(
+ "URL fragment `#{}` in git URL is ignored for dependency ({}). \
If you were trying to specify a specific git revision, \
use `rev = \"{}\"` in the dependency declaration.",
- fragment, name_in_toml, fragment
- );
- cx.warnings.push(msg)
- }
-
- SourceId::for_git(&loc, reference)?
- }
- (None, Some(path), _, _) => {
- let path = path.resolve(cx.config);
- cx.nested_paths.push(path.clone());
- // If the source ID for the package we're parsing is a path
- // source, then we normalize the path here to get rid of
- // components like `..`.
- //
- // The purpose of this is to get a canonical ID for the package
- // that we're depending on to ensure that builds of this package
- // always end up hashing to the same value no matter where it's
- // built from.
- if cx.source_id.is_path() {
- let path = cx.root.join(path);
- let path = paths::normalize_path(&path);
- SourceId::for_path(&path)?
- } else {
- cx.source_id
- }
- }
- (None, None, Some(registry), None) => SourceId::alt_registry(cx.config, registry)?,
- (None, None, None, Some(registry_index)) => {
- let url = registry_index.into_url()?;
- SourceId::for_registry(&url)?
+ fragment, name_in_toml, fragment
+ );
+ cx.warnings.push(msg)
}
- (None, None, None, None) => SourceId::crates_io(cx.config)?,
- };
- let (pkg_name, explicit_name_in_toml) = match self.package {
- Some(ref s) => (&s[..], Some(name_in_toml)),
- None => (name_in_toml, None),
- };
-
- let version = self.version.as_deref();
- let mut dep = Dependency::parse(pkg_name, version, new_source_id)?;
- if self.default_features.is_some() && self.default_features2.is_some() {
- warn_on_deprecated("default-features", name_in_toml, "dependency", cx.warnings);
- }
- dep.set_features(self.features.iter().flatten())
- .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 {
- let registry_id = SourceId::alt_registry(cx.config, registry)?;
- dep.set_registry_id(registry_id);
+ SourceId::for_git(&loc, reference)?
+ }
+ (None, Some(path), _, _) => {
+ let path = path.resolve(cx.config);
+ cx.nested_paths.push(path.clone());
+ // If the source ID for the package we're parsing is a path
+ // source, then we normalize the path here to get rid of
+ // components like `..`.
+ //
+ // The purpose of this is to get a canonical ID for the package
+ // that we're depending on to ensure that builds of this package
+ // always end up hashing to the same value no matter where it's
+ // built from.
+ if cx.source_id.is_path() {
+ let path = cx.root.join(path);
+ let path = paths::normalize_path(&path);
+ SourceId::for_path(&path)?
+ } else {
+ cx.source_id
+ }
}
- if let Some(registry_index) = &self.registry_index {
+ (None, None, Some(registry), None) => SourceId::alt_registry(cx.config, registry)?,
+ (None, None, None, Some(registry_index)) => {
let url = registry_index.into_url()?;
- let registry_id = SourceId::for_registry(&url)?;
- dep.set_registry_id(registry_id);
+ SourceId::for_registry(&url)?
}
+ (None, None, None, None) => SourceId::crates_io(cx.config)?,
+ };
- if let Some(kind) = kind {
- dep.set_kind(kind);
- }
- if let Some(name_in_toml) = explicit_name_in_toml {
- dep.set_explicit_name_in_toml(name_in_toml);
- }
+ let (pkg_name, explicit_name_in_toml) = match orig.package {
+ Some(ref s) => (&s[..], Some(name_in_toml)),
+ None => (name_in_toml, None),
+ };
- if let Some(p) = self.public {
- cx.features.require(Feature::public_dependency())?;
+ let version = orig.version.as_deref();
+ let mut dep = Dependency::parse(pkg_name, version, new_source_id)?;
+ if orig.default_features.is_some() && orig.default_features2.is_some() {
+ warn_on_deprecated("default-features", name_in_toml, "dependency", cx.warnings);
+ }
+ dep.set_features(orig.features.iter().flatten())
+ .set_default_features(orig.default_features().unwrap_or(true))
+ .set_optional(orig.optional.unwrap_or(false))
+ .set_platform(cx.platform.clone());
+ if let Some(registry) = &orig.registry {
+ let registry_id = SourceId::alt_registry(cx.config, registry)?;
+ dep.set_registry_id(registry_id);
+ }
+ if let Some(registry_index) = &orig.registry_index {
+ let url = registry_index.into_url()?;
+ let registry_id = SourceId::for_registry(&url)?;
+ dep.set_registry_id(registry_id);
+ }
- if dep.kind() != DepKind::Normal {
- bail!("'public' specifier can only be used on regular dependencies, not {:?} dependencies", dep.kind());
- }
+ if let Some(kind) = kind {
+ dep.set_kind(kind);
+ }
+ if let Some(name_in_toml) = explicit_name_in_toml {
+ dep.set_explicit_name_in_toml(name_in_toml);
+ }
- dep.set_public(p);
+ if let Some(p) = orig.public {
+ cx.features.require(Feature::public_dependency())?;
+
+ if dep.kind() != DepKind::Normal {
+ bail!("'public' specifier can only be used on regular dependencies, not {:?} dependencies", dep.kind());
}
- if let (Some(artifact), is_lib, target) = (
- self.artifact.as_ref(),
- self.lib.unwrap_or(false),
- self.target.as_deref(),
- ) {
- if cx.config.cli_unstable().bindeps {
- let artifact = Artifact::parse(&artifact.0, is_lib, target)?;
- if dep.kind() != DepKind::Build
- && artifact.target() == Some(ArtifactTarget::BuildDependencyAssumeTarget)
- {
- bail!(
- r#"`target = "target"` in normal- or dev-dependencies has no effect ({})"#,
- name_in_toml
- );
- }
- dep.set_artifact(artifact)
- } else {
- bail!("`artifact = …` requires `-Z bindeps` ({})", name_in_toml);
- }
- } else if self.lib.is_some() || self.target.is_some() {
- for (is_set, specifier) in [
- (self.lib.is_some(), "lib"),
- (self.target.is_some(), "target"),
- ] {
- if !is_set {
- continue;
- }
+ dep.set_public(p);
+ }
+
+ if let (Some(artifact), is_lib, target) = (
+ orig.artifact.as_ref(),
+ orig.lib.unwrap_or(false),
+ orig.target.as_deref(),
+ ) {
+ if cx.config.cli_unstable().bindeps {
+ let artifact = Artifact::parse(&artifact.0, is_lib, target)?;
+ if dep.kind() != DepKind::Build
+ && artifact.target() == Some(ArtifactTarget::BuildDependencyAssumeTarget)
+ {
bail!(
- "'{}' specifier cannot be used without an 'artifact = …' value ({})",
- specifier,
+ r#"`target = "target"` in normal- or dev-dependencies has no effect ({})"#,
name_in_toml
- )
+ );
+ }
+ dep.set_artifact(artifact)
+ } else {
+ bail!("`artifact = …` requires `-Z bindeps` ({})", name_in_toml);
+ }
+ } else if orig.lib.is_some() || orig.target.is_some() {
+ for (is_set, specifier) in [
+ (orig.lib.is_some(), "lib"),
+ (orig.target.is_some(), "target"),
+ ] {
+ if !is_set {
+ continue;
}
+ bail!(
+ "'{}' specifier cannot be used without an 'artifact = …' value ({})",
+ specifier,
+ name_in_toml
+ )
}
- Ok(dep)
}
+ Ok(dep)
}
-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(())
+/// 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_profiles(
+ profiles: &manifest::TomlProfiles,
+ cli_unstable: &CliUnstable,
+ features: &Features,
+ warnings: &mut Vec<String>,
+) -> CargoResult<()> {
+ for (name, profile) in &profiles.0 {
+ validate_profile(profile, name, cli_unstable, features, warnings)?;
}
+ Ok(())
}
-impl schema::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,
- )?;
- }
+/// Checks stytax validity and unstable feature gate for a given profile.
+pub fn validate_profile(
+ root: &manifest::TomlProfile,
+ name: &str,
+ cli_unstable: &CliUnstable,
+ features: &Features,
+ warnings: &mut Vec<String>,
+) -> CargoResult<()> {
+ validate_profile_layer(root, name, cli_unstable, features)?;
+ if let Some(ref profile) = root.build_override {
+ validate_profile_override(profile, "build-override")?;
+ validate_profile_layer(
+ profile,
+ &format!("{name}.build-override"),
+ cli_unstable,
+ features,
+ )?;
+ }
+ if let Some(ref packages) = root.package {
+ for (override_name, profile) in packages {
+ validate_profile_override(profile, "package")?;
+ validate_profile_layer(
+ 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, \
+ if let Some(dir_name) = &root.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
- );
- }
+ dir_name,
+ name
+ );
+ }
- // `inherits` validation
- if matches!(self.inherits.as_deref(), Some("debug")) {
- bail!(
- "profile.{}.inherits=\"debug\" should be profile.{}.inherits=\"dev\"",
- name,
- name
- );
- }
+ // `inherits` validation
+ if matches!(root.inherits.as_deref(), 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))
- }
+ match name {
+ "doc" => {
+ warnings.push("profile `doc` is deprecated and has no effect".to_string());
+ }
+ "test" | "bench" => {
+ if root.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, \
+ if let Some(panic) = &root.panic {
+ if panic != "unwind" && panic != "abort" {
+ bail!(
+ "`panic` setting of `{}` is not a valid setting, \
must be `unwind` or `abort`",
- panic
- );
- }
+ panic
+ );
}
+ }
- 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 \
+ if let Some(manifest::StringOrBool::String(arg)) = &root.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(())
}
- /// 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),
- _ => {}
- }
+ Ok(())
+}
- 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),
- _ => {}
- }
+/// Validates a profile.
+///
+/// This is a shallow check, which is reused for the profile itself and any overrides.
+fn validate_profile_layer(
+ profile: &manifest::TomlProfile,
+ name: &str,
+ cli_unstable: &CliUnstable,
+ features: &Features,
+) -> CargoResult<()> {
+ if let Some(codegen_backend) = &profile.codegen_backend {
+ match (
+ features.require(Feature::codegen_backend()),
+ cli_unstable.codegen_backend,
+ ) {
+ (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)
+ 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,
+ );
}
- Ok(())
}
-
- /// 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());
- }
-
- 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()),
- }
- }
-
- 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.clone());
- }
-
- if let Some(v) = &profile.dir_name {
- self.dir_name = Some(v.clone());
- }
-
- if let Some(v) = &profile.strip {
- self.strip = Some(v.clone());
- }
-
- if let Some(v) = &profile.trim_paths {
- self.trim_paths = Some(v.clone())
+ if profile.rustflags.is_some() {
+ match (
+ features.require(Feature::profile_rustflags()),
+ cli_unstable.profile_rustflags,
+ ) {
+ (Err(e), false) => return Err(e),
+ _ => {}
}
}
-}
-
-impl schema::MaybeWorkspaceLints {
- fn resolve<'a>(
- self,
- 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");
- }
- get_ws_inheritable().with_context(|| {
- "error inheriting `lints` from workspace root manifest's `workspace.lints`"
- })
- } else {
- Ok(self.lints)
+ if profile.trim_paths.is_some() {
+ match (
+ features.require(Feature::trim_paths()),
+ cli_unstable.trim_paths,
+ ) {
+ (Err(e), false) => return Err(e),
+ _ => {}
}
}
+ Ok(())
}
-impl schema::TomlLintLevel {
- fn flag(&self) -> &'static str {
- match self {
- Self::Forbid => "--forbid",
- Self::Deny => "--deny",
- Self::Warn => "--warn",
- Self::Allow => "--allow",
- }
+/// Validation that is specific to an override.
+fn validate_profile_override(profile: &manifest::TomlProfile, which: &str) -> CargoResult<()> {
+ if profile.package.is_some() {
+ bail!("package-specific profiles cannot be nested");
}
+ if profile.build_override.is_some() {
+ bail!("build-override profiles cannot be nested");
+ }
+ if profile.panic.is_some() {
+ bail!("`panic` may not be specified in a `{}` profile", which)
+ }
+ if profile.lto.is_some() {
+ bail!("`lto` may not be specified in a `{}` profile", which)
+ }
+ if profile.rpath.is_some() {
+ bail!("`rpath` may not be specified in a `{}` profile", which)
+ }
+ Ok(())
}
pub trait ResolveToPath {
diff --git a/src/tools/cargo/src/cargo/util/toml/targets.rs b/src/tools/cargo/src/cargo/util/toml/targets.rs
index 9d456ffd7..7d0f1891e 100644
--- a/src/tools/cargo/src/cargo/util/toml/targets.rs
+++ b/src/tools/cargo/src/cargo/util/toml/targets.rs
@@ -14,16 +14,16 @@ use std::collections::HashSet;
use std::fs::{self, DirEntry};
use std::path::{Path, PathBuf};
-use super::schema::{
- PathValue, StringOrBool, StringOrVec, TomlBenchTarget, TomlBinTarget, TomlExampleTarget,
- TomlLibTarget, TomlManifest, TomlTarget, TomlTestTarget,
-};
use crate::core::compiler::rustdoc::RustdocScrapeExamples;
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 crate::util_schemas::manifest::{
+ PathValue, StringOrBool, StringOrVec, TomlBenchTarget, TomlBinTarget, TomlExampleTarget,
+ TomlLibTarget, TomlManifest, TomlTarget, TomlTestTarget,
+};
use anyhow::Context as _;
@@ -127,7 +127,7 @@ pub(super) fn targets(
// Verify names match available build deps.
let bdeps = manifest.build_dependencies.as_ref();
for name in &metabuild.0 {
- if !bdeps.map_or(false, |bd| bd.contains_key(name)) {
+ if !bdeps.map_or(false, |bd| bd.contains_key(name.as_str())) {
anyhow::bail!(
"metabuild package `{}` must be specified in `build-dependencies`",
name
@@ -1004,7 +1004,7 @@ fn name_or_panic(target: &TomlTarget) -> &str {
}
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() {
+ if target.proc_macro.is_some() && target.proc_macro2.is_some() {
warn_on_deprecated(
"proc-macro",
name_or_panic(target),
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 88298fa8d..6ffd214b4 100644
--- a/src/tools/cargo/src/cargo/util/toml_mut/dependency.rs
+++ b/src/tools/cargo/src/cargo/util/toml_mut/dependency.rs
@@ -25,6 +25,9 @@ pub struct Dependency {
/// Whether the dependency is opted-in with a feature flag.
pub optional: Option<bool>,
+ /// Whether the dependency is marked as public.
+ pub public: Option<bool>,
+
/// List of features to add (or None to keep features unchanged).
pub features: Option<IndexSet<String>>,
/// Whether default features are enabled.
@@ -48,6 +51,7 @@ impl Dependency {
Self {
name: name.into(),
optional: None,
+ public: None,
features: None,
default_features: None,
inherited_features: None,
@@ -163,6 +167,11 @@ impl Dependency {
self.optional
}
+ /// Get whether the dep is public.
+ pub fn public(&self) -> Option<bool> {
+ self.public
+ }
+
/// Get the SourceID for this dependency.
pub fn source_id(&self, config: &Config) -> CargoResult<MaybeWorkspace<SourceId>> {
match &self.source.as_ref() {
@@ -325,16 +334,18 @@ impl Dependency {
};
let optional = table.get("optional").and_then(|v| v.as_bool());
+ let public = table.get("public").and_then(|v| v.as_bool());
let dep = Self {
name,
- rename,
- source: Some(source),
- registry,
- default_features,
- features,
optional,
+ public,
+ features,
+ default_features,
inherited_features: None,
+ source: Some(source),
+ registry,
+ rename,
};
Ok(dep)
} else {
@@ -366,6 +377,7 @@ impl Dependency {
crate_root.display()
);
let table: toml_edit::Item = match (
+ self.public.unwrap_or(false),
self.optional.unwrap_or(false),
self.features.as_ref(),
self.default_features.unwrap_or(true),
@@ -376,20 +388,21 @@ impl Dependency {
// Extra short when version flag only
(
false,
+ false,
None,
true,
Some(Source::Registry(RegistrySource { version: v })),
None,
None,
) => toml_edit::value(v),
- (false, None, true, Some(Source::Workspace(WorkspaceSource {})), None, None) => {
+ (false, false, None, true, Some(Source::Workspace(WorkspaceSource {})), None, None) => {
let mut table = toml_edit::InlineTable::default();
table.set_dotted(true);
table.insert("workspace", true.into());
toml_edit::value(toml_edit::Value::InlineTable(table))
}
// Other cases are represented as an inline table
- (_, _, _, _, _, _) => {
+ (_, _, _, _, _, _, _) => {
let mut table = toml_edit::InlineTable::default();
match &self.source {
@@ -442,6 +455,9 @@ impl Dependency {
if let Some(v) = self.optional {
table.insert("optional", v.into());
}
+ if let Some(v) = self.public {
+ table.insert("public", v.into());
+ }
toml_edit::value(toml_edit::Value::InlineTable(table))
}
@@ -579,6 +595,15 @@ impl Dependency {
table.remove("optional");
}
}
+ match self.public {
+ Some(v) => {
+ table.set_dotted(false);
+ overwrite_value(table, "public", v);
+ }
+ None => {
+ table.remove("public");
+ }
+ }
} else {
unreachable!("Invalid dependency type: {}", item.type_name());
}
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 e859af215..3e3b4e69a 100644
--- a/src/tools/cargo/src/cargo/util/toml_mut/manifest.rs
+++ b/src/tools/cargo/src/cargo/util/toml_mut/manifest.rs
@@ -420,7 +420,7 @@ impl LocalManifest {
}
}
- fn is_explicit_dep_activation(&self, dep_key: &str) -> bool {
+ pub fn is_explicit_dep_activation(&self, dep_key: &str) -> bool {
if let Some(toml_edit::Item::Table(feature_table)) = self.data.as_table().get("features") {
for values in feature_table
.iter()
diff --git a/src/tools/cargo/src/cargo/util_schemas/core/mod.rs b/src/tools/cargo/src/cargo/util_schemas/core/mod.rs
new file mode 100644
index 000000000..2001a6bc7
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util_schemas/core/mod.rs
@@ -0,0 +1,6 @@
+mod package_id_spec;
+mod source_kind;
+
+pub use package_id_spec::PackageIdSpec;
+pub use source_kind::GitReference;
+pub use source_kind::SourceKind;
diff --git a/src/tools/cargo/src/cargo/util_schemas/core/package_id_spec.rs b/src/tools/cargo/src/cargo/util_schemas/core/package_id_spec.rs
new file mode 100644
index 000000000..015bfa928
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util_schemas/core/package_id_spec.rs
@@ -0,0 +1,593 @@
+use std::fmt;
+
+use anyhow::bail;
+use anyhow::Result;
+use semver::Version;
+use serde::{de, ser};
+use url::Url;
+
+use crate::util_schemas::core::GitReference;
+use crate::util_schemas::core::SourceKind;
+use crate::util_schemas::manifest::PackageName;
+use crate::util_semver::PartialVersion;
+
+/// Some or all of the data required to identify a package:
+///
+/// 1. the package name (a `String`, required)
+/// 2. the package version (a `Version`, optional)
+/// 3. the package source (a `Url`, optional)
+///
+/// If any of the optional fields are omitted, then the package ID may be ambiguous, there may be
+/// more than one package/version/url combo that will match. However, often just the name is
+/// sufficient to uniquely define a package ID.
+#[derive(Clone, PartialEq, Eq, Debug, Hash, Ord, PartialOrd)]
+pub struct PackageIdSpec {
+ name: String,
+ version: Option<PartialVersion>,
+ url: Option<Url>,
+ kind: Option<SourceKind>,
+}
+
+impl PackageIdSpec {
+ pub fn new(name: String) -> Self {
+ Self {
+ name,
+ version: None,
+ url: None,
+ kind: None,
+ }
+ }
+
+ pub fn with_version(mut self, version: PartialVersion) -> Self {
+ self.version = Some(version);
+ self
+ }
+
+ pub fn with_url(mut self, url: Url) -> Self {
+ self.url = Some(url);
+ self
+ }
+
+ pub fn with_kind(mut self, kind: SourceKind) -> Self {
+ self.kind = Some(kind);
+ self
+ }
+
+ /// Parses a spec string and returns a `PackageIdSpec` if the string was valid.
+ ///
+ /// # Examples
+ /// Some examples of valid strings
+ ///
+ /// ```
+ /// use cargo::core::PackageIdSpec;
+ ///
+ /// let specs = vec![
+ /// "https://crates.io/foo",
+ /// "https://crates.io/foo#1.2.3",
+ /// "https://crates.io/foo#bar:1.2.3",
+ /// "https://crates.io/foo#bar@1.2.3",
+ /// "foo",
+ /// "foo:1.2.3",
+ /// "foo@1.2.3",
+ /// ];
+ /// for spec in specs {
+ /// assert!(PackageIdSpec::parse(spec).is_ok());
+ /// }
+ pub fn parse(spec: &str) -> Result<PackageIdSpec> {
+ if spec.contains("://") {
+ if let Ok(url) = Url::parse(spec) {
+ return PackageIdSpec::from_url(url);
+ }
+ } else if spec.contains('/') || spec.contains('\\') {
+ let abs = std::env::current_dir().unwrap_or_default().join(spec);
+ if abs.exists() {
+ let maybe_url = Url::from_file_path(abs)
+ .map_or_else(|_| "a file:// URL".to_string(), |url| url.to_string());
+ bail!(
+ "package ID specification `{}` looks like a file path, \
+ maybe try {}",
+ spec,
+ maybe_url
+ );
+ }
+ }
+ let mut parts = spec.splitn(2, [':', '@']);
+ let name = parts.next().unwrap();
+ let version = match parts.next() {
+ Some(version) => Some(version.parse::<PartialVersion>()?),
+ None => None,
+ };
+ PackageName::new(name)?;
+ Ok(PackageIdSpec {
+ name: String::from(name),
+ version,
+ url: None,
+ kind: None,
+ })
+ }
+
+ /// Tries to convert a valid `Url` to a `PackageIdSpec`.
+ fn from_url(mut url: Url) -> Result<PackageIdSpec> {
+ let mut kind = None;
+ if let Some((kind_str, scheme)) = url.scheme().split_once('+') {
+ match kind_str {
+ "git" => {
+ let git_ref = GitReference::from_query(url.query_pairs());
+ url.set_query(None);
+ kind = Some(SourceKind::Git(git_ref));
+ url = strip_url_protocol(&url);
+ }
+ "registry" => {
+ if url.query().is_some() {
+ bail!("cannot have a query string in a pkgid: {url}")
+ }
+ kind = Some(SourceKind::Registry);
+ url = strip_url_protocol(&url);
+ }
+ "sparse" => {
+ if url.query().is_some() {
+ bail!("cannot have a query string in a pkgid: {url}")
+ }
+ kind = Some(SourceKind::SparseRegistry);
+ // Leave `sparse` as part of URL, see `SourceId::new`
+ // url = strip_url_protocol(&url);
+ }
+ "path" => {
+ if url.query().is_some() {
+ bail!("cannot have a query string in a pkgid: {url}")
+ }
+ if scheme != "file" {
+ anyhow::bail!("`path+{scheme}` is unsupported; `path+file` and `file` schemes are supported");
+ }
+ kind = Some(SourceKind::Path);
+ url = strip_url_protocol(&url);
+ }
+ kind => anyhow::bail!("unsupported source protocol: {kind}"),
+ }
+ } else {
+ if url.query().is_some() {
+ bail!("cannot have a query string in a pkgid: {url}")
+ }
+ }
+
+ let frag = url.fragment().map(|s| s.to_owned());
+ url.set_fragment(None);
+
+ let (name, version) = {
+ let mut path = url
+ .path_segments()
+ .ok_or_else(|| anyhow::format_err!("pkgid urls must have a path: {}", url))?;
+ let path_name = path.next_back().ok_or_else(|| {
+ anyhow::format_err!(
+ "pkgid urls must have at least one path \
+ component: {}",
+ url
+ )
+ })?;
+ match frag {
+ Some(fragment) => match fragment.split_once([':', '@']) {
+ Some((name, part)) => {
+ let version = part.parse::<PartialVersion>()?;
+ (String::from(name), Some(version))
+ }
+ None => {
+ if fragment.chars().next().unwrap().is_alphabetic() {
+ (String::from(fragment.as_str()), None)
+ } else {
+ let version = fragment.parse::<PartialVersion>()?;
+ (String::from(path_name), Some(version))
+ }
+ }
+ },
+ None => (String::from(path_name), None),
+ }
+ };
+ PackageName::new(&name)?;
+ Ok(PackageIdSpec {
+ name,
+ version,
+ url: Some(url),
+ kind,
+ })
+ }
+
+ 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.to_version())
+ }
+
+ pub fn partial_version(&self) -> Option<&PartialVersion> {
+ self.version.as_ref()
+ }
+
+ pub fn url(&self) -> Option<&Url> {
+ self.url.as_ref()
+ }
+
+ pub fn set_url(&mut self, url: Url) {
+ self.url = Some(url);
+ }
+
+ pub fn kind(&self) -> Option<&SourceKind> {
+ self.kind.as_ref()
+ }
+
+ pub fn set_kind(&mut self, kind: SourceKind) {
+ self.kind = Some(kind);
+ }
+}
+
+fn strip_url_protocol(url: &Url) -> Url {
+ // Ridiculous hoop because `Url::set_scheme` errors when changing to http/https
+ let raw = url.to_string();
+ raw.split_once('+').unwrap().1.parse().unwrap()
+}
+
+impl fmt::Display for PackageIdSpec {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let mut printed_name = false;
+ match self.url {
+ Some(ref url) => {
+ if let Some(protocol) = self.kind.as_ref().and_then(|k| k.protocol()) {
+ write!(f, "{protocol}+")?;
+ }
+ write!(f, "{}", url)?;
+ if let Some(SourceKind::Git(git_ref)) = self.kind.as_ref() {
+ if let Some(pretty) = git_ref.pretty_ref(true) {
+ write!(f, "?{}", pretty)?;
+ }
+ }
+ if url.path_segments().unwrap().next_back().unwrap() != &*self.name {
+ printed_name = true;
+ write!(f, "#{}", self.name)?;
+ }
+ }
+ None => {
+ printed_name = true;
+ write!(f, "{}", self.name)?;
+ }
+ }
+ if let Some(ref v) = self.version {
+ write!(f, "{}{}", if printed_name { "@" } else { "#" }, v)?;
+ }
+ Ok(())
+ }
+}
+
+impl ser::Serialize for PackageIdSpec {
+ 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 PackageIdSpec {
+ fn deserialize<D>(d: D) -> Result<PackageIdSpec, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ let string = String::deserialize(d)?;
+ PackageIdSpec::parse(&string).map_err(de::Error::custom)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::PackageIdSpec;
+ use crate::util_schemas::core::{GitReference, SourceKind};
+ use url::Url;
+
+ #[test]
+ fn good_parsing() {
+ #[track_caller]
+ fn ok(spec: &str, expected: PackageIdSpec, expected_rendered: &str) {
+ let parsed = PackageIdSpec::parse(spec).unwrap();
+ assert_eq!(parsed, expected);
+ 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: String::from("foo"),
+ version: None,
+ url: Some(Url::parse("https://crates.io/foo").unwrap()),
+ kind: None,
+ },
+ "https://crates.io/foo",
+ );
+ ok(
+ "https://crates.io/foo#1.2.3",
+ PackageIdSpec {
+ name: String::from("foo"),
+ version: Some("1.2.3".parse().unwrap()),
+ url: Some(Url::parse("https://crates.io/foo").unwrap()),
+ kind: None,
+ },
+ "https://crates.io/foo#1.2.3",
+ );
+ ok(
+ "https://crates.io/foo#1.2",
+ PackageIdSpec {
+ name: String::from("foo"),
+ version: Some("1.2".parse().unwrap()),
+ url: Some(Url::parse("https://crates.io/foo").unwrap()),
+ kind: None,
+ },
+ "https://crates.io/foo#1.2",
+ );
+ ok(
+ "https://crates.io/foo#bar:1.2.3",
+ PackageIdSpec {
+ name: String::from("bar"),
+ version: Some("1.2.3".parse().unwrap()),
+ url: Some(Url::parse("https://crates.io/foo").unwrap()),
+ kind: None,
+ },
+ "https://crates.io/foo#bar@1.2.3",
+ );
+ ok(
+ "https://crates.io/foo#bar@1.2.3",
+ PackageIdSpec {
+ name: String::from("bar"),
+ version: Some("1.2.3".parse().unwrap()),
+ url: Some(Url::parse("https://crates.io/foo").unwrap()),
+ kind: None,
+ },
+ "https://crates.io/foo#bar@1.2.3",
+ );
+ ok(
+ "https://crates.io/foo#bar@1.2",
+ PackageIdSpec {
+ name: String::from("bar"),
+ version: Some("1.2".parse().unwrap()),
+ url: Some(Url::parse("https://crates.io/foo").unwrap()),
+ kind: None,
+ },
+ "https://crates.io/foo#bar@1.2",
+ );
+ ok(
+ "registry+https://crates.io/foo#bar@1.2",
+ PackageIdSpec {
+ name: String::from("bar"),
+ version: Some("1.2".parse().unwrap()),
+ url: Some(Url::parse("https://crates.io/foo").unwrap()),
+ kind: Some(SourceKind::Registry),
+ },
+ "registry+https://crates.io/foo#bar@1.2",
+ );
+ ok(
+ "sparse+https://crates.io/foo#bar@1.2",
+ PackageIdSpec {
+ name: String::from("bar"),
+ version: Some("1.2".parse().unwrap()),
+ url: Some(Url::parse("sparse+https://crates.io/foo").unwrap()),
+ kind: Some(SourceKind::SparseRegistry),
+ },
+ "sparse+https://crates.io/foo#bar@1.2",
+ );
+ ok(
+ "foo",
+ PackageIdSpec {
+ name: String::from("foo"),
+ version: None,
+ url: None,
+ kind: None,
+ },
+ "foo",
+ );
+ ok(
+ "foo:1.2.3",
+ PackageIdSpec {
+ name: String::from("foo"),
+ version: Some("1.2.3".parse().unwrap()),
+ url: None,
+ kind: None,
+ },
+ "foo@1.2.3",
+ );
+ ok(
+ "foo@1.2.3",
+ PackageIdSpec {
+ name: String::from("foo"),
+ version: Some("1.2.3".parse().unwrap()),
+ url: None,
+ kind: None,
+ },
+ "foo@1.2.3",
+ );
+ ok(
+ "foo@1.2",
+ PackageIdSpec {
+ name: String::from("foo"),
+ version: Some("1.2".parse().unwrap()),
+ url: None,
+ kind: None,
+ },
+ "foo@1.2",
+ );
+
+ // pkgid-spec.md
+ ok(
+ "regex",
+ PackageIdSpec {
+ name: String::from("regex"),
+ version: None,
+ url: None,
+ kind: None,
+ },
+ "regex",
+ );
+ ok(
+ "regex@1.4",
+ PackageIdSpec {
+ name: String::from("regex"),
+ version: Some("1.4".parse().unwrap()),
+ url: None,
+ kind: None,
+ },
+ "regex@1.4",
+ );
+ ok(
+ "regex@1.4.3",
+ PackageIdSpec {
+ name: String::from("regex"),
+ version: Some("1.4.3".parse().unwrap()),
+ url: None,
+ kind: 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()),
+ kind: None,
+ },
+ "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()),
+ kind: None,
+ },
+ "https://github.com/rust-lang/crates.io-index#regex@1.4.3",
+ );
+ ok(
+ "sparse+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("sparse+https://github.com/rust-lang/crates.io-index").unwrap(),
+ ),
+ kind: Some(SourceKind::SparseRegistry),
+ },
+ "sparse+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()),
+ kind: None,
+ },
+ "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()),
+ kind: None,
+ },
+ "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()),
+ kind: None,
+ },
+ "ssh://git@github.com/rust-lang/regex.git#regex@1.4.3",
+ );
+ ok(
+ "git+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()),
+ kind: Some(SourceKind::Git(GitReference::DefaultBranch)),
+ },
+ "git+ssh://git@github.com/rust-lang/regex.git#regex@1.4.3",
+ );
+ ok(
+ "git+ssh://git@github.com/rust-lang/regex.git?branch=dev#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()),
+ kind: Some(SourceKind::Git(GitReference::Branch("dev".to_owned()))),
+ },
+ "git+ssh://git@github.com/rust-lang/regex.git?branch=dev#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()),
+ kind: None,
+ },
+ "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()),
+ kind: None,
+ },
+ "file:///path/to/my/project/foo#1.1.8",
+ );
+ ok(
+ "path+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()),
+ kind: Some(SourceKind::Path),
+ },
+ "path+file:///path/to/my/project/foo#1.1.8",
+ );
+ }
+
+ #[test]
+ fn bad_parsing() {
+ assert!(PackageIdSpec::parse("baz:").is_err());
+ assert!(PackageIdSpec::parse("baz:*").is_err());
+ assert!(PackageIdSpec::parse("baz@").is_err());
+ assert!(PackageIdSpec::parse("baz@*").is_err());
+ assert!(PackageIdSpec::parse("baz@^1.0").is_err());
+ assert!(PackageIdSpec::parse("https://baz:1.0").is_err());
+ assert!(PackageIdSpec::parse("https://#baz:1.0").is_err());
+ assert!(
+ PackageIdSpec::parse("foobar+https://github.com/rust-lang/crates.io-index").is_err()
+ );
+ assert!(PackageIdSpec::parse("path+https://github.com/rust-lang/crates.io-index").is_err());
+
+ // Only `git+` can use `?`
+ assert!(PackageIdSpec::parse("file:///path/to/my/project/foo?branch=dev").is_err());
+ assert!(PackageIdSpec::parse("path+file:///path/to/my/project/foo?branch=dev").is_err());
+ assert!(PackageIdSpec::parse(
+ "registry+https://github.com/rust-lang/cargo#0.52.0?branch=dev"
+ )
+ .is_err());
+ assert!(PackageIdSpec::parse(
+ "sparse+https://github.com/rust-lang/cargo#0.52.0?branch=dev"
+ )
+ .is_err());
+ assert!(PackageIdSpec::parse("@1.2.3").is_err());
+ assert!(PackageIdSpec::parse("registry+https://github.com").is_err());
+ assert!(PackageIdSpec::parse("https://crates.io/1foo#1.2.3").is_err())
+ }
+}
diff --git a/src/tools/cargo/src/cargo/util_schemas/core/source_kind.rs b/src/tools/cargo/src/cargo/util_schemas/core/source_kind.rs
new file mode 100644
index 000000000..7b2ecaeec
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util_schemas/core/source_kind.rs
@@ -0,0 +1,201 @@
+use std::cmp::Ordering;
+
+/// The possible kinds of code source.
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum SourceKind {
+ /// A git repository.
+ Git(GitReference),
+ /// A local path.
+ Path,
+ /// A remote registry.
+ Registry,
+ /// A sparse registry.
+ SparseRegistry,
+ /// A local filesystem-based registry.
+ LocalRegistry,
+ /// A directory-based registry.
+ Directory,
+}
+
+impl SourceKind {
+ pub 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, see `SourceId::new`
+ SourceKind::SparseRegistry => None,
+ SourceKind::LocalRegistry => Some("local-registry"),
+ SourceKind::Directory => Some("directory"),
+ }
+ }
+}
+
+/// Note that this is specifically not derived on `SourceKind` although the
+/// implementation here is very similar to what it might look like if it were
+/// otherwise derived.
+///
+/// The reason for this is somewhat obtuse. First of all the hash value of
+/// `SourceKind` makes its way into `~/.cargo/registry/index/github.com-XXXX`
+/// which means that changes to the hash means that all Rust users need to
+/// redownload the crates.io index and all their crates. If possible we strive
+/// to not change this to make this redownloading behavior happen as little as
+/// possible. How is this connected to `Ord` you might ask? That's a good
+/// question!
+///
+/// Since the beginning of time `SourceKind` has had `#[derive(Hash)]`. It for
+/// the longest time *also* derived the `Ord` and `PartialOrd` traits. In #8522,
+/// however, the implementation of `Ord` changed. This handwritten implementation
+/// forgot to sync itself with the originally derived implementation, namely
+/// placing git dependencies as sorted after all other dependencies instead of
+/// first as before.
+///
+/// This regression in #8522 (Rust 1.47) went unnoticed. When we switched back
+/// to a derived implementation in #9133 (Rust 1.52 beta) we only then ironically
+/// saw an issue (#9334). In #9334 it was observed that stable Rust at the time
+/// (1.51) was sorting git dependencies last, whereas Rust 1.52 beta would sort
+/// git dependencies first. This is because the `PartialOrd` implementation in
+/// 1.51 used #8522, the buggy implementation, which put git deps last. In 1.52
+/// it was (unknowingly) restored to the pre-1.47 behavior with git dependencies
+/// first.
+///
+/// Because the breakage was only witnessed after the original breakage, this
+/// trait implementation is preserving the "broken" behavior. Put a different way:
+///
+/// * Rust pre-1.47 sorted git deps first.
+/// * Rust 1.47 to Rust 1.51 sorted git deps last, a breaking change (#8522) that
+/// was never noticed.
+/// * Rust 1.52 restored the pre-1.47 behavior (#9133, without knowing it did
+/// so), and breakage was witnessed by actual users due to difference with
+/// 1.51.
+/// * Rust 1.52 (the source as it lives now) was fixed to match the 1.47-1.51
+/// behavior (#9383), which is now considered intentionally breaking from the
+/// pre-1.47 behavior.
+///
+/// Note that this was all discovered when Rust 1.53 was in nightly and 1.52 was
+/// in beta. #9133 was in both beta and nightly at the time of discovery. For
+/// 1.52 #9383 reverted #9133, meaning 1.52 is the same as 1.51. On nightly
+/// (1.53) #9397 was created to fix the regression introduced by #9133 relative
+/// to the current stable (1.51).
+///
+/// That's all a long winded way of saying "it's weird that git deps hash first
+/// and are sorted last, but it's the way it is right now". The author of this
+/// comment chose to handwrite the `Ord` implementation instead of the `Hash`
+/// implementation, but it's only required that at most one of them is
+/// hand-written because the other can be derived. Perhaps one day in
+/// the future someone can figure out how to remove this behavior.
+impl Ord for SourceKind {
+ fn cmp(&self, other: &SourceKind) -> Ordering {
+ match (self, other) {
+ (SourceKind::Path, SourceKind::Path) => Ordering::Equal,
+ (SourceKind::Path, _) => Ordering::Less,
+ (_, SourceKind::Path) => Ordering::Greater,
+
+ (SourceKind::Registry, SourceKind::Registry) => Ordering::Equal,
+ (SourceKind::Registry, _) => Ordering::Less,
+ (_, SourceKind::Registry) => Ordering::Greater,
+
+ (SourceKind::SparseRegistry, SourceKind::SparseRegistry) => Ordering::Equal,
+ (SourceKind::SparseRegistry, _) => Ordering::Less,
+ (_, SourceKind::SparseRegistry) => Ordering::Greater,
+
+ (SourceKind::LocalRegistry, SourceKind::LocalRegistry) => Ordering::Equal,
+ (SourceKind::LocalRegistry, _) => Ordering::Less,
+ (_, SourceKind::LocalRegistry) => Ordering::Greater,
+
+ (SourceKind::Directory, SourceKind::Directory) => Ordering::Equal,
+ (SourceKind::Directory, _) => Ordering::Less,
+ (_, SourceKind::Directory) => Ordering::Greater,
+
+ (SourceKind::Git(a), SourceKind::Git(b)) => a.cmp(b),
+ }
+ }
+}
+
+/// Forwards to `Ord`
+impl PartialOrd for SourceKind {
+ fn partial_cmp(&self, other: &SourceKind) -> Option<Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+/// Information to find a specific commit in a Git repository.
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum GitReference {
+ /// From a tag.
+ Tag(String),
+ /// From a branch.
+ Branch(String),
+ /// From a specific revision. Can be a commit hash (either short or full),
+ /// or a named reference like `refs/pull/493/head`.
+ Rev(String),
+ /// The default branch of the repository, the reference named `HEAD`.
+ DefaultBranch,
+}
+
+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<'_>> {
+ match self {
+ GitReference::DefaultBranch => None,
+ _ => Some(PrettyRef {
+ inner: self,
+ url_encoded,
+ }),
+ }
+ }
+}
+
+/// A git reference that can be `Display`ed
+pub struct PrettyRef<'a> {
+ inner: &'a GitReference,
+ url_encoded: bool,
+}
+
+impl<'a> std::fmt::Display for PrettyRef<'a> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ let value: &str;
+ match self.inner {
+ GitReference::Branch(s) => {
+ write!(f, "branch=")?;
+ value = s;
+ }
+ GitReference::Tag(s) => {
+ write!(f, "tag=")?;
+ value = s;
+ }
+ GitReference::Rev(s) => {
+ write!(f, "rev=")?;
+ value = s;
+ }
+ GitReference::DefaultBranch => unreachable!(),
+ }
+ if self.url_encoded {
+ for value in url::form_urlencoded::byte_serialize(value.as_bytes()) {
+ write!(f, "{value}")?;
+ }
+ } else {
+ write!(f, "{value}")?;
+ }
+ Ok(())
+ }
+}
diff --git a/src/tools/cargo/src/cargo/util/toml/schema.rs b/src/tools/cargo/src/cargo/util_schemas/manifest.rs
index 6ea93e021..390658b0e 100644
--- a/src/tools/cargo/src/cargo/util/toml/schema.rs
+++ b/src/tools/cargo/src/cargo/util_schemas/manifest.rs
@@ -1,15 +1,24 @@
+//! `Cargo.toml` / Manifest schema definition
+//!
+//! ## Style
+//!
+//! - Fields duplicated for an alias will have an accessor with the primary field's name
+//! - Keys that exist for bookkeeping but don't correspond to the schema have a `_` prefix
+
use std::collections::BTreeMap;
use std::fmt::{self, Display, Write};
use std::path::PathBuf;
use std::str;
+use anyhow::Result;
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;
+use crate::util_schemas::core::PackageIdSpec;
+use crate::util_schemas::restricted_names;
+use crate::util_semver::PartialVersion;
/// This type is used to deserialize `Cargo.toml` files.
#[derive(Debug, Deserialize, Serialize)]
@@ -24,20 +33,21 @@ pub struct TomlManifest {
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>>,
+ pub dependencies: Option<BTreeMap<PackageName, InheritableDependency>>,
+ pub dev_dependencies: Option<BTreeMap<PackageName, InheritableDependency>>,
#[serde(rename = "dev_dependencies")]
- pub dev_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- pub build_dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ pub dev_dependencies2: Option<BTreeMap<PackageName, InheritableDependency>>,
+ pub build_dependencies: Option<BTreeMap<PackageName, InheritableDependency>>,
#[serde(rename = "build_dependencies")]
- pub build_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- pub features: Option<BTreeMap<String, Vec<String>>>,
+ pub build_dependencies2: Option<BTreeMap<PackageName, InheritableDependency>>,
+ pub features: Option<BTreeMap<FeatureName, Vec<String>>>,
pub target: Option<BTreeMap<String, TomlPlatform>>,
pub replace: Option<BTreeMap<String, TomlDependency>>,
- pub patch: Option<BTreeMap<String, BTreeMap<String, TomlDependency>>>,
+ pub patch: Option<BTreeMap<String, BTreeMap<PackageName, TomlDependency>>>,
pub workspace: Option<TomlWorkspace>,
- pub badges: Option<MaybeWorkspaceBtreeMap>,
- pub lints: Option<MaybeWorkspaceLints>,
+ pub badges: Option<InheritableBtreeMap>,
+ pub lints: Option<InheritableLints>,
+ // when adding new fields, be sure to check whether `to_virtual_manifest` should disallow them
}
impl TomlManifest {
@@ -45,19 +55,23 @@ impl TomlManifest {
self.profile.is_some()
}
- pub fn dev_dependencies(&self) -> Option<&BTreeMap<String, MaybeWorkspaceDependency>> {
+ pub fn package(&self) -> Option<&Box<TomlPackage>> {
+ self.package.as_ref().or(self.project.as_ref())
+ }
+
+ pub fn dev_dependencies(&self) -> Option<&BTreeMap<PackageName, InheritableDependency>> {
self.dev_dependencies
.as_ref()
.or(self.dev_dependencies2.as_ref())
}
- pub fn build_dependencies(&self) -> Option<&BTreeMap<String, MaybeWorkspaceDependency>> {
+ pub fn build_dependencies(&self) -> Option<&BTreeMap<PackageName, InheritableDependency>> {
self.build_dependencies
.as_ref()
.or(self.build_dependencies2.as_ref())
}
- pub fn features(&self) -> Option<&BTreeMap<String, Vec<String>>> {
+ pub fn features(&self) -> Option<&BTreeMap<FeatureName, Vec<String>>> {
self.features.as_ref()
}
}
@@ -72,22 +86,15 @@ pub struct TomlWorkspace {
pub metadata: Option<toml::Value>,
// Properties that can be inherited by members.
- pub package: Option<InheritableFields>,
- pub dependencies: Option<BTreeMap<String, TomlDependency>>,
+ pub package: Option<InheritablePackage>,
+ pub dependencies: Option<BTreeMap<PackageName, 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 struct InheritablePackage {
pub version: Option<semver::Version>,
pub authors: Option<Vec<String>>,
pub description: Option<String>,
@@ -105,10 +112,6 @@ pub struct InheritableFields {
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`.
@@ -120,19 +123,19 @@ pub struct InheritableFields {
#[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 edition: Option<InheritableString>,
+ pub rust_version: Option<InheritableRustVersion>,
+ pub name: PackageName,
+ pub version: Option<InheritableSemverVersion>,
+ pub authors: Option<InheritableVecString>,
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 exclude: Option<InheritableVecString>,
+ pub include: Option<InheritableVecString>,
+ pub publish: Option<InheritableVecStringOrBool>,
pub workspace: Option<String>,
pub im_a_teapot: Option<bool>,
pub autobins: Option<bool>,
@@ -142,15 +145,15 @@ pub struct TomlPackage {
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 description: Option<InheritableString>,
+ pub homepage: Option<InheritableString>,
+ pub documentation: Option<InheritableString>,
+ pub readme: Option<InheritableStringOrBool>,
+ pub keywords: Option<InheritableVecString>,
+ pub categories: Option<InheritableVecString>,
+ pub license: Option<InheritableString>,
+ pub license_file: Option<InheritableString>,
+ pub repository: Option<InheritableString>,
pub resolver: Option<String>,
pub metadata: Option<toml::Value>,
@@ -163,16 +166,25 @@ pub struct TomlPackage {
/// 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),
+pub enum InheritableField<T> {
+ /// The type that that is used when not inheriting from a workspace.
+ Value(T),
/// The type when inheriting from a workspace.
- Workspace(W),
+ Inherit(TomlInheritedField),
+}
+
+impl<T> InheritableField<T> {
+ pub fn as_value(&self) -> Option<&T> {
+ match self {
+ InheritableField::Inherit(_) => None,
+ InheritableField::Value(defined) => Some(defined),
+ }
+ }
}
//. This already has a `Deserialize` impl from version_trim_whitespace
-pub type MaybeWorkspaceSemverVersion = MaybeWorkspace<semver::Version, TomlWorkspaceField>;
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceSemverVersion {
+pub type InheritableSemverVersion = InheritableField<semver::Version>;
+impl<'de> de::Deserialize<'de> for InheritableSemverVersion {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
@@ -181,17 +193,17 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceSemverVersion {
.expecting("SemVer version")
.string(
|value| match value.trim().parse().map_err(de::Error::custom) {
- Ok(parsed) => Ok(MaybeWorkspace::Defined(parsed)),
+ Ok(parsed) => Ok(InheritableField::Value(parsed)),
Err(e) => Err(e),
},
)
- .map(|value| value.deserialize().map(MaybeWorkspace::Workspace))
+ .map(|value| value.deserialize().map(InheritableField::Inherit))
.deserialize(d)
}
}
-pub type MaybeWorkspaceString = MaybeWorkspace<String, TomlWorkspaceField>;
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceString {
+pub type InheritableString = InheritableField<String>;
+impl<'de> de::Deserialize<'de> for InheritableString {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
@@ -199,7 +211,7 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceString {
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
- type Value = MaybeWorkspaceString;
+ type Value = InheritableString;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
f.write_str("a string or workspace")
@@ -209,7 +221,7 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceString {
where
E: de::Error,
{
- Ok(MaybeWorkspaceString::Defined(value))
+ Ok(InheritableString::Value(value))
}
fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
@@ -217,7 +229,7 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceString {
V: de::MapAccess<'de>,
{
let mvd = de::value::MapAccessDeserializer::new(map);
- TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
+ TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit)
}
}
@@ -225,8 +237,8 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceString {
}
}
-pub type MaybeWorkspaceRustVersion = MaybeWorkspace<RustVersion, TomlWorkspaceField>;
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceRustVersion {
+pub type InheritableRustVersion = InheritableField<RustVersion>;
+impl<'de> de::Deserialize<'de> for InheritableRustVersion {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
@@ -234,7 +246,7 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceRustVersion {
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
- type Value = MaybeWorkspaceRustVersion;
+ type Value = InheritableRustVersion;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
f.write_str("a semver or workspace")
@@ -245,7 +257,7 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceRustVersion {
E: de::Error,
{
let value = value.parse::<RustVersion>().map_err(|e| E::custom(e))?;
- Ok(MaybeWorkspaceRustVersion::Defined(value))
+ Ok(InheritableRustVersion::Value(value))
}
fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
@@ -253,7 +265,7 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceRustVersion {
V: de::MapAccess<'de>,
{
let mvd = de::value::MapAccessDeserializer::new(map);
- TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
+ TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit)
}
}
@@ -261,8 +273,8 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceRustVersion {
}
}
-pub type MaybeWorkspaceVecString = MaybeWorkspace<Vec<String>, TomlWorkspaceField>;
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecString {
+pub type InheritableVecString = InheritableField<Vec<String>>;
+impl<'de> de::Deserialize<'de> for InheritableVecString {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
@@ -270,7 +282,7 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecString {
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
- type Value = MaybeWorkspaceVecString;
+ type Value = InheritableVecString;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str("a vector of strings or workspace")
@@ -280,7 +292,7 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecString {
A: de::SeqAccess<'de>,
{
let seq = de::value::SeqAccessDeserializer::new(v);
- Vec::deserialize(seq).map(MaybeWorkspace::Defined)
+ Vec::deserialize(seq).map(InheritableField::Value)
}
fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
@@ -288,7 +300,7 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecString {
V: de::MapAccess<'de>,
{
let mvd = de::value::MapAccessDeserializer::new(map);
- TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
+ TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit)
}
}
@@ -296,8 +308,8 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecString {
}
}
-pub type MaybeWorkspaceStringOrBool = MaybeWorkspace<StringOrBool, TomlWorkspaceField>;
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceStringOrBool {
+pub type InheritableStringOrBool = InheritableField<StringOrBool>;
+impl<'de> de::Deserialize<'de> for InheritableStringOrBool {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
@@ -305,7 +317,7 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceStringOrBool {
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
- type Value = MaybeWorkspaceStringOrBool;
+ type Value = InheritableStringOrBool;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str("a string, a bool, or workspace")
@@ -316,7 +328,7 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceStringOrBool {
E: de::Error,
{
let b = de::value::BoolDeserializer::new(v);
- StringOrBool::deserialize(b).map(MaybeWorkspace::Defined)
+ StringOrBool::deserialize(b).map(InheritableField::Value)
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
@@ -324,7 +336,7 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceStringOrBool {
E: de::Error,
{
let string = de::value::StringDeserializer::new(v);
- StringOrBool::deserialize(string).map(MaybeWorkspace::Defined)
+ StringOrBool::deserialize(string).map(InheritableField::Value)
}
fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
@@ -332,7 +344,7 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceStringOrBool {
V: de::MapAccess<'de>,
{
let mvd = de::value::MapAccessDeserializer::new(map);
- TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
+ TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit)
}
}
@@ -340,8 +352,8 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceStringOrBool {
}
}
-pub type MaybeWorkspaceVecStringOrBool = MaybeWorkspace<VecStringOrBool, TomlWorkspaceField>;
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecStringOrBool {
+pub type InheritableVecStringOrBool = InheritableField<VecStringOrBool>;
+impl<'de> de::Deserialize<'de> for InheritableVecStringOrBool {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
@@ -349,7 +361,7 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecStringOrBool {
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
- type Value = MaybeWorkspaceVecStringOrBool;
+ type Value = InheritableVecStringOrBool;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str("a boolean, a vector of strings, or workspace")
@@ -360,7 +372,7 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecStringOrBool {
E: de::Error,
{
let b = de::value::BoolDeserializer::new(v);
- VecStringOrBool::deserialize(b).map(MaybeWorkspace::Defined)
+ VecStringOrBool::deserialize(b).map(InheritableField::Value)
}
fn visit_seq<A>(self, v: A) -> Result<Self::Value, A::Error>
@@ -368,7 +380,7 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecStringOrBool {
A: de::SeqAccess<'de>,
{
let seq = de::value::SeqAccessDeserializer::new(v);
- VecStringOrBool::deserialize(seq).map(MaybeWorkspace::Defined)
+ VecStringOrBool::deserialize(seq).map(InheritableField::Value)
}
fn visit_map<V>(self, map: V) -> Result<Self::Value, V::Error>
@@ -376,7 +388,7 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecStringOrBool {
V: de::MapAccess<'de>,
{
let mvd = de::value::MapAccessDeserializer::new(map);
- TomlWorkspaceField::deserialize(mvd).map(MaybeWorkspace::Workspace)
+ TomlInheritedField::deserialize(mvd).map(InheritableField::Inherit)
}
}
@@ -384,35 +396,46 @@ impl<'de> de::Deserialize<'de> for MaybeWorkspaceVecStringOrBool {
}
}
-pub type MaybeWorkspaceBtreeMap =
- MaybeWorkspace<BTreeMap<String, BTreeMap<String, String>>, TomlWorkspaceField>;
+pub type InheritableBtreeMap = InheritableField<BTreeMap<String, BTreeMap<String, String>>>;
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceBtreeMap {
+impl<'de> de::Deserialize<'de> for InheritableBtreeMap {
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(
+ if let Ok(w) = TomlInheritedField::deserialize(
serde_value::ValueDeserializer::<D::Error>::new(value.clone()),
) {
return if w.workspace {
- Ok(MaybeWorkspace::Workspace(w))
+ Ok(InheritableField::Inherit(w))
} else {
Err(de::Error::custom("`workspace` cannot be false"))
};
}
BTreeMap::deserialize(serde_value::ValueDeserializer::<D::Error>::new(value))
- .map(MaybeWorkspace::Defined)
+ .map(InheritableField::Value)
}
}
#[derive(Deserialize, Serialize, Copy, Clone, Debug)]
#[serde(rename_all = "kebab-case")]
-pub struct TomlWorkspaceField {
+pub struct TomlInheritedField {
#[serde(deserialize_with = "bool_no_false")]
- pub workspace: bool,
+ workspace: bool,
+}
+
+impl TomlInheritedField {
+ pub fn new() -> Self {
+ TomlInheritedField { workspace: true }
+ }
+}
+
+impl Default for TomlInheritedField {
+ fn default() -> Self {
+ Self::new()
+ }
}
fn bool_no_false<'de, D: de::Deserializer<'de>>(deserializer: D) -> Result<bool, D::Error> {
@@ -424,42 +447,49 @@ fn bool_no_false<'de, D: de::Deserializer<'de>>(deserializer: D) -> Result<bool,
}
}
-pub type MaybeWorkspaceDependency = MaybeWorkspace<TomlDependency, TomlWorkspaceDependency>;
+#[derive(Serialize, Clone, Debug)]
+#[serde(untagged)]
+pub enum InheritableDependency {
+ /// The type that that is used when not inheriting from a workspace.
+ Value(TomlDependency),
+ /// The type when inheriting from a workspace.
+ Inherit(TomlInheritedDependency),
+}
-impl MaybeWorkspaceDependency {
+impl InheritableDependency {
pub fn unused_keys(&self) -> Vec<String> {
match self {
- MaybeWorkspaceDependency::Defined(d) => d.unused_keys(),
- MaybeWorkspaceDependency::Workspace(w) => w.unused_keys.keys().cloned().collect(),
+ InheritableDependency::Value(d) => d.unused_keys(),
+ InheritableDependency::Inherit(w) => w._unused_keys.keys().cloned().collect(),
}
}
}
-impl<'de> de::Deserialize<'de> for MaybeWorkspaceDependency {
+impl<'de> de::Deserialize<'de> for InheritableDependency {
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::<
+ if let Ok(w) = TomlInheritedDependency::deserialize(serde_value::ValueDeserializer::<
D::Error,
>::new(value.clone()))
{
return if w.workspace {
- Ok(MaybeWorkspace::Workspace(w))
+ Ok(InheritableDependency::Inherit(w))
} else {
Err(de::Error::custom("`workspace` cannot be false"))
};
}
TomlDependency::deserialize(serde_value::ValueDeserializer::<D::Error>::new(value))
- .map(MaybeWorkspace::Defined)
+ .map(InheritableDependency::Value)
}
}
#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(rename_all = "kebab-case")]
-pub struct TomlWorkspaceDependency {
+pub struct TomlInheritedDependency {
pub workspace: bool,
pub features: Option<Vec<String>>,
pub default_features: Option<bool>,
@@ -471,10 +501,10 @@ pub struct TomlWorkspaceDependency {
/// 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>,
+ pub _unused_keys: BTreeMap<String, toml::Value>,
}
-impl TomlWorkspaceDependency {
+impl TomlInheritedDependency {
pub fn default_features(&self) -> Option<bool> {
self.default_features.or(self.default_features2)
}
@@ -489,7 +519,7 @@ pub enum TomlDependency<P: Clone = String> {
/// The simple format is equivalent to a detailed dependency
/// specifying only a version, eg.
/// `package = { version = "<version>" }`
- Detailed(DetailedTomlDependency<P>),
+ Detailed(TomlDetailedDependency<P>),
}
impl TomlDependency {
@@ -507,10 +537,17 @@ impl TomlDependency {
}
}
+ pub fn is_public(&self) -> bool {
+ match self {
+ TomlDependency::Detailed(d) => d.public.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(),
+ TomlDependency::Detailed(detailed) => detailed._unused_keys.keys().cloned().collect(),
}
}
}
@@ -533,9 +570,9 @@ impl<'de, P: Deserialize<'de> + Clone> de::Deserialize<'de> for TomlDependency<P
#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(rename_all = "kebab-case")]
-pub struct DetailedTomlDependency<P: Clone = String> {
+pub struct TomlDetailedDependency<P: Clone = String> {
pub version: Option<String>,
- pub registry: Option<String>,
+ pub registry: Option<RegistryName>,
/// 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
@@ -555,7 +592,7 @@ pub struct DetailedTomlDependency<P: Clone = String> {
pub default_features: Option<bool>,
#[serde(rename = "default_features")]
pub default_features2: Option<bool>,
- pub package: Option<String>,
+ pub package: Option<PackageName>,
pub public: Option<bool>,
/// One or more of `bin`, `cdylib`, `staticlib`, `bin:<name>`.
@@ -568,17 +605,17 @@ pub struct DetailedTomlDependency<P: Clone = 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>,
+ pub _unused_keys: BTreeMap<String, toml::Value>,
}
-impl<P: Clone> DetailedTomlDependency<P> {
+impl<P: Clone> TomlDetailedDependency<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> {
+impl<P: Clone> Default for TomlDetailedDependency<P> {
fn default() -> Self {
Self {
version: Default::default(),
@@ -598,16 +635,16 @@ impl<P: Clone> Default for DetailedTomlDependency<P> {
artifact: Default::default(),
lib: Default::default(),
target: Default::default(),
- unused_keys: Default::default(),
+ _unused_keys: Default::default(),
}
}
}
#[derive(Deserialize, Serialize, Clone, Debug, Default)]
-pub struct TomlProfiles(pub BTreeMap<String, TomlProfile>);
+pub struct TomlProfiles(pub BTreeMap<ProfileName, TomlProfile>);
impl TomlProfiles {
- pub fn get_all(&self) -> &BTreeMap<String, TomlProfile> {
+ pub fn get_all(&self) -> &BTreeMap<ProfileName, TomlProfile> {
&self.0
}
@@ -643,6 +680,98 @@ pub struct TomlProfile {
pub trim_paths: Option<TomlTrimPaths>,
}
+impl TomlProfile {
+ /// Overwrite self's values with the given profile.
+ pub fn merge(&mut self, profile: &Self) {
+ 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.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()),
+ }
+ }
+
+ 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.clone());
+ }
+
+ if let Some(v) = &profile.dir_name {
+ self.dir_name = Some(v.clone());
+ }
+
+ if let Some(v) = &profile.strip {
+ self.strip = Some(v.clone());
+ }
+
+ if let Some(v) = &profile.trim_paths {
+ self.trim_paths = Some(v.clone())
+ }
+ }
+}
+
#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub enum ProfilePackageSpec {
Spec(PackageIdSpec),
@@ -950,10 +1079,9 @@ pub struct TomlTarget {
pub doc: Option<bool>,
pub plugin: Option<bool>,
pub doc_scrape_examples: Option<bool>,
- #[serde(rename = "proc-macro")]
- pub proc_macro_raw: Option<bool>,
+ pub proc_macro: Option<bool>,
#[serde(rename = "proc_macro")]
- pub proc_macro_raw2: Option<bool>,
+ pub proc_macro2: Option<bool>,
pub harness: Option<bool>,
pub required_features: Option<Vec<String>>,
pub edition: Option<String>,
@@ -965,7 +1093,7 @@ impl TomlTarget {
}
pub fn proc_macro(&self) -> Option<bool> {
- self.proc_macro_raw.or(self.proc_macro_raw2).or_else(|| {
+ self.proc_macro.or(self.proc_macro2).or_else(|| {
if let Some(types) = self.crate_types() {
if types.contains(&"proc-macro".to_string()) {
return Some(true);
@@ -982,27 +1110,138 @@ impl TomlTarget {
}
}
+macro_rules! str_newtype {
+ ($name:ident) => {
+ /// Verified string newtype
+ #[derive(Serialize, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+ #[serde(transparent)]
+ pub struct $name<T: AsRef<str> = String>(T);
+
+ impl<T: AsRef<str>> $name<T> {
+ pub fn into_inner(self) -> T {
+ self.0
+ }
+ }
+
+ impl<T: AsRef<str>> AsRef<str> for $name<T> {
+ fn as_ref(&self) -> &str {
+ self.0.as_ref()
+ }
+ }
+
+ impl<T: AsRef<str>> std::ops::Deref for $name<T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+ }
+
+ impl<T: AsRef<str>> std::borrow::Borrow<str> for $name<T> {
+ fn borrow(&self) -> &str {
+ self.0.as_ref()
+ }
+ }
+
+ impl<'a> std::str::FromStr for $name<String> {
+ type Err = anyhow::Error;
+
+ fn from_str(value: &str) -> Result<Self, Self::Err> {
+ Self::new(value.to_owned())
+ }
+ }
+
+ impl<'de, T: AsRef<str> + serde::Deserialize<'de>> serde::Deserialize<'de> for $name<T> {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: serde::Deserializer<'de>,
+ {
+ let inner = T::deserialize(deserializer)?;
+ Self::new(inner).map_err(serde::de::Error::custom)
+ }
+ }
+
+ impl<T: AsRef<str>> Display for $name<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0.as_ref().fmt(f)
+ }
+ }
+ };
+}
+
+str_newtype!(PackageName);
+
+impl<T: AsRef<str>> PackageName<T> {
+ /// Validated package name
+ pub fn new(name: T) -> Result<Self> {
+ restricted_names::validate_package_name(name.as_ref(), "package name", "")?;
+ Ok(Self(name))
+ }
+}
+
+impl PackageName {
+ /// Coerce a value to be a validate package name
+ ///
+ /// Replaces invalid values with `placeholder`
+ pub fn sanitize(name: impl AsRef<str>, placeholder: char) -> Self {
+ PackageName(restricted_names::sanitize_package_name(
+ name.as_ref(),
+ placeholder,
+ ))
+ }
+}
+
+str_newtype!(RegistryName);
+
+impl<T: AsRef<str>> RegistryName<T> {
+ /// Validated registry name
+ pub fn new(name: T) -> Result<Self> {
+ restricted_names::validate_package_name(name.as_ref(), "registry name", "")?;
+ Ok(Self(name))
+ }
+}
+
+str_newtype!(ProfileName);
+
+impl<T: AsRef<str>> ProfileName<T> {
+ /// Validated profile name
+ pub fn new(name: T) -> Result<Self> {
+ restricted_names::validate_profile_name(name.as_ref())?;
+ Ok(Self(name))
+ }
+}
+
+str_newtype!(FeatureName);
+
+impl<T: AsRef<str>> FeatureName<T> {
+ /// Validated feature name
+ pub fn new(name: T) -> Result<Self> {
+ restricted_names::validate_feature_name(name.as_ref())?;
+ Ok(Self(name))
+ }
+}
+
/// 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>>,
+ pub dependencies: Option<BTreeMap<PackageName, InheritableDependency>>,
+ pub build_dependencies: Option<BTreeMap<PackageName, InheritableDependency>>,
#[serde(rename = "build_dependencies")]
- pub build_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
- pub dev_dependencies: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ pub build_dependencies2: Option<BTreeMap<PackageName, InheritableDependency>>,
+ pub dev_dependencies: Option<BTreeMap<PackageName, InheritableDependency>>,
#[serde(rename = "dev_dependencies")]
- pub dev_dependencies2: Option<BTreeMap<String, MaybeWorkspaceDependency>>,
+ pub dev_dependencies2: Option<BTreeMap<PackageName, InheritableDependency>>,
}
impl TomlPlatform {
- pub fn dev_dependencies(&self) -> Option<&BTreeMap<String, MaybeWorkspaceDependency>> {
+ pub fn dev_dependencies(&self) -> Option<&BTreeMap<PackageName, InheritableDependency>> {
self.dev_dependencies
.as_ref()
.or(self.dev_dependencies2.as_ref())
}
- pub fn build_dependencies(&self) -> Option<&BTreeMap<String, MaybeWorkspaceDependency>> {
+ pub fn build_dependencies(&self) -> Option<&BTreeMap<PackageName, InheritableDependency>> {
self.build_dependencies
.as_ref()
.or(self.build_dependencies2.as_ref())
@@ -1012,7 +1251,7 @@ impl TomlPlatform {
#[derive(Deserialize, Serialize, Debug, Clone)]
#[serde(expecting = "a lints table")]
#[serde(rename_all = "kebab-case")]
-pub struct MaybeWorkspaceLints {
+pub struct InheritableLints {
#[serde(skip_serializing_if = "is_false")]
#[serde(deserialize_with = "bool_no_false", default)]
pub workspace: bool,
@@ -1082,6 +1321,51 @@ pub enum TomlLintLevel {
Allow,
}
+#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Debug, serde::Serialize)]
+#[serde(transparent)]
+pub struct RustVersion(PartialVersion);
+
+impl std::ops::Deref for RustVersion {
+ type Target = PartialVersion;
+
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+impl std::str::FromStr for RustVersion {
+ type Err = anyhow::Error;
+
+ fn from_str(value: &str) -> Result<Self, Self::Err> {
+ let partial = value.parse::<PartialVersion>()?;
+ if partial.pre.is_some() {
+ anyhow::bail!("unexpected prerelease field, expected a version like \"1.32\"")
+ }
+ if partial.build.is_some() {
+ anyhow::bail!("unexpected prerelease field, expected a version like \"1.32\"")
+ }
+ Ok(Self(partial))
+ }
+}
+
+impl<'de> serde::Deserialize<'de> for RustVersion {
+ 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)
+ }
+}
+
+impl Display for RustVersion {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
#[derive(Copy, Clone, Debug)]
pub struct InvalidCargoFeatures {}
diff --git a/src/tools/cargo/src/cargo/util_schemas/mod.rs b/src/tools/cargo/src/cargo/util_schemas/mod.rs
new file mode 100644
index 000000000..84b6c39a8
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util_schemas/mod.rs
@@ -0,0 +1,11 @@
+//! Low-level Cargo format schemas
+//!
+//! This is types with logic mostly focused on `serde` and `FromStr` for use in reading files and
+//! parsing command-lines.
+//! Any logic for getting final semantics from these will likely need other tools to process, like
+//! `cargo metadata`.
+
+pub mod core;
+pub mod manifest;
+
+mod restricted_names;
diff --git a/src/tools/cargo/src/cargo/util_schemas/restricted_names.rs b/src/tools/cargo/src/cargo/util_schemas/restricted_names.rs
new file mode 100644
index 000000000..2d22ce4f2
--- /dev/null
+++ b/src/tools/cargo/src/cargo/util_schemas/restricted_names.rs
@@ -0,0 +1,218 @@
+//! Helpers for validating and checking names like package and crate names.
+
+use anyhow::bail;
+use anyhow::Result;
+
+/// Check the base requirements for a package name.
+///
+/// This can be used for other things than package names, to enforce some
+/// level of sanity. Note that package names have other restrictions
+/// elsewhere. `cargo new` has a few restrictions, such as checking for
+/// reserved names. crates.io has even more restrictions.
+pub fn validate_package_name(name: &str, what: &str, help: &str) -> Result<()> {
+ if name.is_empty() {
+ bail!("{what} cannot be empty");
+ }
+
+ let mut chars = name.chars();
+ if let Some(ch) = chars.next() {
+ if ch.is_digit(10) {
+ // A specific error for a potentially common case.
+ bail!(
+ "the name `{}` cannot be used as a {}, \
+ the name cannot start with a digit{}",
+ name,
+ what,
+ help
+ );
+ }
+ if !(unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_') {
+ bail!(
+ "invalid character `{}` in {}: `{}`, \
+ the first character must be a Unicode XID start character \
+ (most letters or `_`){}",
+ ch,
+ what,
+ name,
+ help
+ );
+ }
+ }
+ for ch in chars {
+ if !(unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-') {
+ bail!(
+ "invalid character `{}` in {}: `{}`, \
+ characters must be Unicode XID characters \
+ (numbers, `-`, `_`, or most letters){}",
+ ch,
+ what,
+ name,
+ help
+ );
+ }
+ }
+ Ok(())
+}
+
+/// Ensure a package name is [valid][validate_package_name]
+pub fn sanitize_package_name(name: &str, placeholder: char) -> String {
+ let mut slug = String::new();
+ let mut chars = name.chars();
+ while let Some(ch) = chars.next() {
+ if (unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_') && !ch.is_digit(10) {
+ slug.push(ch);
+ break;
+ }
+ }
+ while let Some(ch) = chars.next() {
+ if unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-' {
+ slug.push(ch);
+ } else {
+ slug.push(placeholder);
+ }
+ }
+ if slug.is_empty() {
+ slug.push_str("package");
+ }
+ slug
+}
+
+/// Validate dir-names and profile names according to RFC 2678.
+pub fn validate_profile_name(name: &str) -> Result<()> {
+ 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(())
+}
+
+pub fn validate_feature_name(name: &str) -> Result<()> {
+ if name.is_empty() {
+ bail!("feature name cannot be empty");
+ }
+
+ if name.starts_with("dep:") {
+ bail!("feature named `{name}` is not allowed to start with `dep:`",);
+ }
+ if name.contains('/') {
+ bail!("feature named `{name}` is not allowed to contain slashes",);
+ }
+ let mut chars = name.chars();
+ if let Some(ch) = chars.next() {
+ if !(unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' || ch.is_digit(10)) {
+ bail!(
+ "invalid character `{ch}` in feature `{name}`, \
+ the first character must be a Unicode XID start character or digit \
+ (most letters or `_` or `0` to `9`)",
+ );
+ }
+ }
+ for ch in chars {
+ if !(unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-' || ch == '+' || ch == '.') {
+ bail!(
+ "invalid character `{ch}` in feature `{name}`, \
+ characters must be Unicode XID characters, '-', `+`, or `.` \
+ (numbers, `+`, `-`, `_`, `.`, or most letters)",
+ );
+ }
+ }
+ Ok(())
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn valid_feature_names() {
+ assert!(validate_feature_name("c++17").is_ok());
+ assert!(validate_feature_name("128bit").is_ok());
+ assert!(validate_feature_name("_foo").is_ok());
+ assert!(validate_feature_name("feat-name").is_ok());
+ assert!(validate_feature_name("feat_name").is_ok());
+ assert!(validate_feature_name("foo.bar").is_ok());
+
+ assert!(validate_feature_name("").is_err());
+ assert!(validate_feature_name("+foo").is_err());
+ assert!(validate_feature_name("-foo").is_err());
+ assert!(validate_feature_name(".foo").is_err());
+ assert!(validate_feature_name("dep:bar").is_err());
+ assert!(validate_feature_name("foo/bar").is_err());
+ assert!(validate_feature_name("foo:bar").is_err());
+ assert!(validate_feature_name("foo?").is_err());
+ assert!(validate_feature_name("?foo").is_err());
+ assert!(validate_feature_name("ⒶⒷⒸ").is_err());
+ assert!(validate_feature_name("a¼").is_err());
+ assert!(validate_feature_name("").is_err());
+ }
+}
diff --git a/src/tools/cargo/src/doc/contrib/src/issues.md b/src/tools/cargo/src/doc/contrib/src/issues.md
index b82492d27..c77c083b2 100644
--- a/src/tools/cargo/src/doc/contrib/src/issues.md
+++ b/src/tools/cargo/src/doc/contrib/src/issues.md
@@ -33,10 +33,9 @@ relevant are:
* [`rust-lang/crates.io`] --- Home for the [crates.io] website.
Issues with [`cargo fix`] can be tricky to know where they should be filed,
-since the fixes are driven by `rustc`, processed by [`rustfix`], and the
-front-interface is implemented in Cargo. Feel free to file in the Cargo issue
-tracker, and it will get moved to one of the other issue trackers if
-necessary.
+since the fixes are driven by `rustc`, and the front-interface is implemented
+in Cargo. Feel free to file in the Cargo issue tracker, and it will get moved
+to the [`rust-lang/rust`] issue tracker if necessary.
[Process]: process/index.md
[security policy]: https://www.rust-lang.org/security.html
@@ -51,7 +50,6 @@ necessary.
[`rustup`]: https://rust-lang.github.io/rustup/
[`rust-lang/crates.io`]: https://github.com/rust-lang/crates.io
[crates.io]: https://crates.io/
-[`rustfix`]: https://github.com/rust-lang/rustfix/
[`cargo fix`]: https://doc.rust-lang.org/cargo/commands/cargo-fix.html
## Issue labels
diff --git a/src/tools/cargo/src/doc/contrib/src/process/release.md b/src/tools/cargo/src/doc/contrib/src/process/release.md
index 169d63ed8..3c3062f32 100644
--- a/src/tools/cargo/src/doc/contrib/src/process/release.md
+++ b/src/tools/cargo/src/doc/contrib/src/process/release.md
@@ -146,10 +146,9 @@ Cargo team, or the Release team) should publish it manually using `cargo
publish`.
Some packages are not published automatically because they are not part of the
-Rust release train. These currently include all of the [`credential`] packages
-and the [`home`] package. These are published manually on an as-needed or
-as-requested basis by whoever has permissions (currently [@ehuss] or the
-Release/Infra team).
+Rust release train. This currently only includes the [`home`] package. These
+are published manually on an as-needed or as-requested basis by whoever has
+permissions (currently [@ehuss] or the Release/Infra team).
In the future, these manual publishing options should be integrated with
GitHub Actions so that any team member can trigger them. Likely that should
@@ -158,7 +157,6 @@ Secrets, and setting up GitHub Actions workflows with the appropriate
permissions which can be manually triggered to launch a release.
[`home`]: https://github.com/rust-lang/cargo/tree/master/crates/home
-[`credential`]: https://github.com/rust-lang/cargo/tree/master/credential
[`publish.py` script]: https://github.com/rust-lang/cargo/blob/master/publish.py
## Beta backports
diff --git a/src/tools/cargo/src/doc/contrib/src/team.md b/src/tools/cargo/src/doc/contrib/src/team.md
index 51b8eeddd..39b428849 100644
--- a/src/tools/cargo/src/doc/contrib/src/team.md
+++ b/src/tools/cargo/src/doc/contrib/src/team.md
@@ -61,6 +61,10 @@ Members are given privileges, such as:
The team meets on a weekly basis on a video chat.
If you are interested in participating, feel free to contact us on [Zulip].
+Minutes for all meetings are recorded on [HackMD][meeting-minutes].
+
+[meeting-minutes]: https://hackmd.io/team/rust-cargo-team?nav=overview&tags=%5B%22meetings%22%5D
+
### Becoming a member
A contributor can become a member of the Cargo Team by requesting a review or being nominated by one of the existing members.
@@ -71,6 +75,27 @@ Contributors who wish to join the team should exhibit an interest in carrying th
Members may leave at any time, preferably by letting the team know ahead of time.
+## Team resources
+
+### Zulip
+
+The Cargo team has several streams on Zulip:
+
+- [`#t-cargo`](https://rust-lang.zulipchat.com/#narrow/stream/246057-t-cargo) --- General public channel for discussing any topics related to Cargo.
+- [`#t-cargo/build-integration`](https://rust-lang.zulipchat.com/#narrow/stream/334885-t-cargo.2Fbuild-integration) --- Discussions about integration with build systems.
+- [`#t-cargo/PubGrub`](https://rust-lang.zulipchat.com/#narrow/stream/260232-t-cargo.2FPubGrub) --- Discussions about the [PubGrub](https://github.com/pubgrub-rs/pubgrub) project.
+
+The following are private streams for the Cargo team. The team should avoid using this unless necessary (for example, discussing a security issue or team and meeting organization).
+
+- [`#t-cargo/private`](https://rust-lang.zulipchat.com/#narrow/stream/296752-t-cargo.2Fprivate) --- Private channel for discussions among the team.
+- [`#t-cargo/meetings`](https://rust-lang.zulipchat.com/#narrow/stream/364532-t-cargo.2Fmeetings) --- Private channel for discussions about team meetings, including non-members who regularly attend the meetings.
+
+### HackMD
+
+The Cargo team has a shared, public workspace on HackMD at <https://hackmd.io/team/rust-cargo-team> for drafting documents and recording meeting minutes.
+
+Since this HackMD workspace is using the free service, it does not support private documents. If you need to draft a private document, create it in your personal workspace and use private channels to share the link.
+
## Decision process
The team uses a consensus-driven process for making decisions ranging from new features and major changes to management of the team itself.
diff --git a/src/tools/cargo/src/doc/man/cargo-search.md b/src/tools/cargo/src/doc/man/cargo-search.md
index f3d87cb12..fbecb79ec 100644
--- a/src/tools/cargo/src/doc/man/cargo-search.md
+++ b/src/tools/cargo/src/doc/man/cargo-search.md
@@ -2,7 +2,7 @@
## NAME
-cargo-search --- Search packages in crates.io
+cargo-search --- Search packages in the registry. Default registry is crates.io
## SYNOPSIS
@@ -49,4 +49,5 @@ Limit the number of results (default: 10, max: 100).
cargo search serde
## SEE ALSO
+
{{man "cargo" 1}}, {{man "cargo-install" 1}}, {{man "cargo-publish" 1}}
diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-search.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-search.txt
index 74bbda9f7..9fd89b67e 100644
--- a/src/tools/cargo/src/doc/man/generated_txt/cargo-search.txt
+++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-search.txt
@@ -1,7 +1,8 @@
CARGO-SEARCH(1)
NAME
- cargo-search — Search packages in crates.io
+ cargo-search — Search packages in the registry. Default registry is
+ crates.io
SYNOPSIS
cargo search [options] [query…]
diff --git a/src/tools/cargo/src/doc/src/commands/cargo-search.md b/src/tools/cargo/src/doc/src/commands/cargo-search.md
index 72e2accf3..1a98f2869 100644
--- a/src/tools/cargo/src/doc/src/commands/cargo-search.md
+++ b/src/tools/cargo/src/doc/src/commands/cargo-search.md
@@ -2,7 +2,7 @@
## NAME
-cargo-search --- Search packages in crates.io
+cargo-search --- Search packages in the registry. Default registry is crates.io
## SYNOPSIS
@@ -131,4 +131,5 @@ details on environment variables that Cargo reads.
cargo search serde
## SEE ALSO
+
[cargo(1)](cargo.html), [cargo-install(1)](cargo-install.html), [cargo-publish(1)](cargo-publish.html)
diff --git a/src/tools/cargo/src/doc/src/guide/continuous-integration.md b/src/tools/cargo/src/doc/src/guide/continuous-integration.md
index 0c3cec889..282ca1ab8 100644
--- a/src/tools/cargo/src/doc/src/guide/continuous-integration.md
+++ b/src/tools/cargo/src/doc/src/guide/continuous-integration.md
@@ -156,6 +156,30 @@ jobs:
For projects with higher risks of per-platform or per-Rust version failures,
more combinations may want to be tested.
+## Verifying `rust-version`
+
+When publishing packages that specify [`rust-version`](../reference/manifest.md#the-rust-version-field),
+it is important to verify the correctness of that field.
+
+Some third-party tools that can help with this include:
+- [`cargo-msrv`](https://crates.io/crates/cargo-msrv)
+- [`cargo-hack`](https://crates.io/crates/cargo-hack)
+
+An example of one way to do this, using GitHub Actions:
+```yaml
+jobs:
+ msrv:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ - uses: taiki-e/install-action@cargo-hack
+ - run: cargo hack check --rust-version --workspace --all-targets --ignore-private
+```
+This tries to balance thoroughness with turnaround time:
+- A single platform is used as most projects are platform-agnostic, trusting platform-specific dependencies to verify their behavior.
+- `cargo check` is used as most issues contributors will run into are API availability and not behavior.
+- Unpublished packages are skipped as this assumes only consumers of the verified project, through a registry, will care about `rust-version`.
+
[`cargo add`]: ../commands/cargo-add.md
[`cargo install`]: ../commands/cargo-install.md
[Dependabot]: https://docs.github.com/en/code-security/dependabot/working-with-dependabot
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 3f0552613..40a0d32f1 100644
--- a/src/tools/cargo/src/doc/src/reference/environment-variables.md
+++ b/src/tools/cargo/src/doc/src/reference/environment-variables.md
@@ -265,6 +265,7 @@ corresponding environment variable is set to the empty string, `""`.
where integration tests or benchmarks are free to put any data needed by
the tests/benches. Cargo initially creates this directory but doesn't
manage its content in any way, this is the responsibility of the test code.
+* `CARGO_RUSTC_CURRENT_DIR` --- This is a path that `rustc` is invoked from **(nightly only)**.
[Cargo target]: cargo-targets.md
[binaries]: cargo-targets.md#binaries
@@ -352,6 +353,8 @@ let out_dir = env::var("OUT_DIR").unwrap();
* `CARGO_CFG_TARGET_POINTER_WIDTH=64` --- The CPU [pointer width].
* `CARGO_CFG_TARGET_ENDIAN=little` --- The CPU [target endianness].
* `CARGO_CFG_TARGET_FEATURE=mmx,sse` --- List of CPU [target features] enabled.
+ > Note that different [target triples][Target Triple] have different sets of `cfg` values,
+ > hence variables present in one target triple might not be available in the other.
* `OUT_DIR` --- the folder in which all output and intermediate artifacts should
be placed. This folder is inside the build directory for the
package being built, and it is unique for the package in question.
diff --git a/src/tools/cargo/src/doc/src/reference/manifest.md b/src/tools/cargo/src/doc/src/reference/manifest.md
index e3168a47f..cb6974537 100644
--- a/src/tools/cargo/src/doc/src/reference/manifest.md
+++ b/src/tools/cargo/src/doc/src/reference/manifest.md
@@ -185,6 +185,10 @@ The `rust-version` may be ignored using the `--ignore-rust-version` option.
Setting the `rust-version` key in `[package]` will affect all targets/crates in
the package, including test suites, benchmarks, binaries, examples, etc.
+To find the minimum `rust-version` compatible with your project, you can use third-party tools like [`cargo-msrv`](https://crates.io/crates/cargo-msrv).
+
+When used on packages that get published, we recommend [verifying the `rust-version`](../guide/continuous-integration.md#verifying-rust-version).
+
### The `description` field
The description is a short blurb about the package. [crates.io] will display
@@ -551,7 +555,7 @@ This is short-hand for:
unsafe_code = { level = "forbid", priority = 0 }
```
-`level` corresponds to the lint levels in `rustc`:
+`level` corresponds to the [lint levels](https://doc.rust-lang.org/rustc/lints/levels.html) in `rustc`:
- `forbid`
- `deny`
- `warn`
@@ -576,6 +580,11 @@ unsafe_code = "forbid"
enum_glob_use = "deny"
```
+Generally, these will only affect local development of the current package.
+Cargo only applies these to the current package and not to dependencies.
+As for dependents, Cargo suppresses lints from non-path dependencies with features like
+[`--cap-lints`](../../rustc/lints/levels.html#capping-lints).
+
## The `[badges]` section
The `[badges]` section is for specifying status badges that can be displayed
diff --git a/src/tools/cargo/src/doc/src/reference/pkgid-spec.md b/src/tools/cargo/src/doc/src/reference/pkgid-spec.md
index 7f20973b5..b2dfe827b 100644
--- a/src/tools/cargo/src/doc/src/reference/pkgid-spec.md
+++ b/src/tools/cargo/src/doc/src/reference/pkgid-spec.md
@@ -21,11 +21,13 @@ qualified with a version to make it unique, such as `regex@1.4.3`.
The formal grammar for a Package Id Specification is:
```notrust
-spec := pkgname
- | proto "://" hostname-and-path [ "#" ( pkgname | semver ) ]
+spec := pkgname |
+ [ kind "+" ] proto "://" hostname-and-path [ "?" query] [ "#" ( pkgname | semver ) ]
+query = ( "branch" | "tag" | "rev" ) "=" ref
pkgname := name [ ("@" | ":" ) semver ]
semver := digits [ "." digits [ "." digits [ "-" prerelease ] [ "+" build ]]]
+kind = "registry" | "git" | "file"
proto := "http" | "git" | ...
```
@@ -38,28 +40,32 @@ that come from different sources such as different registries.
The following are references to the `regex` package on `crates.io`:
-| Spec | Name | Version |
-|:------------------------------------------------------------|:-------:|:-------:|
-| `regex` | `regex` | `*` |
-| `regex@1.4` | `regex` | `1.4.*` |
-| `regex@1.4.3` | `regex` | `1.4.3` |
-| `https://github.com/rust-lang/crates.io-index#regex` | `regex` | `*` |
-| `https://github.com/rust-lang/crates.io-index#regex@1.4.3` | `regex` | `1.4.3` |
+| Spec | Name | Version |
+|:------------------------------------------------------------------|:-------:|:-------:|
+| `regex` | `regex` | `*` |
+| `regex@1.4` | `regex` | `1.4.*` |
+| `regex@1.4.3` | `regex` | `1.4.3` |
+| `https://github.com/rust-lang/crates.io-index#regex` | `regex` | `*` |
+| `https://github.com/rust-lang/crates.io-index#regex@1.4.3` | `regex` | `1.4.3` |
+| `registry+https://github.com/rust-lang/crates.io-index#regex@1.4.3` | `regex` | `1.4.3` |
The following are some examples of specs for several different git dependencies:
-| Spec | Name | Version |
-|:----------------------------------------------------------|:----------------:|:--------:|
-| `https://github.com/rust-lang/cargo#0.52.0` | `cargo` | `0.52.0` |
-| `https://github.com/rust-lang/cargo#cargo-platform@0.1.2` | <nobr>`cargo-platform`</nobr> | `0.1.2` |
-| `ssh://git@github.com/rust-lang/regex.git#regex@1.4.3` | `regex` | `1.4.3` |
+| Spec | Name | Version |
+|:-----------------------------------------------------------|:----------------:|:--------:|
+| `https://github.com/rust-lang/cargo#0.52.0` | `cargo` | `0.52.0` |
+| `https://github.com/rust-lang/cargo#cargo-platform@0.1.2` | <nobr>`cargo-platform`</nobr> | `0.1.2` |
+| `ssh://git@github.com/rust-lang/regex.git#regex@1.4.3` | `regex` | `1.4.3` |
+| `git+ssh://git@github.com/rust-lang/regex.git#regex@1.4.3` | `regex` | `1.4.3` |
+| `git+ssh://git@github.com/rust-lang/regex.git?branch=dev#regex@1.4.3` | `regex` | `1.4.3` |
Local packages on the filesystem can use `file://` URLs to reference them:
-| Spec | Name | Version |
-|:---------------------------------------|:-----:|:-------:|
-| `file:///path/to/my/project/foo` | `foo` | `*` |
-| `file:///path/to/my/project/foo#1.1.8` | `foo` | `1.1.8` |
+| Spec | Name | Version |
+|:--------------------------------------------|:-----:|:-------:|
+| `file:///path/to/my/project/foo` | `foo` | `*` |
+| `file:///path/to/my/project/foo#1.1.8` | `foo` | `1.1.8` |
+| `path+file:///path/to/my/project/foo#1.1.8` | `foo` | `1.1.8` |
### Brevity of specifications
diff --git a/src/tools/cargo/src/doc/src/reference/profiles.md b/src/tools/cargo/src/doc/src/reference/profiles.md
index 165b41d60..fac84f836 100644
--- a/src/tools/cargo/src/doc/src/reference/profiles.md
+++ b/src/tools/cargo/src/doc/src/reference/profiles.md
@@ -52,7 +52,7 @@ It is recommended to experiment with different levels to find the right
balance for your project. There may be surprising results, such as level `3`
being slower than `2`, or the `"s"` and `"z"` levels not being necessarily
smaller. You may also want to reevaluate your settings over time as newer
-versions of `rustc` changes optimization behavior.
+versions of `rustc` change optimization behavior.
See also [Profile Guided Optimization] for more advanced optimization
techniques.
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 f07bf7066..0508900c0 100644
--- a/src/tools/cargo/src/doc/src/reference/registry-authentication.md
+++ b/src/tools/cargo/src/doc/src/reference/registry-authentication.md
@@ -10,7 +10,7 @@ provider is used if no providers are configured.
Cargo also includes platform-specific providers that use the operating system to securely store
tokens. The `cargo:token` provider is also included which stores credentials in unencrypted plain
-text in the [credentials](config.md#credentials) file.
+text in the [credentials](config.md#credentials) file.
## Recommended configuration
It's recommended to configure a global credential provider list in `$CARGO_HOME/config.toml`
@@ -71,8 +71,12 @@ The Keychain Access app can be used to view stored tokens.
### `cargo:libsecret`
Uses [libsecret](https://wiki.gnome.org/Projects/Libsecret) to store tokens.
-On GNOME, credentials can be viewed using [GNOME Keyring](https://wiki.gnome.org/Projects/GnomeKeyring)
-applications.
+Any password manager with libsecret support can be used to view stored tokens.
+The following are a few examples (non-exhaustive):
+
+- [GNOME Keyring](https://wiki.gnome.org/Projects/GnomeKeyring)
+- [KDE Wallet Manager](https://apps.kde.org/kwalletmanager5/) (since KDE Frameworks 5.97.0)
+- [KeePassXC](https://keepassxc.org/) (since 2.5.0)
### `cargo:token-from-stdout <command> <args>`
Launch a subprocess that returns a token on stdout. Newlines will be trimmed.
diff --git a/src/tools/cargo/src/doc/src/reference/registry-web-api.md b/src/tools/cargo/src/doc/src/reference/registry-web-api.md
index 7b0f3511b..d36b4f16d 100644
--- a/src/tools/cargo/src/doc/src/reference/registry-web-api.md
+++ b/src/tools/cargo/src/doc/src/reference/registry-web-api.md
@@ -10,7 +10,7 @@ visit the registry's website to obtain a token, and Cargo can store the token
using the [`cargo login`] command, or by passing the token on the
command-line.
-Responses use the 200 response code for success.
+Responses use a 2xx response code for success.
Errors should use an appropriate response code, such as 404.
Failure
responses should have a JSON object with the following structure:
diff --git a/src/tools/cargo/src/doc/src/reference/semver.md b/src/tools/cargo/src/doc/src/reference/semver.md
index f09250f1a..ed7095ae7 100644
--- a/src/tools/cargo/src/doc/src/reference/semver.md
+++ b/src/tools/cargo/src/doc/src/reference/semver.md
@@ -319,7 +319,7 @@ fn main() {
#### Minor: `repr(C)` add enum variant {#repr-c-enum-variant-new}
-It is usually safe to add variants to a `repr(C)` enum, if the enum uses `non_exhastive`.
+It is usually safe to add variants to a `repr(C)` enum, if the enum uses `non_exhaustive`.
See [enum-variant-new](#enum-variant-new) for more discussion.
Note that this may be a breaking change since it changes the size and alignment of the type.
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 2bdbbceee..c9e1e4f93 100644
--- a/src/tools/cargo/src/doc/src/reference/specifying-dependencies.md
+++ b/src/tools/cargo/src/doc/src/reference/specifying-dependencies.md
@@ -113,6 +113,7 @@ Here are some examples of comparison requirements:
= 1.2.3
```
+<span id="multiple-requirements"></span>
### Multiple version requirements
As shown in the examples above, multiple version requirements can be
diff --git a/src/tools/cargo/src/doc/src/reference/unstable.md b/src/tools/cargo/src/doc/src/reference/unstable.md
index 0683daa3c..813276cb6 100644
--- a/src/tools/cargo/src/doc/src/reference/unstable.md
+++ b/src/tools/cargo/src/doc/src/reference/unstable.md
@@ -85,6 +85,7 @@ For the latest nightly, see the [nightly version] of this page.
* [check-cfg](#check-cfg) --- Compile-time validation of `cfg` expressions.
* [host-config](#host-config) --- Allows setting `[target]`-like configuration settings for host build targets.
* [target-applies-to-host](#target-applies-to-host) --- Alters whether certain flags will be passed to host build targets.
+ * [gc](#gc) --- Global cache garbage collection.
* rustdoc
* [rustdoc-map](#rustdoc-map) --- Provides mappings for documentation to link to external sites like [docs.rs](https://docs.rs/).
* [scrape-examples](#scrape-examples) --- Shows examples within documentation.
@@ -303,6 +304,9 @@ my_dep = { version = "1.2.3", public = true }
private_dep = "2.0.0" # Will be 'private' by default
```
+Documentation updates:
+- For workspace's "The `dependencies` table" section, include `public` as an unsupported field for `workspace.dependencies`
+
## msrv-policy
- [#9930](https://github.com/rust-lang/cargo/issues/9930) (MSRV-aware resolver)
- [#10653](https://github.com/rust-lang/cargo/issues/10653) (MSRV-aware cargo-add)
@@ -1383,6 +1387,78 @@ This will not affect any hard-coded paths in the source code, such as in strings
Common paths requiring sanitization include `OUT_DIR` and `CARGO_MANIFEST_DIR`,
plus any other introduced by the build script, such as include directories.
+## gc
+
+* Tracking Issue: [#12633](https://github.com/rust-lang/cargo/issues/12633)
+
+The `-Zgc` flag enables garbage-collection within cargo's global cache within the cargo home directory.
+This includes downloaded dependencies such as compressed `.crate` files, extracted `src` directories, registry index caches, and git dependencies.
+When `-Zgc` is present, cargo will track the last time any index and dependency was used,
+and then uses those timestamps to manually or automatically delete cache entries that have not been used for a while.
+
+```sh
+cargo build -Zgc
+```
+
+### Automatic garbage collection
+
+Automatic deletion happens on commands that are already doing a significant amount of work,
+such as all of the build commands (`cargo build`, `cargo test`, `cargo check`, etc.), and `cargo fetch`.
+The deletion happens just after resolution and packages have been downloaded.
+Automatic deletion is only done once per day (see `gc.auto.frequency` to configure).
+Automatic deletion is disabled if cargo is offline such as with `--offline` or `--frozen` to avoid deleting artifacts that may need to be used if you are offline for a long period of time.
+
+#### Automatic gc configuration
+
+The automatic gc behavior can be specified via a cargo configuration setting.
+The settings available are:
+
+```toml
+# Example config.toml file.
+
+# This table defines the behavior for automatic garbage collection.
+[gc.auto]
+# The maximum frequency that automatic garbage collection happens.
+# Can be "never" to disable automatic-gc, or "always" to run on every command.
+frequency = "1 day"
+# Anything older than this duration will be deleted in the source cache.
+max-src-age = "1 month"
+# Anything older than this duration will be deleted in the compressed crate cache.
+max-crate-age = "3 months"
+# Any index older than this duration will be deleted from the index cache.
+max-index-age = "3 months"
+# Any git checkout older than this duration will be deleted from the checkout cache.
+max-git-co-age = "1 month"
+# Any git clone older than this duration will be deleted from the git cache.
+max-git-db-age = "3 months"
+```
+
+### Manual garbage collection with `cargo clean`
+
+Manual deletion can be done with the `cargo clean gc` command.
+Deletion of cache contents can be performed by passing one of the cache options:
+
+- `--max-src-age=DURATION` --- Deletes source cache files that have not been used since the given age.
+- `--max-crate-age=DURATION` --- Deletes crate cache files that have not been used since the given age.
+- `--max-index-age=DURATION` --- Deletes registry indexes that have not been used since then given age (including their `.crate` and `src` files).
+- `--max-git-co-age=DURATION` --- Deletes git dependency checkouts that have not been used since then given age.
+- `--max-git-db-age=DURATION` --- Deletes git dependency clones that have not been used since then given age.
+- `--max-download-age=DURATION` --- Deletes any downloaded cache data that has not been used since then given age.
+- `--max-src-size=SIZE` --- Deletes the oldest source cache files until the cache is under the given size.
+- `--max-crate-size=SIZE` --- Deletes the oldest crate cache files until the cache is under the given size.
+- `--max-git-size=SIZE` --- Deletes the oldest git dependency caches until the cache is under the given size.
+- `--max-download-size=SIZE` --- Deletes the oldest downloaded cache data until the cache is under the given size.
+
+A DURATION is specified in the form "N seconds/minutes/days/weeks/months" where N is an integer.
+
+A SIZE is specified in the form "N *suffix*" where *suffix* is B, kB, MB, GB, kiB, MiB, or GiB, and N is an integer or floating point number. If no suffix is specified, the number is the number of bytes.
+
+```sh
+cargo clean gc
+cargo clean gc --max-download-age=1week
+cargo clean gc --max-git-size=0 --max-download-size=100MB
+```
+
# Stabilized and removed features
## Compile progress
diff --git a/src/tools/cargo/src/doc/src/reference/workspaces.md b/src/tools/cargo/src/doc/src/reference/workspaces.md
index 17637d6c7..abd2feeca 100644
--- a/src/tools/cargo/src/doc/src/reference/workspaces.md
+++ b/src/tools/cargo/src/doc/src/reference/workspaces.md
@@ -227,7 +227,7 @@ rand.workspace = true
The `workspace.lints` table is where you define lint configuration to be inherited by members of a workspace.
-Specifying a workspace lint configuration is similar to package lints.
+Specifying a workspace lint configuration is similar to [package lints](manifest.md#the-lints-section).
Example:
diff --git a/src/tools/cargo/src/etc/cargo.bashcomp.sh b/src/tools/cargo/src/etc/cargo.bashcomp.sh
index a1e800bc3..c0ba62752 100644
--- a/src/tools/cargo/src/etc/cargo.bashcomp.sh
+++ b/src/tools/cargo/src/etc/cargo.bashcomp.sh
@@ -209,12 +209,12 @@ _get_names_from_array()
line=${line##*=}
line=${line%%\"}
line=${line##*\"}
- names+=($line)
+ names+=("$line")
fi
fi
last_line=$line
- done < $manifest
+ done < "$manifest"
echo "${names[@]}"
}
diff --git a/src/tools/cargo/src/etc/man/cargo-search.1 b/src/tools/cargo/src/etc/man/cargo-search.1
index 245d4e65d..b1d458bd5 100644
--- a/src/tools/cargo/src/etc/man/cargo-search.1
+++ b/src/tools/cargo/src/etc/man/cargo-search.1
@@ -4,7 +4,7 @@
.ad l
.ss \n[.ss] 0
.SH "NAME"
-cargo\-search \[em] Search packages in crates.io
+cargo\-search \[em] Search packages in the registry. Default registry is crates.io
.SH "SYNOPSIS"
\fBcargo search\fR [\fIoptions\fR] [\fIquery\fR\[u2026]]
.SH "DESCRIPTION"
diff --git a/src/tools/cargo/tests/build-std/main.rs b/src/tools/cargo/tests/build-std/main.rs
index 47a4bb671..c905deb49 100644
--- a/src/tools/cargo/tests/build-std/main.rs
+++ b/src/tools/cargo/tests/build-std/main.rs
@@ -18,6 +18,8 @@
//! `CARGO_RUN_BUILD_STD_TESTS` env var to be set to actually run these tests.
//! Otherwise the tests are skipped.
+#![allow(clippy::disallowed_methods)]
+
use cargo_test_support::*;
use std::env;
use std::path::Path;
@@ -227,3 +229,46 @@ fn custom_test_framework() {
.build_std_arg("core")
.run();
}
+
+// Fixing rust-lang/rust#117839.
+// on macOS it never gets remapped.
+// Might be a separate issue, so only run on Linux.
+#[cargo_test(build_std_real)]
+#[cfg(target_os = "linux")]
+fn remap_path_scope() {
+ let p = project()
+ .file(
+ "src/main.rs",
+ "
+ fn main() {
+ panic!(\"remap to /rustc/<hash>\");
+ }
+ ",
+ )
+ .file(
+ ".cargo/config.toml",
+ "
+ [profile.release]
+ debug = \"line-tables-only\"
+ ",
+ )
+ .build();
+
+ p.cargo("run --release -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .env("RUST_BACKTRACE", "1")
+ .build_std()
+ .target_host()
+ .with_status(101)
+ .with_stderr_contains(
+ "\
+[FINISHED] release [optimized + debuginfo] [..]
+[RUNNING] [..]
+[..]thread '[..]' panicked at [..]src/main.rs:3:[..]",
+ )
+ .with_stderr_contains("remap to /rustc/<hash>")
+ .with_stderr_contains("[..]at /rustc/[..]/library/std/src/[..]")
+ .with_stderr_contains("[..]at src/main.rs:3[..]")
+ .with_stderr_contains("[..]at /rustc/[..]/library/core/src/[..]")
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/alt_registry.rs b/src/tools/cargo/tests/testsuite/alt_registry.rs
index d6d7dd531..e347af1c7 100644
--- a/src/tools/cargo/tests/testsuite/alt_registry.rs
+++ b/src/tools/cargo/tests/testsuite/alt_registry.rs
@@ -715,7 +715,14 @@ fn bad_registry_name() {
[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`
Caused by:
- invalid character ` ` in registry name: `bad name`, [..]",
+ TOML parse error at line 7, column 17
+ |
+ 7 | [dependencies.bar]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ invalid character ` ` in registry name: `bad name`, [..]
+
+
+",
)
.run();
@@ -1546,3 +1553,86 @@ or use environment variable CARGO_REGISTRY_TOKEN",
)
.run();
}
+
+#[cargo_test]
+fn config_empty_registry_name() {
+ let _ = RegistryBuilder::new()
+ .no_configure_token()
+ .alternative()
+ .build();
+ let p = project()
+ .file("src/lib.rs", "")
+ .file(
+ ".cargo/config.toml",
+ "[registry.'']
+ ",
+ )
+ .build();
+
+ p.cargo("publish")
+ .arg("--registry")
+ .arg("")
+ .with_status(101)
+ .with_stderr(
+ "\
+[ERROR] registry name cannot be empty",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn empty_registry_flag() {
+ let p = project().file("src/lib.rs", "").build();
+
+ p.cargo("publish")
+ .arg("--registry")
+ .arg("")
+ .with_status(101)
+ .with_stderr(
+ "\
+[ERROR] registry name cannot be empty",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn empty_dependency_registry() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ bar = { version = "0.1.0", registry = "" }
+ "#,
+ )
+ .file(
+ "src/lib.rs",
+ "
+ extern crate bar;
+ pub fn f() { bar::bar(); }
+ ",
+ )
+ .build();
+
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr(
+ "\
+[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`
+
+Caused by:
+ TOML parse error at line 7, column 23
+ |
+ 7 | bar = { version = \"0.1.0\", registry = \"\" }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ registry name cannot be empty
+
+
+",
+ )
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/artifact_dep.rs b/src/tools/cargo/tests/testsuite/artifact_dep.rs
index c51298735..01b4ecf47 100644
--- a/src/tools/cargo/tests/testsuite/artifact_dep.rs
+++ b/src/tools/cargo/tests/testsuite/artifact_dep.rs
@@ -2167,8 +2167,11 @@ fn doc_lib_true() {
p.cargo("doc -Z bindeps")
.masquerade_as_nightly_cargo(&["bindeps"])
- .env("CARGO_LOG", "cargo::ops::cargo_rustc::fingerprint")
- .with_stdout("")
+ .with_stderr(
+ "\
+[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html",
+ )
.run();
assert!(p.root().join("target/doc").is_dir());
diff --git a/src/tools/cargo/tests/testsuite/build.rs b/src/tools/cargo/tests/testsuite/build.rs
index 23840ad9a..dd67161d6 100644
--- a/src/tools/cargo/tests/testsuite/build.rs
+++ b/src/tools/cargo/tests/testsuite/build.rs
@@ -460,7 +460,11 @@ fn cargo_compile_with_empty_package_name() {
[ERROR] failed to parse manifest at `[..]`
Caused by:
- package name cannot be an empty string
+ TOML parse error at line 3, column 16
+ |
+ 3 | name = \"\"
+ | ^^
+ package name cannot be empty
",
)
.run();
@@ -479,6 +483,10 @@ fn cargo_compile_with_invalid_package_name() {
[ERROR] failed to parse manifest at `[..]`
Caused by:
+ TOML parse error at line 3, column 16
+ |
+ 3 | name = \"foo::bar\"
+ | ^^^^^^^^^^
invalid character `:` in package name: `foo::bar`, [..]
",
)
@@ -760,7 +768,7 @@ fn cargo_compile_with_invalid_code() {
p.cargo("build")
.with_status(101)
.with_stderr_contains(
- "[ERROR] could not compile `foo` (bin \"foo\") due to previous error\n",
+ "[ERROR] could not compile `foo` (bin \"foo\") due to 1 previous error\n",
)
.run();
assert!(p.root().join("Cargo.lock").is_file());
@@ -1182,7 +1190,11 @@ fn cargo_compile_with_invalid_dep_rename() {
error: failed to parse manifest at `[..]`
Caused by:
- invalid character ` ` in dependency name: `haha this isn't a valid name 🐛`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters)
+ TOML parse error at line 7, column 17
+ |
+ 7 | \"haha this isn't a valid name 🐛\" = { package = \"libc\", version = \"0.1\" }
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ invalid character ` ` in package name: `haha this isn't a valid name 🐛`, characters must be Unicode XID characters (numbers, `-`, `_`, or most letters)
",
)
.run();
@@ -1545,6 +1557,11 @@ fn crate_env_vars() {
// Verify CARGO_TARGET_TMPDIR isn't set for bins
assert!(option_env!("CARGO_TARGET_TMPDIR").is_none());
+
+ // Verify CARGO_RUSTC_CURRENT_DIR is set for examples
+ let workspace_dir = std::path::Path::new(option_env!("CARGO_RUSTC_CURRENT_DIR").expect("CARGO_RUSTC_CURRENT_DIR"));
+ let file_path = workspace_dir.join(file!());
+ assert!(file_path.exists(), "{}", file_path.display());
}
"#,
)
@@ -1581,14 +1598,26 @@ fn crate_env_vars() {
// Check that CARGO_TARGET_TMPDIR isn't set for lib code
assert!(option_env!("CARGO_TARGET_TMPDIR").is_none());
env::var("CARGO_TARGET_TMPDIR").unwrap_err();
+
+ // Verify CARGO_RUSTC_CURRENT_DIR is set for examples
+ let workspace_dir = std::path::Path::new(option_env!("CARGO_RUSTC_CURRENT_DIR").expect("CARGO_RUSTC_CURRENT_DIR"));
+ let file_path = workspace_dir.join(file!());
+ assert!(file_path.exists(), "{}", file_path.display());
}
#[test]
- fn env() {
+ fn unit_env_cargo_target_tmpdir() {
// Check that CARGO_TARGET_TMPDIR isn't set for unit tests
assert!(option_env!("CARGO_TARGET_TMPDIR").is_none());
env::var("CARGO_TARGET_TMPDIR").unwrap_err();
}
+
+ #[test]
+ fn unit_env_cargo_rustc_current_dir() {
+ let workspace_dir = std::path::Path::new(option_env!("CARGO_RUSTC_CURRENT_DIR").expect("CARGO_RUSTC_CURRENT_DIR"));
+ let file_path = workspace_dir.join(file!());
+ assert!(file_path.exists(), "{}", file_path.display());
+ }
"#,
)
.file(
@@ -1605,6 +1634,11 @@ fn crate_env_vars() {
// Verify CARGO_TARGET_TMPDIR isn't set for examples
assert!(option_env!("CARGO_TARGET_TMPDIR").is_none());
+
+ // Verify CARGO_RUSTC_CURRENT_DIR is set for examples
+ let workspace_dir = std::path::Path::new(option_env!("CARGO_RUSTC_CURRENT_DIR").expect("CARGO_RUSTC_CURRENT_DIR"));
+ let file_path = workspace_dir.join(file!());
+ assert!(file_path.exists(), "{}", file_path.display());
}
"#,
)
@@ -1612,9 +1646,16 @@ fn crate_env_vars() {
"tests/env.rs",
r#"
#[test]
- fn env() {
+ fn integration_env_cargo_target_tmpdir() {
foo::check_tmpdir(option_env!("CARGO_TARGET_TMPDIR"));
}
+
+ #[test]
+ fn integration_env_cargo_rustc_current_dir() {
+ let workspace_dir = std::path::Path::new(option_env!("CARGO_RUSTC_CURRENT_DIR").expect("CARGO_RUSTC_CURRENT_DIR"));
+ let file_path = workspace_dir.join(file!());
+ assert!(file_path.exists(), "{}", file_path.display());
+ }
"#,
);
@@ -1627,9 +1668,16 @@ fn crate_env_vars() {
use test::Bencher;
#[bench]
- fn env(_: &mut Bencher) {
+ fn bench_env_cargo_target_tmpdir(_: &mut Bencher) {
foo::check_tmpdir(option_env!("CARGO_TARGET_TMPDIR"));
}
+
+ #[test]
+ fn bench_env_cargo_rustc_current_dir() {
+ let workspace_dir = std::path::Path::new(option_env!("CARGO_RUSTC_CURRENT_DIR").expect("CARGO_RUSTC_CURRENT_DIR"));
+ let file_path = workspace_dir.join(file!());
+ assert!(file_path.exists(), "{}", file_path.display());
+ }
"#,
)
.build()
@@ -1638,7 +1686,9 @@ fn crate_env_vars() {
};
println!("build");
- p.cargo("build -v").run();
+ p.cargo("build -v")
+ .masquerade_as_nightly_cargo(&["CARGO_RUSTC_CURRENT_DIR"])
+ .run();
println!("bin");
p.process(&p.bin("foo-bar"))
@@ -1646,15 +1696,175 @@ fn crate_env_vars() {
.run();
println!("example");
- p.cargo("run --example ex-env-vars -v").run();
+ p.cargo("run --example ex-env-vars -v")
+ .masquerade_as_nightly_cargo(&["CARGO_RUSTC_CURRENT_DIR"])
+ .run();
println!("test");
- p.cargo("test -v").run();
+ p.cargo("test -v")
+ .masquerade_as_nightly_cargo(&["CARGO_RUSTC_CURRENT_DIR"])
+ .run();
if is_nightly() {
println!("bench");
- p.cargo("bench -v").run();
+ p.cargo("bench -v")
+ .masquerade_as_nightly_cargo(&["CARGO_RUSTC_CURRENT_DIR"])
+ .run();
+ }
+}
+
+#[cargo_test]
+fn cargo_rustc_current_dir_foreign_workspace_dep() {
+ let foo = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [workspace]
+
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies]
+ baz.path = "../baz"
+ baz_member.path = "../baz/baz_member"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+ let _baz = project()
+ .at("baz")
+ .file(
+ "Cargo.toml",
+ r#"
+ [workspace]
+ members = ["baz_member"]
+
+ [package]
+ name = "baz"
+ version = "0.1.0"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file(
+ "tests/env.rs",
+ r#"
+ use std::path::Path;
+
+ #[test]
+ fn baz_env() {
+ let workspace_dir = Path::new(option_env!("CARGO_RUSTC_CURRENT_DIR").expect("CARGO_RUSTC_CURRENT_DIR"));
+ let manifest_dir = Path::new(option_env!("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"));
+ let current_dir = std::env::current_dir().expect("current_dir");
+ let file_path = workspace_dir.join(file!());
+ assert!(file_path.exists(), "{}", file_path.display());
+ let workspace_dir = std::fs::canonicalize(current_dir.join(workspace_dir)).expect("CARGO_RUSTC_CURRENT_DIR");
+ let manifest_dir = std::fs::canonicalize(current_dir.join(manifest_dir)).expect("CARGO_MANIFEST_DIR");
+ assert_eq!(workspace_dir, manifest_dir);
+ }
+ "#,
+ )
+ .file(
+ "baz_member/Cargo.toml",
+ r#"
+ [package]
+ name = "baz_member"
+ version = "0.1.0"
+ authors = []
+ "#,
+ )
+ .file("baz_member/src/lib.rs", "")
+ .file(
+ "baz_member/tests/env.rs",
+ r#"
+ use std::path::Path;
+
+ #[test]
+ fn baz_member_env() {
+ let workspace_dir = Path::new(option_env!("CARGO_RUSTC_CURRENT_DIR").expect("CARGO_RUSTC_CURRENT_DIR"));
+ let file_path = workspace_dir.join(file!());
+ assert!(file_path.exists(), "{}", file_path.display());
+ }
+ "#,
+ )
+ .build();
+
+ // Verify it works from a different workspace
+ foo.cargo("test -p baz")
+ .masquerade_as_nightly_cargo(&["CARGO_RUSTC_CURRENT_DIR"])
+ .with_stdout_contains("running 1 test\ntest baz_env ... ok")
+ .run();
+ foo.cargo("test -p baz_member")
+ .masquerade_as_nightly_cargo(&["CARGO_RUSTC_CURRENT_DIR"])
+ .with_stdout_contains("running 1 test\ntest baz_member_env ... ok")
+ .run();
+}
+
+#[cargo_test]
+fn cargo_rustc_current_dir_non_local_dep() {
+ Package::new("bar", "0.1.0")
+ .file(
+ "tests/bar_env.rs",
+ r#"
+ use std::path::Path;
+
+ #[test]
+ fn bar_env() {
+ let workspace_dir = Path::new(option_env!("CARGO_RUSTC_CURRENT_DIR").expect("CARGO_RUSTC_CURRENT_DIR"));
+ let manifest_dir = Path::new(option_env!("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR"));
+ let current_dir = std::env::current_dir().expect("current_dir");
+ let file_path = workspace_dir.join(file!());
+ assert!(file_path.exists(), "{}", file_path.display());
+ let workspace_dir = std::fs::canonicalize(current_dir.join(workspace_dir)).expect("CARGO_RUSTC_CURRENT_DIR");
+ let manifest_dir = std::fs::canonicalize(current_dir.join(manifest_dir)).expect("CARGO_MANIFEST_DIR");
+ assert_eq!(workspace_dir, manifest_dir);
+ }
+ "#,
+ )
+ .publish();
+
+ let p = project()
+ .file("src/lib.rs", "")
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ bar = "0.1.0"
+ "#,
+ )
+ .build();
+
+ p.cargo("test -p bar")
+ .masquerade_as_nightly_cargo(&["CARGO_RUSTC_CURRENT_DIR"])
+ .with_stdout_contains("running 1 test\ntest bar_env ... ok")
+ .run();
+}
+
+#[cargo_test]
+fn cargo_rustc_current_dir_is_not_stable() {
+ if is_nightly() {
+ return;
}
+ let p = project()
+ .file(
+ "tests/env.rs",
+ r#"
+ use std::path::Path;
+
+ #[test]
+ fn env() {
+ assert_eq!(option_env!("CARGO_RUSTC_CURRENT_DIR"), None);
+ }
+ "#,
+ )
+ .build();
+
+ p.cargo("test").run();
}
#[cargo_test]
@@ -2959,12 +3169,12 @@ fn freshness_ignores_excluded() {
// Smoke test to make sure it doesn't compile again
println!("first pass");
- foo.cargo("build").with_stdout("").run();
+ foo.cargo("build").with_stderr("[FINISHED] [..]").run();
// Modify an ignored file and make sure we don't rebuild
println!("second pass");
foo.change_file("src/bar.rs", "");
- foo.cargo("build").with_stdout("").run();
+ foo.cargo("build").with_stderr("[FINISHED] [..]").run();
}
#[cargo_test]
@@ -3064,7 +3274,7 @@ fn recompile_space_in_name() {
.build();
foo.cargo("build").run();
foo.root().move_into_the_past();
- foo.cargo("build").with_stdout("").run();
+ foo.cargo("build").with_stderr("[FINISHED] [..]").run();
}
#[cfg(unix)]
diff --git a/src/tools/cargo/tests/testsuite/build_script.rs b/src/tools/cargo/tests/testsuite/build_script.rs
index 408ce6457..f7361fcf3 100644
--- a/src/tools/cargo/tests/testsuite/build_script.rs
+++ b/src/tools/cargo/tests/testsuite/build_script.rs
@@ -1028,7 +1028,7 @@ versions that meet the requirements `*` are: 0.5.0
the package `a-sys` links to the native library `a`, but it conflicts with a previous package which links to `a` as well:
package `foo v0.5.0 ([..])`
-Only one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. Try to adjust your dependencies so that only one package uses the links ='a-sys' value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links.
+Only one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. Try to adjust your dependencies so that only one package uses the `links = \"a\"` value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links.
failed to select a version for `a-sys` which could resolve this conflict
").run();
@@ -1148,7 +1148,7 @@ versions that meet the requirements `*` are: 0.5.0
the package `a-sys` links to the native library `a`, but it conflicts with a previous package which links to `a` as well:
package `foo v0.5.0 ([..])`
-Only one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. Try to adjust your dependencies so that only one package uses the links ='a-sys' value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links.
+Only one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. Try to adjust your dependencies so that only one package uses the `links = \"a\"` value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links.
failed to select a version for `a-sys` which could resolve this conflict
").run();
@@ -1716,7 +1716,7 @@ fn build_deps_not_for_normal() {
.with_stderr_contains("[..]can't find crate for `aaaaa`[..]")
.with_stderr_contains(
"\
-[ERROR] could not compile `foo` (lib) due to previous error
+[ERROR] could not compile `foo` (lib) due to 1 previous error
Caused by:
process didn't exit successfully: [..]
@@ -3245,7 +3245,6 @@ fn fresh_builds_possible_with_multiple_metadata_overrides() {
.run();
p.cargo("build -v")
- .env("CARGO_LOG", "cargo::ops::cargo_rustc::fingerprint=info")
.with_stderr(
"\
[FRESH] foo v0.5.0 ([..])
@@ -3472,7 +3471,7 @@ fn rebuild_only_on_explicit_paths() {
// random other files do not affect freshness
println!("run baz");
- p.change_file("baz", "");
+ p.change_file("baz", "// modified");
p.cargo("build -v")
.with_stderr(
"\
@@ -3484,7 +3483,7 @@ fn rebuild_only_on_explicit_paths() {
// but changing dependent files does
println!("run foo change");
- p.change_file("foo", "");
+ p.change_file("foo", "// modified");
p.cargo("build -v")
.with_stderr(
"\
@@ -4382,7 +4381,7 @@ versions that meet the requirements `*` are: 0.5.0
the package `a` links to the native library `a`, but it conflicts with a previous package which links to `a` as well:
package `foo v0.5.0 ([..])`
-Only one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. Try to adjust your dependencies so that only one package uses the links ='a' value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links.
+Only one package in the dependency graph may specify the same links value. This helps ensure that only one copy of a native library is linked in the final binary. Try to adjust your dependencies so that only one package uses the `links = \"a\"` value. For more information, see https://doc.rust-lang.org/cargo/reference/resolver.html#links.
failed to select a version for `a` which could resolve this conflict
").run();
diff --git a/src/tools/cargo/tests/testsuite/build_script_env.rs b/src/tools/cargo/tests/testsuite/build_script_env.rs
index afa2925f1..5220506a7 100644
--- a/src/tools/cargo/tests/testsuite/build_script_env.rs
+++ b/src/tools/cargo/tests/testsuite/build_script_env.rs
@@ -96,7 +96,7 @@ fn rerun_if_env_or_file_changes() {
.with_stderr("[FINISHED] [..]")
.run();
sleep_ms(1000);
- p.change_file("foo", "");
+ p.change_file("foo", "// modified");
p.cargo("check")
.env("FOO", "bar")
.with_stderr(
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/change_rename_target/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/change_rename_target/out/Cargo.toml
index 70cd31826..bc29fac8e 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/change_rename_target/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/change_rename_target/out/Cargo.toml
@@ -6,3 +6,6 @@ version = "0.0.0"
[dependencies]
some-package = { package = "my-package2", version = "99999.0.0", optional = true }
+
+[features]
+some-package = ["dep:some-package"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/primary/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/primary/Cargo.toml
index 6dd7fb6d6..38ff36eb3 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/primary/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_optional/out/primary/Cargo.toml
@@ -4,3 +4,6 @@ version = "0.0.0"
[dependencies]
foo = { workspace = true, optional = true }
+
+[features]
+foo = ["dep:foo"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/Cargo.toml
new file mode 100644
index 000000000..24c50556b
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/Cargo.toml
@@ -0,0 +1,5 @@
+[workspace]
+members = ["primary", "dependency"]
+
+[workspace.dependencies]
+foo = { version = "0.0.0", path = "./dependency"} \ No newline at end of file
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/dependency/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/dependency/Cargo.toml
new file mode 100644
index 000000000..2d247d4d2
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/dependency/Cargo.toml
@@ -0,0 +1,3 @@
+[package]
+name = "foo"
+version = "0.0.0"
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/dependency/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/dependency/src/lib.rs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/dependency/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/primary/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/primary/Cargo.toml
new file mode 100644
index 000000000..b5923a106
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/primary/Cargo.toml
@@ -0,0 +1,4 @@
+cargo-features = ["public-dependency"]
+[package]
+name = "bar"
+version = "0.0.0" \ No newline at end of file
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/primary/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/primary/src/lib.rs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/in/primary/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/mod.rs
new file mode 100644
index 000000000..680d4c4e3
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/mod.rs
@@ -0,0 +1,26 @@
+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();
+
+ 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")
+ .args(["foo", "-p", "bar", "--public"])
+ .masquerade_as_nightly_cargo(&["public-dependency"])
+ .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/detect_workspace_inherit_public/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/out/Cargo.toml
new file mode 100644
index 000000000..24c50556b
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/out/Cargo.toml
@@ -0,0 +1,5 @@
+[workspace]
+members = ["primary", "dependency"]
+
+[workspace.dependencies]
+foo = { version = "0.0.0", path = "./dependency"} \ No newline at end of file
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/out/dependency/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/out/dependency/Cargo.toml
new file mode 100644
index 000000000..2d247d4d2
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/out/dependency/Cargo.toml
@@ -0,0 +1,3 @@
+[package]
+name = "foo"
+version = "0.0.0"
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/out/primary/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/out/primary/Cargo.toml
new file mode 100644
index 000000000..665c6ae5e
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/out/primary/Cargo.toml
@@ -0,0 +1,7 @@
+cargo-features = ["public-dependency"]
+[package]
+name = "bar"
+version = "0.0.0"
+
+[dependencies]
+foo = { workspace = true, public = true }
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/stderr.log
new file mode 100644
index 000000000..efa1ae9fa
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/stderr.log
@@ -0,0 +1 @@
+ Adding foo (workspace) to public dependencies.
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/stdout.log b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_public/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/in b/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/in
new file mode 120000
index 000000000..6c6a27fcf
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/in
@@ -0,0 +1 @@
+../add-basic.in \ No newline at end of file
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/mod.rs
new file mode 100644
index 000000000..d7044ee11
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/mod.rs
@@ -0,0 +1,24 @@
+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();
+ 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("@1.2.3")
+ .current_dir(cwd)
+ .assert()
+ .failure()
+ .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/empty_dep_name/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/out/Cargo.toml
new file mode 100644
index 000000000..3ecdb6681
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/out/Cargo.toml
@@ -0,0 +1,5 @@
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/stderr.log
new file mode 100644
index 000000000..d9547a42a
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/stderr.log
@@ -0,0 +1 @@
+error: package name cannot be empty
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/stdout.log b/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_name/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_add/help/stdout.log
index cf2a91313..d2931ae9c 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_add/help/stdout.log
@@ -32,6 +32,17 @@ Options:
The package will be removed from your features.
+ --public
+ Mark the dependency as public
+
+ The dependency can be referenced in your library's public API.
+
+ --no-public
+ Mark the dependency as private
+
+ While you can use the crate in your implementation, it cannot be referenced in your public
+ API.
+
--rename <NAME>
Rename the dependency
@@ -45,12 +56,12 @@ Options:
-n, --dry-run
Don't actually write the manifest
- -q, --quiet
- Do not print cargo log messages
-
-v, --verbose...
Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet
+ Do not print cargo log messages
+
--color <WHEN>
Coloring: auto, always, never
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/mod.rs
index e8633b0c4..653c2db94 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/mod.rs
@@ -12,10 +12,12 @@ mod deprecated_section;
mod detect_workspace_inherit;
mod detect_workspace_inherit_features;
mod detect_workspace_inherit_optional;
+mod detect_workspace_inherit_public;
mod dev;
mod dev_build_conflict;
mod dev_prefer_existing_version;
mod dry_run;
+mod empty_dep_name;
mod empty_dep_table;
mod features;
mod features_activated_over_limit;
@@ -65,6 +67,7 @@ mod namever;
mod no_args;
mod no_default_features;
mod no_optional;
+mod no_public;
mod offline_empty_cache;
mod optional;
mod overwrite_default_features;
@@ -81,11 +84,16 @@ mod overwrite_no_default_features;
mod overwrite_no_default_features_with_default_features;
mod overwrite_no_optional;
mod overwrite_no_optional_with_optional;
+mod overwrite_no_public;
+mod overwrite_no_public_with_public;
mod overwrite_optional;
mod overwrite_optional_with_no_optional;
+mod overwrite_optional_with_optional;
mod overwrite_path_noop;
mod overwrite_path_with_version;
mod overwrite_preserves_inline_table;
+mod overwrite_public;
+mod overwrite_public_with_no_public;
mod overwrite_rename_with_no_rename;
mod overwrite_rename_with_rename;
mod overwrite_rename_with_rename_noop;
@@ -103,6 +111,7 @@ mod preserve_dep_std_table;
mod preserve_features_table;
mod preserve_sorted;
mod preserve_unsorted;
+mod public;
mod quiet;
mod registry;
mod rename;
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/no_optional/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/no_optional/mod.rs
index 9145528bf..cc7e79b97 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/no_optional/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/no_optional/mod.rs
@@ -7,19 +7,7 @@ use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
cargo_test_support::registry::init();
- for name in ["my-package1", "my-package2"] {
- for ver in [
- "0.1.1+my-package",
- "0.2.0+my-package",
- "0.2.3+my-package",
- "0.4.1+my-package",
- "20.0.0+my-package",
- "99999.0.0+my-package",
- "99999.0.0-alpha.1+my-package",
- ] {
- cargo_test_support::registry::Package::new(name, ver).publish();
- }
- }
+ cargo_test_support::registry::Package::new("my-package", "0.1.0").publish();
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
@@ -27,7 +15,7 @@ fn case() {
snapbox::cmd::Command::cargo_ui()
.arg("add")
- .arg_line("my-package1 my-package2@0.4.1 --no-optional")
+ .arg_line("my-package --no-optional")
.current_dir(cwd)
.assert()
.success()
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/no_optional/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/no_optional/out/Cargo.toml
index c5e017892..496ac8a62 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/no_optional/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/no_optional/out/Cargo.toml
@@ -5,5 +5,4 @@ name = "cargo-list-test-fixture"
version = "0.0.0"
[dependencies]
-my-package1 = "99999.0.0"
-my-package2 = "0.4.1"
+my-package = "0.1.0"
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/no_optional/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/no_optional/stderr.log
index fb8d4903d..8e025739f 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/no_optional/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_add/no_optional/stderr.log
@@ -1,3 +1,2 @@
Updating `dummy-registry` index
- Adding my-package1 v99999.0.0 to dependencies.
- Adding my-package2 v0.4.1 to dependencies.
+ Adding my-package v0.1.0 to dependencies.
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/no_public/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/no_public/in/Cargo.toml
new file mode 100644
index 000000000..e9087535b
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/no_public/in/Cargo.toml
@@ -0,0 +1,6 @@
+cargo-features = ["public-dependency"]
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/no_public/in/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_add/no_public/in/src/lib.rs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/no_public/in/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/no_public/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/no_public/mod.rs
new file mode 100644
index 000000000..912ac3fd3
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/no_public/mod.rs
@@ -0,0 +1,26 @@
+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("my-package", "0.1.0").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("my-package --no-public")
+ .current_dir(cwd)
+ .masquerade_as_nightly_cargo(&["public-dependency"])
+ .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/no_public/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/no_public/out/Cargo.toml
new file mode 100644
index 000000000..b9f045116
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/no_public/out/Cargo.toml
@@ -0,0 +1,9 @@
+cargo-features = ["public-dependency"]
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+
+[dependencies]
+my-package = "0.1.0"
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/no_public/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/no_public/stderr.log
new file mode 100644
index 000000000..8e025739f
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/no_public/stderr.log
@@ -0,0 +1,2 @@
+ Updating `dummy-registry` index
+ Adding my-package v0.1.0 to dependencies.
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/no_public/stdout.log b/src/tools/cargo/tests/testsuite/cargo_add/no_public/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/no_public/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/optional/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/optional/mod.rs
index 408a46ed3..8daaa961d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/optional/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/optional/mod.rs
@@ -7,19 +7,7 @@ use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
cargo_test_support::registry::init();
- for name in ["my-package1", "my-package2"] {
- for ver in [
- "0.1.1+my-package",
- "0.2.0+my-package",
- "0.2.3+my-package",
- "0.4.1+my-package",
- "20.0.0+my-package",
- "99999.0.0+my-package",
- "99999.0.0-alpha.1+my-package",
- ] {
- cargo_test_support::registry::Package::new(name, ver).publish();
- }
- }
+ cargo_test_support::registry::Package::new("my-package", "0.1.0").publish();
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
@@ -27,7 +15,7 @@ fn case() {
snapbox::cmd::Command::cargo_ui()
.arg("add")
- .arg_line("my-package1 my-package2@0.4.1 --optional")
+ .arg_line("my-package --optional")
.current_dir(cwd)
.assert()
.success()
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/optional/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/optional/out/Cargo.toml
index eda5445c5..a8789b033 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/optional/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/optional/out/Cargo.toml
@@ -5,5 +5,7 @@ name = "cargo-list-test-fixture"
version = "0.0.0"
[dependencies]
-my-package1 = { version = "99999.0.0", optional = true }
-my-package2 = { version = "0.4.1", optional = true }
+my-package = { version = "0.1.0", optional = true }
+
+[features]
+my-package = ["dep:my-package"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/optional/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/optional/stderr.log
index 8cf4812cf..595ac276b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/optional/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_add/optional/stderr.log
@@ -1,3 +1,2 @@
Updating `dummy-registry` index
- Adding my-package1 v99999.0.0 to optional dependencies.
- Adding my-package2 v0.4.1 to optional dependencies.
+ Adding my-package v0.1.0 to optional dependencies.
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_git_with_path/out/primary/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_git_with_path/out/primary/Cargo.toml
index ad1205481..27e6e175d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_git_with_path/out/primary/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_git_with_path/out/primary/Cargo.toml
@@ -6,3 +6,6 @@ version = "0.0.0"
[dependencies]
cargo-list-test-fixture-dependency = { optional = true, path = "../dependency", version = "0.0.0" }
+
+[features]
+cargo-list-test-fixture-dependency = ["dep:cargo-list-test-fixture-dependency"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/primary/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/primary/Cargo.toml
index 6dd7fb6d6..38ff36eb3 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/primary/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/out/primary/Cargo.toml
@@ -4,3 +4,6 @@ version = "0.0.0"
[dependencies]
foo = { workspace = true, optional = true }
+
+[features]
+foo = ["dep:foo"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_noop/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_noop/out/Cargo.toml
index bbaf4f552..717252191 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_noop/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_noop/out/Cargo.toml
@@ -7,3 +7,6 @@ version = "0.0.0"
[dependencies]
your-face = { version = "0.0.0", path = "dependency", optional = true, default-features = false, features = ["nose", "mouth"], registry = "alternative" }
+
+[features]
+your-face = ["dep:your-face"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/in/Cargo.toml
index c5e017892..496ac8a62 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/in/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/in/Cargo.toml
@@ -5,5 +5,4 @@ name = "cargo-list-test-fixture"
version = "0.0.0"
[dependencies]
-my-package1 = "99999.0.0"
-my-package2 = "0.4.1"
+my-package = "0.1.0"
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/mod.rs
index 9145528bf..cc7e79b97 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/mod.rs
@@ -7,19 +7,7 @@ use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
cargo_test_support::registry::init();
- for name in ["my-package1", "my-package2"] {
- for ver in [
- "0.1.1+my-package",
- "0.2.0+my-package",
- "0.2.3+my-package",
- "0.4.1+my-package",
- "20.0.0+my-package",
- "99999.0.0+my-package",
- "99999.0.0-alpha.1+my-package",
- ] {
- cargo_test_support::registry::Package::new(name, ver).publish();
- }
- }
+ cargo_test_support::registry::Package::new("my-package", "0.1.0").publish();
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
@@ -27,7 +15,7 @@ fn case() {
snapbox::cmd::Command::cargo_ui()
.arg("add")
- .arg_line("my-package1 my-package2@0.4.1 --no-optional")
+ .arg_line("my-package --no-optional")
.current_dir(cwd)
.assert()
.success()
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/out/Cargo.toml
index c5e017892..496ac8a62 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/out/Cargo.toml
@@ -5,5 +5,4 @@ name = "cargo-list-test-fixture"
version = "0.0.0"
[dependencies]
-my-package1 = "99999.0.0"
-my-package2 = "0.4.1"
+my-package = "0.1.0"
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/stderr.log
index fb8d4903d..8e025739f 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional/stderr.log
@@ -1,3 +1,2 @@
Updating `dummy-registry` index
- Adding my-package1 v99999.0.0 to dependencies.
- Adding my-package2 v0.4.1 to dependencies.
+ Adding my-package v0.1.0 to dependencies.
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/in/Cargo.toml
index 8cd2616d4..98e2f7da1 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/in/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/in/Cargo.toml
@@ -5,5 +5,4 @@ name = "cargo-list-test-fixture"
version = "0.0.0"
[dependencies]
-my-package1 = { version = "99999.0.0", optional = false }
-my-package2 = { version = "0.4.1", optional = false }
+my-package = { version = "0.1.0", optional = false }
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/mod.rs
index 408a46ed3..8daaa961d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/mod.rs
@@ -7,19 +7,7 @@ use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
cargo_test_support::registry::init();
- for name in ["my-package1", "my-package2"] {
- for ver in [
- "0.1.1+my-package",
- "0.2.0+my-package",
- "0.2.3+my-package",
- "0.4.1+my-package",
- "20.0.0+my-package",
- "99999.0.0+my-package",
- "99999.0.0-alpha.1+my-package",
- ] {
- cargo_test_support::registry::Package::new(name, ver).publish();
- }
- }
+ cargo_test_support::registry::Package::new("my-package", "0.1.0").publish();
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
@@ -27,7 +15,7 @@ fn case() {
snapbox::cmd::Command::cargo_ui()
.arg("add")
- .arg_line("my-package1 my-package2@0.4.1 --optional")
+ .arg_line("my-package --optional")
.current_dir(cwd)
.assert()
.success()
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/out/Cargo.toml
index eda5445c5..a8789b033 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/out/Cargo.toml
@@ -5,5 +5,7 @@ name = "cargo-list-test-fixture"
version = "0.0.0"
[dependencies]
-my-package1 = { version = "99999.0.0", optional = true }
-my-package2 = { version = "0.4.1", optional = true }
+my-package = { version = "0.1.0", optional = true }
+
+[features]
+my-package = ["dep:my-package"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/stderr.log
index 8cf4812cf..595ac276b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_optional_with_optional/stderr.log
@@ -1,3 +1,2 @@
Updating `dummy-registry` index
- Adding my-package1 v99999.0.0 to optional dependencies.
- Adding my-package2 v0.4.1 to optional dependencies.
+ Adding my-package v0.1.0 to optional dependencies.
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/in/Cargo.toml
new file mode 100644
index 000000000..43d0d8238
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/in/Cargo.toml
@@ -0,0 +1,9 @@
+cargo-features = ["public-dependency"]
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+
+[dependencies]
+my-package = "0.1.0" \ No newline at end of file
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/in/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/in/src/lib.rs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/in/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/mod.rs
new file mode 100644
index 000000000..912ac3fd3
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/mod.rs
@@ -0,0 +1,26 @@
+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("my-package", "0.1.0").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("my-package --no-public")
+ .current_dir(cwd)
+ .masquerade_as_nightly_cargo(&["public-dependency"])
+ .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/overwrite_no_public/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/out/Cargo.toml
new file mode 100644
index 000000000..b9f045116
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/out/Cargo.toml
@@ -0,0 +1,9 @@
+cargo-features = ["public-dependency"]
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+
+[dependencies]
+my-package = "0.1.0"
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/stderr.log
new file mode 100644
index 000000000..8e025739f
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/stderr.log
@@ -0,0 +1,2 @@
+ Updating `dummy-registry` index
+ Adding my-package v0.1.0 to dependencies.
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/stdout.log b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/in/Cargo.toml
new file mode 100644
index 000000000..c6e61bdca
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/in/Cargo.toml
@@ -0,0 +1,9 @@
+cargo-features = ["public-dependency"]
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+
+[dependencies]
+my-package = { version = "0.1.0", public = false }
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/in/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/in/src/lib.rs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/in/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/mod.rs
new file mode 100644
index 000000000..bbf8d65a6
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/mod.rs
@@ -0,0 +1,26 @@
+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("my-package", "0.1.0").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("my-package --public")
+ .current_dir(cwd)
+ .masquerade_as_nightly_cargo(&["public-dependency"])
+ .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/overwrite_no_public_with_public/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/out/Cargo.toml
new file mode 100644
index 000000000..77fccaa6a
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/out/Cargo.toml
@@ -0,0 +1,9 @@
+cargo-features = ["public-dependency"]
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+
+[dependencies]
+my-package = { version = "0.1.0", public = true }
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/stderr.log
new file mode 100644
index 000000000..5259bbde8
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/stderr.log
@@ -0,0 +1,2 @@
+ Updating `dummy-registry` index
+ Adding my-package v0.1.0 to public dependencies.
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/stdout.log b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_public_with_public/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/in/Cargo.toml
index c5e017892..496ac8a62 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/in/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/in/Cargo.toml
@@ -5,5 +5,4 @@ name = "cargo-list-test-fixture"
version = "0.0.0"
[dependencies]
-my-package1 = "99999.0.0"
-my-package2 = "0.4.1"
+my-package = "0.1.0"
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/mod.rs
index 408a46ed3..8daaa961d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/mod.rs
@@ -7,19 +7,7 @@ use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
cargo_test_support::registry::init();
- for name in ["my-package1", "my-package2"] {
- for ver in [
- "0.1.1+my-package",
- "0.2.0+my-package",
- "0.2.3+my-package",
- "0.4.1+my-package",
- "20.0.0+my-package",
- "99999.0.0+my-package",
- "99999.0.0-alpha.1+my-package",
- ] {
- cargo_test_support::registry::Package::new(name, ver).publish();
- }
- }
+ cargo_test_support::registry::Package::new("my-package", "0.1.0").publish();
let project = Project::from_template(curr_dir!().join("in"));
let project_root = project.root();
@@ -27,7 +15,7 @@ fn case() {
snapbox::cmd::Command::cargo_ui()
.arg("add")
- .arg_line("my-package1 my-package2@0.4.1 --optional")
+ .arg_line("my-package --optional")
.current_dir(cwd)
.assert()
.success()
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/out/Cargo.toml
index eda5445c5..a8789b033 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/out/Cargo.toml
@@ -5,5 +5,7 @@ name = "cargo-list-test-fixture"
version = "0.0.0"
[dependencies]
-my-package1 = { version = "99999.0.0", optional = true }
-my-package2 = { version = "0.4.1", optional = true }
+my-package = { version = "0.1.0", optional = true }
+
+[features]
+my-package = ["dep:my-package"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/stderr.log
index 8cf4812cf..595ac276b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional/stderr.log
@@ -1,3 +1,2 @@
Updating `dummy-registry` index
- Adding my-package1 v99999.0.0 to optional dependencies.
- Adding my-package2 v0.4.1 to optional dependencies.
+ Adding my-package v0.1.0 to optional dependencies.
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/in/Cargo.toml
index 5ef953209..a7722da07 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/in/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/in/Cargo.toml
@@ -10,4 +10,3 @@ other = ["your-face/nose"]
[dependencies]
your-face = { version = "99999.0.0", optional = true }
-my-package2 = { version = "0.4.1", optional = true }
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/mod.rs
index 3090a7527..511b31e29 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/mod.rs
@@ -7,17 +7,7 @@ use cargo_test_support::curr_dir;
#[cargo_test]
fn case() {
cargo_test_support::registry::init();
- for ver in [
- "0.1.1+my-package",
- "0.2.0+my-package",
- "0.2.3+my-package",
- "0.4.1+my-package",
- "20.0.0+my-package",
- "99999.0.0+my-package",
- "99999.0.0-alpha.1+my-package",
- ] {
- cargo_test_support::registry::Package::new("my-package2", ver).publish();
- }
+
cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package")
.feature("nose", &[])
.feature("mouth", &[])
@@ -31,7 +21,7 @@ fn case() {
snapbox::cmd::Command::cargo_ui()
.arg("add")
- .arg_line("your-face my-package2@0.4.1 --no-optional")
+ .arg_line("your-face --no-optional")
.current_dir(cwd)
.assert()
.success()
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/out/Cargo.toml
index bf6c52963..b57286ed5 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/out/Cargo.toml
@@ -10,4 +10,3 @@ other = ["your-face/nose"]
[dependencies]
your-face = { version = "99999.0.0" }
-my-package2 = { version = "0.4.1" }
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/stderr.log
index 5fe113e86..796b9601b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_no_optional/stderr.log
@@ -5,4 +5,3 @@
- eyes
- mouth
- nose
- Adding my-package2 v0.4.1 to dependencies.
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/in/Cargo.toml
new file mode 100644
index 000000000..e446eca38
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/in/Cargo.toml
@@ -0,0 +1,11 @@
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+
+[dependencies]
+my-package1 = { version = "99999.0.0", optional = true }
+
+[features]
+default = ["dep:my-package1"] \ No newline at end of file
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/in/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/in/src/lib.rs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/in/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/mod.rs
new file mode 100644
index 000000000..434124e93
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/mod.rs
@@ -0,0 +1,26 @@
+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("my-package1", "99999.0.0").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("my-package1 --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/overwrite_optional_with_optional/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/out/Cargo.toml
new file mode 100644
index 000000000..e68fcdb3c
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/out/Cargo.toml
@@ -0,0 +1,11 @@
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+
+[dependencies]
+my-package1 = { version = "99999.0.0", optional = true }
+
+[features]
+default = ["dep:my-package1"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/stderr.log
new file mode 100644
index 000000000..ba9cb313d
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/stderr.log
@@ -0,0 +1,2 @@
+ Updating `dummy-registry` index
+ Adding my-package1 v99999.0.0 to optional dependencies.
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/stdout.log b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_optional_with_optional/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_noop/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_noop/out/Cargo.toml
index bbaf4f552..717252191 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_noop/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_noop/out/Cargo.toml
@@ -7,3 +7,6 @@ version = "0.0.0"
[dependencies]
your-face = { version = "0.0.0", path = "dependency", optional = true, default-features = false, features = ["nose", "mouth"], registry = "alternative" }
+
+[features]
+your-face = ["dep:your-face"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_with_version/out/primary/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_with_version/out/primary/Cargo.toml
index a20f2095d..c45f79491 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_with_version/out/primary/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_with_version/out/primary/Cargo.toml
@@ -6,3 +6,6 @@ version = "0.0.0"
[dependencies]
cargo-list-test-fixture-dependency = { optional = true, version = "20.0" }
+
+[features]
+cargo-list-test-fixture-dependency = ["dep:cargo-list-test-fixture-dependency"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/in/Cargo.toml
new file mode 100644
index 000000000..43d0d8238
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/in/Cargo.toml
@@ -0,0 +1,9 @@
+cargo-features = ["public-dependency"]
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+
+[dependencies]
+my-package = "0.1.0" \ No newline at end of file
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/in/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/in/src/lib.rs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/in/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/mod.rs
new file mode 100644
index 000000000..bbf8d65a6
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/mod.rs
@@ -0,0 +1,26 @@
+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("my-package", "0.1.0").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("my-package --public")
+ .current_dir(cwd)
+ .masquerade_as_nightly_cargo(&["public-dependency"])
+ .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/overwrite_public/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/out/Cargo.toml
new file mode 100644
index 000000000..77fccaa6a
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/out/Cargo.toml
@@ -0,0 +1,9 @@
+cargo-features = ["public-dependency"]
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+
+[dependencies]
+my-package = { version = "0.1.0", public = true }
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/stderr.log
new file mode 100644
index 000000000..5259bbde8
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/stderr.log
@@ -0,0 +1,2 @@
+ Updating `dummy-registry` index
+ Adding my-package v0.1.0 to public dependencies.
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/stdout.log b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/in/Cargo.toml
new file mode 100644
index 000000000..cc7ec1a9b
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/in/Cargo.toml
@@ -0,0 +1,9 @@
+cargo-features = ["public-dependency"]
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+
+[dependencies]
+my-package = { version = "0.1.0", public = true } \ No newline at end of file
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/in/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/in/src/lib.rs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/in/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/mod.rs
new file mode 100644
index 000000000..912ac3fd3
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/mod.rs
@@ -0,0 +1,26 @@
+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("my-package", "0.1.0").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("my-package --no-public")
+ .current_dir(cwd)
+ .masquerade_as_nightly_cargo(&["public-dependency"])
+ .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/overwrite_public_with_no_public/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/out/Cargo.toml
new file mode 100644
index 000000000..cfa80cc13
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/out/Cargo.toml
@@ -0,0 +1,9 @@
+cargo-features = ["public-dependency"]
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+
+[dependencies]
+my-package = { version = "0.1.0" }
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/stderr.log
new file mode 100644
index 000000000..8e025739f
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/stderr.log
@@ -0,0 +1,2 @@
+ Updating `dummy-registry` index
+ Adding my-package v0.1.0 to dependencies.
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/stdout.log b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_public_with_no_public/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/out/Cargo.toml
index 450229245..0217d4176 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/out/Cargo.toml
@@ -6,3 +6,6 @@ version = "0.0.0"
[dependencies]
a1 = { package = "versioned-package", version = "0.1.1", optional = true }
+
+[features]
+a1 = ["dep:a1"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_git/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_git/out/Cargo.toml
index 260014024..a3eabd065 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_git/out/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_git/out/Cargo.toml
@@ -6,3 +6,6 @@ version = "0.0.0"
[dependencies]
versioned-package = { version = "0.3.0", optional = true, git = "[ROOTURL]/versioned-package" }
+
+[features]
+versioned-package = ["dep:versioned-package"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_path/out/primary/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_path/out/primary/Cargo.toml
index 07253670a..bd460a11b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_path/out/primary/Cargo.toml
+++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_path/out/primary/Cargo.toml
@@ -6,3 +6,6 @@ version = "0.0.0"
[dependencies]
cargo-list-test-fixture-dependency = { version = "0.0.0", optional = true, path = "../dependency" }
+
+[features]
+cargo-list-test-fixture-dependency = ["dep:cargo-list-test-fixture-dependency"]
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/public/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/public/in/Cargo.toml
new file mode 100644
index 000000000..e9087535b
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/public/in/Cargo.toml
@@ -0,0 +1,6 @@
+cargo-features = ["public-dependency"]
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/public/in/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_add/public/in/src/lib.rs
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/public/in/src/lib.rs
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/public/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/public/mod.rs
new file mode 100644
index 000000000..bbf8d65a6
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/public/mod.rs
@@ -0,0 +1,26 @@
+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("my-package", "0.1.0").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("my-package --public")
+ .current_dir(cwd)
+ .masquerade_as_nightly_cargo(&["public-dependency"])
+ .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/public/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/public/out/Cargo.toml
new file mode 100644
index 000000000..77fccaa6a
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/public/out/Cargo.toml
@@ -0,0 +1,9 @@
+cargo-features = ["public-dependency"]
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+
+[dependencies]
+my-package = { version = "0.1.0", public = true }
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/public/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/public/stderr.log
new file mode 100644
index 000000000..5259bbde8
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/public/stderr.log
@@ -0,0 +1,2 @@
+ Updating `dummy-registry` index
+ Adding my-package v0.1.0 to public dependencies.
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/public/stdout.log b/src/tools/cargo/tests/testsuite/cargo_add/public/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_add/public/stdout.log
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/rust_version_ignore/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/rust_version_ignore/mod.rs
index f8aac0ad8..0404d12b4 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/rust_version_ignore/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_add/rust_version_ignore/mod.rs
@@ -26,7 +26,7 @@ fn case() {
.current_dir(cwd)
.masquerade_as_nightly_cargo(&["msrv-policy"])
.assert()
- .code(101)
+ .code(0)
.stdout_matches_path(curr_dir!().join("stdout.log"))
.stderr_matches_path(curr_dir!().join("stderr.log"));
diff --git a/src/tools/cargo/tests/testsuite/cargo_add/rust_version_ignore/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/rust_version_ignore/stderr.log
index 96bcbddc2..430abe31b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_add/rust_version_ignore/stderr.log
+++ b/src/tools/cargo/tests/testsuite/cargo_add/rust_version_ignore/stderr.log
@@ -1,7 +1,2 @@
Updating `dummy-registry` index
Adding rust-version-user v0.2.1 to dependencies.
-error: failed to select a version for the requirement `rust-version-user = "^0.2.1"`
-candidate versions found which didn't match: 0.2.1, 0.1.0
-location searched: `dummy-registry` index (which is replacing registry `crates-io`)
-required by package `cargo-list-test-fixture v0.0.0 ([ROOT]/case)`
-perhaps a crate was updated and forgotten to be re-vendored?
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 95546b4a3..cfea6e01e 100644
--- a/src/tools/cargo/tests/testsuite/cargo_bench/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_bench/help/stdout.log
@@ -11,8 +11,8 @@ Options:
--no-fail-fast Run all benchmarks regardless of failure
--ignore-rust-version Ignore `rust-version` specification in packages
--message-format <FMT> Error format
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
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 58b12cdcd..3918fd44a 100644
--- a/src/tools/cargo/tests/testsuite/cargo_build/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_build/help/stdout.log
@@ -6,8 +6,8 @@ Options:
--ignore-rust-version Ignore `rust-version` specification in packages
--future-incompat-report Outputs a future incompatibility report at the end of the build
--message-format <FMT> Error format
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
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 bbf090d1d..7b6289798 100644
--- a/src/tools/cargo/tests/testsuite/cargo_check/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_check/help/stdout.log
@@ -6,8 +6,8 @@ Options:
--ignore-rust-version Ignore `rust-version` specification in packages
--future-incompat-report Outputs a future incompatibility report at the end of the build
--message-format <FMT> Error format
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
diff --git a/src/tools/cargo/tests/testsuite/cargo_clean/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_clean/help/stdout.log
index 6e9e82772..80571e5dc 100644
--- a/src/tools/cargo/tests/testsuite/cargo_clean/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_clean/help/stdout.log
@@ -4,9 +4,9 @@ Usage: cargo[EXE] clean [OPTIONS]
Options:
--doc Whether or not to clean just the documentation directory
- -q, --quiet Do not print cargo log messages
-n, --dry-run Display what would be deleted without deleting anything
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
diff --git a/src/tools/cargo/tests/testsuite/cargo_config/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_config/help/stdout.log
index 50caca72a..5c14335fc 100644
--- a/src/tools/cargo/tests/testsuite/cargo_config/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_config/help/stdout.log
@@ -7,6 +7,7 @@ Commands:
Options:
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
diff --git a/src/tools/cargo/tests/testsuite/cargo_doc/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_doc/help/stdout.log
index 8ff5f9b72..e1a19bedd 100644
--- a/src/tools/cargo/tests/testsuite/cargo_doc/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_doc/help/stdout.log
@@ -8,8 +8,8 @@ Options:
--document-private-items Document private items
--ignore-rust-version Ignore `rust-version` specification in packages
--message-format <FMT> Error format
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
diff --git a/src/tools/cargo/tests/testsuite/cargo_features.rs b/src/tools/cargo/tests/testsuite/cargo_features.rs
index d319ed686..8b7daa214 100644
--- a/src/tools/cargo/tests/testsuite/cargo_features.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_features.rs
@@ -296,7 +296,7 @@ fn allow_features_to_rustc() {
"src/lib.rs",
r#"
#![allow(internal_features)]
- #![feature(test_2018_feature)]
+ #![feature(rustc_attrs)]
"#,
)
.build();
@@ -307,7 +307,7 @@ fn allow_features_to_rustc() {
.with_stderr_contains("[..]E0725[..]")
.run();
- p.cargo("-Zallow-features=test_2018_feature check")
+ p.cargo("-Zallow-features=rustc_attrs check")
.masquerade_as_nightly_cargo(&["allow-features"])
.with_stderr(
"\
diff --git a/src/tools/cargo/tests/testsuite/cargo_fetch/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_fetch/help/stdout.log
index 32f29f1b3..5645a6c03 100644
--- a/src/tools/cargo/tests/testsuite/cargo_fetch/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_fetch/help/stdout.log
@@ -3,8 +3,8 @@ Fetch dependencies of a package from the network
Usage: cargo[EXE] fetch [OPTIONS]
Options:
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
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 3e8b1427f..a93215c50 100644
--- a/src/tools/cargo/tests/testsuite/cargo_fix/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_fix/help/stdout.log
@@ -11,8 +11,8 @@ Options:
--allow-staged Fix code even if the working directory has staged changes
--ignore-rust-version Ignore `rust-version` specification in packages
--message-format <FMT> Error format
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
diff --git a/src/tools/cargo/tests/testsuite/cargo_generate_lockfile/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_generate_lockfile/help/stdout.log
index 07eff888a..5d0bf1359 100644
--- a/src/tools/cargo/tests/testsuite/cargo_generate_lockfile/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_generate_lockfile/help/stdout.log
@@ -3,8 +3,8 @@ Generate the lockfile for a package
Usage: cargo[EXE] generate-lockfile [OPTIONS]
Options:
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
diff --git a/src/tools/cargo/tests/testsuite/cargo_help/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_help/help/stdout.log
index a03946b45..1f7a710d6 100644
--- a/src/tools/cargo/tests/testsuite/cargo_help/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_help/help/stdout.log
@@ -7,6 +7,7 @@ Arguments:
Options:
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
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 588b45ccf..197a5f8d1 100644
--- a/src/tools/cargo/tests/testsuite/cargo_init/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_init/help/stdout.log
@@ -15,8 +15,8 @@ Options:
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
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
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 5e3458d37..baaba7a72 100644
--- a/src/tools/cargo/tests/testsuite/cargo_install/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_install/help/stdout.log
@@ -20,9 +20,9 @@ Options:
--list list all installed packages and their versions
--ignore-rust-version Ignore `rust-version` specification in packages
--message-format <FMT> Error format
- -q, --quiet Do not print cargo log messages
--debug Build in debug mode (with the 'dev' profile) instead of release mode
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
diff --git a/src/tools/cargo/tests/testsuite/cargo_locate_project/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_locate_project/help/stdout.log
index 1c6ea7b25..f39d61b7a 100644
--- a/src/tools/cargo/tests/testsuite/cargo_locate_project/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_locate_project/help/stdout.log
@@ -5,8 +5,8 @@ Usage: cargo[EXE] locate-project [OPTIONS]
Options:
--workspace Locate Cargo.toml of the workspace root
--message-format <FMT> Output representation [possible values: json, plain]
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
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 e0d5e7e69..0a699f72f 100644
--- a/src/tools/cargo/tests/testsuite/cargo_login/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_login/help/stdout.log
@@ -8,8 +8,8 @@ Arguments:
Options:
--registry <REGISTRY> Registry to use
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
diff --git a/src/tools/cargo/tests/testsuite/cargo_logout/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_logout/help/stdout.log
index fe328d765..3f9679f9b 100644
--- a/src/tools/cargo/tests/testsuite/cargo_logout/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_logout/help/stdout.log
@@ -4,8 +4,8 @@ Usage: cargo[EXE] logout [OPTIONS]
Options:
--registry <REGISTRY> Registry to use
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
diff --git a/src/tools/cargo/tests/testsuite/cargo_metadata/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_metadata/help/stdout.log
index 939fc40c9..f44f66c88 100644
--- a/src/tools/cargo/tests/testsuite/cargo_metadata/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_metadata/help/stdout.log
@@ -8,8 +8,8 @@ Options:
--no-deps Output information only about the workspace members and don't
fetch dependencies
--format-version <VERSION> Format version [possible values: 1]
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/empty_name/in/.keep b/src/tools/cargo/tests/testsuite/cargo_new/empty_name/in/.keep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/empty_name/in/.keep
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/empty_name/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/empty_name/mod.rs
new file mode 100644
index 000000000..a7d56630f
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/empty_name/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(["foo", "--name", ""])
+ .current_dir(cwd)
+ .assert()
+ .failure()
+ .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/empty_name/out/.keep b/src/tools/cargo/tests/testsuite/cargo_new/empty_name/out/.keep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/empty_name/out/.keep
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/empty_name/stderr.log b/src/tools/cargo/tests/testsuite/cargo_new/empty_name/stderr.log
new file mode 100644
index 000000000..d9547a42a
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/empty_name/stderr.log
@@ -0,0 +1 @@
+error: package name cannot be empty
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/empty_name/stdout.log b/src/tools/cargo/tests/testsuite/cargo_new/empty_name/stdout.log
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/cargo_new/empty_name/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 3df5eceb8..52a6f83a1 100644
--- a/src/tools/cargo/tests/testsuite/cargo_new/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_new/help/stdout.log
@@ -15,8 +15,8 @@ Options:
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
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
diff --git a/src/tools/cargo/tests/testsuite/cargo_new/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/mod.rs
index da0304409..806bd2ec3 100644
--- a/src/tools/cargo/tests/testsuite/cargo_new/mod.rs
+++ b/src/tools/cargo/tests/testsuite/cargo_new/mod.rs
@@ -4,6 +4,7 @@ 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 empty_name;
mod help;
mod inherit_workspace_lints;
mod inherit_workspace_package_table;
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 110df8e9a..b6f436d04 100644
--- a/src/tools/cargo/tests/testsuite/cargo_owner/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_owner/help/stdout.log
@@ -12,8 +12,8 @@ Options:
--index <INDEX> Registry index URL to modify owners for
--registry <REGISTRY> Registry to modify owners for
--token <TOKEN> API token to use when authenticating
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
diff --git a/src/tools/cargo/tests/testsuite/cargo_package/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_package/help/stdout.log
index 5079c2a6f..ec2464a8d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_package/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_package/help/stdout.log
@@ -7,8 +7,8 @@ Options:
--no-verify Don't verify the contents by building them
--no-metadata Ignore warnings about a lack of human-usable metadata
--allow-dirty Allow dirty working directories to be packaged
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
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 5971e88dc..657bb9e5d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_pkgid/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_pkgid/help/stdout.log
@@ -6,8 +6,8 @@ Arguments:
[SPEC]
Options:
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
diff --git a/src/tools/cargo/tests/testsuite/cargo_publish/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_publish/help/stdout.log
index df2594fb4..d598c93d6 100644
--- a/src/tools/cargo/tests/testsuite/cargo_publish/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_publish/help/stdout.log
@@ -9,8 +9,8 @@ Options:
--token <TOKEN> Token to use when uploading
--no-verify Don't verify the contents by building them
--allow-dirty Allow dirty working directories to be packaged
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
diff --git a/src/tools/cargo/tests/testsuite/cargo_read_manifest/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_read_manifest/help/stdout.log
index 83db5413d..a645ea3c2 100644
--- a/src/tools/cargo/tests/testsuite/cargo_read_manifest/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_read_manifest/help/stdout.log
@@ -5,8 +5,8 @@ Deprecated, use `cargo metadata --no-deps` instead.
Usage: cargo[EXE] read-manifest [OPTIONS]
Options:
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
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 47d2c87ad..c3dc74b2d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/help/stdout.log
@@ -7,8 +7,8 @@ Arguments:
Options:
-n, --dry-run Don't actually write the manifest
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/in/Cargo.lock b/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/in/Cargo.lock
index 06c2052d5..2302220f2 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/in/Cargo.lock
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/in/Cargo.lock
@@ -43,13 +43,13 @@ checksum = "31162e7d23a085553c42dee375787b451a481275473f7779c4a63bcc267a24fd"
name = "semver"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3031434e07edc922bf1b8262f075fac1522694f17b1ee7ad314c4cabd5d2723f"
+checksum = "106bee742e3199d9e59f4269e458dfc825c1b4648c483b1c2b7a45cd2610a308"
[[package]]
name = "serde"
version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75d9264696ebbf5315a6b068e9910c4df9274365afac2d88abf66525df660218"
+checksum = "be7d269f612a60e3c2c4a4a120e2d878a3f3298a5285eda6e95453905a107d9a"
[[package]]
name = "toml"
diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/out/Cargo.lock b/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/out/Cargo.lock
index bd8c90f46..0946cee47 100644
--- a/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/out/Cargo.lock
+++ b/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/out/Cargo.lock
@@ -36,13 +36,13 @@ checksum = "84949cb53285a6c481d0133065a7b669871acfd9e20f273f4ce1283c309775d5"
name = "semver"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3031434e07edc922bf1b8262f075fac1522694f17b1ee7ad314c4cabd5d2723f"
+checksum = "106bee742e3199d9e59f4269e458dfc825c1b4648c483b1c2b7a45cd2610a308"
[[package]]
name = "serde"
version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75d9264696ebbf5315a6b068e9910c4df9274365afac2d88abf66525df660218"
+checksum = "be7d269f612a60e3c2c4a4a120e2d878a3f3298a5285eda6e95453905a107d9a"
[[package]]
name = "toml"
diff --git a/src/tools/cargo/tests/testsuite/cargo_report/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_report/help/stdout.log
index 67819de55..95872662e 100644
--- a/src/tools/cargo/tests/testsuite/cargo_report/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_report/help/stdout.log
@@ -7,6 +7,7 @@ Commands:
Options:
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
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 97c13382a..2e39d4f9e 100644
--- a/src/tools/cargo/tests/testsuite/cargo_run/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_run/help/stdout.log
@@ -8,8 +8,8 @@ Arguments:
Options:
--ignore-rust-version Ignore `rust-version` specification in packages
--message-format <FMT> Error format
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
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 60069f526..8952330b0 100644
--- a/src/tools/cargo/tests/testsuite/cargo_rustc/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_rustc/help/stdout.log
@@ -11,8 +11,8 @@ Options:
--future-incompat-report Outputs a future incompatibility report at the end of the build
--ignore-rust-version Ignore `rust-version` specification in packages
--message-format <FMT> Error format
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
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 67ee27e6b..a0a3cde5d 100644
--- a/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_rustdoc/help/stdout.log
@@ -9,8 +9,8 @@ Options:
--open Opens the docs in a browser after the operation
--ignore-rust-version Ignore `rust-version` specification in packages
--message-format <FMT> Error format
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
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 9cc508bba..e2024a990 100644
--- a/src/tools/cargo/tests/testsuite/cargo_search/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_search/help/stdout.log
@@ -1,4 +1,4 @@
-Search packages in crates.io
+Search packages in the registry. Default registry is crates.io
Usage: cargo[EXE] search [OPTIONS] [QUERY]...
@@ -9,8 +9,8 @@ Options:
--limit <LIMIT> Limit the number of results (default: 10, max: 100)
--index <INDEX> Registry index URL to search packages in
--registry <REGISTRY> Registry to search packages in
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
diff --git a/src/tools/cargo/tests/testsuite/cargo_tree/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_tree/help/stdout.log
index 4170583a8..9865fd59e 100644
--- a/src/tools/cargo/tests/testsuite/cargo_tree/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_tree/help/stdout.log
@@ -3,7 +3,6 @@ Display a tree visualization of a dependency graph
Usage: cargo[EXE] tree [OPTIONS]
Options:
- -q, --quiet Do not print cargo log messages
-e, --edges <KINDS> The kinds of dependencies to display (features, normal, build, dev, all,
no-normal, no-build, no-dev, no-proc-macro)
-i, --invert [<SPEC>] Invert the tree direction and focus on the given package
@@ -17,6 +16,7 @@ Options:
ascii]
-f, --format <FORMAT> Format string used for printing dependencies [default: {p}]
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
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 efdf11c03..1988e7a0e 100644
--- a/src/tools/cargo/tests/testsuite/cargo_uninstall/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_uninstall/help/stdout.log
@@ -7,8 +7,8 @@ Arguments:
Options:
--root <DIR> Directory to uninstall packages from
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
diff --git a/src/tools/cargo/tests/testsuite/cargo_update/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_update/help/stdout.log
index 92caeb656..8e0bf2ccb 100644
--- a/src/tools/cargo/tests/testsuite/cargo_update/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_update/help/stdout.log
@@ -6,8 +6,8 @@ Options:
-n, --dry-run Don't actually write the lockfile
--recursive Force updating all dependencies of [SPEC]... as well
--precise <PRECISE> Update [SPEC] to exactly PRECISE
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
diff --git a/src/tools/cargo/tests/testsuite/cargo_vendor/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_vendor/help/stdout.log
index 7f37ab56e..4e05e75c8 100644
--- a/src/tools/cargo/tests/testsuite/cargo_vendor/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_vendor/help/stdout.log
@@ -10,8 +10,8 @@ Options:
-s, --sync <TOML> Additional `Cargo.toml` to sync and vendor
--respect-source-config Respect `[source]` config in `.cargo/config`
--versioned-dirs Always include version in subdir name
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for
diff --git a/src/tools/cargo/tests/testsuite/cargo_verify_project/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_verify_project/help/stdout.log
index a61534500..7adc34e6c 100644
--- a/src/tools/cargo/tests/testsuite/cargo_verify_project/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_verify_project/help/stdout.log
@@ -3,8 +3,8 @@ Check correctness of crate manifest
Usage: cargo[EXE] verify-project [OPTIONS]
Options:
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
diff --git a/src/tools/cargo/tests/testsuite/cargo_version/help/stdout.log b/src/tools/cargo/tests/testsuite/cargo_version/help/stdout.log
index 3f79051ad..2ad1c551c 100644
--- a/src/tools/cargo/tests/testsuite/cargo_version/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_version/help/stdout.log
@@ -3,8 +3,8 @@ Show version information
Usage: cargo[EXE] version [OPTIONS]
Options:
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
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 61dc800c7..072ceaac7 100644
--- a/src/tools/cargo/tests/testsuite/cargo_yank/help/stdout.log
+++ b/src/tools/cargo/tests/testsuite/cargo_yank/help/stdout.log
@@ -11,8 +11,8 @@ Options:
--index <INDEX> Registry index URL to yank from
--registry <REGISTRY> Registry to yank from
--token <TOKEN> API token to use when authenticating
- -q, --quiet Do not print cargo log messages
-v, --verbose... Use verbose output (-vv very verbose/build.rs output)
+ -q, --quiet Do not print cargo log messages
--color <WHEN> Coloring: auto, always, never
--config <KEY=VALUE> Override a configuration value
-Z <FLAG> Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details
diff --git a/src/tools/cargo/tests/testsuite/check.rs b/src/tools/cargo/tests/testsuite/check.rs
index 03611ae67..b7ad3eb1d 100644
--- a/src/tools/cargo/tests/testsuite/check.rs
+++ b/src/tools/cargo/tests/testsuite/check.rs
@@ -3,11 +3,12 @@
use std::fmt::{self, Write};
use crate::messages::raw_rustc_output;
+use cargo_test_support::compare;
use cargo_test_support::install::exe;
use cargo_test_support::paths::CargoPathExt;
use cargo_test_support::registry::Package;
+use cargo_test_support::tools;
use cargo_test_support::{basic_bin_manifest, basic_manifest, git, project};
-use cargo_test_support::{tools, wrapped_clippy_driver};
#[cargo_test]
fn check_success() {
@@ -804,7 +805,7 @@ fn short_message_format() {
.with_stderr_contains(
"\
src/lib.rs:1:27: error[E0308]: mismatched types
-error: could not compile `foo` (lib) due to previous error
+error: could not compile `foo` (lib) due to 1 previous error
",
)
.run();
@@ -1250,7 +1251,7 @@ fn check_fixable_error_no_fix() {
[CHECKING] foo v0.0.1 ([..])
{}\
[WARNING] `foo` (lib) generated 1 warning
-[ERROR] could not compile `foo` (lib) due to previous error; 1 warning emitted
+[ERROR] could not compile `foo` (lib) due to 1 previous error; 1 warning emitted
",
rustc_message
);
@@ -1432,7 +1433,7 @@ fn check_fixable_warning_for_clippy() {
foo.cargo("check")
// We can't use `clippy` so we use a `rustc` workspace wrapper instead
- .env("RUSTC_WORKSPACE_WRAPPER", wrapped_clippy_driver())
+ .env("RUSTC_WORKSPACE_WRAPPER", tools::wrapped_clippy_driver())
.with_stderr_contains("[..] (run `cargo clippy --fix --lib -p foo` to apply 1 suggestion)")
.run();
}
@@ -1519,3 +1520,43 @@ fn versionless_package() {
)
.run();
}
+
+#[cargo_test]
+fn pkgid_querystring_works() {
+ let git_project = git::new("gitdep", |p| {
+ p.file("Cargo.toml", &basic_manifest("gitdep", "1.0.0"))
+ .file("src/lib.rs", "")
+ });
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+
+ [dependencies]
+ gitdep = {{ git = "{}", branch = "master" }}
+ "#,
+ git_project.url()
+ ),
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("generate-lockfile").run();
+
+ let output = p.cargo("pkgid").arg("gitdep").exec_with_output().unwrap();
+ let gitdep_pkgid = String::from_utf8(output.stdout).unwrap();
+ let gitdep_pkgid = gitdep_pkgid.trim();
+ compare::assert_match_exact("git+file://[..]/gitdep?branch=master#1.0.0", &gitdep_pkgid);
+
+ p.cargo("build -p")
+ .arg(gitdep_pkgid)
+ .with_stderr(
+ "\
+[COMPILING] gitdep v1.0.0 (file:///[..]/gitdep?branch=master#[..])
+[FINISHED] dev [..]",
+ )
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/check_cfg.rs b/src/tools/cargo/tests/testsuite/check_cfg.rs
index 57d5f8053..42cfe6065 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 $($first_value:tt $($other_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(", $("/\"", $first_value, "/\"", $(", ", "/\"", $other_values, "/\"",)*)* "))", '"', "[..]")
+ $what, '(', $who, ", values(", "/\"", $first_value, "/\"", $(", ", "/\"", $other_values, "/\"",)* "))", '"', "[..]")
}
#[cfg(not(windows))]
{
concat!("[RUNNING] [..]", $tool, "[..] --check-cfg '",
- $what, '(', $who, ", values(", $("\"", $first_value, "\"", $(", ", "\"", $other_values, "\"",)*)* "))", "'", "[..]")
+ $what, '(', $who, ", values(", "\"", $first_value, "\"", $(", ", "\"", $other_values, "\"",)* "))", "'", "[..]")
}
}};
}
@@ -142,6 +142,77 @@ fn features_with_namespaced_features() {
}
#[cargo_test(nightly, reason = "--check-cfg is unstable")]
+fn features_fingerprint() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [features]
+ f_a = []
+ f_b = []
+ "#,
+ )
+ .file("src/lib.rs", "#[cfg(feature = \"f_b\")] fn entry() {}")
+ .build();
+
+ p.cargo("check -v -Zcheck-cfg")
+ .masquerade_as_nightly_cargo(&["check-cfg"])
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a" "f_b"))
+ .with_stderr_does_not_contain("[..]unexpected_cfgs[..]")
+ .run();
+
+ p.cargo("check -v -Zcheck-cfg")
+ .masquerade_as_nightly_cargo(&["check-cfg"])
+ .with_stderr_does_not_contain("[..]rustc[..]")
+ .run();
+
+ // checking that re-ordering the features does not invalid the fingerprint
+ p.change_file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [features]
+ f_b = []
+ f_a = []
+ "#,
+ );
+
+ p.cargo("check -v -Zcheck-cfg")
+ .masquerade_as_nightly_cargo(&["check-cfg"])
+ .with_stderr_does_not_contain("[..]rustc[..]")
+ .run();
+
+ p.change_file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [features]
+ f_a = []
+ "#,
+ );
+
+ p.cargo("check -v -Zcheck-cfg")
+ .masquerade_as_nightly_cargo(&["check-cfg"])
+ // we check that the fingerprint is indeed dirty
+ .with_stderr_contains("[..]Dirty[..]the list of declared features changed")
+ // that is cause rustc to be called again with the new check-cfg args
+ .with_stderr_contains(x!("rustc" => "cfg" of "feature" with "f_a"))
+ // and that we indeed found a new warning from the unexpected_cfgs lint
+ .with_stderr_contains("[..]unexpected_cfgs[..]")
+ .run();
+}
+
+#[cargo_test(nightly, reason = "--check-cfg is unstable")]
fn well_known_names_values() {
let p = project()
.file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
@@ -150,7 +221,7 @@ fn well_known_names_values() {
p.cargo("check -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "cfg" of "feature" with))
+ .with_stderr_contains(x!("rustc" => "cfg"))
.run();
}
@@ -213,7 +284,7 @@ fn well_known_names_values_test() {
p.cargo("test -v -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "cfg" of "feature" with))
+ .with_stderr_contains(x!("rustc" => "cfg"))
.run();
}
@@ -226,8 +297,8 @@ fn well_known_names_values_doctest() {
p.cargo("test -v --doc -Zcheck-cfg")
.masquerade_as_nightly_cargo(&["check-cfg"])
- .with_stderr_contains(x!("rustc" => "cfg" of "feature" with))
- .with_stderr_contains(x!("rustdoc" => "cfg" of "feature" with))
+ .with_stderr_contains(x!("rustc" => "cfg"))
+ .with_stderr_contains(x!("rustdoc" => "cfg"))
.run();
}
diff --git a/src/tools/cargo/tests/testsuite/clean.rs b/src/tools/cargo/tests/testsuite/clean.rs
index fbb4d3e5b..913bf19cb 100644
--- a/src/tools/cargo/tests/testsuite/clean.rs
+++ b/src/tools/cargo/tests/testsuite/clean.rs
@@ -1,5 +1,6 @@
//! Tests for the `cargo clean` command.
+use cargo_test_support::paths::CargoPathExt;
use cargo_test_support::registry::Package;
use cargo_test_support::{
basic_bin_manifest, basic_manifest, git, main_file, project, project_in, rustc_host,
@@ -33,7 +34,10 @@ fn different_dir() {
p.cargo("build").run();
assert!(p.build_dir().is_dir());
- p.cargo("clean").cwd("src").with_stdout("").run();
+ p.cargo("clean")
+ .cwd("src")
+ .with_stderr("[REMOVED] [..]")
+ .run();
assert!(!p.build_dir().is_dir());
}
@@ -81,7 +85,7 @@ fn clean_multiple_packages() {
p.cargo("clean -p d1 -p d2")
.cwd("src")
- .with_stdout("")
+ .with_stderr("[REMOVED] [..]")
.run();
assert!(p.bin("foo").is_file());
assert!(!d1_path.is_file());
@@ -226,7 +230,9 @@ fn clean_release() {
p.cargo("build --release").run();
p.cargo("clean -p foo").run();
- p.cargo("build --release").with_stdout("").run();
+ p.cargo("build --release")
+ .with_stderr("[FINISHED] [..]")
+ .run();
p.cargo("clean -p foo --release").run();
p.cargo("build --release")
@@ -354,7 +360,7 @@ fn clean_git() {
.build();
p.cargo("build").run();
- p.cargo("clean -p dep").with_stdout("").run();
+ p.cargo("clean -p dep").with_stderr("[REMOVED] [..]").run();
p.cargo("build").run();
}
@@ -379,7 +385,7 @@ fn registry() {
Package::new("bar", "0.1.0").publish();
p.cargo("build").run();
- p.cargo("clean -p bar").with_stdout("").run();
+ p.cargo("clean -p bar").with_stderr("[REMOVED] [..]").run();
p.cargo("build").run();
}
@@ -805,15 +811,6 @@ fn clean_dry_run() {
.file("src/lib.rs", "")
.build();
- let ls_r = || -> Vec<_> {
- let mut file_list: Vec<_> = walkdir::WalkDir::new(p.build_dir())
- .into_iter()
- .filter_map(|e| e.map(|e| e.path().to_owned()).ok())
- .collect();
- file_list.sort();
- file_list
- };
-
// Start with no files.
p.cargo("clean --dry-run")
.with_stdout("")
@@ -823,7 +820,7 @@ fn clean_dry_run() {
)
.run();
p.cargo("check").run();
- let before = ls_r();
+ let before = p.build_dir().ls_r();
p.cargo("clean --dry-run")
.with_stderr(
"[SUMMARY] [..] files, [..] total\n\
@@ -831,7 +828,7 @@ fn clean_dry_run() {
)
.run();
// Verify it didn't delete anything.
- let after = ls_r();
+ let after = p.build_dir().ls_r();
assert_eq!(before, after);
let expected = cargo::util::iter_join(before.iter().map(|p| p.to_str().unwrap()), "\n");
eprintln!("{expected}");
@@ -854,3 +851,29 @@ fn doc_with_package_selection() {
.with_stderr("error: --doc cannot be used with -p")
.run();
}
+
+#[cargo_test]
+fn quiet_does_not_show_summary() {
+ // Checks that --quiet works with `cargo clean`, since there was a
+ // subtle issue with how the flag is defined as a global flag.
+ let p = project()
+ .file("Cargo.toml", &basic_manifest("foo", "0.1.0"))
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("check").run();
+ p.cargo("clean --quiet --dry-run")
+ .with_stdout("")
+ .with_stderr("")
+ .run();
+ // Verify exact same command without -q would actually display something.
+ p.cargo("clean --dry-run")
+ .with_stdout("")
+ .with_stderr(
+ "\
+[SUMMARY] [..] files, [..] total
+[WARNING] no files deleted due to --dry-run
+",
+ )
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/config.rs b/src/tools/cargo/tests/testsuite/config.rs
index e5078bd8e..bcd126020 100644
--- a/src/tools/cargo/tests/testsuite/config.rs
+++ b/src/tools/cargo/tests/testsuite/config.rs
@@ -2,9 +2,9 @@
use cargo::core::{PackageIdSpec, Shell};
use cargo::util::config::{self, Config, Definition, JobsConfig, SslVersionConfig, StringList};
-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::util_schemas::manifest::TomlTrimPaths;
+use cargo::util_schemas::manifest::TomlTrimPathsValue;
+use cargo::util_schemas::manifest::{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};
diff --git a/src/tools/cargo/tests/testsuite/custom_target.rs b/src/tools/cargo/tests/testsuite/custom_target.rs
index a04029075..45cb3ac9f 100644
--- a/src/tools/cargo/tests/testsuite/custom_target.rs
+++ b/src/tools/cargo/tests/testsuite/custom_target.rs
@@ -178,6 +178,8 @@ fn changing_spec_rebuilds() {
}
#[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 changing_spec_relearns_crate_types() {
// Changing the .json file will invalidate the cache of crate types.
let p = project()
diff --git a/src/tools/cargo/tests/testsuite/doc.rs b/src/tools/cargo/tests/testsuite/doc.rs
index 65169d214..37dd47d76 100644
--- a/src/tools/cargo/tests/testsuite/doc.rs
+++ b/src/tools/cargo/tests/testsuite/doc.rs
@@ -75,7 +75,14 @@ fn doc_twice() {
)
.run();
- p.cargo("doc").with_stdout("").run();
+ p.cargo("doc")
+ .with_stderr(
+ "\
+[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
+",
+ )
+ .run();
}
#[cargo_test]
@@ -118,9 +125,14 @@ fn doc_deps() {
assert_eq!(p.glob("target/debug/**/*.rlib").count(), 0);
assert_eq!(p.glob("target/debug/deps/libbar-*.rmeta").count(), 1);
+ // Make sure it doesn't recompile.
p.cargo("doc")
- .env("CARGO_LOG", "cargo::ops::cargo_rustc::fingerprint")
- .with_stdout("")
+ .with_stderr(
+ "\
+[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html
+",
+ )
.run();
assert!(p.root().join("target/doc").is_dir());
@@ -1686,6 +1698,7 @@ fn doc_message_format() {
r#"
{
"message": {
+ "$message_type": "diagnostic",
"children": "{...}",
"code": "{...}",
"level": "error",
diff --git a/src/tools/cargo/tests/testsuite/docscrape.rs b/src/tools/cargo/tests/testsuite/docscrape.rs
index d4d011ff3..91871be04 100644
--- a/src/tools/cargo/tests/testsuite/docscrape.rs
+++ b/src/tools/cargo/tests/testsuite/docscrape.rs
@@ -48,6 +48,84 @@ fn basic() {
assert!(p.build_dir().join("doc/src/ex/ex.rs.html").exists());
}
+// This test ensures that even if there is no `[workspace]` in the top-level `Cargo.toml` file, the
+// dependencies will get their examples scraped and that they appear in the generated documentation.
+#[cargo_test(nightly, reason = "-Zrustdoc-scrape-examples is unstable")]
+fn scrape_examples_for_non_workspace_reexports() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ edition = "2021"
+ authors = []
+
+ [dependencies]
+ a = { path = "crates/a" }
+ "#,
+ )
+ .file("src/lib.rs", "pub use a::*;")
+ // Example
+ .file(
+ "examples/one.rs",
+ r#"use foo::*;
+fn main() {
+ let foo = Foo::new("yes".into());
+ foo.maybe();
+}"#,
+ )
+ // `a` crate
+ .file(
+ "crates/a/Cargo.toml",
+ r#"
+ [package]
+ name = "a"
+ version = "0.0.1"
+ authors = []
+ "#,
+ )
+ .file(
+ "crates/a/src/lib.rs",
+ r#"
+#[derive(Debug)]
+pub struct Foo {
+ foo: String,
+ yes: bool,
+}
+
+impl Foo {
+ pub fn new(foo: String) -> Self {
+ Self { foo, yes: true }
+ }
+
+ pub fn maybe(&self) {
+ if self.yes {
+ println!("{}", self.foo)
+ }
+ }
+}"#,
+ )
+ .build();
+
+ p.cargo("doc -Zunstable-options -Zrustdoc-scrape-examples --no-deps")
+ .masquerade_as_nightly_cargo(&["rustdoc-scrape-examples"])
+ .with_stderr_unordered(
+ "\
+[CHECKING] a v0.0.1 ([CWD]/crates/a)
+[CHECKING] foo v0.0.1 ([CWD])
+[SCRAPING] foo v0.0.1 ([CWD])
+[DOCUMENTING] foo v0.0.1 ([CWD])
+[FINISHED] [..]
+[GENERATED] [CWD]/target/doc/foo/index.html",
+ )
+ .run();
+
+ let doc_html = p.read_file("target/doc/foo/struct.Foo.html");
+ assert!(doc_html.contains("Examples found in repository"));
+}
+
#[cargo_test(nightly, reason = "rustdoc scrape examples flags are unstable")]
fn avoid_build_script_cycle() {
let p = project()
diff --git a/src/tools/cargo/tests/testsuite/features.rs b/src/tools/cargo/tests/testsuite/features.rs
index 4b7455c37..febdf52fe 100644
--- a/src/tools/cargo/tests/testsuite/features.rs
+++ b/src/tools/cargo/tests/testsuite/features.rs
@@ -60,6 +60,10 @@ fn empty_feature_name() {
[ERROR] failed to parse manifest at `[..]`
Caused by:
+ TOML parse error at line 8, column 17
+ |
+ 8 | \"\" = []
+ | ^^
feature name cannot be empty
",
)
@@ -627,7 +631,14 @@ fn cyclic_feature2() {
.file("src/main.rs", "fn main() {}")
.build();
- p.cargo("check").with_stdout("").run();
+ p.cargo("check")
+ .with_stderr(
+ "\
+[CHECKING] foo [..]
+[FINISHED] [..]
+",
+ )
+ .run();
}
#[cargo_test]
@@ -1047,8 +1058,8 @@ fn no_rebuild_when_frobbing_default_feature() {
.build();
p.cargo("check").run();
- p.cargo("check").with_stdout("").run();
- p.cargo("check").with_stdout("").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
}
#[cargo_test]
@@ -1098,8 +1109,8 @@ fn unions_work_with_no_default_features() {
.build();
p.cargo("check").run();
- p.cargo("check").with_stdout("").run();
- p.cargo("check").with_stdout("").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
}
#[cargo_test]
@@ -2048,7 +2059,11 @@ fn invalid_feature_names_error() {
error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
Caused by:
- invalid character `+` in feature `+foo` in package foo v0.1.0 ([ROOT]/foo), \
+ TOML parse error at line 8, column 17
+ |
+ 8 | \"+foo\" = []
+ | ^^^^^^
+ invalid character `+` in feature `+foo`, \
the first character must be a Unicode XID start character or digit \
(most letters or `_` or `0` to `9`)
",
@@ -2075,7 +2090,11 @@ Caused by:
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), \
+ TOML parse error at line 8, column 13
+ |
+ 8 | \"a&b\" = []
+ | ^^^^^
+ invalid character `&` in feature `a&b`, \
characters must be Unicode XID characters, '-', `+`, or `.` \
(numbers, `+`, `-`, `_`, `.`, or most letters)
",
@@ -2108,6 +2127,10 @@ fn invalid_feature_name_slash_error() {
error: failed to parse manifest at `[CWD]/Cargo.toml`
Caused by:
+ TOML parse error at line 7, column 17
+ |
+ 7 | \"foo/bar\" = []
+ | ^^^^^^^^^
feature named `foo/bar` is not allowed to contain slashes
",
)
diff --git a/src/tools/cargo/tests/testsuite/features_namespaced.rs b/src/tools/cargo/tests/testsuite/features_namespaced.rs
index f24186c15..b79be55e8 100644
--- a/src/tools/cargo/tests/testsuite/features_namespaced.rs
+++ b/src/tools/cargo/tests/testsuite/features_namespaced.rs
@@ -439,6 +439,10 @@ fn crate_syntax_bad_name() {
[ERROR] failed to parse manifest at [..]/foo/Cargo.toml`
Caused by:
+ TOML parse error at line 10, column 17
+ |
+ 10 | \"dep:bar\" = []
+ | ^^^^^^^^^
feature named `dep:bar` is not allowed to start with `dep:`
",
)
diff --git a/src/tools/cargo/tests/testsuite/fetch.rs b/src/tools/cargo/tests/testsuite/fetch.rs
index f90131a59..9be5f79d0 100644
--- a/src/tools/cargo/tests/testsuite/fetch.rs
+++ b/src/tools/cargo/tests/testsuite/fetch.rs
@@ -11,7 +11,7 @@ fn no_deps() {
.file("src/a.rs", "")
.build();
- p.cargo("fetch").with_stdout("").run();
+ p.cargo("fetch").with_stderr("").run();
}
#[cargo_test]
diff --git a/src/tools/cargo/tests/testsuite/fix.rs b/src/tools/cargo/tests/testsuite/fix.rs
index 33de721cd..7cb5bd65e 100644
--- a/src/tools/cargo/tests/testsuite/fix.rs
+++ b/src/tools/cargo/tests/testsuite/fix.rs
@@ -5,8 +5,8 @@ use cargo_test_support::compare::assert_match_exact;
use cargo_test_support::git::{self, init};
use cargo_test_support::paths::{self, CargoPathExt};
use cargo_test_support::registry::{Dependency, Package};
+use cargo_test_support::tools;
use cargo_test_support::{basic_manifest, is_nightly, project, Project};
-use cargo_test_support::{tools, wrapped_clippy_driver};
#[cargo_test]
fn do_not_fix_broken_builds() {
@@ -29,7 +29,7 @@ fn do_not_fix_broken_builds() {
p.cargo("fix --allow-no-vcs")
.env("__CARGO_FIX_YOLO", "1")
.with_status(101)
- .with_stderr_contains("[ERROR] could not compile `foo` (lib) due to previous error")
+ .with_stderr_contains("[ERROR] could not compile `foo` (lib) due to 1 previous error")
.run();
assert!(p.read_file("src/lib.rs").contains("let mut x = 3;"));
}
@@ -110,6 +110,7 @@ fn rustc_shim_for_cargo_fix() -> Project {
}
let status = Command::new("rustc")
.args(env::args().skip(1))
+ .env_remove("CARGO_MAKEFLAGS")
.status()
.expect("failed to run rustc");
process::exit(status.code().unwrap_or(2));
@@ -193,7 +194,7 @@ fn broken_clippy_fixes_backed_out() {
.env("__CARGO_FIX_YOLO", "1")
.env("RUSTC", p.root().join("foo/target/debug/foo"))
// We can't use `clippy` so we use a `rustc` workspace wrapper instead
- .env("RUSTC_WORKSPACE_WRAPPER", wrapped_clippy_driver())
+ .env("RUSTC_WORKSPACE_WRAPPER", tools::wrapped_clippy_driver())
.with_stderr_contains(
"warning: failed to automatically apply fixes suggested by rustc \
to crate `bar`\n\
@@ -1857,9 +1858,22 @@ fn non_edition_lint_migration() {
assert!(contents.contains("from_utf8(crate::foo::FOO)"));
}
-// For rust-lang/cargo#9857
#[cargo_test]
fn fix_in_dependency() {
+ // Tests what happens if rustc emits a suggestion to modify a file from a
+ // dependency in cargo's home directory. This should never happen, and
+ // indicates a bug in rustc. However, there are several known bugs in
+ // rustc where it does this (often involving macros), so `cargo fix` has a
+ // guard that says if the suggestion points to some location in CARGO_HOME
+ // to not apply it.
+ //
+ // See https://github.com/rust-lang/cargo/issues/9857 for some other
+ // examples.
+ //
+ // This test uses a simulated rustc which replays a suggestion via a JSON
+ // message that points into CARGO_HOME. This does not use the real rustc
+ // because as the bugs are fixed in the real rustc, that would cause this
+ // test to stop working.
Package::new("bar", "1.0.0")
.file(
"src/lib.rs",
@@ -1895,8 +1909,146 @@ fn fix_in_dependency() {
"#,
)
.build();
+ p.cargo("fetch").run();
+
+ // The path in CARGO_HOME.
+ let bar_path = std::fs::read_dir(paths::home().join(".cargo/registry/src"))
+ .unwrap()
+ .next()
+ .unwrap()
+ .unwrap()
+ .path();
+ // Since this is a substitution into a Rust string (representing a JSON
+ // string), deal with backslashes like on Windows.
+ let bar_path_str = bar_path.to_str().unwrap().replace("\\", "/");
+
+ // This is a fake rustc that will emit a JSON message when the `foo` crate
+ // builds that tells cargo to modify a file it shouldn't.
+ let rustc = project()
+ .at("rustc-replay")
+ .file("Cargo.toml", &basic_manifest("rustc-replay", "1.0.0"))
+ .file("src/main.rs",
+ &r##"
+ fn main() {
+ let pkg_name = match std::env::var("CARGO_PKG_NAME") {
+ Ok(pkg_name) => pkg_name,
+ Err(_) => {
+ let r = std::process::Command::new("rustc")
+ .args(std::env::args_os().skip(1))
+ .status();
+ std::process::exit(r.unwrap().code().unwrap_or(2));
+ }
+ };
+ if pkg_name == "foo" {
+ eprintln!("{}", r#"{
+ "$message_type": "diagnostic",
+ "message": "unused variable: `abc`",
+ "code":
+ {
+ "code": "unused_variables",
+ "explanation": null
+ },
+ "level": "warning",
+ "spans":
+ [
+ {
+ "file_name": "__BAR_PATH__/bar-1.0.0/src/lib.rs",
+ "byte_start": 127,
+ "byte_end": 129,
+ "line_start": 5,
+ "line_end": 5,
+ "column_start": 29,
+ "column_end": 31,
+ "is_primary": true,
+ "text":
+ [
+ {
+ "text": " let $i = 1;",
+ "highlight_start": 29,
+ "highlight_end": 31
+ }
+ ],
+ "label": null,
+ "suggested_replacement": null,
+ "suggestion_applicability": null,
+ "expansion": null
+ }
+ ],
+ "children":
+ [
+ {
+ "message": "`#[warn(unused_variables)]` on by default",
+ "code": null,
+ "level": "note",
+ "spans":
+ [],
+ "children":
+ [],
+ "rendered": null
+ },
+ {
+ "message": "if this is intentional, prefix it with an underscore",
+ "code": null,
+ "level": "help",
+ "spans":
+ [
+ {
+ "file_name": "__BAR_PATH__/bar-1.0.0/src/lib.rs",
+ "byte_start": 127,
+ "byte_end": 129,
+ "line_start": 5,
+ "line_end": 5,
+ "column_start": 29,
+ "column_end": 31,
+ "is_primary": true,
+ "text":
+ [
+ {
+ "text": " let $i = 1;",
+ "highlight_start": 29,
+ "highlight_end": 31
+ }
+ ],
+ "label": null,
+ "suggested_replacement": "_abc",
+ "suggestion_applicability": "MachineApplicable",
+ "expansion": null
+ }
+ ],
+ "children":
+ [],
+ "rendered": null
+ }
+ ],
+ "rendered": "warning: unused variable: `abc`\n --> __BAR_PATH__/bar-1.0.0/src/lib.rs:5:29\n |\n5 | let $i = 1;\n | ^^ help: if this is intentional, prefix it with an underscore: `_abc`\n |\n = note: `#[warn(unused_variables)]` on by default\n\n"
+ }"#.replace("\n", ""));
+ }
+ }
+ "##.replace("__BAR_PATH__", &bar_path_str))
+ .build();
+ rustc.cargo("build").run();
+ let rustc_bin = rustc.bin("rustc-replay");
- p.cargo("fix --allow-no-vcs")
- .with_stderr_does_not_contain("[FIXED] [..]")
+ // The output here should not say `Fixed`.
+ //
+ // It is OK to compare the full diagnostic output here because the text is
+ // hard-coded in rustc-replay. Normally tests should not be checking the
+ // compiler output.
+ p.cargo("fix --lib --allow-no-vcs")
+ .env("RUSTC", &rustc_bin)
+ .with_stderr("\
+[CHECKING] bar v1.0.0
+[CHECKING] foo v0.1.0 [..]
+warning: unused variable: `abc`
+ --> [ROOT]/home/.cargo/registry/src/[..]/bar-1.0.0/src/lib.rs:5:29
+ |
+5 | let $i = 1;
+ | ^^ help: if this is intentional, prefix it with an underscore: `_abc`
+ |
+ = note: `#[warn(unused_variables)]` on by default
+
+warning: `foo` (lib) generated 1 warning (run `cargo fix --lib -p foo` to apply 1 suggestion)
+[FINISHED] [..]
+")
.run();
}
diff --git a/src/tools/cargo/tests/testsuite/freshness.rs b/src/tools/cargo/tests/testsuite/freshness.rs
index d450cbbd9..2d9b3df68 100644
--- a/src/tools/cargo/tests/testsuite/freshness.rs
+++ b/src/tools/cargo/tests/testsuite/freshness.rs
@@ -34,7 +34,7 @@ fn modifying_and_moving() {
)
.run();
- p.cargo("build").with_stdout("").run();
+ p.cargo("build").with_stderr("[FINISHED] [..]").run();
p.root().move_into_the_past();
p.root().join("target").move_into_the_past();
@@ -223,7 +223,7 @@ fn changing_lib_features_caches_targets() {
.with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]")
.run();
- p.cargo("build").with_stdout("").run();
+ p.cargo("build").with_stderr("[FINISHED] [..]").run();
p.cargo("build --features foo")
.with_stderr("[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]")
@@ -666,7 +666,7 @@ fn rerun_if_changed_in_dep() {
.build();
p.cargo("build").run();
- p.cargo("build").with_stdout("").run();
+ p.cargo("build").with_stderr("[FINISHED] [..]").run();
}
#[cargo_test]
@@ -1522,7 +1522,7 @@ fn bust_patched_dep() {
sleep_ms(1000);
}
- p.change_file("reg1new/src/lib.rs", "");
+ p.change_file("reg1new/src/lib.rs", "// modified");
if is_coarse_mtime() {
sleep_ms(1000);
}
diff --git a/src/tools/cargo/tests/testsuite/generate_lockfile.rs b/src/tools/cargo/tests/testsuite/generate_lockfile.rs
index d2b633605..ed282fc19 100644
--- a/src/tools/cargo/tests/testsuite/generate_lockfile.rs
+++ b/src/tools/cargo/tests/testsuite/generate_lockfile.rs
@@ -161,13 +161,13 @@ fn cargo_update_generate_lockfile() {
let lockfile = p.root().join("Cargo.lock");
assert!(!lockfile.is_file());
- p.cargo("update").with_stdout("").run();
+ p.cargo("update").with_stderr("").run();
assert!(lockfile.is_file());
fs::remove_file(p.root().join("Cargo.lock")).unwrap();
assert!(!lockfile.is_file());
- p.cargo("update").with_stdout("").run();
+ p.cargo("update").with_stderr("").run();
assert!(lockfile.is_file());
}
diff --git a/src/tools/cargo/tests/testsuite/git.rs b/src/tools/cargo/tests/testsuite/git.rs
index e27315346..d9289acc6 100644
--- a/src/tools/cargo/tests/testsuite/git.rs
+++ b/src/tools/cargo/tests/testsuite/git.rs
@@ -591,12 +591,12 @@ fn recompilation() {
.run();
// Don't recompile the second time
- p.cargo("check").with_stdout("").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
// Modify a file manually, shouldn't trigger a recompile
git_project.change_file("src/bar.rs", r#"pub fn bar() { println!("hello!"); }"#);
- p.cargo("check").with_stdout("").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
p.cargo("update")
.with_stderr(&format!(
@@ -605,7 +605,7 @@ fn recompilation() {
))
.run();
- p.cargo("check").with_stdout("").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
// Commit the changes and make sure we don't trigger a recompile because the
// lock file says not to change
@@ -614,7 +614,7 @@ fn recompilation() {
git::commit(&repo);
println!("compile after commit");
- p.cargo("check").with_stdout("").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
p.root().move_into_the_past();
// Update the dependency and carry on!
@@ -638,7 +638,7 @@ fn recompilation() {
.run();
// Make sure clean only cleans one dep
- p.cargo("clean -p foo").with_stdout("").run();
+ p.cargo("clean -p foo").with_stderr("[REMOVED] [..]").run();
p.cargo("check")
.with_stderr(
"[CHECKING] foo v0.5.0 ([CWD])\n\
@@ -742,7 +742,14 @@ fn update_with_shared_deps() {
// By default, not transitive updates
println!("dep1 update");
- p.cargo("update dep1").with_stdout("").run();
+ p.cargo("update dep1")
+ .with_stderr(
+ "\
+[UPDATING] git repository [..]
+[UPDATING] bar v0.5.0 [..]
+",
+ )
+ .run();
// Don't do anything bad on a weird --precise argument
println!("bar bad precise update");
@@ -766,7 +773,7 @@ Caused by:
println!("bar precise update");
p.cargo("update bar --precise")
.arg(&old_head.to_string())
- .with_stdout("")
+ .with_stderr("[UPDATING] bar v0.5.0 [..]")
.run();
// Updating recursively should, however, update the repo.
@@ -1496,12 +1503,12 @@ fn git_build_cmd_freshness() {
// Smoke test to make sure it doesn't compile again
println!("first pass");
- foo.cargo("check").with_stdout("").run();
+ foo.cargo("check").with_stderr("[FINISHED] [..]").run();
// Modify an ignored file and make sure we don't rebuild
println!("second pass");
foo.change_file("src/bar.rs", "");
- foo.cargo("check").with_stdout("").run();
+ foo.cargo("check").with_stderr("[FINISHED] [..]").run();
}
#[cargo_test]
@@ -1636,7 +1643,7 @@ fn git_repo_changing_no_rebuild() {
// And now for the real test! Make sure that p1 doesn't get rebuilt
// even though the git repo has changed.
- p1.cargo("check").with_stdout("").run();
+ p1.cargo("check").with_stderr("[FINISHED] [..]").run();
}
#[cargo_test]
@@ -1741,7 +1748,7 @@ fn fetch_downloads() {
))
.run();
- p.cargo("fetch").with_stdout("").run();
+ p.cargo("fetch").with_stderr("").run();
}
#[cargo_test]
@@ -1786,7 +1793,7 @@ fn fetch_downloads_with_git2_first_then_with_gitoxide_and_vice_versa() {
.run();
Package::new("bar", "1.0.0").publish(); // trigger a crates-index change.
- p.cargo("fetch").with_stdout("").run();
+ p.cargo("fetch").with_stderr("").run();
}
#[cargo_test]
diff --git a/src/tools/cargo/tests/testsuite/git_auth.rs b/src/tools/cargo/tests/testsuite/git_auth.rs
index b6e68fa3d..c79ae7ce0 100644
--- a/src/tools/cargo/tests/testsuite/git_auth.rs
+++ b/src/tools/cargo/tests/testsuite/git_auth.rs
@@ -105,11 +105,6 @@ fn setup_failed_auth_test() -> (SocketAddr, JoinHandle<()>, Arc<AtomicUsize>) {
// Tests that HTTP auth is offered from `credential.helper`.
#[cargo_test]
fn http_auth_offered() {
- // TODO(Seb): remove this once possible.
- if cargo_uses_gitoxide() {
- // Without the fixes in https://github.com/Byron/gitoxide/releases/tag/gix-v0.41.0 this test is flaky.
- return;
- }
let (addr, t, connections) = setup_failed_auth_test();
let p = project()
.file(
@@ -372,11 +367,6 @@ Caused by:
#[cargo_test]
fn instead_of_url_printed() {
- // TODO(Seb): remove this once possible.
- if cargo_uses_gitoxide() {
- // Without the fixes in https://github.com/Byron/gitoxide/releases/tag/gix-v0.41.0 this test is flaky.
- return;
- }
let (addr, t, _connections) = setup_failed_auth_test();
let config = paths::home().join(".gitconfig");
let mut config = git2::Config::open(&config).unwrap();
diff --git a/src/tools/cargo/tests/testsuite/global_cache_tracker.rs b/src/tools/cargo/tests/testsuite/global_cache_tracker.rs
new file mode 100644
index 000000000..68a606902
--- /dev/null
+++ b/src/tools/cargo/tests/testsuite/global_cache_tracker.rs
@@ -0,0 +1,1862 @@
+//! Tests for last-use tracking and auto-gc.
+//!
+//! Cargo supports an environment variable called `__CARGO_TEST_LAST_USE_NOW`
+//! to have cargo pretend that the current time is the given time (in seconds
+//! since the unix epoch). This is used throughout these tests to simulate
+//! what happens when time passes. The [`days_ago_unix`] and
+//! [`months_ago_unix`] functions help with setting this value.
+
+use super::config::ConfigBuilder;
+use cargo::core::global_cache_tracker::{self, DeferredGlobalLastUse, GlobalCacheTracker};
+use cargo::util::cache_lock::CacheLockMode;
+use cargo::util::interning::InternedString;
+use cargo::Config;
+use cargo_test_support::paths::{self, CargoPathExt};
+use cargo_test_support::registry::{Package, RegistryBuilder};
+use cargo_test_support::{
+ basic_manifest, cargo_process, execs, git, project, retry, sleep_ms, thread_wait_timeout,
+ Project,
+};
+use itertools::Itertools;
+use std::fmt::Write;
+use std::path::PathBuf;
+use std::process::Stdio;
+use std::time::{Duration, SystemTime};
+
+/// Helper to create a simple `foo` project which depends on a registry
+/// dependency called `bar`.
+fn basic_foo_bar_project() -> Project {
+ Package::new("bar", "1.0.0").publish();
+ project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = "1.0"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build()
+}
+
+/// Helper to get the names of files in a directory as strings.
+fn get_names(glob: &str) -> Vec<String> {
+ let mut names: Vec<_> = glob::glob(paths::home().join(glob).to_str().unwrap())
+ .unwrap()
+ .map(|p| p.unwrap().file_name().unwrap().to_str().unwrap().to_owned())
+ .collect();
+ names.sort();
+ names
+}
+
+fn get_registry_names(which: &str) -> Vec<String> {
+ get_names(&format!(".cargo/registry/{which}/*/*"))
+}
+
+fn get_index_names() -> Vec<String> {
+ get_names(&format!(".cargo/registry/index/*"))
+}
+
+fn get_git_db_names() -> Vec<String> {
+ get_names(&format!(".cargo/git/db/*"))
+}
+
+fn get_git_checkout_names(db_name: &str) -> Vec<String> {
+ get_names(&format!(".cargo/git/checkouts/{db_name}/*"))
+}
+
+fn days_ago(n: u64) -> SystemTime {
+ SystemTime::now() - Duration::from_secs(60 * 60 * 24 * n)
+}
+
+/// Helper for simulating running cargo in the past. Use with the
+/// __CARGO_TEST_LAST_USE_NOW environment variable.
+fn days_ago_unix(n: u64) -> String {
+ days_ago(n)
+ .duration_since(SystemTime::UNIX_EPOCH)
+ .unwrap()
+ .as_secs()
+ .to_string()
+}
+
+/// Helper for simulating running cargo in the past. Use with the
+/// __CARGO_TEST_LAST_USE_NOW environment variable.
+fn months_ago_unix(n: u64) -> String {
+ days_ago_unix(n * 30)
+}
+
+/// Populates last-use database and the cache files.
+///
+/// This makes it easier to more accurately specify exact sizes. Creating
+/// specific sizes with `Package` is too difficult.
+fn populate_cache(config: &Config, test_crates: &[(&str, u64, u64, u64)]) -> (PathBuf, PathBuf) {
+ let cache_dir = paths::home().join(".cargo/registry/cache/example.com-a6c4a5adcb232b9a");
+ let src_dir = paths::home().join(".cargo/registry/src/example.com-a6c4a5adcb232b9a");
+
+ GlobalCacheTracker::db_path(&config)
+ .into_path_unlocked()
+ .rm_rf();
+
+ let _lock = config
+ .acquire_package_cache_lock(CacheLockMode::MutateExclusive)
+ .unwrap();
+ let mut tracker = GlobalCacheTracker::new(&config).unwrap();
+ let mut deferred = DeferredGlobalLastUse::new();
+
+ cache_dir.rm_rf();
+ cache_dir.mkdir_p();
+ src_dir.rm_rf();
+ src_dir.mkdir_p();
+ paths::home()
+ .join(".cargo/registry/index/example.com-a6c4a5adcb232b9a")
+ .mkdir_p();
+ let mut create = |name: &str, age, crate_size: u64, src_size: u64| {
+ let crate_filename = InternedString::new(&format!("{name}.crate"));
+ deferred.mark_registry_crate_used_stamp(
+ global_cache_tracker::RegistryCrate {
+ encoded_registry_name: "example.com-a6c4a5adcb232b9a".into(),
+ crate_filename,
+ size: crate_size,
+ },
+ Some(&days_ago(age)),
+ );
+ deferred.mark_registry_src_used_stamp(
+ global_cache_tracker::RegistrySrc {
+ encoded_registry_name: "example.com-a6c4a5adcb232b9a".into(),
+ package_dir: name.into(),
+ size: Some(src_size),
+ },
+ Some(&days_ago(age)),
+ );
+ std::fs::write(
+ cache_dir.join(crate_filename),
+ "x".repeat(crate_size as usize),
+ )
+ .unwrap();
+ let path = src_dir.join(name);
+ path.mkdir_p();
+ std::fs::write(path.join("data"), "x".repeat(src_size as usize)).unwrap()
+ };
+
+ for (name, age, crate_size, src_size) in test_crates {
+ create(name, *age, *crate_size, *src_size);
+ }
+ deferred.save(&mut tracker).unwrap();
+
+ (cache_dir, src_dir)
+}
+
+#[cargo_test]
+fn auto_gc_gated() {
+ // Requires -Zgc to both track last-use data and to run auto-gc.
+ let p = basic_foo_bar_project();
+ p.cargo("check")
+ .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4))
+ .run();
+ // Check that it did not create a database or delete anything.
+ let config = ConfigBuilder::new().build();
+ assert!(!GlobalCacheTracker::db_path(&config)
+ .into_path_unlocked()
+ .exists());
+ assert_eq!(get_index_names().len(), 1);
+
+ // Again in the future, shouldn't auto-gc.
+ p.cargo("check").run();
+ assert!(!GlobalCacheTracker::db_path(&config)
+ .into_path_unlocked()
+ .exists());
+ assert_eq!(get_index_names().len(), 1);
+}
+
+#[cargo_test]
+fn clean_gc_gated() {
+ cargo_process("clean gc")
+ .with_status(101)
+ .with_stderr(
+ "\
+error: the `cargo clean gc` command is unstable, and only available on the \
+nightly channel of Cargo, but this is the `stable` channel
+See [..]
+See [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn implies_source() {
+ // Checks that when a src, crate, or checkout is marked as used, the
+ // corresponding index or git db also gets marked as used.
+ let config = ConfigBuilder::new().unstable_flag("gc").build();
+ let _lock = config
+ .acquire_package_cache_lock(CacheLockMode::MutateExclusive)
+ .unwrap();
+ let mut deferred = DeferredGlobalLastUse::new();
+ let mut tracker = GlobalCacheTracker::new(&config).unwrap();
+
+ deferred.mark_registry_crate_used(global_cache_tracker::RegistryCrate {
+ encoded_registry_name: "example.com-a6c4a5adcb232b9a".into(),
+ crate_filename: "regex-1.8.4.crate".into(),
+ size: 123,
+ });
+ deferred.mark_registry_src_used(global_cache_tracker::RegistrySrc {
+ encoded_registry_name: "index.crates.io-6f17d22bba15001f".into(),
+ package_dir: "rand-0.8.5".into(),
+ size: None,
+ });
+ deferred.mark_git_checkout_used(global_cache_tracker::GitCheckout {
+ encoded_git_name: "cargo-e7ff1db891893a9e".into(),
+ short_name: "f0a4ee0".into(),
+ size: None,
+ });
+ deferred.save(&mut tracker).unwrap();
+
+ let mut indexes = tracker.registry_index_all().unwrap();
+ assert_eq!(indexes.len(), 2);
+ indexes.sort_by(|a, b| a.0.encoded_registry_name.cmp(&b.0.encoded_registry_name));
+ assert_eq!(
+ indexes[0].0.encoded_registry_name,
+ "example.com-a6c4a5adcb232b9a"
+ );
+ assert_eq!(
+ indexes[1].0.encoded_registry_name,
+ "index.crates.io-6f17d22bba15001f"
+ );
+
+ let dbs = tracker.git_db_all().unwrap();
+ assert_eq!(dbs.len(), 1);
+ assert_eq!(dbs[0].0.encoded_git_name, "cargo-e7ff1db891893a9e");
+}
+
+#[cargo_test]
+fn auto_gc_defaults() {
+ // Checks that the auto-gc deletes old entries, and leaves new ones intact.
+ Package::new("old", "1.0.0").publish();
+ Package::new("new", "1.0.0").publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ old = "1.0"
+ new = "1.0"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+ // Populate the last-use data.
+ p.cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4))
+ .run();
+ assert_eq!(get_registry_names("src"), ["new-1.0.0", "old-1.0.0"]);
+ assert_eq!(
+ get_registry_names("cache"),
+ ["new-1.0.0.crate", "old-1.0.0.crate"]
+ );
+
+ // Run again with just one package. Make sure the old src gets deleted,
+ // but .crate does not.
+ p.change_file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ new = "1.0"
+ "#,
+ );
+ p.cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(2))
+ .run();
+ assert_eq!(get_registry_names("src"), ["new-1.0.0"]);
+ assert_eq!(
+ get_registry_names("cache"),
+ ["new-1.0.0.crate", "old-1.0.0.crate"]
+ );
+
+ // Run again after the .crate should have aged out.
+ p.cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .run();
+ assert_eq!(get_registry_names("src"), ["new-1.0.0"]);
+ assert_eq!(get_registry_names("cache"), ["new-1.0.0.crate"]);
+}
+
+#[cargo_test]
+fn auto_gc_config() {
+ // Can configure auto gc settings.
+ Package::new("old", "1.0.0").publish();
+ Package::new("new", "1.0.0").publish();
+ let p = project()
+ .file(
+ ".cargo/config.toml",
+ r#"
+ [gc.auto]
+ frequency = "always"
+ max-src-age = "1 day"
+ max-crate-age = "3 days"
+ max-index-age = "3 days"
+ max-git-co-age = "1 day"
+ max-git-db-age = "3 days"
+ "#,
+ )
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ old = "1.0"
+ new = "1.0"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+ // Populate the last-use data.
+ p.cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(4))
+ .run();
+ assert_eq!(get_registry_names("src"), ["new-1.0.0", "old-1.0.0"]);
+ assert_eq!(
+ get_registry_names("cache"),
+ ["new-1.0.0.crate", "old-1.0.0.crate"]
+ );
+
+ // Run again with just one package. Make sure the old src gets deleted,
+ // but .crate does not.
+ p.change_file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ new = "1.0"
+ "#,
+ );
+ p.cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(2))
+ .run();
+ assert_eq!(get_registry_names("src"), ["new-1.0.0"]);
+ assert_eq!(
+ get_registry_names("cache"),
+ ["new-1.0.0.crate", "old-1.0.0.crate"]
+ );
+
+ // Run again after the .crate should have aged out.
+ p.cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .run();
+ assert_eq!(get_registry_names("src"), ["new-1.0.0"]);
+ assert_eq!(get_registry_names("cache"), ["new-1.0.0.crate"]);
+}
+
+#[cargo_test]
+fn frequency() {
+ // gc.auto.frequency settings
+ let p = basic_foo_bar_project();
+ p.change_file(
+ ".cargo/config.toml",
+ r#"
+ [gc.auto]
+ frequency = "never"
+ "#,
+ );
+ // Populate data in the past.
+ p.cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4))
+ .run();
+ assert_eq!(get_index_names().len(), 1);
+ assert_eq!(get_registry_names("src"), ["bar-1.0.0"]);
+ assert_eq!(get_registry_names("cache"), ["bar-1.0.0.crate"]);
+
+ p.change_file("Cargo.toml", &basic_manifest("foo", "0.2.0"));
+
+ // Try after the default expiration time, with "never" it shouldn't gc.
+ p.cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .run();
+ assert_eq!(get_index_names().len(), 1);
+ assert_eq!(get_registry_names("src"), ["bar-1.0.0"]);
+ assert_eq!(get_registry_names("cache"), ["bar-1.0.0.crate"]);
+
+ // Try again with a setting that allows it to run.
+ p.cargo("check -Zgc")
+ .env("CARGO_GC_AUTO_FREQUENCY", "1 day")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .run();
+ assert_eq!(get_index_names().len(), 0);
+ assert_eq!(get_registry_names("src").len(), 0);
+ assert_eq!(get_registry_names("cache").len(), 0);
+}
+
+#[cargo_test]
+fn auto_gc_index() {
+ // Deletes the index if it hasn't been used in a while.
+ let p = basic_foo_bar_project();
+ p.cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4))
+ .run();
+ assert_eq!(get_index_names().len(), 1);
+
+ // Make sure it stays within the time frame.
+ p.change_file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+ "#,
+ );
+ p.cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(2))
+ .run();
+ assert_eq!(get_index_names().len(), 1);
+
+ // After it expires, it should be deleted.
+ p.cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .run();
+ assert_eq!(get_index_names().len(), 0);
+}
+
+#[cargo_test]
+fn auto_gc_git() {
+ // auto-gc should delete git checkouts and dbs.
+
+ // Returns the short git name of a a checkout.
+ let short_id = |repo: &git2::Repository| -> String {
+ let head = repo.revparse_single("HEAD").unwrap();
+ let short_id = head.short_id().unwrap();
+ short_id.as_str().unwrap().to_owned()
+ };
+
+ // Set up a git dependency and fetch it and populate the database,
+ // 6 months in the past.
+ let (git_project, git_repo) = git::new_repo("bar", |p| {
+ p.file("Cargo.toml", &basic_manifest("bar", "1.0.0"))
+ .file("src/lib.rs", "")
+ });
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = {{ git = '{}' }}
+ "#,
+ git_project.url()
+ ),
+ )
+ .file("src/lib.rs", "")
+ .build();
+ p.cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(6))
+ .run();
+ let db_names = get_git_db_names();
+ assert_eq!(db_names.len(), 1);
+ let first_short_oid = short_id(&git_repo);
+ assert_eq!(
+ get_git_checkout_names(&db_names[0]),
+ [first_short_oid.clone()]
+ );
+
+ // Use a new git checkout, should keep both.
+ git_project.change_file("src/lib.rs", "// modified");
+ git::add(&git_repo);
+ git::commit(&git_repo);
+ p.cargo("update -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(6))
+ .run();
+ assert_eq!(get_git_db_names().len(), 1);
+ let second_short_oid = short_id(&git_repo);
+ let mut both = vec![first_short_oid, second_short_oid.clone()];
+ both.sort();
+ assert_eq!(get_git_checkout_names(&db_names[0]), both);
+
+ // In the future, using the second checkout should delete the first.
+ p.cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4))
+ .run();
+ assert_eq!(get_git_db_names().len(), 1);
+ assert_eq!(
+ get_git_checkout_names(&db_names[0]),
+ [second_short_oid.clone()]
+ );
+
+ // After three months, the db should get deleted.
+ p.change_file("Cargo.toml", &basic_manifest("foo", "0.2.0"));
+ p.cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .run();
+ assert_eq!(get_git_db_names().len(), 0);
+ assert_eq!(get_git_checkout_names(&db_names[0]).len(), 0);
+}
+
+#[cargo_test]
+fn auto_gc_various_commands() {
+ // Checks that auto gc works with a variety of commands.
+ //
+ // Auto-gc is only run on a subset of commands. Generally it is run on
+ // commands that are already doing a lot of work, or heavily involve the
+ // use of the registry.
+ Package::new("bar", "1.0.0").publish();
+ let cmds = ["check", "fetch"];
+ for cmd in cmds {
+ eprintln!("checking command {cmd}");
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = "1.0"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+ // Populate the last-use data.
+ p.cargo(cmd)
+ .arg("-Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4))
+ .run();
+ let config = ConfigBuilder::new().unstable_flag("gc").build();
+ let lock = config
+ .acquire_package_cache_lock(CacheLockMode::MutateExclusive)
+ .unwrap();
+ let tracker = GlobalCacheTracker::new(&config).unwrap();
+ let indexes = tracker.registry_index_all().unwrap();
+ assert_eq!(indexes.len(), 1);
+ let crates = tracker.registry_crate_all().unwrap();
+ assert_eq!(crates.len(), 1);
+ let srcs = tracker.registry_src_all().unwrap();
+ assert_eq!(srcs.len(), 1);
+ drop(lock);
+
+ // After everything is aged out, it should all be deleted.
+ p.change_file("Cargo.toml", &basic_manifest("foo", "0.2.0"));
+ p.cargo(cmd)
+ .arg("-Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .run();
+ let lock = config
+ .acquire_package_cache_lock(CacheLockMode::MutateExclusive)
+ .unwrap();
+ let indexes = tracker.registry_index_all().unwrap();
+ assert_eq!(indexes.len(), 0);
+ let crates = tracker.registry_crate_all().unwrap();
+ assert_eq!(crates.len(), 0);
+ let srcs = tracker.registry_src_all().unwrap();
+ assert_eq!(srcs.len(), 0);
+ drop(tracker);
+ drop(lock);
+ paths::home().join(".cargo/registry").rm_rf();
+ GlobalCacheTracker::db_path(&config)
+ .into_path_unlocked()
+ .rm_rf();
+ }
+}
+
+#[cargo_test]
+fn updates_last_use_various_commands() {
+ // Checks that last-use tracking is updated by various commands.
+ //
+ // Not *all* commands update the index tracking, even though they
+ // technically involve reading the index. There isn't a convenient place
+ // to ensure it gets saved while avoiding saving too often in other
+ // commands. For the most part, this should be fine, since these commands
+ // usually aren't run without running one of the commands that does save
+ // the tracking. Some of the commands are:
+ //
+ // - login, owner, yank, search
+ // - report future-incompatibilities
+ // - package --no-verify
+ // - fetch --locked
+ Package::new("bar", "1.0.0").publish();
+ let cmds = [
+ // name, expected_crates (0=doesn't download)
+ ("check", 1),
+ ("fetch", 1),
+ ("tree", 1),
+ ("generate-lockfile", 0),
+ ("update", 0),
+ ("metadata", 1),
+ ("vendor --respect-source-config", 1),
+ ];
+ for (cmd, expected_crates) in cmds {
+ eprintln!("checking command {cmd}");
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = "1.0"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+ // Populate the last-use data.
+ p.cargo(cmd)
+ .arg("-Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .run();
+ let config = ConfigBuilder::new().unstable_flag("gc").build();
+ let lock = config
+ .acquire_package_cache_lock(CacheLockMode::MutateExclusive)
+ .unwrap();
+ let tracker = GlobalCacheTracker::new(&config).unwrap();
+ let indexes = tracker.registry_index_all().unwrap();
+ assert_eq!(indexes.len(), 1);
+ let crates = tracker.registry_crate_all().unwrap();
+ assert_eq!(crates.len(), expected_crates);
+ let srcs = tracker.registry_src_all().unwrap();
+ assert_eq!(srcs.len(), expected_crates);
+ drop(tracker);
+ drop(lock);
+ paths::home().join(".cargo/registry").rm_rf();
+ GlobalCacheTracker::db_path(&config)
+ .into_path_unlocked()
+ .rm_rf();
+ }
+}
+
+#[cargo_test]
+fn both_git_and_http_index_cleans() {
+ // Checks that either the git or http index cache gets cleaned.
+ let _crates_io = RegistryBuilder::new().build();
+ let _alternative = RegistryBuilder::new().alternative().http_index().build();
+ Package::new("from_git", "1.0.0").publish();
+ Package::new("from_http", "1.0.0")
+ .alternative(true)
+ .publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ from_git = "1.0"
+ from_http = { version = "1.0", registry = "alternative" }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ p.cargo("update -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4))
+ .run();
+ let config = ConfigBuilder::new().unstable_flag("gc").build();
+ let lock = config
+ .acquire_package_cache_lock(CacheLockMode::MutateExclusive)
+ .unwrap();
+ let tracker = GlobalCacheTracker::new(&config).unwrap();
+ let indexes = tracker.registry_index_all().unwrap();
+ assert_eq!(indexes.len(), 2);
+ assert_eq!(get_index_names().len(), 2);
+ drop(lock);
+
+ // Running in the future without these indexes should delete them.
+ p.change_file("Cargo.toml", &basic_manifest("foo", "0.2.0"));
+ p.cargo("clean gc -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .run();
+ let lock = config
+ .acquire_package_cache_lock(CacheLockMode::MutateExclusive)
+ .unwrap();
+ let indexes = tracker.registry_index_all().unwrap();
+ assert_eq!(indexes.len(), 0);
+ assert_eq!(get_index_names().len(), 0);
+ drop(lock);
+}
+
+#[cargo_test]
+fn clean_gc_dry_run() {
+ // Basic `clean --gc --dry-run` test.
+ let p = basic_foo_bar_project();
+ // Populate the last-use data.
+ p.cargo("fetch -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4))
+ .run();
+
+ let registry_root = paths::home().join(".cargo/registry");
+ let glob_registry = |name| -> PathBuf {
+ let mut paths: Vec<_> = glob::glob(registry_root.join(name).join("*").to_str().unwrap())
+ .unwrap()
+ .map(|p| p.unwrap())
+ .collect();
+ assert_eq!(paths.len(), 1);
+ paths.pop().unwrap()
+ };
+ let index = glob_registry("index").ls_r();
+ let src = glob_registry("src").ls_r();
+ let cache = glob_registry("cache").ls_r();
+ let expected_files = index
+ .iter()
+ .chain(src.iter())
+ .chain(cache.iter())
+ .map(|p| p.to_str().unwrap())
+ .join("\n");
+
+ p.cargo("clean gc --dry-run -v -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stdout_unordered(&expected_files)
+ .with_stderr(
+ "[SUMMARY] [..] files, [..] total\n\
+ [WARNING] no files deleted due to --dry-run",
+ )
+ .run();
+
+ // Again, make sure the information is still tracked.
+ p.cargo("clean gc --dry-run -v -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stdout_unordered(&expected_files)
+ .with_stderr(
+ "[SUMMARY] [..] files, [..] total\n\
+ [WARNING] no files deleted due to --dry-run",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn clean_default_gc() {
+ // `clean gc` without options should also gc
+ let p = basic_foo_bar_project();
+ // Populate the last-use data.
+ p.cargo("fetch -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4))
+ .run();
+ p.cargo("clean gc -v -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr_unordered(
+ "\
+[REMOVING] [ROOT]/home/.cargo/registry/index/[..]
+[REMOVING] [ROOT]/home/.cargo/registry/src/[..]
+[REMOVING] [ROOT]/home/.cargo/registry/cache/[..]
+[REMOVED] [..] files, [..] total
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn tracks_sizes() {
+ // Checks that sizes are properly tracked in the db.
+ Package::new("dep1", "1.0.0")
+ .file("src/lib.rs", "")
+ .publish();
+ Package::new("dep2", "1.0.0")
+ .file("src/lib.rs", "")
+ .file("data", &"abcdefghijklmnopqrstuvwxyz".repeat(1000))
+ .publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ dep1 = "1.0"
+ dep2 = "1.0"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+ p.cargo("fetch -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .run();
+
+ // Check that the crate sizes are the same as on disk.
+ let config = ConfigBuilder::new().unstable_flag("gc").build();
+ let _lock = config
+ .acquire_package_cache_lock(CacheLockMode::MutateExclusive)
+ .unwrap();
+ let tracker = GlobalCacheTracker::new(&config).unwrap();
+ let mut crates = tracker.registry_crate_all().unwrap();
+ crates.sort_by(|a, b| a.0.crate_filename.cmp(&b.0.crate_filename));
+ let db_sizes: Vec<_> = crates.iter().map(|c| c.0.size).collect();
+
+ let mut actual: Vec<_> = p
+ .glob(paths::home().join(".cargo/registry/cache/*/*"))
+ .map(|p| p.unwrap())
+ .collect();
+ actual.sort();
+ let actual_sizes: Vec<_> = actual
+ .iter()
+ .map(|path| std::fs::metadata(path).unwrap().len())
+ .collect();
+ assert_eq!(db_sizes, actual_sizes);
+
+ // Also check the src sizes are computed.
+ let mut srcs = tracker.registry_src_all().unwrap();
+ srcs.sort_by(|a, b| a.0.package_dir.cmp(&b.0.package_dir));
+ let db_sizes: Vec<_> = srcs.iter().map(|c| c.0.size.unwrap()).collect();
+ let mut actual: Vec<_> = p
+ .glob(paths::home().join(".cargo/registry/src/*/*"))
+ .map(|p| p.unwrap())
+ .collect();
+ actual.sort();
+ // .cargo-ok is not tracked in the size.
+ actual.iter().for_each(|p| p.join(".cargo-ok").rm_rf());
+ let actual_sizes: Vec<_> = actual
+ .iter()
+ .map(|path| cargo_util::du(path, &[]).unwrap())
+ .collect();
+ assert_eq!(db_sizes, actual_sizes);
+ assert!(db_sizes[1] > 26000);
+}
+
+#[cargo_test]
+fn max_size() {
+ // Checks --max-crate-size and --max-src-size with various cleaning thresholds.
+ let config = ConfigBuilder::new().unstable_flag("gc").build();
+
+ let test_crates = [
+ // name, age, crate_size, src_size
+ ("a-1.0.0", 5, 1, 1),
+ ("b-1.0.0", 6, 2, 2),
+ ("c-1.0.0", 3, 3, 3),
+ ("d-1.0.0", 2, 4, 4),
+ ("e-1.0.0", 2, 5, 5),
+ ("f-1.0.0", 9, 6, 6),
+ ("g-1.0.0", 1, 1, 1),
+ ];
+
+ // Determine the order things get deleted so they can be verified.
+ let mut names_by_timestamp: Vec<_> = test_crates
+ .iter()
+ .map(|(name, age, _, _)| (days_ago_unix(*age), name))
+ .collect();
+ names_by_timestamp.sort();
+ let names_by_timestamp: Vec<_> = names_by_timestamp
+ .into_iter()
+ .map(|(_, name)| name)
+ .collect();
+
+ // This exercises the different boundary conditions.
+ for (clean_size, files, bytes) in [
+ (22, 0, 0),
+ (21, 1, 6),
+ (16, 1, 6),
+ (15, 2, 8),
+ (14, 2, 8),
+ (13, 3, 9),
+ (12, 4, 12),
+ (10, 4, 12),
+ (9, 5, 16),
+ (6, 5, 16),
+ (5, 6, 21),
+ (1, 6, 21),
+ (0, 7, 22),
+ ] {
+ let (removed, kept) = names_by_timestamp.split_at(files);
+ // --max-crate-size
+ let (cache_dir, src_dir) = populate_cache(&config, &test_crates);
+ let mut stderr = String::new();
+ for name in removed {
+ writeln!(stderr, "[REMOVING] [..]{name}.crate").unwrap();
+ }
+ let total_display = if removed.is_empty() {
+ String::new()
+ } else {
+ format!(", {bytes}B total")
+ };
+ let files_display = if files == 1 {
+ format!("1 file")
+ } else {
+ format!("{files} files")
+ };
+ write!(stderr, "[REMOVED] {files_display}{total_display}").unwrap();
+ cargo_process(&format!("clean gc -Zgc -v --max-crate-size={clean_size}"))
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr_unordered(&stderr)
+ .run();
+ for name in kept {
+ assert!(cache_dir.join(format!("{name}.crate")).exists());
+ }
+ for name in removed {
+ assert!(!cache_dir.join(format!("{name}.crate")).exists());
+ }
+
+ // --max-src-size
+ populate_cache(&config, &test_crates);
+ let mut stderr = String::new();
+ for name in removed {
+ writeln!(stderr, "[REMOVING] [..]{name}").unwrap();
+ }
+ let total_display = if files == 0 {
+ String::new()
+ } else {
+ format!(", {bytes}B total")
+ };
+ write!(stderr, "[REMOVED] {files_display}{total_display}").unwrap();
+ cargo_process(&format!("clean gc -Zgc -v --max-src-size={clean_size}"))
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr_unordered(&stderr)
+ .run();
+ for name in kept {
+ assert!(src_dir.join(name).exists());
+ }
+ for name in removed {
+ assert!(!src_dir.join(name).exists());
+ }
+ }
+}
+
+#[cargo_test]
+fn max_size_untracked_crate() {
+ // When a .crate file exists from an older version of cargo that did not
+ // track sizes, `clean --max-crate-size` should populate the db with the
+ // sizes.
+ let config = ConfigBuilder::new().unstable_flag("gc").build();
+ let cache = paths::home().join(".cargo/registry/cache/example.com-a6c4a5adcb232b9a");
+ cache.mkdir_p();
+ paths::home()
+ .join(".cargo/registry/index/example.com-a6c4a5adcb232b9a")
+ .mkdir_p();
+ // Create the `.crate files.
+ let test_crates = [
+ // name, size
+ ("a-1.0.0.crate", 1234),
+ ("b-1.0.0.crate", 42),
+ ("c-1.0.0.crate", 0),
+ ];
+ for (name, size) in test_crates {
+ std::fs::write(cache.join(name), "x".repeat(size as usize)).unwrap()
+ }
+ // This should scan the directory and populate the db with the size information.
+ cargo_process("clean gc -Zgc -v --max-crate-size=100000")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr("[REMOVED] 0 files")
+ .run();
+ // Check that it stored the size data.
+ let _lock = config
+ .acquire_package_cache_lock(CacheLockMode::MutateExclusive)
+ .unwrap();
+ let tracker = GlobalCacheTracker::new(&config).unwrap();
+ let crates = tracker.registry_crate_all().unwrap();
+ let mut actual: Vec<_> = crates
+ .iter()
+ .map(|(rc, _time)| (rc.crate_filename.as_str(), rc.size))
+ .collect();
+ actual.sort();
+ assert_eq!(test_crates, actual.as_slice());
+}
+
+/// Helper to prepare the max-size test.
+fn max_size_untracked_prepare() -> (Config, Project) {
+ // First, publish and download a dependency.
+ let p = basic_foo_bar_project();
+ p.cargo("fetch").run();
+ // Pretend it was an older version that did not track last-use.
+ let config = ConfigBuilder::new().unstable_flag("gc").build();
+ GlobalCacheTracker::db_path(&config)
+ .into_path_unlocked()
+ .rm_rf();
+ (config, p)
+}
+
+/// Helper to verify the max-size test.
+fn max_size_untracked_verify(config: &Config) {
+ let actual: Vec<_> = glob::glob(
+ paths::home()
+ .join(".cargo/registry/src/*/*")
+ .to_str()
+ .unwrap(),
+ )
+ .unwrap()
+ .map(|p| p.unwrap())
+ .collect();
+ assert_eq!(actual.len(), 1);
+ let actual_size = cargo_util::du(&actual[0], &[]).unwrap();
+ let lock = config
+ .acquire_package_cache_lock(CacheLockMode::MutateExclusive)
+ .unwrap();
+ let tracker = GlobalCacheTracker::new(&config).unwrap();
+ let srcs = tracker.registry_src_all().unwrap();
+ assert_eq!(srcs.len(), 1);
+ assert_eq!(srcs[0].0.size, Some(actual_size));
+ drop(lock);
+}
+
+#[cargo_test]
+fn max_size_untracked_src_from_use() {
+ // When a src directory exists from an older version of cargo that did not
+ // track sizes, doing a build should populate the db with an entry with an
+ // unknown size. `clean --max-src-size` should then fix the size.
+ let (config, p) = max_size_untracked_prepare();
+
+ // Run a command that will update the db with an unknown src size.
+ p.cargo("tree -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .run();
+ // Check that it is None.
+ let lock = config
+ .acquire_package_cache_lock(CacheLockMode::MutateExclusive)
+ .unwrap();
+ let tracker = GlobalCacheTracker::new(&config).unwrap();
+ let srcs = tracker.registry_src_all().unwrap();
+ assert_eq!(srcs.len(), 1);
+ assert_eq!(srcs[0].0.size, None);
+ drop(lock);
+
+ // Fix the size.
+ p.cargo("clean gc -v --max-src-size=10000 -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr("[REMOVED] 0 files")
+ .run();
+ max_size_untracked_verify(&config);
+}
+
+#[cargo_test]
+fn max_size_untracked_src_from_clean() {
+ // When a src directory exists from an older version of cargo that did not
+ // track sizes, `clean --max-src-size` should populate the db with the
+ // sizes.
+ let (config, p) = max_size_untracked_prepare();
+
+ // Clean should scan the src and update the db.
+ p.cargo("clean gc -v --max-src-size=10000 -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr("[REMOVED] 0 files")
+ .run();
+ max_size_untracked_verify(&config);
+}
+
+#[cargo_test]
+fn max_download_size() {
+ // --max-download-size
+ //
+ // This creates some sample crates of specific sizes, and then tries
+ // deleting at various specific size thresholds that exercise different
+ // edge conditions.
+ let config = ConfigBuilder::new().unstable_flag("gc").build();
+
+ let test_crates = [
+ // name, age, crate_size, src_size
+ ("d-1.0.0", 4, 4, 5),
+ ("c-1.0.0", 3, 3, 3),
+ ("a-1.0.0", 1, 2, 5),
+ ("b-1.0.0", 1, 1, 7),
+ ];
+
+ for (max_size, num_deleted, files_deleted, bytes) in [
+ (30, 0, 0, 0),
+ (29, 1, 1, 5),
+ (24, 2, 2, 9),
+ (20, 3, 3, 12),
+ (1, 7, 7, 29),
+ (0, 8, 8, 30),
+ ] {
+ populate_cache(&config, &test_crates);
+ // Determine the order things will be deleted.
+ let delete_order: Vec<String> = test_crates
+ .iter()
+ .flat_map(|(name, _, _, _)| [name.to_string(), format!("{name}.crate")])
+ .collect();
+ let (removed, _kept) = delete_order.split_at(num_deleted);
+ let mut stderr = String::new();
+ for name in removed {
+ writeln!(stderr, "[REMOVING] [..]{name}").unwrap();
+ }
+ let files_display = if files_deleted == 1 {
+ format!("1 file")
+ } else {
+ format!("{files_deleted} files")
+ };
+ let total_display = if removed.is_empty() {
+ String::new()
+ } else {
+ format!(", {bytes}B total")
+ };
+ write!(stderr, "[REMOVED] {files_display}{total_display}",).unwrap();
+ cargo_process(&format!("clean gc -Zgc -v --max-download-size={max_size}"))
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr_unordered(&stderr)
+ .run();
+ }
+}
+
+#[cargo_test]
+fn package_cache_lock_during_build() {
+ // Verifies that a shared lock is held during a build. Resolution and
+ // downloads should be OK while that is held, but mutation should block.
+ //
+ // This works by launching a build with a build script that will pause.
+ // Then it performs other cargo commands and verifies their behavior.
+ Package::new("bar", "1.0.0").publish();
+ let p_foo = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = "1.0"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file(
+ "build.rs",
+ r#"
+ fn main() {
+ std::fs::write("blocking", "").unwrap();
+ let path = std::path::Path::new("ready");
+ loop {
+ if path.exists() {
+ break;
+ } else {
+ std::thread::sleep(std::time::Duration::from_millis(100))
+ }
+ }
+ }
+ "#,
+ )
+ .build();
+ let p_foo2 = project()
+ .at("foo2")
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo2"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = "1.0"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+
+ // Start a build that will pause once the build starts.
+ let mut foo_child = p_foo
+ .cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .build_command()
+ .stdout(Stdio::piped())
+ .stderr(Stdio::piped())
+ .spawn()
+ .unwrap();
+
+ // Wait for it to enter build script.
+ retry(100, || p_foo.root().join("blocking").exists().then_some(()));
+
+ // Start a build with a different target directory. It should not block,
+ // even though it gets a download lock, and then a shared lock.
+ //
+ // Also verify that auto-gc gets disabled.
+ p_foo2
+ .cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("CARGO_GC_AUTO_FREQUENCY", "always")
+ .env("CARGO_LOG", "gc=debug")
+ .with_stderr_contains("[UPDATING] `dummy-registry` index")
+ .with_stderr_contains("[CHECKING] bar v1.0.0")
+ .with_stderr_contains("[CHECKING] foo2 v0.1.0 [..]")
+ .with_stderr_contains("[FINISHED] [..]")
+ .with_stderr_contains("[..]unable to acquire mutate lock, auto gc disabled")
+ .run();
+
+ // Ensure that the first build really blocked.
+ assert!(matches!(foo_child.try_wait(), Ok(None)));
+
+ // Cleaning while a command is running should block.
+ let mut clean_cmd = p_foo2
+ .cargo("clean gc --max-download-size=0 -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .build_command();
+ clean_cmd.stderr(Stdio::piped());
+ let mut clean_child = clean_cmd.spawn().unwrap();
+
+ // Give the clean command a chance to finish (it shouldn't).
+ sleep_ms(500);
+ // They should both still be running.
+ assert!(matches!(foo_child.try_wait(), Ok(None)));
+ assert!(matches!(clean_child.try_wait(), Ok(None)));
+
+ // Let the original build finish.
+ p_foo.change_file("ready", "");
+
+ // Wait for clean to finish.
+ let thread = std::thread::spawn(|| clean_child.wait_with_output().unwrap());
+ let output = thread_wait_timeout(100, thread);
+ assert!(output.status.success());
+ // Validate the output of the clean.
+ execs()
+ .with_stderr(
+ "\
+[BLOCKING] waiting for file lock on package cache mutation
+[REMOVED] [..]
+",
+ )
+ .run_output(&output);
+}
+
+#[cargo_test]
+fn read_only_locking_auto_gc() {
+ // Tests the behavior for auto-gc on a read-only directory.
+ let p = basic_foo_bar_project();
+ // Populate cache.
+ p.cargo("fetch -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .run();
+ let cargo_home = paths::home().join(".cargo");
+ let mut perms = std::fs::metadata(&cargo_home).unwrap().permissions();
+ // Test when it can't update auto-gc db.
+ perms.set_readonly(true);
+ std::fs::set_permissions(&cargo_home, perms.clone()).unwrap();
+ p.cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr(
+ "\
+[CHECKING] bar v1.0.0
+[CHECKING] foo v0.1.0 [..]
+[FINISHED] [..]
+",
+ )
+ .run();
+ // Try again without the last-use existing (such as if the cache was
+ // populated by an older version of cargo).
+ perms.set_readonly(false);
+ std::fs::set_permissions(&cargo_home, perms.clone()).unwrap();
+ let config = ConfigBuilder::new().build();
+ GlobalCacheTracker::db_path(&config)
+ .into_path_unlocked()
+ .rm_rf();
+ perms.set_readonly(true);
+ std::fs::set_permissions(&cargo_home, perms.clone()).unwrap();
+ p.cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr("[FINISHED] [..]")
+ .run();
+ perms.set_readonly(false);
+ std::fs::set_permissions(&cargo_home, perms).unwrap();
+}
+
+#[cargo_test]
+fn delete_index_also_deletes_crates() {
+ // Checks that when an index is delete that src and cache directories also get deleted.
+ let p = basic_foo_bar_project();
+ p.cargo("fetch -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4))
+ .run();
+
+ assert_eq!(get_registry_names("src"), ["bar-1.0.0"]);
+ assert_eq!(get_registry_names("cache"), ["bar-1.0.0.crate"]);
+
+ p.cargo("clean gc")
+ .arg("--max-index-age=0 days")
+ .arg("-Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr("[REMOVED] [..]")
+ .run();
+
+ assert_eq!(get_registry_names("src").len(), 0);
+ assert_eq!(get_registry_names("cache").len(), 0);
+}
+
+#[cargo_test]
+fn clean_syncs_missing_files() {
+ // When files go missing in the cache, clean operations that need to track
+ // the size should also remove them from the database.
+ Package::new("bar", "1.0.0").publish();
+ Package::new("baz", "1.0.0").publish();
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = "1.0"
+ baz = "1.0"
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+ p.cargo("fetch -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .run();
+
+ // Verify things are tracked.
+ let config = ConfigBuilder::new().unstable_flag("gc").build();
+ let lock = config
+ .acquire_package_cache_lock(CacheLockMode::MutateExclusive)
+ .unwrap();
+ let tracker = GlobalCacheTracker::new(&config).unwrap();
+ let crates = tracker.registry_crate_all().unwrap();
+ assert_eq!(crates.len(), 2);
+ let srcs = tracker.registry_src_all().unwrap();
+ assert_eq!(srcs.len(), 2);
+ drop(lock);
+
+ // Remove the files.
+ for pattern in [
+ ".cargo/registry/cache/*/bar-1.0.0.crate",
+ ".cargo/registry/src/*/bar-1.0.0",
+ ] {
+ p.glob(paths::home().join(pattern))
+ .map(|p| p.unwrap())
+ .next()
+ .unwrap()
+ .rm_rf();
+ }
+
+ // Clean should update the db.
+ p.cargo("clean gc -v --max-download-size=1GB -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr("[REMOVED] 0 files")
+ .run();
+
+ // Verify
+ let crates = tracker.registry_crate_all().unwrap();
+ assert_eq!(crates.len(), 1);
+ let srcs = tracker.registry_src_all().unwrap();
+ assert_eq!(srcs.len(), 1);
+}
+
+#[cargo_test]
+fn offline_doesnt_auto_gc() {
+ // When running offline, auto-gc shouldn't run.
+ let p = basic_foo_bar_project();
+ p.cargo("fetch -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4))
+ .run();
+ // Remove the dependency.
+ p.change_file("Cargo.toml", &basic_manifest("foo", "0.1.0"));
+ // Run offline, make sure it doesn't delete anything
+ p.cargo("check --offline -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr("[CHECKING] foo v0.1.0[..]\n[FINISHED][..]")
+ .run();
+ assert_eq!(get_registry_names("src"), ["bar-1.0.0"]);
+ assert_eq!(get_registry_names("cache"), ["bar-1.0.0.crate"]);
+ // Run online, make sure auto-gc runs.
+ p.cargo("check -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr("[FINISHED][..]")
+ .run();
+ assert_eq!(get_registry_names("src"), &[] as &[String]);
+ assert_eq!(get_registry_names("cache"), &[] as &[String]);
+}
+
+#[cargo_test]
+fn can_handle_future_schema() -> anyhow::Result<()> {
+ // It should work when a future version of cargo has made schema changes
+ // to the database.
+ let p = basic_foo_bar_project();
+ p.cargo("fetch -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4))
+ .run();
+ // Modify the schema to pretend this is done by a future version of cargo.
+ let config = ConfigBuilder::new().build();
+ let db_path = GlobalCacheTracker::db_path(&config).into_path_unlocked();
+ let conn = rusqlite::Connection::open(&db_path)?;
+ let user_version: u32 =
+ conn.query_row("SELECT user_version FROM pragma_user_version", [], |row| {
+ row.get(0)
+ })?;
+ conn.execute("ALTER TABLE global_data ADD COLUMN foo DEFAULT 123", [])?;
+ conn.pragma_update(None, "user_version", &(user_version + 1))?;
+ drop(conn);
+ // Verify it doesn't blow up.
+ p.cargo("clean gc --max-download-size=0 -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr("[REMOVED] 4 files, [..] total")
+ .run();
+ Ok(())
+}
+
+#[cargo_test]
+fn clean_max_git_age() {
+ // --max-git-*-age flags
+ let (git_a, git_a_repo) = git::new_repo("git_a", |p| {
+ p.file("Cargo.toml", &basic_manifest("git_a", "1.0.0"))
+ .file("src/lib.rs", "")
+ });
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ git_a = {{ git = '{}' }}
+ "#,
+ git_a.url()
+ ),
+ )
+ .file("src/lib.rs", "")
+ .build();
+ // Populate last-use tracking.
+ p.cargo("fetch -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(4))
+ .run();
+ // Update git_a to create a separate checkout.
+ git_a.change_file("src/lib.rs", "// test");
+ git::add(&git_a_repo);
+ git::commit(&git_a_repo);
+ // Update last-use tracking, where the first git checkout will stay "old".
+ p.cargo("update -p git_a -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(2))
+ .with_stderr(
+ "\
+[UPDATING] git repository [..]
+[UPDATING] git_a v1.0.0 [..]
+",
+ )
+ .run();
+
+ let db_names = get_git_db_names();
+ assert_eq!(db_names.len(), 1);
+ let db_name = &db_names[0];
+ let co_names = get_git_checkout_names(&db_name);
+ assert_eq!(co_names.len(), 2);
+
+ // Delete the first checkout
+ p.cargo("clean gc -v -Zgc")
+ .arg("--max-git-co-age=3 days")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr(
+ "\
+[REMOVING] [ROOT]/home/.cargo/git/checkouts/git_a-[..]/[..]
+[REMOVED] [..]
+",
+ )
+ .run();
+
+ let db_names = get_git_db_names();
+ assert_eq!(db_names.len(), 1);
+ let co_names = get_git_checkout_names(&db_name);
+ assert_eq!(co_names.len(), 1);
+
+ // delete the second checkout
+ p.cargo("clean gc -v -Zgc")
+ .arg("--max-git-co-age=0 days")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr(
+ "\
+[REMOVING] [ROOT]/home/.cargo/git/checkouts/git_a-[..]/[..]
+[REMOVED] [..]
+",
+ )
+ .run();
+
+ let db_names = get_git_db_names();
+ assert_eq!(db_names.len(), 1);
+ let co_names = get_git_checkout_names(&db_name);
+ assert_eq!(co_names.len(), 0);
+
+ // delete the db
+ p.cargo("clean gc -v -Zgc")
+ .arg("--max-git-db-age=1 days")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr(
+ "\
+[REMOVING] [ROOT]/home/.cargo/git/db/git_a-[..]
+[REMOVING] [ROOT]/home/.cargo/git/checkouts/git_a-[..]
+[REMOVED] [..]
+",
+ )
+ .run();
+
+ let db_names = get_git_db_names();
+ assert_eq!(db_names.len(), 0);
+ let co_names = get_git_checkout_names(&db_name);
+ assert_eq!(co_names.len(), 0);
+}
+
+#[cargo_test]
+fn clean_max_src_crate_age() {
+ // --max-src-age and --max-crate-age flags
+ let p = basic_foo_bar_project();
+ // Populate last-use tracking.
+ p.cargo("fetch -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(4))
+ .run();
+ // Update bar to create a separate copy with a different timestamp.
+ Package::new("bar", "1.0.1").publish();
+ p.cargo("update -p bar -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(2))
+ .with_stderr(
+ "\
+[UPDATING] `dummy-registry` index
+[UPDATING] bar v1.0.0 -> v1.0.1
+",
+ )
+ .run();
+ p.cargo("fetch -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(2))
+ .with_stderr(
+ "\
+[DOWNLOADING] crates ...
+[DOWNLOADED] bar v1.0.1 [..]
+",
+ )
+ .run();
+
+ assert_eq!(get_registry_names("src"), ["bar-1.0.0", "bar-1.0.1"]);
+ assert_eq!(
+ get_registry_names("cache"),
+ ["bar-1.0.0.crate", "bar-1.0.1.crate"]
+ );
+
+ // Delete the old src.
+ p.cargo("clean gc -v -Zgc")
+ .arg("--max-src-age=3 days")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr(
+ "\
+[REMOVING] [..]/bar-1.0.0
+[REMOVED] [..]
+",
+ )
+ .run();
+
+ // delete the second src
+ p.cargo("clean gc -v -Zgc")
+ .arg("--max-src-age=0 days")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr(
+ "\
+[REMOVING] [..]/bar-1.0.1
+[REMOVED] [..]
+",
+ )
+ .run();
+
+ // delete the old crate
+ p.cargo("clean gc -v -Zgc")
+ .arg("--max-crate-age=3 days")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr(
+ "\
+[REMOVING] [..]/bar-1.0.0.crate
+[REMOVED] [..]
+",
+ )
+ .run();
+
+ // delete the seecond crate
+ p.cargo("clean gc -v -Zgc")
+ .arg("--max-crate-age=0 days")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr(
+ "\
+[REMOVING] [..]/bar-1.0.1.crate
+[REMOVED] [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn clean_max_git_size() {
+ // clean --max-git-size
+ //
+ // Creates two checkouts. The sets a size threshold to delete one. And
+ // then with 0 max size to delete everything.
+ let (git_project, git_repo) = git::new_repo("bar", |p| {
+ p.file("Cargo.toml", &basic_manifest("bar", "1.0.0"))
+ .file("src/lib.rs", "")
+ });
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = {{ git = '{}' }}
+ "#,
+ git_project.url()
+ ),
+ )
+ .file("src/lib.rs", "")
+ .build();
+ // Fetch and populate db.
+ p.cargo("fetch -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(3))
+ .run();
+
+ // Figure out the name of the first checkout.
+ let git_root = paths::home().join(".cargo/git");
+ let db_names = get_git_db_names();
+ assert_eq!(db_names.len(), 1);
+ let db_name = &db_names[0];
+ let co_names = get_git_checkout_names(&db_name);
+ assert_eq!(co_names.len(), 1);
+ let first_co_name = &co_names[0];
+
+ // Make an update and create a new checkout.
+ git_project.change_file("src/lib.rs", "// modified");
+ git::add(&git_repo);
+ git::commit(&git_repo);
+ p.cargo("update -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ // Use a different time so that the first checkout timestamp is less
+ // than the second.
+ .env("__CARGO_TEST_LAST_USE_NOW", days_ago_unix(2))
+ .run();
+
+ // Figure out the threshold to use.
+ let mut co_names = get_git_checkout_names(&db_name);
+ assert_eq!(co_names.len(), 2);
+ co_names.retain(|name| name != first_co_name);
+ assert_eq!(co_names.len(), 1);
+ let second_co_name = &co_names[0];
+ let second_co_path = git_root
+ .join("checkouts")
+ .join(db_name)
+ .join(second_co_name);
+ let second_co_size = cargo_util::du(&second_co_path, &["!.git"]).unwrap();
+
+ let db_size = cargo_util::du(&git_root.join("db").join(db_name), &[]).unwrap();
+
+ let threshold = db_size + second_co_size;
+
+ p.cargo(&format!("clean gc --max-git-size={threshold} -Zgc -v"))
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr(&format!(
+ "\
+[REMOVING] [ROOT]/home/.cargo/git/checkouts/{db_name}/{first_co_name}
+[REMOVED] [..]
+"
+ ))
+ .run();
+
+ // And then try cleaning everything.
+ p.cargo("clean gc --max-git-size=0 -Zgc -v")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr_unordered(&format!(
+ "\
+[REMOVING] [ROOT]/home/.cargo/git/checkouts/{db_name}/{second_co_name}
+[REMOVING] [ROOT]/home/.cargo/git/db/{db_name}
+[REMOVED] [..]
+"
+ ))
+ .run();
+}
+
+// Helper for setting up fake git sizes for git size cleaning.
+fn setup_fake_git_sizes(db_name: &str, db_size: usize, co_sizes: &[usize]) {
+ let base_git = paths::home().join(".cargo/git");
+ let db_path = base_git.join("db").join(db_name);
+ db_path.mkdir_p();
+ std::fs::write(db_path.join("test"), "x".repeat(db_size)).unwrap();
+ let base_co = base_git.join("checkouts").join(db_name);
+ for (i, size) in co_sizes.iter().enumerate() {
+ let co_name = format!("co{i}");
+ let co_path = base_co.join(co_name);
+ co_path.mkdir_p();
+ std::fs::write(co_path.join("test"), "x".repeat(*size)).unwrap();
+ }
+}
+
+#[cargo_test]
+fn clean_max_git_size_untracked() {
+ // If there are git directories that aren't tracked in the database,
+ // `--max-git-size` should pick it up.
+ //
+ // The db_name of "example" depends on the sorting order of the names ("e"
+ // should be after "c"), so that the db comes after the checkouts.
+ setup_fake_git_sizes("example", 5000, &[1000, 2000]);
+ cargo_process(&format!("clean gc -Zgc -v --max-git-size=7000"))
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr(
+ "\
+[REMOVING] [ROOT]/home/.cargo/git/checkouts/example/co0
+[REMOVED] [..]
+",
+ )
+ .run();
+ cargo_process(&format!("clean gc -Zgc -v --max-git-size=5000"))
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr(
+ "\
+[REMOVING] [ROOT]/home/.cargo/git/checkouts/example/co1
+[REMOVED] [..]
+",
+ )
+ .run();
+ cargo_process(&format!("clean gc -Zgc -v --max-git-size=0"))
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr(
+ "\
+[REMOVING] [ROOT]/home/.cargo/git/db/example
+[REMOVED] [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn clean_max_git_size_deletes_co_from_db() {
+ // In the scenario where it thinks it needs to delete the db, it should
+ // also delete all the checkouts.
+ //
+ // The db_name of "abc" depends on the sorting order of the names ("a"
+ // should be before "c"), so that the db comes before the checkouts.
+ setup_fake_git_sizes("abc", 5000, &[1000, 2000]);
+ // This deletes everything because it tries to delete the db, which then
+ // deletes all checkouts.
+ cargo_process(&format!("clean gc -Zgc -v --max-git-size=3000"))
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr(
+ "\
+[REMOVING] [ROOT]/home/.cargo/git/db/abc
+[REMOVING] [ROOT]/home/.cargo/git/checkouts/abc/co1
+[REMOVING] [ROOT]/home/.cargo/git/checkouts/abc/co0
+[REMOVED] [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn handles_missing_index() {
+ // Checks behavior when index is missing.
+ let p = basic_foo_bar_project();
+ p.cargo("fetch -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .run();
+ paths::home().join(".cargo/registry/index").rm_rf();
+ cargo_process("clean gc -v --max-download-size=0 -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr_unordered(
+ "\
+[REMOVING] [ROOT]/home/.cargo/registry/cache/[..]
+[REMOVING] [ROOT]/home/.cargo/registry/src/[..]
+[REMOVED] [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn handles_missing_git_db() {
+ // Checks behavior when git db is missing.
+ let git_project = git::new("bar", |p| {
+ p.file("Cargo.toml", &basic_manifest("bar", "1.0.0"))
+ .file("src/lib.rs", "")
+ });
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = {{ git = '{}' }}
+ "#,
+ git_project.url()
+ ),
+ )
+ .file("src/lib.rs", "")
+ .build();
+ p.cargo("fetch -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .run();
+ paths::home().join(".cargo/git/db").rm_rf();
+ cargo_process("clean gc -v --max-git-size=0 -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stderr(
+ "\
+[REMOVING] [ROOT]/home/.cargo/git/checkouts/[..]
+[REMOVED] [..]
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn clean_gc_quiet_is_quiet() {
+ // Checks that --quiet works with `cargo clean gc`, since there was a
+ // subtle issue with how the flag is defined as a global flag.
+ let p = basic_foo_bar_project();
+ p.cargo("fetch -Zgc")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .env("__CARGO_TEST_LAST_USE_NOW", months_ago_unix(4))
+ .run();
+ p.cargo("clean gc --quiet -Zgc --dry-run")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stdout("")
+ .with_stderr("")
+ .run();
+ // Verify exact same command without -q would actually display something.
+ p.cargo("clean gc -Zgc --dry-run")
+ .masquerade_as_nightly_cargo(&["gc"])
+ .with_stdout("")
+ .with_stderr(
+ "\
+[SUMMARY] [..] files, [..] total
+[WARNING] no files deleted due to --dry-run
+",
+ )
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/install.rs b/src/tools/cargo/tests/testsuite/install.rs
index fd53b607b..16ed32ee7 100644
--- a/src/tools/cargo/tests/testsuite/install.rs
+++ b/src/tools/cargo/tests/testsuite/install.rs
@@ -3,6 +3,7 @@
use std::fs::{self, OpenOptions};
use std::io::prelude::*;
use std::path::Path;
+use std::thread;
use cargo_test_support::compare;
use cargo_test_support::cross_compile;
@@ -11,10 +12,10 @@ use cargo_test_support::registry::{self, registry_path, Package};
use cargo_test_support::{
basic_manifest, cargo_process, no_such_file_err_msg, project, project_in, symlink_supported, t,
};
-use cargo_util::ProcessError;
+use cargo_util::{ProcessBuilder, ProcessError};
use cargo_test_support::install::{
- assert_has_installed_exe, assert_has_not_installed_exe, cargo_home,
+ assert_has_installed_exe, assert_has_not_installed_exe, cargo_home, exe,
};
use cargo_test_support::paths::{self, CargoPathExt};
use std::env;
@@ -1010,7 +1011,7 @@ fn compile_failure() {
.with_status(101)
.with_stderr_contains(
"\
-[ERROR] could not compile `foo` (bin \"foo\") due to previous error
+[ERROR] could not compile `foo` (bin \"foo\") due to 1 previous error
[ERROR] failed to compile `foo v0.0.1 ([..])`, intermediate artifacts can be \
found at `[..]target`.\nTo reuse those artifacts with a future compilation, \
set the environment variable `CARGO_TARGET_DIR` to that path.
@@ -2507,3 +2508,118 @@ fn install_incompat_msrv() {
")
.with_status(101).run();
}
+
+fn assert_tracker_noexistence(key: &str) {
+ let v1_data: toml::Value =
+ toml::from_str(&fs::read_to_string(cargo_home().join(".crates.toml")).unwrap()).unwrap();
+ let v2_data: serde_json::Value =
+ serde_json::from_str(&fs::read_to_string(cargo_home().join(".crates2.json")).unwrap())
+ .unwrap();
+
+ assert!(v1_data["v1"].get(key).is_none());
+ assert!(v2_data["installs"][key].is_null());
+}
+
+#[cargo_test]
+fn uninstall_running_binary() {
+ use std::io::Write;
+
+ Package::new("foo", "0.0.1")
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ "#,
+ )
+ .file(
+ "src/main.rs",
+ r#"
+ use std::net::TcpStream;
+ use std::env::var;
+ use std::io::Read;
+ fn main() {
+ for i in 0..2 {
+ TcpStream::connect(&var("__ADDR__").unwrap()[..])
+ .unwrap()
+ .read_to_end(&mut Vec::new())
+ .unwrap();
+ }
+ }
+ "#,
+ )
+ .publish();
+
+ cargo_process("install 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");
+
+ let foo_bin = cargo_home().join("bin").join(exe("foo"));
+ let l = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
+ let addr = l.local_addr().unwrap().to_string();
+ let t = thread::spawn(move || {
+ ProcessBuilder::new(foo_bin)
+ .env("__ADDR__", addr)
+ .exec()
+ .unwrap();
+ });
+ let key = "foo 0.0.1 (registry+https://github.com/rust-lang/crates.io-index)";
+
+ #[cfg(windows)]
+ {
+ // Ensure foo is running before the first `cargo uninstall` call
+ l.accept().unwrap().0.write_all(&[1]).unwrap();
+ cargo_process("uninstall foo")
+ .with_status(101)
+ .with_stderr_contains("[ERROR] failed to remove file `[CWD]/home/.cargo/bin/foo[EXE]`")
+ .run();
+ // Ensure foo is stopped before the second `cargo uninstall` call
+ l.accept().unwrap().0.write_all(&[1]).unwrap();
+ t.join().unwrap();
+ cargo_process("uninstall foo")
+ .with_stderr("[REMOVING] [CWD]/home/.cargo/bin/foo[EXE]")
+ .run();
+ };
+
+ #[cfg(not(windows))]
+ {
+ // Ensure foo is running before the first `cargo uninstall` call
+ l.accept().unwrap().0.write_all(&[1]).unwrap();
+ cargo_process("uninstall foo")
+ .with_stderr("[REMOVING] [CWD]/home/.cargo/bin/foo[EXE]")
+ .run();
+ l.accept().unwrap().0.write_all(&[1]).unwrap();
+ t.join().unwrap();
+ };
+
+ assert_has_not_installed_exe(cargo_home(), "foo");
+ assert_tracker_noexistence(key);
+
+ cargo_process("install foo")
+ .with_stderr(
+ "\
+[UPDATING] `[..]` index
+[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();
+}
diff --git a/src/tools/cargo/tests/testsuite/main.rs b/src/tools/cargo/tests/testsuite/main.rs
index 07f749e34..8e2d6dedf 100644
--- a/src/tools/cargo/tests/testsuite/main.rs
+++ b/src/tools/cargo/tests/testsuite/main.rs
@@ -1,6 +1,6 @@
-// See src/cargo/lib.rs for notes on these lint settings.
-#![warn(rust_2018_idioms)]
-#![allow(clippy::all)]
+#![allow(clippy::disallowed_methods)]
+#![allow(clippy::print_stderr)]
+#![allow(clippy::print_stdout)]
#[macro_use]
extern crate cargo_test_macro;
@@ -98,6 +98,7 @@ mod git_auth;
mod git_gc;
mod git_shallow;
mod glob_targets;
+mod global_cache_tracker;
mod help;
mod https;
mod inheritable_workspace_fields;
diff --git a/src/tools/cargo/tests/testsuite/messages.rs b/src/tools/cargo/tests/testsuite/messages.rs
index 2c534d8f0..fb92593bc 100644
--- a/src/tools/cargo/tests/testsuite/messages.rs
+++ b/src/tools/cargo/tests/testsuite/messages.rs
@@ -136,7 +136,7 @@ fn deduplicate_errors() {
.with_stderr(&format!(
"\
[COMPILING] foo v0.0.1 [..]
-{}error: could not compile `foo` (lib) due to previous error
+{}error: could not compile `foo` (lib) due to 1 previous error
",
rustc_message
))
diff --git a/src/tools/cargo/tests/testsuite/metabuild.rs b/src/tools/cargo/tests/testsuite/metabuild.rs
index 022d0bff0..1c0196c98 100644
--- a/src/tools/cargo/tests/testsuite/metabuild.rs
+++ b/src/tools/cargo/tests/testsuite/metabuild.rs
@@ -740,6 +740,7 @@ fn metabuild_failed_build_json() {
r#"
{
"message": {
+ "$message_type": "diagnostic",
"children": "{...}",
"code": "{...}",
"level": "error",
diff --git a/src/tools/cargo/tests/testsuite/package.rs b/src/tools/cargo/tests/testsuite/package.rs
index 4ec4fc0d6..371157e4e 100644
--- a/src/tools/cargo/tests/testsuite/package.rs
+++ b/src/tools/cargo/tests/testsuite/package.rs
@@ -5,6 +5,7 @@ use cargo_test_support::publish::validate_crate_contents;
use cargo_test_support::registry::{self, Package};
use cargo_test_support::{
basic_manifest, cargo_process, git, path2url, paths, project, symlink_supported, t,
+ ProjectBuilder,
};
use flate2::read::GzDecoder;
use std::fs::{self, read_to_string, File};
@@ -54,7 +55,19 @@ src/main.rs
",
)
.run();
- p.cargo("package").with_stdout("").run();
+ p.cargo("package")
+ .with_stderr(
+ "\
+[WARNING] manifest has no documentation[..]
+See [..]
+[PACKAGING] foo v0.0.1 ([CWD])
+[VERIFYING] foo v0.0.1 ([CWD])
+[COMPILING] foo v0.0.1 ([CWD][..])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[PACKAGED] 4 files, [..] ([..] compressed)
+",
+ )
+ .run();
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
validate_crate_contents(
@@ -695,6 +708,7 @@ fn ignore_nested() {
authors = []
license = "MIT"
description = "foo"
+ homepage = "https://example.com/"
"#;
let main_rs = r#"
fn main() { println!("hello"); }
@@ -711,8 +725,6 @@ fn ignore_nested() {
p.cargo("package")
.with_stderr(
"\
-[WARNING] manifest has no documentation[..]
-See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
[PACKAGING] foo v0.0.1 ([CWD])
[VERIFYING] foo v0.0.1 ([CWD])
[COMPILING] foo v0.0.1 ([CWD][..])
@@ -732,7 +744,17 @@ src/main.rs
",
)
.run();
- p.cargo("package").with_stdout("").run();
+ p.cargo("package")
+ .with_stderr(
+ "\
+[PACKAGING] foo v0.0.1 ([CWD])
+[VERIFYING] foo v0.0.1 ([CWD])
+[COMPILING] foo v0.0.1 ([CWD][..])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[PACKAGED] 4 files, [..] ([..] compressed)
+",
+ )
+ .run();
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
validate_crate_contents(
@@ -2730,6 +2752,7 @@ fn basic_filesizes() {
exclude = ["*.txt"]
license = "MIT"
description = "foo"
+ homepage = "https://example.com/"
"#;
let main_rs_contents = r#"fn main() { println!("🦀"); }"#;
let cargo_toml_contents = format!(
@@ -2740,6 +2763,7 @@ version = "0.0.1"
authors = []
exclude = ["*.txt"]
description = "foo"
+homepage = "https://example.com/"
license = "MIT"
"#,
cargo::core::package::MANIFEST_PREAMBLE
@@ -2775,7 +2799,17 @@ src/main.rs
",
)
.run();
- p.cargo("package").with_stdout("").run();
+ p.cargo("package")
+ .with_stderr(
+ "\
+[PACKAGING] foo v0.0.1 [..]
+[VERIFYING] foo v0.0.1 [..]
+[COMPILING] foo v0.0.1 [..]
+[FINISHED] [..]
+[PACKAGED] 4 files[..]
+",
+ )
+ .run();
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
let compressed_size = f.metadata().unwrap().len();
@@ -2802,6 +2836,7 @@ fn larger_filesizes() {
authors = []
license = "MIT"
description = "foo"
+ documentation = "https://example.com/"
"#;
let lots_of_crabs = std::iter::repeat("🦀").take(1337).collect::<String>();
let main_rs_contents = format!(r#"fn main() {{ println!("{}"); }}"#, lots_of_crabs);
@@ -2820,6 +2855,7 @@ name = "foo"
version = "0.0.1"
authors = []
description = "foo"
+documentation = "https://example.com/"
license = "MIT"
"#,
cargo::core::package::MANIFEST_PREAMBLE
@@ -2857,7 +2893,17 @@ src/main.rs
",
)
.run();
- p.cargo("package").with_stdout("").run();
+ p.cargo("package")
+ .with_stderr(
+ "\
+[PACKAGING] foo v0.0.1 [..]
+[VERIFYING] foo v0.0.1 [..]
+[COMPILING] foo v0.0.1 [..]
+[FINISHED] [..]
+[PACKAGED] 5 files, [..]
+",
+ )
+ .run();
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
let compressed_size = f.metadata().unwrap().len();
@@ -2895,6 +2941,7 @@ fn symlink_filesizes() {
authors = []
license = "MIT"
description = "foo"
+ homepage = "https://example.com/"
"#;
let lots_of_crabs = std::iter::repeat("🦀").take(1337).collect::<String>();
let main_rs_contents = format!(r#"fn main() {{ println!("{}"); }}"#, lots_of_crabs);
@@ -2913,6 +2960,7 @@ name = "foo"
version = "0.0.1"
authors = []
description = "foo"
+homepage = "https://example.com/"
license = "MIT"
"#,
cargo::core::package::MANIFEST_PREAMBLE
@@ -2955,7 +3003,17 @@ src/main.rs.bak
",
)
.run();
- p.cargo("package").with_stdout("").run();
+ p.cargo("package")
+ .with_stderr(
+ "\
+[PACKAGING] foo v0.0.1 [..]
+[VERIFYING] foo v0.0.1 [..]
+[COMPILING] foo v0.0.1 [..]
+[FINISHED] [..]
+[PACKAGED] 7 files, [..]
+",
+ )
+ .run();
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
let compressed_size = f.metadata().unwrap().len();
@@ -3031,7 +3089,19 @@ src/main.rs
",
)
.run();
- p.cargo("package").with_stdout("").run();
+ p.cargo("package")
+ .with_stderr(
+ "\
+[WARNING] manifest has no documentation[..]
+See [..]
+[PACKAGING] foo v0.0.1 ([CWD])
+[VERIFYING] foo v0.0.1 ([CWD])
+[COMPILING] foo v0.0.1 ([CWD][..])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[PACKAGED] 4 files, [..] ([..] compressed)
+",
+ )
+ .run();
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
validate_crate_contents(
@@ -3085,7 +3155,19 @@ src/main.rs
",
)
.run();
- p.cargo("package").with_stdout("").run();
+ p.cargo("package")
+ .with_stderr(
+ "\
+[WARNING] manifest has no documentation[..]
+See [..]
+[PACKAGING] foo v0.0.1 ([CWD])
+[VERIFYING] foo v0.0.1 ([CWD])
+[COMPILING] foo v0.0.1 ([CWD][..])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+[PACKAGED] 4 files, [..] ([..] compressed)
+",
+ )
+ .run();
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
validate_crate_contents(
@@ -3132,3 +3214,142 @@ See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for
&[],
);
}
+
+#[cargo_test]
+fn include_files_called_target_project() {
+ // https://github.com/rust-lang/cargo/issues/12790
+ // files and folders called "target" should be included, unless they're the actual target directory
+ let p = init_and_add_inner_target(project())
+ .file("target/foo.txt", "")
+ .build();
+
+ p.cargo("package -l")
+ .with_stdout(
+ "\
+Cargo.lock
+Cargo.toml
+Cargo.toml.orig
+data/not_target
+data/target
+derp/not_target/foo.txt
+derp/target/foo.txt
+src/main.rs
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn include_files_called_target_git() {
+ // https://github.com/rust-lang/cargo/issues/12790
+ // files and folders called "target" should be included, unless they're the actual target directory
+ let (p, repo) = git::new_repo("foo", |p| init_and_add_inner_target(p));
+ // add target folder but not committed.
+ _ = fs::create_dir(p.build_dir()).unwrap();
+ _ = fs::write(p.build_dir().join("foo.txt"), "").unwrap();
+ p.cargo("package -l")
+ .with_stdout(
+ "\
+.cargo_vcs_info.json
+Cargo.lock
+Cargo.toml
+Cargo.toml.orig
+data/not_target
+data/target
+derp/not_target/foo.txt
+derp/target/foo.txt
+src/main.rs
+",
+ )
+ .run();
+
+ // if target is committed, it should be include.
+ git::add(&repo);
+ git::commit(&repo);
+ p.cargo("package -l")
+ .with_stdout(
+ "\
+.cargo_vcs_info.json
+Cargo.lock
+Cargo.toml
+Cargo.toml.orig
+data/not_target
+data/target
+derp/not_target/foo.txt
+derp/target/foo.txt
+src/main.rs
+target/foo.txt
+",
+ )
+ .run();
+}
+
+fn init_and_add_inner_target(p: ProjectBuilder) -> ProjectBuilder {
+ p.file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ license = "MIT"
+ description = "foo"
+ "#,
+ )
+ .file("src/main.rs", r#"fn main() { println!("hello"); }"#)
+ // file called target, should be included
+ .file("data/target", "")
+ .file("data/not_target", "")
+ // folder called target, should be included
+ .file("derp/target/foo.txt", "")
+ .file("derp/not_target/foo.txt", "")
+}
+
+#[cargo_test]
+fn build_script_outside_pkg_root() {
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ license = "MIT"
+ description = "foo"
+ authors = []
+ build = "../t_custom_build/custom_build.rs"
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .build();
+ let mut expect_msg = String::from("\
+warning: manifest has no documentation, homepage or repository.
+See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
+error: the source file of build script doesn't appear to exist.
+This may cause issue during packaging, as modules resolution and resources included via macros are often relative to the path of source files.
+Please update the `build` setting in the manifest at `[CWD]/Cargo.toml` and point to a path inside the root of the package.
+");
+ // custom_build.rs does not exist
+ p.cargo("package -l")
+ .with_status(101)
+ .with_stderr(&expect_msg)
+ .run();
+
+ // custom_build.rs outside the package root
+ let custom_build_root = paths::root().join("t_custom_build");
+ _ = fs::create_dir(&custom_build_root).unwrap();
+ _ = fs::write(&custom_build_root.join("custom_build.rs"), "fn main() {}");
+ expect_msg = format!(
+ "\
+warning: manifest has no documentation, homepage or repository.
+See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
+error: the source file of build script doesn't appear to be a path inside of the package.
+It is at `{}/t_custom_build/custom_build.rs`, whereas the root the package is `[CWD]`.
+This may cause issue during packaging, as modules resolution and resources included via macros are often relative to the path of source files.
+Please update the `build` setting in the manifest at `[CWD]/Cargo.toml` and point to a path inside the root of the package.
+", paths::root().display());
+ p.cargo("package -l")
+ .with_status(101)
+ .with_stderr(&expect_msg)
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/patch.rs b/src/tools/cargo/tests/testsuite/patch.rs
index a467f60b5..a4522e822 100644
--- a/src/tools/cargo/tests/testsuite/patch.rs
+++ b/src/tools/cargo/tests/testsuite/patch.rs
@@ -2700,3 +2700,86 @@ perhaps a crate was updated and forgotten to be re-vendored?"#,
)
.run();
}
+
+#[cargo_test]
+fn from_config_empty() {
+ Package::new("bar", "0.1.0").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies]
+ bar = "0.1.0"
+ "#,
+ )
+ .file(
+ ".cargo/config.toml",
+ r#"
+ [patch.'']
+ bar = { path = 'bar' }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1"))
+ .file("bar/src/lib.rs", r#""#)
+ .build();
+
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr(
+ "\
+[ERROR] [patch] entry `` should be a URL or registry name
+
+Caused by:
+ invalid url ``: relative URL without a base
+",
+ )
+ .run();
+}
+
+#[cargo_test]
+fn from_manifest_empty() {
+ Package::new("bar", "0.1.0").publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+
+ [dependencies]
+ bar = "0.1.0"
+
+ [patch.'']
+ bar = { path = 'bar' }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .file("bar/Cargo.toml", &basic_manifest("bar", "0.1.1"))
+ .file("bar/src/lib.rs", r#""#)
+ .build();
+
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr(
+ "\
+[ERROR] failed to parse manifest at `[CWD]/Cargo.toml`
+
+Caused by:
+ [patch] entry `` should be a URL or registry name
+
+Caused by:
+ invalid url ``: relative URL without a base
+",
+ )
+ .run();
+}
diff --git a/src/tools/cargo/tests/testsuite/path.rs b/src/tools/cargo/tests/testsuite/path.rs
index ebbb72f9a..f458717cd 100644
--- a/src/tools/cargo/tests/testsuite/path.rs
+++ b/src/tools/cargo/tests/testsuite/path.rs
@@ -83,7 +83,12 @@ fn cargo_compile_with_nested_deps_shorthand() {
p.process(&p.bin("foo")).with_stdout("test passed\n").run();
println!("cleaning");
- p.cargo("clean -v").with_stdout("").run();
+ p.cargo("clean -v")
+ .with_stderr(
+ "[REMOVING] [CWD]/target\n\
+ [REMOVED] [..]",
+ )
+ .run();
println!("building baz");
p.cargo("build -p baz")
.with_stderr(
@@ -350,7 +355,7 @@ fn deep_dependencies_trigger_rebuild() {
in [..]\n",
)
.run();
- p.cargo("check").with_stdout("").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
// Make sure an update to baz triggers a rebuild of bar
//
@@ -437,7 +442,7 @@ fn no_rebuild_two_deps() {
)
.run();
assert!(p.bin("foo").is_file());
- p.cargo("build").with_stdout("").run();
+ p.cargo("build").with_stderr("[FINISHED] [..]").run();
assert!(p.bin("foo").is_file());
}
diff --git a/src/tools/cargo/tests/testsuite/pkgid.rs b/src/tools/cargo/tests/testsuite/pkgid.rs
index 88d991e80..fee45b215 100644
--- a/src/tools/cargo/tests/testsuite/pkgid.rs
+++ b/src/tools/cargo/tests/testsuite/pkgid.rs
@@ -1,5 +1,7 @@
//! Tests for the `cargo pkgid` command.
+use cargo_test_support::basic_lib_manifest;
+use cargo_test_support::git;
use cargo_test_support::project;
use cargo_test_support::registry::Package;
@@ -34,7 +36,10 @@ fn local() {
p.cargo("generate-lockfile").run();
p.cargo("pkgid foo")
- .with_stdout(format!("file://[..]{}#0.1.0", p.root().to_str().unwrap()))
+ .with_stdout(format!(
+ "path+file://[..]{}#0.1.0",
+ p.root().to_str().unwrap()
+ ))
.run();
// Bad file URL.
@@ -89,7 +94,7 @@ fn registry() {
p.cargo("generate-lockfile").run();
p.cargo("pkgid crates-io")
- .with_stdout("https://github.com/rust-lang/crates.io-index#crates-io@0.1.0")
+ .with_stdout("registry+https://github.com/rust-lang/crates.io-index#crates-io@0.1.0")
.run();
// Bad URL.
@@ -143,7 +148,7 @@ fn multiple_versions() {
p.cargo("generate-lockfile").run();
p.cargo("pkgid two-ver:0.2.0")
- .with_stdout("https://github.com/rust-lang/crates.io-index#two-ver@0.2.0")
+ .with_stdout("registry+https://github.com/rust-lang/crates.io-index#two-ver@0.2.0")
.run();
// Incomplete version.
@@ -163,7 +168,7 @@ Please re-run this command with one of the following specifications:
p.cargo("pkgid two-ver@0.2")
.with_stdout(
"\
-https://github.com/rust-lang/crates.io-index#two-ver@0.2.0
+registry+https://github.com/rust-lang/crates.io-index#two-ver@0.2.0
",
)
.run();
@@ -195,3 +200,88 @@ Did you mean one of these?
)
.run();
}
+
+// Not for `cargo pkgid` but the `PackageIdSpec` format
+#[cargo_test]
+fn multiple_git_same_version() {
+ // Test what happens if different packages refer to the same git repo with
+ // different refs, and the package version is the same.
+ let (xyz_project, xyz_repo) = git::new_repo("xyz", |project| {
+ project
+ .file("Cargo.toml", &basic_lib_manifest("xyz"))
+ .file("src/lib.rs", "fn example() {}")
+ });
+ let rev1 = xyz_repo.revparse_single("HEAD").unwrap().id();
+ xyz_project.change_file("src/lib.rs", "pub fn example() {}");
+ git::add(&xyz_repo);
+ let rev2 = git::commit(&xyz_repo);
+ // Both rev1 and rev2 point to version 0.1.0.
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dependencies]
+ bar = {{ path = "bar" }}
+ xyz = {{ git = "{}", rev = "{}" }}
+
+ "#,
+ xyz_project.url(),
+ rev1
+ ),
+ )
+ .file("src/lib.rs", "")
+ .file(
+ "bar/Cargo.toml",
+ &format!(
+ r#"
+ [package]
+ name = "bar"
+ version = "0.1.0"
+
+ [dependencies]
+ xyz = {{ git = "{}", rev = "{}" }}
+ "#,
+ xyz_project.url(),
+ rev2
+ ),
+ )
+ .file("bar/src/lib.rs", "")
+ .build();
+
+ p.cargo("check").run();
+ p.cargo("tree")
+ .with_stdout(&format!(
+ "\
+foo v0.1.0 ([..]/foo)
+├── bar v0.1.0 ([..]/foo/bar)
+│ └── xyz v0.5.0 (file://[..]/xyz?rev={}#{})
+└── xyz v0.5.0 (file://[..]/xyz?rev={}#{})
+",
+ rev2,
+ &rev2.to_string()[..8],
+ rev1,
+ &rev1.to_string()[..8]
+ ))
+ .run();
+ // FIXME: This fails since xyz is ambiguous, but the
+ // possible pkgids are also ambiguous.
+ p.cargo("pkgid xyz")
+ .with_status(101)
+ .with_stderr(
+ "\
+error: There are multiple `xyz` packages in your project, and the specification `xyz` is ambiguous.
+Please re-run this command with one of the following specifications:
+ git+file://[..]/xyz?rev=[..]#0.5.0
+ git+file://[..]/xyz?rev=[..]#0.5.0
+",
+ )
+ .run();
+ // TODO, what should the `-p` value be here?
+ //p.cargo("update -p")
+}
diff --git a/src/tools/cargo/tests/testsuite/profile_config.rs b/src/tools/cargo/tests/testsuite/profile_config.rs
index 710a0d8ef..f8a9ae744 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::schema::TomlDebugInfo;
+use cargo::util_schemas::manifest::TomlDebugInfo;
use cargo_test_support::paths::CargoPathExt;
use cargo_test_support::registry::Package;
use cargo_test_support::{basic_lib_manifest, paths, project};
@@ -428,8 +428,8 @@ fn named_config_profile() {
let profiles = Profiles::new(&ws, profile_name).unwrap();
let crates_io = cargo::core::SourceId::crates_io(&config).unwrap();
- let a_pkg = PackageId::new("a", "0.1.0", crates_io).unwrap();
- let dep_pkg = PackageId::new("dep", "0.1.0", crates_io).unwrap();
+ let a_pkg = PackageId::try_new("a", "0.1.0", crates_io).unwrap();
+ let dep_pkg = PackageId::try_new("dep", "0.1.0", crates_io).unwrap();
// normal package
let kind = CompileKind::Host;
diff --git a/src/tools/cargo/tests/testsuite/profile_custom.rs b/src/tools/cargo/tests/testsuite/profile_custom.rs
index f7139e552..cf9828d37 100644
--- a/src/tools/cargo/tests/testsuite/profile_custom.rs
+++ b/src/tools/cargo/tests/testsuite/profile_custom.rs
@@ -86,6 +86,10 @@ fn invalid_profile_name() {
[ERROR] failed to parse manifest at [..]
Caused by:
+ TOML parse error at line 7, column 26
+ |
+ 7 | [profile.'.release-lto']
+ | ^^^^^^^^^^^^^^
invalid character `.` in profile name `.release-lto`
Allowed characters are letters, numbers, underscore, and hyphen.
",
@@ -626,6 +630,7 @@ See https://doc.rust-lang.org/cargo/reference/profiles.html for more on configur
),
);
+ let highlight = "^".repeat(name.len());
p.cargo("build")
.with_status(101)
.with_stderr(&format!(
@@ -633,11 +638,14 @@ See https://doc.rust-lang.org/cargo/reference/profiles.html for more on configur
error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
Caused by:
- profile name `{}` is reserved
+ TOML parse error at line 6, column 30
+ |
+ 6 | [profile.{name}]
+ | {highlight}
+ profile name `{name}` is reserved
Please choose a different name.
See https://doc.rust-lang.org/cargo/reference/profiles.html for more on configuring profiles.
",
- name
))
.run();
}
@@ -663,6 +671,10 @@ Caused by:
error: failed to parse manifest at `[ROOT]/foo/Cargo.toml`
Caused by:
+ TOML parse error at line 7, column 25
+ |
+ 7 | [profile.debug]
+ | ^^^^^
profile name `debug` is reserved
To configure the default development profile, use the name `dev` as in [profile.dev]
See https://doc.rust-lang.org/cargo/reference/profiles.html for more on configuring profiles.
diff --git a/src/tools/cargo/tests/testsuite/profile_trim_paths.rs b/src/tools/cargo/tests/testsuite/profile_trim_paths.rs
index 1d24c159b..8a883a004 100644
--- a/src/tools/cargo/tests/testsuite/profile_trim_paths.rs
+++ b/src/tools/cargo/tests/testsuite/profile_trim_paths.rs
@@ -83,8 +83,8 @@ fn release_profile_default_to_object() {
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc [..]\
-Zremap-path-scope=object \
- --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
- --remap-path-prefix=[CWD]= [..]
+ --remap-path-prefix=[CWD]=. \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]
[FINISHED] release [..]",
)
.run();
@@ -121,8 +121,8 @@ fn one_option() {
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc [..]\
-Zremap-path-scope={option} \
- --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
- --remap-path-prefix=[CWD]= [..]
+ --remap-path-prefix=[CWD]=. \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]
[FINISHED] dev [..]",
))
.run();
@@ -158,8 +158,8 @@ fn multiple_options() {
[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]= [..]
+ --remap-path-prefix=[CWD]=. \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]
[FINISHED] dev [..]",
)
.run();
@@ -193,8 +193,8 @@ fn profile_merge_works() {
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc [..]\
-Zremap-path-scope=diagnostics \
- --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
- --remap-path-prefix=[CWD]= [..]
+ --remap-path-prefix=[CWD]=. \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]
[FINISHED] custom [..]",
)
.run();
@@ -238,13 +238,13 @@ fn registry_dependency() {
[COMPILING] bar v0.0.1
[RUNNING] `rustc [..]\
-Zremap-path-scope=object \
- --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
- --remap-path-prefix={pkg_remap} [..]
+ --remap-path-prefix={pkg_remap} \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc [..]\
-Zremap-path-scope=object \
- --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
- --remap-path-prefix=[CWD]= [..]
+ --remap-path-prefix=[CWD]=. \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]
[FINISHED] dev [..]
[RUNNING] `target/debug/foo[EXE]`"
))
@@ -292,13 +292,13 @@ fn git_dependency() {
[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} [..]
+ --remap-path-prefix={pkg_remap} \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc [..]\
-Zremap-path-scope=object \
- --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
- --remap-path-prefix=[CWD]= [..]
+ --remap-path-prefix=[CWD]=. \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]
[FINISHED] dev [..]
[RUNNING] `target/debug/foo[EXE]`"
))
@@ -338,13 +338,13 @@ fn path_dependency() {
[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]= [..]
+ --remap-path-prefix=[CWD]=. \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc [..]\
-Zremap-path-scope=object \
- --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
- --remap-path-prefix=[CWD]= [..]
+ --remap-path-prefix=[CWD]=. \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]
[FINISHED] dev [..]
[RUNNING] `target/debug/foo[EXE]`"
))
@@ -387,13 +387,13 @@ fn path_dependency_outside_workspace() {
[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 [..]
+ --remap-path-prefix={bar_path}=bar-0.0.1 \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc [..]\
-Zremap-path-scope=object \
- --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
- --remap-path-prefix=[CWD]= [..]
+ --remap-path-prefix=[CWD]=. \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]
[FINISHED] dev [..]
[RUNNING] `target/debug/foo[EXE]`"
))
@@ -439,31 +439,81 @@ fn diagnostics_works() {
"\
[RUNNING] [..]rustc [..]\
-Zremap-path-scope=diagnostics \
- --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
- --remap-path-prefix={pkg_remap} [..]",
+ --remap-path-prefix={pkg_remap} \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]",
))
.with_stderr_contains(
"\
[RUNNING] [..]rustc [..]\
-Zremap-path-scope=diagnostics \
- --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
- --remap-path-prefix=[CWD]= [..]",
+ --remap-path-prefix=[CWD]=. \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]",
)
.run();
}
+#[cfg(target_os = "macos")]
+mod object_works {
+ use super::*;
+
+ fn inspect_debuginfo(path: &std::path::Path) -> Vec<u8> {
+ std::process::Command::new("nm")
+ .arg("-pa")
+ .arg(path)
+ .output()
+ .expect("nm works")
+ .stdout
+ }
+
+ #[cargo_test(requires_nm, nightly, reason = "-Zremap-path-scope is unstable")]
+ fn with_split_debuginfo_off() {
+ object_works_helper("off", inspect_debuginfo);
+ }
+
+ #[cargo_test(requires_nm, nightly, reason = "-Zremap-path-scope is unstable")]
+ fn with_split_debuginfo_packed() {
+ object_works_helper("packed", inspect_debuginfo);
+ }
+
+ #[cargo_test(requires_nm, nightly, reason = "-Zremap-path-scope is unstable")]
+ fn with_split_debuginfo_unpacked() {
+ object_works_helper("unpacked", inspect_debuginfo);
+ }
+}
+
#[cfg(target_os = "linux")]
-#[cargo_test(requires_readelf, nightly, reason = "-Zremap-path-scope is unstable")]
-fn object_works() {
- use std::os::unix::ffi::OsStrExt;
+mod object_works {
+ use super::*;
- let run_readelf = |path| {
+ fn inspect_debuginfo(path: &std::path::Path) -> Vec<u8> {
std::process::Command::new("readelf")
- .arg("-wi")
+ .arg("--debug-dump=info")
+ .arg("--debug-dump=no-follow-links") // older version can't recognized but just a warning
.arg(path)
.output()
.expect("readelf works")
- };
+ .stdout
+ }
+
+ #[cargo_test(requires_readelf, nightly, reason = "-Zremap-path-scope is unstable")]
+ fn with_split_debuginfo_off() {
+ object_works_helper("off", inspect_debuginfo);
+ }
+
+ #[cargo_test(requires_readelf, nightly, reason = "-Zremap-path-scope is unstable")]
+ fn with_split_debuginfo_packed() {
+ object_works_helper("packed", inspect_debuginfo);
+ }
+
+ #[cargo_test(requires_readelf, nightly, reason = "-Zremap-path-scope is unstable")]
+ fn with_split_debuginfo_unpacked() {
+ object_works_helper("unpacked", inspect_debuginfo);
+ }
+}
+
+#[cfg(unix)]
+fn object_works_helper(split_debuginfo: &str, run: impl Fn(&std::path::Path) -> Vec<u8>) {
+ use std::os::unix::ffi::OsStrExt;
let registry_src = paths::home().join(".cargo/registry/src");
let pkg_remap = format!("{}/[..]/bar-0.0.1=bar-0.0.1", registry_src.display());
@@ -478,14 +528,19 @@ fn object_works() {
let p = project()
.file(
"Cargo.toml",
- r#"
+ &format!(
+ r#"
[package]
name = "foo"
version = "0.0.1"
[dependencies]
bar = "0.0.1"
- "#,
+
+ [profile.dev]
+ split-debuginfo = "{split_debuginfo}"
+ "#
+ ),
)
.file("src/main.rs", "fn main() { bar::f(); }")
.build();
@@ -497,7 +552,7 @@ fn object_works() {
let bin_path = p.bin("foo");
assert!(bin_path.is_file());
- let stdout = run_readelf(bin_path).stdout;
+ let stdout = run(&bin_path);
// 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());
@@ -506,45 +561,69 @@ fn object_works() {
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")
+ .arg("--config")
+ .arg(r#"profile.dev.trim-paths="object""#)
.masquerade_as_nightly_cargo(&["-Ztrim-paths"])
.with_stderr(&format!(
"\
[COMPILING] bar v0.0.1
-[RUNNING] `rustc [..]\
+[RUNNING] `rustc [..]-C split-debuginfo={split_debuginfo} [..]\
-Zremap-path-scope=object \
- --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
- --remap-path-prefix={pkg_remap} [..]
+ --remap-path-prefix={pkg_remap} \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]
[COMPILING] foo v0.0.1 ([CWD])
-[RUNNING] `rustc [..]\
+[RUNNING] `rustc [..]-C split-debuginfo={split_debuginfo} [..]\
-Zremap-path-scope=object \
- --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..] \
- --remap-path-prefix=[CWD]= [..]
+ --remap-path-prefix=[CWD]=. \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]
[FINISHED] dev [..]",
))
.run();
let bin_path = p.bin("foo");
assert!(bin_path.is_file());
- let stdout = run_readelf(bin_path).stdout;
+ let stdout = run(&bin_path);
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());
+ for line in stdout.split(|c| c == &b'\n') {
+ let registry = memchr::memmem::find(line, registry_src_bytes).is_none();
+ let local = memchr::memmem::find(line, pkg_root).is_none();
+ if registry && local {
+ continue;
+ }
+
+ #[cfg(target_os = "macos")]
+ {
+ // `OSO` symbols can't be trimmed at this moment.
+ // See <https://github.com/rust-lang/rust/issues/116948#issuecomment-1793617018>
+ if memchr::memmem::find(line, b" OSO ").is_some() {
+ continue;
+ }
+
+ // on macOS `SO` symbols are embedded in final binaries and should be trimmed.
+ // See rust-lang/rust#117652.
+ if memchr::memmem::find(line, b" SO ").is_some() {
+ continue;
+ }
+ }
+
+ #[cfg(target_os = "linux")]
+ {
+ // There is a bug in rustc `-Zremap-path-scope`.
+ // See rust-lang/rust/pull/118518
+ if memchr::memmem::find(line, b"DW_AT_comp_dir").is_some() {
+ continue;
+ }
+ if memchr::memmem::find(line, b"DW_AT_GNU_dwo_name").is_some() {
+ continue;
+ }
+ }
+
+ panic!(
+ "unexpected untrimmed symbol: {}",
+ String::from_utf8(line.into()).unwrap()
+ );
+ }
}
// TODO: might want to move to test/testsuite/build_script.rs once stabilized.
@@ -612,3 +691,69 @@ fn custom_build_env_var_trim_paths() {
.run();
}
}
+
+#[cfg(unix)]
+#[cargo_test(requires_lldb, nightly, reason = "-Zremap-path-scope is unstable")]
+fn lldb_works_after_trimmed() {
+ use cargo_test_support::compare::match_contains;
+
+ let run_lldb = |path| {
+ std::process::Command::new("lldb")
+ .args(["-o", "breakpoint set --file src/main.rs --line 4"])
+ .args(["-o", "run"])
+ .args(["-o", "continue"])
+ .args(["-o", "exit"])
+ .arg("--no-use-colors")
+ .arg(path)
+ .output()
+ .expect("lldb works")
+ };
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [profile.dev]
+ trim-paths = "object"
+ "#,
+ )
+ .file(
+ "src/main.rs",
+ r#"
+ fn main() {
+ let msg = "Hello, Ferris!";
+ println!("{msg}");
+ }
+ "#,
+ )
+ .build();
+
+ p.cargo("build --verbose -Ztrim-paths")
+ .masquerade_as_nightly_cargo(&["-Ztrim-paths"])
+ .with_stderr_contains(
+ "\
+[RUNNING] `rustc [..]\
+ -Zremap-path-scope=object \
+ --remap-path-prefix=[CWD]=. \
+ --remap-path-prefix=[..]/lib/rustlib/src/rust=/rustc/[..]",
+ )
+ .run();
+
+ let bin_path = p.bin("foo");
+ assert!(bin_path.is_file());
+ let stdout = String::from_utf8(run_lldb(bin_path).stdout).unwrap();
+ match_contains("[..]stopped[..]", &stdout, None).unwrap();
+ match_contains("[..]stop reason = breakpoint[..]", &stdout, None).unwrap();
+ match_contains(
+ "\
+(lldb) continue
+Hello, Ferris!",
+ &stdout,
+ None,
+ )
+ .unwrap();
+}
diff --git a/src/tools/cargo/tests/testsuite/pub_priv.rs b/src/tools/cargo/tests/testsuite/pub_priv.rs
index b2160e0fa..b3d87ce4c 100644
--- a/src/tools/cargo/tests/testsuite/pub_priv.rs
+++ b/src/tools/cargo/tests/testsuite/pub_priv.rs
@@ -199,7 +199,7 @@ Caused by:
}
#[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")]
-fn workspace_dep_made_public() {
+fn workspace_pub_disallowed() {
Package::new("foo1", "0.1.0")
.file("src/lib.rs", "pub struct FromFoo;")
.publish();
@@ -244,5 +244,238 @@ fn workspace_dep_made_public() {
p.cargo("check")
.masquerade_as_nightly_cargo(&["public-dependency"])
+ .with_status(101)
+ .with_stderr(
+ "\
+error: failed to parse manifest at `[CWD]/Cargo.toml`
+
+Caused by:
+ foo2 is public, but workspace dependencies cannot be public
+",
+ )
+ .run()
+}
+
+#[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")]
+fn allow_priv_in_tests() {
+ Package::new("priv_dep", "0.1.0")
+ .file("src/lib.rs", "pub struct FromPriv;")
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ cargo-features = ["public-dependency"]
+
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ priv_dep = {version = "0.1.0", public = false}
+ "#,
+ )
+ .file(
+ "tests/mod.rs",
+ "
+ extern crate priv_dep;
+ pub fn use_priv(_: priv_dep::FromPriv) {}
+ ",
+ )
+ .build();
+
+ p.cargo("check --tests --message-format=short")
+ .masquerade_as_nightly_cargo(&["public-dependency"])
+ .with_stderr(
+ "\
+[UPDATING] `[..]` index
+[DOWNLOADING] crates ...
+[DOWNLOADED] priv_dep v0.1.0 ([..])
+[CHECKING] priv_dep v0.1.0
+[CHECKING] foo v0.0.1 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run()
+}
+
+#[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")]
+fn allow_priv_in_benchs() {
+ Package::new("priv_dep", "0.1.0")
+ .file("src/lib.rs", "pub struct FromPriv;")
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ cargo-features = ["public-dependency"]
+
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ priv_dep = {version = "0.1.0", public = false}
+ "#,
+ )
+ .file(
+ "benches/mod.rs",
+ "
+ extern crate priv_dep;
+ pub fn use_priv(_: priv_dep::FromPriv) {}
+ ",
+ )
+ .build();
+
+ p.cargo("check --benches --message-format=short")
+ .masquerade_as_nightly_cargo(&["public-dependency"])
+ .with_stderr(
+ "\
+[UPDATING] `[..]` index
+[DOWNLOADING] crates ...
+[DOWNLOADED] priv_dep v0.1.0 ([..])
+[CHECKING] priv_dep v0.1.0
+[CHECKING] foo v0.0.1 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run()
+}
+
+#[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")]
+fn allow_priv_in_bins() {
+ Package::new("priv_dep", "0.1.0")
+ .file("src/lib.rs", "pub struct FromPriv;")
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ cargo-features = ["public-dependency"]
+
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ priv_dep = {version = "0.1.0", public = false}
+ "#,
+ )
+ .file(
+ "src/main.rs",
+ "
+ extern crate priv_dep;
+ pub fn use_priv(_: priv_dep::FromPriv) {}
+ fn main() {}
+ ",
+ )
+ .build();
+
+ p.cargo("check --bins --message-format=short")
+ .masquerade_as_nightly_cargo(&["public-dependency"])
+ .with_stderr(
+ "\
+[UPDATING] `[..]` index
+[DOWNLOADING] crates ...
+[DOWNLOADED] priv_dep v0.1.0 ([..])
+[CHECKING] priv_dep v0.1.0
+[CHECKING] foo v0.0.1 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run()
+}
+
+#[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")]
+fn allow_priv_in_examples() {
+ Package::new("priv_dep", "0.1.0")
+ .file("src/lib.rs", "pub struct FromPriv;")
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ cargo-features = ["public-dependency"]
+
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [dependencies]
+ priv_dep = {version = "0.1.0", public = false}
+ "#,
+ )
+ .file(
+ "examples/lib.rs",
+ "
+ extern crate priv_dep;
+ pub fn use_priv(_: priv_dep::FromPriv) {}
+ fn main() {}
+ ",
+ )
+ .build();
+
+ p.cargo("check --examples --message-format=short")
+ .masquerade_as_nightly_cargo(&["public-dependency"])
+ .with_stderr(
+ "\
+[UPDATING] `[..]` index
+[DOWNLOADING] crates ...
+[DOWNLOADED] priv_dep v0.1.0 ([..])
+[CHECKING] priv_dep v0.1.0
+[CHECKING] foo v0.0.1 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
+ .run()
+}
+
+#[cargo_test(nightly, reason = "exported_private_dependencies lint is unstable")]
+fn allow_priv_in_custom_build() {
+ Package::new("priv_dep", "0.1.0")
+ .file("src/lib.rs", "pub struct FromPriv;")
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ cargo-features = ["public-dependency"]
+
+ [package]
+ name = "foo"
+ version = "0.0.1"
+
+ [build-dependencies]
+ priv_dep = "0.1.0"
+ "#,
+ )
+ .file("src/main.rs", "fn main() {}")
+ .file(
+ "build.rs",
+ "
+ extern crate priv_dep;
+ pub fn use_priv(_: priv_dep::FromPriv) {}
+ fn main() {}
+ ",
+ )
+ .build();
+
+ p.cargo("check --all-targets --message-format=short")
+ .masquerade_as_nightly_cargo(&["public-dependency"])
+ .with_stderr(
+ "\
+[UPDATING] `[..]` index
+[DOWNLOADING] crates ...
+[DOWNLOADED] priv_dep v0.1.0 ([..])
+[COMPILING] priv_dep v0.1.0
+[COMPILING] foo v0.0.1 ([CWD])
+[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
+",
+ )
.run()
}
diff --git a/src/tools/cargo/tests/testsuite/publish_lockfile.rs b/src/tools/cargo/tests/testsuite/publish_lockfile.rs
index 35da5131f..0a7c23368 100644
--- a/src/tools/cargo/tests/testsuite/publish_lockfile.rs
+++ b/src/tools/cargo/tests/testsuite/publish_lockfile.rs
@@ -92,7 +92,17 @@ src/main.rs
",
)
.run();
- p.cargo("package").with_stdout("").run();
+ p.cargo("package")
+ .with_stderr(
+ "\
+[PACKAGING] foo v0.0.1 [..]
+[VERIFYING] foo v0.0.1 [..]
+[COMPILING] foo v0.0.1 [..]
+[FINISHED] [..]
+[PACKAGED] 4 files, [..]
+",
+ )
+ .run();
let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap();
validate_crate_contents(
diff --git a/src/tools/cargo/tests/testsuite/registry.rs b/src/tools/cargo/tests/testsuite/registry.rs
index b5dff2746..6107b6f59 100644
--- a/src/tools/cargo/tests/testsuite/registry.rs
+++ b/src/tools/cargo/tests/testsuite/registry.rs
@@ -549,7 +549,7 @@ fn lockfile_locks() {
p.root().move_into_the_past();
Package::new("bar", "0.0.2").publish();
- p.cargo("check").with_stdout("").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
}
#[cargo_test]
@@ -602,7 +602,7 @@ fn lockfile_locks_transitively() {
Package::new("baz", "0.0.2").publish();
Package::new("bar", "0.0.2").dep("baz", "*").publish();
- p.cargo("check").with_stdout("").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
}
#[cargo_test]
@@ -739,7 +739,7 @@ fn yanks_in_lockfiles_are_ok() {
Package::new("bar", "0.0.1").yanked(true).publish();
- p.cargo("check").with_stdout("").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
p.cargo("update")
.with_status(101)
@@ -792,7 +792,7 @@ fn yanks_in_lockfiles_are_ok_for_other_update() {
Package::new("bar", "0.0.1").yanked(true).publish();
Package::new("baz", "0.0.1").publish();
- p.cargo("check").with_stdout("").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
Package::new("baz", "0.0.2").publish();
@@ -868,7 +868,18 @@ fn yanks_in_lockfiles_are_ok_with_new_dep() {
"#,
);
- p.cargo("check").with_stdout("").run();
+ p.cargo("check")
+ .with_stderr(
+ "\
+[UPDATING] `dummy-registry` index
+[DOWNLOADING] crates ...
+[DOWNLOADED] baz v0.0.1 (registry `dummy-registry`)
+[CHECKING] baz v0.0.1
+[CHECKING] foo v0.0.1 [..]
+[FINISHED] [..]
+",
+ )
+ .run();
}
#[cargo_test]
@@ -1272,7 +1283,7 @@ fn git_and_registry_dep() {
p.root().move_into_the_past();
println!("second");
- p.cargo("check").with_stdout("").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
}
#[cargo_test]
diff --git a/src/tools/cargo/tests/testsuite/replace.rs b/src/tools/cargo/tests/testsuite/replace.rs
index b9de51d2f..6c31a023e 100644
--- a/src/tools/cargo/tests/testsuite/replace.rs
+++ b/src/tools/cargo/tests/testsuite/replace.rs
@@ -305,7 +305,7 @@ fn transitive() {
)
.run();
- p.cargo("check").with_stdout("").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
}
#[cargo_test]
@@ -354,7 +354,7 @@ fn persists_across_rebuilds() {
)
.run();
- p.cargo("check").with_stdout("").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
}
#[cargo_test]
@@ -530,7 +530,7 @@ fn override_adds_some_deps() {
)
.run();
- p.cargo("check").with_stdout("").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
Package::new("baz", "0.1.2").publish();
p.cargo("update")
@@ -550,7 +550,7 @@ fn override_adds_some_deps() {
)
.run();
- p.cargo("check").with_stdout("").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
}
#[cargo_test]
@@ -601,8 +601,8 @@ fn locked_means_locked_yes_no_seriously_i_mean_locked() {
p.cargo("check").run();
- p.cargo("check").with_stdout("").run();
- p.cargo("check").with_stdout("").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
+ p.cargo("check").with_stderr("[FINISHED] [..]").run();
}
#[cargo_test]
@@ -1399,7 +1399,7 @@ fn override_respects_spec_metadata() {
[..]
[..]
[..]
-error: could not compile `foo` (lib) due to previous error
+error: could not compile `foo` (lib) due to 1 previous error
",
)
.with_status(101)
diff --git a/src/tools/cargo/tests/testsuite/rust_version.rs b/src/tools/cargo/tests/testsuite/rust_version.rs
index 21321b7c5..d0ea33c83 100644
--- a/src/tools/cargo/tests/testsuite/rust_version.rs
+++ b/src/tools/cargo/tests/testsuite/rust_version.rs
@@ -245,49 +245,81 @@ fn dependency_rust_version_newer_than_package() {
.file("src/main.rs", "fn main(){}")
.build();
+ p.cargo("check")
+ .arg("-Zmsrv-policy")
+ .masquerade_as_nightly_cargo(&["msrv-policy"])
+ .run();
p.cargo("check --ignore-rust-version")
.arg("-Zmsrv-policy")
.masquerade_as_nightly_cargo(&["msrv-policy"])
- // This shouldn't fail
- .with_status(101)
+ .run();
+}
+
+#[cargo_test]
+fn dependency_rust_version_older_and_newer_than_package() {
+ Package::new("bar", "1.5.0")
+ .rust_version("1.55.0")
+ .file("src/lib.rs", "fn other_stuff() {}")
+ .publish();
+ Package::new("bar", "1.6.0")
+ .rust_version("1.65.0")
+ .file("src/lib.rs", "fn other_stuff() {}")
+ .publish();
+
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.0.1"
+ authors = []
+ rust-version = "1.60.0"
+ [dependencies]
+ bar = "1.0.0"
+ "#,
+ )
+ .file("src/main.rs", "fn main(){}")
+ .build();
+
+ p.cargo("check --ignore-rust-version")
+ .arg("-Zmsrv-policy")
+ .masquerade_as_nightly_cargo(&["msrv-policy"])
+ // This should pick 1.6.0
.with_stderr(
"\
[UPDATING] `dummy-registry` index
-[ERROR] failed to select a version for the requirement `bar = \"^1.0.0\"`
-candidate versions found which didn't match: 1.6.0
-location searched: `dummy-registry` index (which is replacing registry `crates-io`)
-required by package `foo v0.0.1 ([CWD])`
-perhaps a crate was updated and forgotten to be re-vendored?
+[DOWNLOADING] crates ...
+[DOWNLOADED] bar v1.5.0 (registry `dummy-registry`)
+[CHECKING] bar v1.5.0
+[CHECKING] [..]
+[FINISHED] [..]
",
)
.run();
p.cargo("check")
.arg("-Zmsrv-policy")
.masquerade_as_nightly_cargo(&["msrv-policy"])
- .with_status(101)
- // This should have a better error message
.with_stderr(
"\
-[UPDATING] `dummy-registry` index
-[ERROR] failed to select a version for the requirement `bar = \"^1.0.0\"`
-candidate versions found which didn't match: 1.6.0
-location searched: `dummy-registry` index (which is replacing registry `crates-io`)
-required by package `foo v0.0.1 ([CWD])`
-perhaps a crate was updated and forgotten to be re-vendored?
+[FINISHED] [..]
",
)
.run();
}
#[cargo_test]
-fn dependency_rust_version_older_and_newer_than_package() {
- Package::new("bar", "1.5.0")
- .rust_version("1.55.0")
+fn dependency_rust_version_backtracking() {
+ Package::new("has-rust-version", "1.6.0")
+ .rust_version("1.65.0")
.file("src/lib.rs", "fn other_stuff() {}")
.publish();
- Package::new("bar", "1.6.0")
- .rust_version("1.65.0")
+ Package::new("no-rust-version", "2.1.0")
+ .file("src/lib.rs", "fn other_stuff() {}")
+ .publish();
+ Package::new("no-rust-version", "2.2.0")
.file("src/lib.rs", "fn other_stuff() {}")
+ .dep("has-rust-version", "1.6.0")
.publish();
let p = project()
@@ -300,7 +332,7 @@ fn dependency_rust_version_older_and_newer_than_package() {
authors = []
rust-version = "1.60.0"
[dependencies]
- bar = "1.0.0"
+ no-rust-version = "2"
"#,
)
.file("src/main.rs", "fn main(){}")
@@ -309,13 +341,14 @@ fn dependency_rust_version_older_and_newer_than_package() {
p.cargo("check --ignore-rust-version")
.arg("-Zmsrv-policy")
.masquerade_as_nightly_cargo(&["msrv-policy"])
- // This should pick 1.6.0
.with_stderr(
"\
[UPDATING] `dummy-registry` index
[DOWNLOADING] crates ...
-[DOWNLOADED] bar v1.5.0 (registry `dummy-registry`)
-[CHECKING] bar v1.5.0
+[DOWNLOADED] no-rust-version v2.2.0 (registry `dummy-registry`)
+[DOWNLOADED] has-rust-version v1.6.0 (registry `dummy-registry`)
+[CHECKING] has-rust-version v1.6.0
+[CHECKING] no-rust-version v2.2.0
[CHECKING] [..]
[FINISHED] [..]
",
diff --git a/src/tools/cargo/tests/testsuite/rustflags.rs b/src/tools/cargo/tests/testsuite/rustflags.rs
index 6677beb04..788889951 100644
--- a/src/tools/cargo/tests/testsuite/rustflags.rs
+++ b/src/tools/cargo/tests/testsuite/rustflags.rs
@@ -456,7 +456,7 @@ fn env_rustflags_no_recompile() {
p.cargo("check").env("RUSTFLAGS", "--cfg foo").run();
p.cargo("check")
.env("RUSTFLAGS", "--cfg foo")
- .with_stdout("")
+ .with_stderr("[FINISHED] [..]")
.run();
}
@@ -944,7 +944,7 @@ fn build_rustflags_no_recompile() {
p.cargo("check").env("RUSTFLAGS", "--cfg foo").run();
p.cargo("check")
.env("RUSTFLAGS", "--cfg foo")
- .with_stdout("")
+ .with_stderr("[FINISHED] [..]")
.run();
}
@@ -1658,7 +1658,7 @@ fn host_config_rustflags_with_target() {
// regression test for https://github.com/rust-lang/cargo/issues/10206
let p = project()
.file("src/lib.rs", "")
- .file("build.rs.rs", "fn main() { assert!(cfg!(foo)); }")
+ .file("build.rs", "fn main() { assert!(cfg!(foo)); }")
.file(".cargo/config.toml", "target-applies-to-host = false")
.build();
diff --git a/src/tools/cargo/tests/testsuite/test.rs b/src/tools/cargo/tests/testsuite/test.rs
index 5f6528109..6357b950c 100644
--- a/src/tools/cargo/tests/testsuite/test.rs
+++ b/src/tools/cargo/tests/testsuite/test.rs
@@ -1433,7 +1433,7 @@ fn test_then_build() {
.with_stdout_contains("running 0 tests")
.run();
- p.cargo("build").with_stdout("").run();
+ p.cargo("build").with_stderr("[FINISHED] [..]").run();
}
#[cargo_test]
@@ -2423,7 +2423,7 @@ fn dylib_doctest2() {
)
.build();
- p.cargo("test").with_stdout("").run();
+ p.cargo("test").with_stderr("[FINISHED] [..]").run();
}
#[cargo_test]
@@ -3556,6 +3556,39 @@ fn cyclic_dev() {
}
#[cargo_test]
+fn cyclical_dep_with_missing_feature() {
+ // Checks for error handling when a cyclical dev-dependency specify a
+ // feature that doesn't exist.
+ let p = project()
+ .file(
+ "Cargo.toml",
+ r#"
+ [package]
+ name = "foo"
+ version = "0.1.0"
+
+ [dev-dependencies]
+ foo = { path = ".", features = ["missing"] }
+ "#,
+ )
+ .file("src/lib.rs", "")
+ .build();
+ p.cargo("check")
+ .with_status(101)
+ .with_stderr(
+ "error: failed to select a version for `foo`.
+ ... required by package `foo v0.1.0 ([..]/foo)`
+versions that meet the requirements `*` are: 0.1.0
+
+the package `foo` depends on `foo`, with features: `missing` but `foo` does not have these features.
+
+
+failed to select a version for `foo` which could resolve this conflict",
+ )
+ .run();
+}
+
+#[cargo_test]
fn publish_a_crate_without_tests() {
Package::new("testless", "0.1.0")
.file(
diff --git a/src/tools/cargo/tests/testsuite/update.rs b/src/tools/cargo/tests/testsuite/update.rs
index e636435b0..40bc0b476 100644
--- a/src/tools/cargo/tests/testsuite/update.rs
+++ b/src/tools/cargo/tests/testsuite/update.rs
@@ -1091,11 +1091,8 @@ rustdns.workspace = true
p.cargo("update -p rootcrate")
.with_stderr(&format!(
"\
-[UPDATING] git repository `{}`
[UPDATING] rootcrate v2.29.8 ([CWD]/rootcrate) -> v2.29.81
-[UPDATING] rustdns v0.5.0 ([..]) -> [..]
[UPDATING] subcrate v2.29.8 ([CWD]/subcrate) -> v2.29.81",
- git_project.url(),
))
.run();
}
@@ -1182,11 +1179,96 @@ rustdns.workspace = true
p.cargo("update -p crate2")
.with_stderr(&format!(
"\
-[UPDATING] git repository `{}`
[UPDATING] crate1 v2.29.8 ([CWD]/crate1) -> v2.29.81
-[UPDATING] crate2 v2.29.8 ([CWD]/crate2) -> v2.29.81
-[UPDATING] rustdns v0.5.0 ([..]) -> [..]",
+[UPDATING] crate2 v2.29.8 ([CWD]/crate2) -> v2.29.81",
+ ))
+ .run();
+}
+
+#[cargo_test]
+fn update_only_members_with_workspace() {
+ let git_project = git::new("rustdns", |project| {
+ project
+ .file("Cargo.toml", &basic_lib_manifest("rustdns"))
+ .file("src/lib.rs", "pub fn bar() {}")
+ });
+
+ let workspace_toml = format!(
+ r#"
+[workspace.package]
+version = "2.29.8"
+edition = "2021"
+publish = false
+
+[workspace]
+members = [
+ "crate2",
+ "crate1",
+]
+resolver = "2"
+
+[workspace.dependencies]
+# Internal crates
+crate1 = {{ version = "*", path = "./crate1" }}
+
+# External dependencies
+rustdns = {{ version = "0.5.0", default-features = false, git = "{}" }}
+ "#,
+ git_project.url()
+ );
+ let p = project()
+ .file("Cargo.toml", &workspace_toml)
+ .file(
+ "crate2/Cargo.toml",
+ r#"
+[package]
+name = "crate2"
+version.workspace = true
+edition.workspace = true
+publish.workspace = true
+
+[dependencies]
+crate1.workspace = true
+"#,
+ )
+ .file("crate2/src/main.rs", "fn main() {}")
+ .file(
+ "crate1/Cargo.toml",
+ r#"
+[package]
+name = "crate1"
+version.workspace = true
+edition.workspace = true
+publish.workspace = true
+
+[dependencies]
+rustdns.workspace = true
+"#,
+ )
+ .file("crate1/src/lib.rs", "pub foo() {}")
+ .build();
+
+ // First time around we should compile both foo and bar
+ p.cargo("generate-lockfile")
+ .with_stderr(&format!(
+ "[UPDATING] git repository `{}`\n",
git_project.url(),
))
.run();
+ // Modify a file manually, shouldn't trigger a recompile
+ git_project.change_file("src/lib.rs", r#"pub fn bar() { println!("hello!"); }"#);
+ // Commit the changes and make sure we don't trigger a recompile because the
+ // lock file says not to change
+ let repo = git2::Repository::open(&git_project.root()).unwrap();
+ git::add(&repo);
+ git::commit(&repo);
+ p.change_file("Cargo.toml", &workspace_toml.replace("2.29.8", "2.29.81"));
+
+ p.cargo("update --workspace")
+ .with_stderr(
+ "\
+[UPDATING] crate1 v2.29.8 ([CWD]/crate1) -> v2.29.81
+[UPDATING] crate2 v2.29.8 ([CWD]/crate2) -> v2.29.81",
+ )
+ .run();
}
diff --git a/src/tools/cargo/tests/testsuite/workspaces.rs b/src/tools/cargo/tests/testsuite/workspaces.rs
index 94b5142f4..53ddc1616 100644
--- a/src/tools/cargo/tests/testsuite/workspaces.rs
+++ b/src/tools/cargo/tests/testsuite/workspaces.rs
@@ -2198,6 +2198,7 @@ fn ws_err_unused() {
"[features]",
"[target]",
"[badges]",
+ "[lints]",
] {
let p = project()
.file(
diff --git a/src/tools/cargo/triagebot.toml b/src/tools/cargo/triagebot.toml
index cdf1090a1..c344a0b4d 100644
--- a/src/tools/cargo/triagebot.toml
+++ b/src/tools/cargo/triagebot.toml
@@ -262,6 +262,7 @@ trigger_files = [
"src/bin/cargo/commands/fix.rs",
"src/cargo/ops/fix.rs",
"src/cargo/util/lockserver.rs",
+ "crates/rustfix/",
]
[autolabel."Command-generate-lockfile"]
diff --git a/src/tools/cargo/windows.manifest.xml b/src/tools/cargo/windows.manifest.xml
new file mode 100644
index 000000000..8a9d5d1b7
--- /dev/null
+++ b/src/tools/cargo/windows.manifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!--
+This is a Windows application manifest file.
+See: https://docs.microsoft.com/en-us/windows/win32/sbscs/application-manifests
+-->
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+ <!-- Versions cargo supports as hosts -->
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <!-- Windows 7 --><supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+ <!-- Windows 8 --><supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+ <!-- Windows 8.1 --><supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+ <!-- Windows 10 and 11 --><supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+ </application>
+ </compatibility>
+ <!-- Use UTF-8 code page -->
+ <asmv3:application>
+ <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">
+ <activeCodePage>UTF-8</activeCodePage>
+ </asmv3:windowsSettings>
+ </asmv3:application>
+ <!-- Remove (most) legacy path limits -->
+ <asmv3:application>
+ <asmv3:windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
+ <ws2:longPathAware>true</ws2:longPathAware>
+ </asmv3:windowsSettings>
+ </asmv3:application>
+</assembly>