diff options
Diffstat (limited to 'src/tools/cargo')
336 files changed, 9377 insertions, 4032 deletions
diff --git a/src/tools/cargo/.github/workflows/main.yml b/src/tools/cargo/.github/workflows/main.yml index c522a19a1..3deae6355 100644 --- a/src/tools/cargo/.github/workflows/main.yml +++ b/src/tools/cargo/.github/workflows/main.yml @@ -12,9 +12,6 @@ defaults: permissions: contents: read -env: - CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse - jobs: # Check Code style quickly by running `rustfmt` over all code rustfmt: @@ -108,10 +105,6 @@ jobs: - uses: actions/checkout@v3 - name: Dump Environment run: ci/dump-environment.sh - - name: Update Rustup (temporary workaround) - run: rustup self update - shell: bash - if: startsWith(matrix.os, 'windows') - run: rustup update --no-self-update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - run: rustup target add ${{ matrix.other }} - run: rustup component add rustc-dev llvm-tools-preview rust-docs @@ -225,6 +218,7 @@ jobs: name: bors build finished needs: - build_std + - clippy - docs - lockfile - resolver @@ -241,6 +235,7 @@ jobs: name: bors build finished needs: - build_std + - clippy - docs - lockfile - resolver diff --git a/src/tools/cargo/CHANGELOG.md b/src/tools/cargo/CHANGELOG.md index 5cb4d11c5..0784b2638 100644 --- a/src/tools/cargo/CHANGELOG.md +++ b/src/tools/cargo/CHANGELOG.md @@ -1,10 +1,104 @@ # Changelog +## Cargo 1.72 (2023-08-24) +[64fb38c9...HEAD](https://github.com/rust-lang/cargo/compare/64fb38c9...HEAD) + +### Added + +- Add support of the "default" keyword to reset previously set `build.jobs` + parallelism back to the default. + [#12222](https://github.com/rust-lang/cargo/pull/12222) + +### Changed + +- Cargo now warns when an edition 2021 package is in a virtual workspace and + `workspace.resolver` is not set. It is recommended to set the resolver + version for workspaces explicitly. + [#10910](https://github.com/rust-lang/cargo/pull/10910) +- Set IBM AIX shared libraries search path to `LIBPATH`. + [#11968](https://github.com/rust-lang/cargo/pull/11968) +- Don't pass `-C debuginfo=0` to rustc as it is the default value. + [#12022](https://github.com/rust-lang/cargo/pull/12022) + [#12205](https://github.com/rust-lang/cargo/pull/12205) +- Added a message on reusing previous temporary path on `cargo install` failures. + [#12231](https://github.com/rust-lang/cargo/pull/12231) +- Added a message when `rustup` override shorthand is put in a wrong position. + [#12226](https://github.com/rust-lang/cargo/pull/12226) + +### Fixed + +- `cargo clean` uses `remove_dir_all` as a fallback to resolve race conditions. + [#11442](https://github.com/rust-lang/cargo/pull/11442) +- Reduced the chance Cargo re-formats the user's `[features]` table. + [#12191](https://github.com/rust-lang/cargo/pull/12191) +- Fixed nested Git submodules not able to fetch. + [#12244](https://github.com/rust-lang/cargo/pull/12244) + +### Nightly only + +- 🔥 The `-Zscript` is an experimental feature to add unstable support for + single-file packages in Cargo, so we can explore the design and resolve + questions with an implementation to collect feedback on. + ([eRFC 3424](https://github.com/rust-lang/rfcs/blob/master/text/3424-cargo-script.md)) + [docs](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#script) + [#12245](https://github.com/rust-lang/cargo/pull/12245) +- Automatically inherit workspace lints when running `cargo new`/`cargo init`. + [#12174](https://github.com/rust-lang/cargo/pull/12174) + +### Documentation + +- Added a description of `Cargo.lock` conflicts in the Cargo FAQ. + [#12185](https://github.com/rust-lang/cargo/pull/12185) +- Added a small note about indexes ignoring SemVer build metadata. + [#12206](https://github.com/rust-lang/cargo/pull/12206) +- Added doc comments for types and friends in `cargo::sources` module. + [#12192](https://github.com/rust-lang/cargo/pull/12192) + [#12239](https://github.com/rust-lang/cargo/pull/12239) + [#12247](https://github.com/rust-lang/cargo/pull/12247) + +### Internal + +- Updated to `gix` 0.45 for multi-round pack negotiations. + [#12236](https://github.com/rust-lang/cargo/pull/12236) +- Updated to `curl-sys` 0.4.63, which corresponds to curl 8.1.2. + [#12218](https://github.com/rust-lang/cargo/pull/12218) +- Removed unused features from `windows-sys` dependency. + [#12176](https://github.com/rust-lang/cargo/pull/12176) +- Refactored compiler invocations + [#12211](https://github.com/rust-lang/cargo/pull/12211) +- Refactored git and registry sources, and registry data. + [#12203](https://github.com/rust-lang/cargo/pull/12203) + [#12197](https://github.com/rust-lang/cargo/pull/12197) + [#12240](https://github.com/rust-lang/cargo/pull/12240) + [#12248](https://github.com/rust-lang/cargo/pull/12248) +- Lexicographically order `-Z` flags. + [#12182](https://github.com/rust-lang/cargo/pull/12182) + [#12223](https://github.com/rust-lang/cargo/pull/12223) + [#12224](https://github.com/rust-lang/cargo/pull/12224) +- Several Cargo's own test infra improvements and speed-ups. + [#12184](https://github.com/rust-lang/cargo/pull/12184) + [#12188](https://github.com/rust-lang/cargo/pull/12188) + [#12189](https://github.com/rust-lang/cargo/pull/12189) + [#12194](https://github.com/rust-lang/cargo/pull/12194) + [#12199](https://github.com/rust-lang/cargo/pull/12199) +- Migrated print-ban from test to clippy + [#12246](https://github.com/rust-lang/cargo/pull/12246) + ## Cargo 1.71 (2023-07-13) -[84b7041f...HEAD](https://github.com/rust-lang/cargo/compare/84b7041f...HEAD) +[84b7041f...rust-1.71.0](https://github.com/rust-lang/cargo/compare/84b7041f...rust-1.71.0) ### Added +- Allowed named debuginfo options in Cargo.toml. + [docs](https://doc.rust-lang.org/nightly/cargo/reference/profiles.html#debug) + [#11958](https://github.com/rust-lang/cargo/pull/11958) +- Added `workspace_default_members` to the output of `cargo metadata`. + [#11978](https://github.com/rust-lang/cargo/pull/11978) +- `cargo add` now considers `rust-version` when selecting packages. + [#12078](https://github.com/rust-lang/cargo/pull/12078) +- Automatically inherit workspace fields when running `cargo new`/`cargo init`. + [#12069](https://github.com/rust-lang/cargo/pull/12069) + ### Changed - ❗ Optimized the usage under `rustup`. When Cargo detects it will run `rustc` @@ -23,39 +117,151 @@ [#12107](https://github.com/rust-lang/cargo/pull/12107) - Better error message when getting an empty dependency table in Cargo.toml. [#11997](https://github.com/rust-lang/cargo/pull/11997) -- Use restricted Damerau-Levenshtein algorithm to provide typo suggestions. - [#11963](https://github.com/rust-lang/cargo/pull/11963) +- Better error message when empty dependency was specified in Cargo.toml. + [#12001](https://github.com/rust-lang/cargo/pull/12001) +- `--help` text is now wrapping for readability on narrow screens. + [#12013](https://github.com/rust-lang/cargo/pull/12013) +- Tweaked the order of arguments in `--help` text to clarify role of `--bin`. + [#12157](https://github.com/rust-lang/cargo/pull/12157) +- `rust-version` is included in `cargo publish` requests to registries. + [#12041](https://github.com/rust-lang/cargo/pull/12041) ### Fixed - Corrected the bug report URL for `cargo clippy --fix`. [#11882](https://github.com/rust-lang/cargo/pull/11882) +- Cargo now applies `[env]` to rust invocations for target info discovery. + [#12029](https://github.com/rust-lang/cargo/pull/12029) +- Fixed tokens not redacted in http debug when using HTTP/2. + [#12095](https://github.com/rust-lang/cargo/pull/12095) +- Fixed `-C debuginfo` not passed in some situation, leading to build cache miss. + [#12165](https://github.com/rust-lang/cargo/pull/12165) +- Fixed the ambiguity when `cargo install` found packages with the same name. + The ambiguity happened in a situation like a package depending on old versions + of itself. + [#12015](https://github.com/rust-lang/cargo/pull/12015) +- Fixed a false positive that `cargo package` checks for conflict files. + [#12135](https://github.com/rust-lang/cargo/pull/12135) +- Fixed `dep/feat` syntax not working when co-exist with `dep:` syntax, and + trying to enable features of an optional dependency. + [#12130](https://github.com/rust-lang/cargo/pull/12130) +- Fixed `cargo tree` not handling the output with `-e no-proc-macro` correctly. + [#12044](https://github.com/rust-lang/cargo/pull/12044) +- Warn instead of error in `cargo package` on empty `readme` or `license-file` + in Cargo.toml. + [#12036](https://github.com/rust-lang/cargo/pull/12036) +- Fixed when an HTTP proxy is in use and the Cargo executable links to a + certain version of system libcurl, CURL connections might fail. Affected + libcurl versions: 7.87.0, 7.88.0, 7.88.1. + [#12234](https://github.com/rust-lang/cargo/pull/12234) + [#12242](https://github.com/rust-lang/cargo/pull/12242) ### Nightly only +- 🔥 The `-Zgitoxide` feature now supports shallow clones and fetches for + dependencies and registry indexes. + [docs](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#gitoxide) + [#11840](https://github.com/rust-lang/cargo/pull/11840) +- 🔥 The `-Zlints` feature enables configuring lints rules in Cargo.toml + [docs](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#lints) + [#12148](https://github.com/rust-lang/cargo/pull/12148) + [#12168](https://github.com/rust-lang/cargo/pull/12168) - The `-Zbuild-std` breakage of missing features in `nightly-2023-05-04` has been fixed in `nightly-2023-05-05`. [#12088](https://github.com/rust-lang/cargo/pull/12088) - Recompile on profile rustflags changes. [#11981](https://github.com/rust-lang/cargo/pull/11981) +- Added `-Zmsrv-policy` feature flag placeholder. + [#12043](https://github.com/rust-lang/cargo/pull/12043) ### Documentation +- Added Cargo team charter. + [docs](https://doc.crates.io/contrib/team.html) + [#12010](https://github.com/rust-lang/cargo/pull/12010) +- SemVer: Adding `#[non_exhaustive]` on existing items is a breaking change. + [#10877](https://github.com/rust-lang/cargo/pull/10877) +- SemVer: It is not a breaking change to make an unsafe function safe. + [#12116](https://github.com/rust-lang/cargo/pull/12116) +- SemVer: changeing MSRV is generally a minor change. + [#12122](https://github.com/rust-lang/cargo/pull/12122) +- Clarify when and how to `cargo yank`. + [#11862](https://github.com/rust-lang/cargo/pull/11862) +- Clarify that crates.io doesn't link to docs.rs right away. + [#12146](https://github.com/rust-lang/cargo/pull/12146) +- Clarify documentation around test target setting. + [#12032](https://github.com/rust-lang/cargo/pull/12032) +- Specify `rust_version` in Index format. + [#12040](https://github.com/rust-lang/cargo/pull/12040) +- Specify `msg` in owner-remove registry API response. + [#12068](https://github.com/rust-lang/cargo/pull/12068) +- Added more documentation for artifact-dependencies. + [#12110](https://github.com/rust-lang/cargo/pull/12110) +- Added doc comments for `Source` and build script for cargo-the-library. + [#12133](https://github.com/rust-lang/cargo/pull/12133) + [#12153](https://github.com/rust-lang/cargo/pull/12153) + [#12159](https://github.com/rust-lang/cargo/pull/12159) +- Several typo and broken link fixes. + [#12018](https://github.com/rust-lang/cargo/pull/12018) + [#12020](https://github.com/rust-lang/cargo/pull/12020) + [#12049](https://github.com/rust-lang/cargo/pull/12049) + [#12067](https://github.com/rust-lang/cargo/pull/12067) + [#12073](https://github.com/rust-lang/cargo/pull/12073) + [#12143](https://github.com/rust-lang/cargo/pull/12143) +- home: clarify the behavior on each platform + [#12047](https://github.com/rust-lang/cargo/pull/12047) + ### Internal -- Cargo is now a Cargo workspace. We dogfood ourselves finally. +- Updated to `linux-raw-sys` 0.3.2 + [#11998](https://github.com/rust-lang/cargo/pull/11998) +- Updated to `git2` 0.17.1, which corresponds to libgit2 1.6.4. + [#12096](https://github.com/rust-lang/cargo/pull/12096) +- Updated to `windows-sys` 0.48.0 + [#12021](https://github.com/rust-lang/cargo/pull/12021) +- Updated to `libc` 0.2.144 + [#12014](https://github.com/rust-lang/cargo/pull/12014) + [#12098](https://github.com/rust-lang/cargo/pull/12098) +- Updated to `openssl-src` 111.25.3+1.1.1t + [#12005](https://github.com/rust-lang/cargo/pull/12005) +- Updated to `home` 0.5.5 + [#12037](https://github.com/rust-lang/cargo/pull/12037) +- Enabled feature `Win32_System_Console` feature since it is used. + [#12016](https://github.com/rust-lang/cargo/pull/12016) +- Cargo is now a Cargo workspace. We dogfood ourselves finally! [#11851](https://github.com/rust-lang/cargo/pull/11851) [#11994](https://github.com/rust-lang/cargo/pull/11994) [#11996](https://github.com/rust-lang/cargo/pull/11996) -- Allow win/mac credential managers to build on all platforms. - [#11993](https://github.com/rust-lang/cargo/pull/11993) -- Use `openssl` only on non-Windows platforms. - [#11979](https://github.com/rust-lang/cargo/pull/11979) -- A new, straightforward issue labels system for Cargo contributors. + [#12024](https://github.com/rust-lang/cargo/pull/12024) + [#12025](https://github.com/rust-lang/cargo/pull/12025) + [#12057](https://github.com/rust-lang/cargo/pull/12057) +- 🔥 A new, straightforward issue labels system for Cargo contributors. [docs](https://doc.crates.io/contrib/issues.html) [#11995](https://github.com/rust-lang/cargo/pull/11995) [#12002](https://github.com/rust-lang/cargo/pull/12002) [#12003](https://github.com/rust-lang/cargo/pull/12003) +- Allow win/mac credential managers to build on all platforms. + [#11993](https://github.com/rust-lang/cargo/pull/11993) + [#12027](https://github.com/rust-lang/cargo/pull/12027) +- Use `openssl` only on non-Windows platforms. + [#11979](https://github.com/rust-lang/cargo/pull/11979) +- Use restricted Damerau-Levenshtein algorithm to provide typo suggestions. + [#11963](https://github.com/rust-lang/cargo/pull/11963) +- Added a new xtask `cargo build-man`. + [#12048](https://github.com/rust-lang/cargo/pull/12048) +- Added a new xtask `cargo stale-label`. + [#12051](https://github.com/rust-lang/cargo/pull/12051) +- Added a new xtask `cargo unpublished`. + [#12039](https://github.com/rust-lang/cargo/pull/12039) + [#12045](https://github.com/rust-lang/cargo/pull/12045) + [#12085](https://github.com/rust-lang/cargo/pull/12085) +- CI: check if any version bump needed for member crates. + [#12126](https://github.com/rust-lang/cargo/pull/12126) +- Fixed some test infra issues. + [#11976](https://github.com/rust-lang/cargo/pull/11976) + [#12026](https://github.com/rust-lang/cargo/pull/12026) + [#12055](https://github.com/rust-lang/cargo/pull/12055) + [#12117](https://github.com/rust-lang/cargo/pull/12117) ## Cargo 1.70 (2023-06-01) [9880b408...rust-1.70.0](https://github.com/rust-lang/cargo/compare/9880b408...rust-1.70.0) @@ -90,7 +296,7 @@ [#11878](https://github.com/rust-lang/cargo/pull/11878) - Added delays to network retries in Cargo. [#11881](https://github.com/rust-lang/cargo/pull/11881) -- Refined `cargo puslish` message when waiting for a publish complete. +- Refined `cargo publish` message when waiting for a publish complete. [#11713](https://github.com/rust-lang/cargo/pull/11713) - Better error message when `cargo install` from a git repository but found multiple packages. @@ -392,7 +598,7 @@ for each revision from the same git repository. [#10690](https://github.com/rust-lang/cargo/pull/1090) - Cargo contributors can relabel issues via triagebot. - [doc](https://github.com/rust-lang/triagebot/wiki/Labeling) + [doc](https://forge.rust-lang.org/triagebot/labeling.html) [#11498](https://github.com/rust-lang/cargo/pull/11498) - Cargo contributors can write tests in containers. [#11583](https://github.com/rust-lang/cargo/pull/11583) diff --git a/src/tools/cargo/Cargo.lock b/src/tools/cargo/Cargo.lock index 14fd1d056..fe365bbcb 100644 --- a/src/tools/cargo/Cargo.lock +++ b/src/tools/cargo/Cargo.lock @@ -30,50 +30,68 @@ dependencies = [ ] [[package]] +name = "aho-corasick" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +dependencies = [ + "memchr", +] + +[[package]] name = "anstream" -version = "0.2.6" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f" +checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" dependencies = [ "anstyle", "anstyle-parse", + "anstyle-query", "anstyle-wincon", - "concolor-override", - "concolor-query", + "colorchoice", "is-terminal", "utf8parse", ] [[package]] name = "anstyle" -version = "0.3.5" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2" +checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" [[package]] name = "anstyle-parse" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116" +checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" dependencies = [ "utf8parse", ] [[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys 0.48.0", +] + +[[package]] name = "anstyle-wincon" -version = "0.2.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa" +checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" dependencies = [ "anstyle", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] name = "anyhow" -version = "1.0.70" +version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" +checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" [[package]] name = "arc-swap" @@ -112,9 +130,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" -version = "0.21.0" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "base64ct" @@ -156,9 +174,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.2.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24a6904aef64d73cf10ab17ebace7befb918b82164785cb89907993be7f83813" +checksum = "6dbe3c979c178231552ecba20214a8272df4e09f232a87aef4320cf06539aded" [[package]] name = "bitmaps" @@ -180,9 +198,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d4260bcc2e8fc9df1eac4919a720effeb63a3f0952f5bf4944adfa18897f09" +checksum = "a246e68bb43f6cd9db24bea052a53e40405417c5fb372e3d1a8a7f770a564ef5" dependencies = [ "memchr", "once_cell", @@ -201,9 +219,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.12.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" [[package]] name = "byteorder" @@ -244,7 +262,7 @@ dependencies = [ [[package]] name = "cargo" -version = "0.72.1" +version = "0.73.0" dependencies = [ "anyhow", "base64", @@ -253,7 +271,7 @@ dependencies = [ "cargo-test-macro", "cargo-test-support", "cargo-util", - "clap 4.2.1", + "clap 4.3.3", "crates-io", "curl", "curl-sys", @@ -274,10 +292,8 @@ dependencies = [ "ignore", "im-rc", "indexmap", - "is-terminal", "itertools", "jobserver", - "lazy_static", "lazycell", "libc", "libgit2-sys", @@ -289,6 +305,7 @@ dependencies = [ "pasetors", "pathdiff", "pretty_env_logger", + "pulldown-cmark", "rand", "rustfix", "same-file", @@ -301,6 +318,7 @@ dependencies = [ "shell-escape", "snapbox", "strip-ansi-escapes", + "syn 2.0.18", "tar", "tempfile", "termcolor", @@ -399,7 +417,7 @@ dependencies = [ [[package]] name = "cargo-util" -version = "0.2.4" +version = "0.2.5" dependencies = [ "anyhow", "core-foundation", @@ -464,18 +482,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.2.1" +version = "4.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3" +checksum = "ca8f255e4b8027970e78db75e78831229c9815fdbfa67eb1a1b777a62e24b4a0" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.2.1" +version = "4.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f" +checksum = "acd4f3c17c83b0ba34ffbc4f8bbd74f079413f747f84a6f89292f138057e36ab" dependencies = [ "anstream", "anstyle", @@ -487,9 +505,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" +checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" [[package]] name = "clru" @@ -498,19 +516,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" [[package]] -name = "concolor-override" +name = "colorchoice" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f" - -[[package]] -name = "concolor-query" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf" -dependencies = [ - "windows-sys 0.45.0", -] +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "const-oid" @@ -545,9 +554,9 @@ checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" [[package]] name = "cpufeatures" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" +checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58" dependencies = [ "libc", ] @@ -632,9 +641,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.14" +version = "0.9.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" dependencies = [ "autocfg", "cfg-if", @@ -645,18 +654,18 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.15" +version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] [[package]] name = "crypto-bigint" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2538c4e68e52548bacb3e83ac549f903d44f011ac9d5abb5e132e67d0808f7" +checksum = "cf4c2f4e1afd912bc40bfd6fed5d9dc1f288e0ba01bfcc835cc5bc3eb13efe15" dependencies = [ "generic-array", "rand_core", @@ -676,9 +685,9 @@ dependencies = [ [[package]] name = "csv" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b015497079b9a9d69c02ad25de6c0a6edef051ea6360a327d0bd05802ef64ad" +checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086" dependencies = [ "csv-core", "itoa 1.0.6", @@ -728,9 +737,9 @@ dependencies = [ [[package]] name = "curl-sys" -version = "0.4.61+curl-8.0.1" +version = "0.4.63+curl-8.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14d05c10f541ae6f3bc5b3d923c20001f47db7d5f0b2bc6ad16490133842db79" +checksum = "aeb0fef7046022a1e2ad67a004978f0e3cacb9e3123dc62ce768f92197b771dc" dependencies = [ "cc", "libc", @@ -744,9 +753,9 @@ dependencies = [ [[package]] name = "der" -version = "0.7.3" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b10af9f9f9f2134a42d3f8aa74658660f2e0234b0eb81bd171df8aa32779ed" +checksum = "56acb310e15652100da43d130af8d97b509e95af61aab1c5a7939ef24337ee17" dependencies = [ "const-oid", "pem-rfc7468", @@ -761,9 +770,9 @@ checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "digest" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "const-oid", @@ -773,21 +782,22 @@ dependencies = [ [[package]] name = "dunce" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bd4b30a6560bbd9b4620f4de34c3f14f60848e58a9b7216801afcb4c7b31c3c" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" [[package]] name = "ecdsa" -version = "0.16.6" +version = "0.16.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a48e5d537b8a30c0b023116d981b16334be1485af7ca68db3a2b7024cbc957fd" +checksum = "0997c976637b606099b9985693efa3581e84e41f5c11ba5255f88711058ad428" dependencies = [ "der", "digest", "elliptic-curve", "rfc6979", "signature", + "spki", ] [[package]] @@ -807,9 +817,9 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "elliptic-curve" -version = "0.13.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75c71eaa367f2e5d556414a8eea812bc62985c879748d6403edabd9cb03f16e7" +checksum = "968405c8fdc9b3bf4df0a6638858cc0b52462836ab6b1c87377785dd09cf1c0b" dependencies = [ "base16ct", "crypto-bigint", @@ -912,9 +922,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" dependencies = [ "crc32fast", "libz-sys", @@ -944,9 +954,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -974,9 +984,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "js-sys", @@ -987,9 +997,9 @@ dependencies = [ [[package]] name = "git2" -version = "0.17.1" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b7905cdfe33d31a88bb2e8419ddd054451f5432d1da9eaf2ac7804ee1ea12d5" +checksum = "7b989d6a7ca95a362cf2cfc5ad688b3a467be1f87e480b8dad07fee8c79b0044" dependencies = [ "bitflags 1.3.2", "libc", @@ -1014,12 +1024,13 @@ dependencies = [ [[package]] name = "gix" -version = "0.44.1" +version = "0.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bf41b61f7df395284f7a579c0fa1a7e012c5aede655174d4e91299ef1cac643" +checksum = "bf2a03ec66ee24d1b2bae3ab718f8d14f141613810cb7ff6756f7db667f1cd82" dependencies = [ "gix-actor", "gix-attributes", + "gix-commitgraph", "gix-config", "gix-credentials", "gix-date", @@ -1034,6 +1045,7 @@ dependencies = [ "gix-index", "gix-lock", "gix-mailmap", + "gix-negotiate", "gix-object", "gix-odb", "gix-pack", @@ -1062,9 +1074,9 @@ dependencies = [ [[package]] name = "gix-actor" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "848efa0f1210cea8638f95691c82a46f98a74b9e3524f01d4955ebc25a8f84f3" +checksum = "9fe73f9f6be1afbf1bd5be919a9636fa560e2f14d42262a934423ed6760cd838" dependencies = [ "bstr", "btoi", @@ -1076,9 +1088,9 @@ dependencies = [ [[package]] name = "gix-attributes" -version = "0.12.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3015baa01ad2122fbcaab7863c857a603eb7b7ec12ac8141207c42c6439805e2" +checksum = "78b79590ac382f80d87e06416f5fcac6fee5d83dcb152a00ed0bdbaa988acc31" dependencies = [ "bstr", "gix-glob", @@ -1093,36 +1105,50 @@ dependencies = [ [[package]] name = "gix-bitmap" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55a95f4942360766c3880bdb2b4b57f1ef73b190fc424755e7fdf480430af618" +checksum = "fc02feb20ad313d52a450852f2005c2205d24f851e74d82b7807cbe12c371667" dependencies = [ "thiserror", ] [[package]] name = "gix-chunk" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0d39583cab06464b8bf73b3f1707458270f0e7383cb24c3c9c1a16e6f792978" +checksum = "a7acf3bc6c4b91e8fb260086daf5e105ea3a6d913f5fd3318137f7e309d6e540" dependencies = [ "thiserror", ] [[package]] name = "gix-command" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2c6f75c1e0f924de39e750880a6e21307194bb1ab773efe3c7d2d787277f8ab" +checksum = "5f6141b70cfb21255223e42f3379855037cbbe8673b58dd8318d2f09b516fad1" dependencies = [ "bstr", ] [[package]] +name = "gix-commitgraph" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8490ae1b3d55c47e6a71d247c082304a2f79f8d0332c1a2f5693d42a2021a09" +dependencies = [ + "bstr", + "gix-chunk", + "gix-features", + "gix-hash", + "memmap2", + "thiserror", +] + +[[package]] name = "gix-config" -version = "0.22.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d252a0eddb6df74600d3d8872dc9fe98835a7da43110411d705b682f49d4ac1" +checksum = "51f310120ae1ba8f0ca52fb22876ce9bad5b15c8ffb3eb7302e4b64a3b9f681c" dependencies = [ "bstr", "gix-config-value", @@ -1142,11 +1168,11 @@ dependencies = [ [[package]] name = "gix-config-value" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786861e84a5793ad5f863d846de5eb064cd23b87e61ad708c8c402608202e7be" +checksum = "6f216df1c33e6e1555923eff0096858a879e8aaadd35b5d788641e4e8064c892" dependencies = [ - "bitflags 2.2.1", + "bitflags 2.3.2", "bstr", "gix-path", "libc", @@ -1155,9 +1181,9 @@ dependencies = [ [[package]] name = "gix-credentials" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4874a4fc11ffa844a3c2b87a66957bda30a73b577ef1acf15ac34df5745de5ff" +checksum = "c6f89fea8acd28f5ef8fa5042146f1637afd4d834bc8f13439d8fd1e5aca0d65" dependencies = [ "bstr", "gix-command", @@ -1171,9 +1197,9 @@ dependencies = [ [[package]] name = "gix-date" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99056f37270715f5c7584fd8b46899a2296af9cae92463bf58b8bd1f5a78e553" +checksum = "bc164145670e9130a60a21670d9b6f0f4f8de04e5dd256c51fa5a0340c625902" dependencies = [ "bstr", "itoa 1.0.6", @@ -1183,9 +1209,9 @@ dependencies = [ [[package]] name = "gix-diff" -version = "0.29.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644a0f2768bc42d7a69289ada80c9e15c589caefc6a315d2307202df83ed1186" +checksum = "9029ad0083cc286a4bd2f5b3bf66bb66398abc26f2731a2824cd5edfc41a0e33" dependencies = [ "gix-hash", "gix-object", @@ -1195,9 +1221,9 @@ dependencies = [ [[package]] name = "gix-discover" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5012710ebdecf6193c6866d6409a3b702a4aa0d78c605bc343590b44ab9962a1" +checksum = "aba9c6c0d1f2b2efe65581de73de4305004612d49c83773e783202a7ef204f46" dependencies = [ "bstr", "dunce", @@ -1210,9 +1236,9 @@ dependencies = [ [[package]] name = "gix-features" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf69b0f5c701cc3ae22d3204b671907668f6437ca88862d355eaf9bc47a4f897" +checksum = "3a8c493409bf6060d408eec9bbdd1b12ea351266b50012e2a522f75dfc7b8314" dependencies = [ "bytes", "crc32fast", @@ -1230,20 +1256,20 @@ dependencies = [ [[package]] name = "gix-fs" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b37a1832f691fdc09910bd267f9a2e413737c1f9ec68c6e31f9e802616278a9" +checksum = "30da8997008adb87f94e15beb7ee229f8a48e97af585a584bfee4a5a1880aab5" dependencies = [ "gix-features", ] [[package]] name = "gix-glob" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07c98204529ac3f24b34754540a852593d2a4c7349008df389240266627a72a" +checksum = "cd0ade1e80ab1f079703d1824e1daf73009096386aa7fd2f0477f6e4ac0a558e" dependencies = [ - "bitflags 2.2.1", + "bitflags 2.3.2", "bstr", "gix-features", "gix-path", @@ -1251,9 +1277,9 @@ dependencies = [ [[package]] name = "gix-hash" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "078eec3ac2808cc03f0bddd2704cb661da5c5dc33b41a9d7947b141d499c7c42" +checksum = "ee181c85d3955f54c4426e6bfaeeada4428692e1a39b8788c2ac7785fc301dd8" dependencies = [ "hex", "thiserror", @@ -1261,9 +1287,9 @@ dependencies = [ [[package]] name = "gix-hashtable" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afebb85691c6a085b114e01a27f4a61364519298c5826cb87a45c304802299bc" +checksum = "bd259bd0d96e6153e357a8cdaca76c48e103fd34208b6c0ce77b1ad995834bd2" dependencies = [ "gix-hash", "hashbrown 0.13.2", @@ -1272,9 +1298,9 @@ dependencies = [ [[package]] name = "gix-ignore" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba205b6df563e2906768bb22834c82eb46c5fdfcd86ba2c347270bc8309a05b2" +checksum = "fc6f7f101a0ccce808dbf7008ba131dede94e20257e7bde7a44cbb2f8c775625" dependencies = [ "bstr", "gix-glob", @@ -1284,11 +1310,11 @@ dependencies = [ [[package]] name = "gix-index" -version = "0.16.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa282756760f79c401d4f4f42588fbb4aa27bbb4b0830f3b4d3480c21a4ac5a7" +checksum = "616ba958fabfb11263fa042c35690d48a6c7be4e9277e2c7e24ff263b3fe7b82" dependencies = [ - "bitflags 2.2.1", + "bitflags 2.3.2", "bstr", "btoi", "filetime", @@ -1306,20 +1332,20 @@ dependencies = [ [[package]] name = "gix-lock" -version = "5.0.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41b80172055c5d8017a48ddac5cc7a95421c00211047db0165c97853c4f05194" +checksum = "3ec5d5e6f07316d3553aa7425e3ecd935ec29882556021fe1696297a448af8d2" dependencies = [ - "fastrand", "gix-tempfile", + "gix-utils", "thiserror", ] [[package]] name = "gix-mailmap" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8856cec3bdc3610c06970d28b6cb20a0c6621621cf9a8ec48cbd23f2630f362" +checksum = "4653701922c920e009f1bc4309feaff14882ade017770788f9a150928da3fa6a" dependencies = [ "bstr", "gix-actor", @@ -1327,10 +1353,25 @@ dependencies = [ ] [[package]] +name = "gix-negotiate" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "945c3ef1e912e44a5f405fc9e924edf42000566a1b257ed52cb1293300f6f08c" +dependencies = [ + "bitflags 2.3.2", + "gix-commitgraph", + "gix-hash", + "gix-object", + "gix-revision", + "smallvec", + "thiserror", +] + +[[package]] name = "gix-object" -version = "0.29.1" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9bb30ce0818d37096daa29efe361a4bc6dd0b51a5726598898be7e9a40a01e1" +checksum = "8926c8f51c44dec3e709cb5dbc93deb9e8d4064c43c9efc54c158dcdfe8446c7" dependencies = [ "bstr", "btoi", @@ -1347,9 +1388,9 @@ dependencies = [ [[package]] name = "gix-odb" -version = "0.45.0" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2f324aa67672b6d0f2c0fa93f96eb6a7029d260e4c1df5dce3c015f5e5add" +checksum = "4b234d806278eeac2f907c8b5a105c4ba537230c1a9d9236d822bf0db291f8f3" dependencies = [ "arc-swap", "gix-features", @@ -1365,9 +1406,9 @@ dependencies = [ [[package]] name = "gix-pack" -version = "0.35.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164a515900a83257ae4aa80e741655bee7a2e39113fb535d7a5ac623b445ff20" +checksum = "7d2a14cb3156037eedb17d6cb7209b7180522b8949b21fd0fe3184c0a1d0af88" dependencies = [ "clru", "gix-chunk", @@ -1387,9 +1428,9 @@ dependencies = [ [[package]] name = "gix-packetline" -version = "0.16.0" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51f53abaf1171d2fe99f80ac8ed6645904a1bfd706674749ac112bdd2d4f0777" +checksum = "74414f89a6b72fa1a530ce8e646faf1a05499c3f4a5c15441d17ae8c978578eb" dependencies = [ "bstr", "hex", @@ -1398,9 +1439,9 @@ dependencies = [ [[package]] name = "gix-path" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fc78f47095a0c15aea0e66103838f0748f4494bf7a9555dfe0f00425400396c" +checksum = "c1226f2e50adeb4d76c754c1856c06f13a24cad1624801653fbf09b869e5b808" dependencies = [ "bstr", "home 0.5.5", @@ -1410,9 +1451,9 @@ dependencies = [ [[package]] name = "gix-prompt" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330d11fdf88fff3366c2491efde2f3e454958efe7d5ddf60272e8fb1d944bb01" +checksum = "e15fe57fa48572b7d3bf465d6a2a0351cd3c55cba74fd5f0b9c23689f9c1a31e" dependencies = [ "gix-command", "gix-config-value", @@ -1423,9 +1464,9 @@ dependencies = [ [[package]] name = "gix-protocol" -version = "0.32.0" +version = "0.33.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877e49417f1730f4dbc2f7d9a2ab0f8b2f49ef08f97270691403ecde3d961e3a" +checksum = "92a17058b45c461f0847528c5fb6ee6e76115e026979eb2d2202f98ee94f6c24" dependencies = [ "bstr", "btoi", @@ -1440,9 +1481,9 @@ dependencies = [ [[package]] name = "gix-quote" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a282f5a8d9ee0b09ec47390ac727350c48f2f5c76d803cd8da6b3e7ad56e0bcb" +checksum = "29d59489bff95b06dcdabe763b7266d3dc0a628cac1ac1caf65a7ca0a43eeae0" dependencies = [ "bstr", "btoi", @@ -1451,9 +1492,9 @@ dependencies = [ [[package]] name = "gix-ref" -version = "0.29.0" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8212ecfe41815a2f1b059d82171d6276758cfac5506a5e0f04ad45ef0b1924a" +checksum = "ebdd999256f4ce8a5eefa89999879c159c263f3493a951d62aa5ce42c0397e1c" dependencies = [ "gix-actor", "gix-features", @@ -1471,9 +1512,9 @@ dependencies = [ [[package]] name = "gix-refspec" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6ea733820df67e4cd7797deb12727905824d8f5b7c59d943c456d314475892" +checksum = "72bfd622abc86dd8ad1ec51b9eb77b4f1a766b94e3a1b87cf4a022c5b5570cf4" dependencies = [ "bstr", "gix-hash", @@ -1485,25 +1526,40 @@ dependencies = [ [[package]] name = "gix-revision" -version = "0.13.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "810f35e9afeccca999d5d348b239f9c162353127d2e13ff3240e31b919e35476" +checksum = "5044f56cd7a487ce9b034cbe0252ae0b6b47ff56ca3dabd79bc30214d0932cd7" dependencies = [ "bstr", "gix-date", "gix-hash", "gix-hashtable", "gix-object", + "gix-revwalk", + "thiserror", +] + +[[package]] +name = "gix-revwalk" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc2623ba8747914f151f5e12b65adac576ab459dbed5f50a36c7a3e9cbf2d3ca" +dependencies = [ + "gix-commitgraph", + "gix-hash", + "gix-hashtable", + "gix-object", + "smallvec", "thiserror", ] [[package]] name = "gix-sec" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794520043d5a024dfeac335c6e520cb616f6963e30dab995892382e998c12897" +checksum = "b2b7b38b766eb95dcc5350a9c450030b69892c0902fa35f4a6d0809273bd9dae" dependencies = [ - "bitflags 2.2.1", + "bitflags 2.3.2", "gix-path", "libc", "windows", @@ -1511,10 +1567,11 @@ dependencies = [ [[package]] name = "gix-tempfile" -version = "5.0.2" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ceb30a610e3f5f2d5f9a5114689fde507ba9417705a8cf3429604275b2153c" +checksum = "b3785cb010e9dc5c446dfbf02bc1119fc17d3a48a27c029efcb3a3c32953eb10" dependencies = [ + "gix-fs", "libc", "once_cell", "parking_lot", @@ -1525,9 +1582,9 @@ dependencies = [ [[package]] name = "gix-transport" -version = "0.31.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f01c2bf7b989c679695ef635fc7d9e80072e08101be4b53193c8e8b649900102" +checksum = "64a39ffed9a9078ed700605e064b15d7c6ae50aa65e7faa36ca6919e8081df15" dependencies = [ "base64", "bstr", @@ -1544,9 +1601,9 @@ dependencies = [ [[package]] name = "gix-traverse" -version = "0.25.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5be1e807f288c33bb005075111886cceb43ed8a167b3182a0f62c186e2a0dd1" +checksum = "b0842e984cb4bf26339dc559f3a1b8bf8cdb83547799b2b096822a59f87f33d9" dependencies = [ "gix-hash", "gix-hashtable", @@ -1556,9 +1613,9 @@ dependencies = [ [[package]] name = "gix-url" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc77f89054297cc81491e31f1bab4027e554b5ef742a44bd7035db9a0f78b76" +checksum = "f1663df25ac42047a2547618d2a6979a26f478073f6306997429235d2cd4c863" dependencies = [ "bstr", "gix-features", @@ -1570,18 +1627,18 @@ dependencies = [ [[package]] name = "gix-utils" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c10b69beac219acb8df673187a1f07dde2d74092f974fb3f9eb385aeb667c909" +checksum = "dbcfcb150c7ef553d76988467d223254045bdcad0dc6724890f32fbe96415da5" dependencies = [ "fastrand", ] [[package]] name = "gix-validate" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd629d3680773e1785e585d76fd4295b740b559cad9141517300d99a0c8c049" +checksum = "57ea5845b506c7728b9d89f4227cc369a5fc5a1d5b26c3add0f0d323413a3a60" dependencies = [ "bstr", "thiserror", @@ -1589,9 +1646,9 @@ dependencies = [ [[package]] name = "gix-worktree" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10bf56a1f5037d84293ea6cece61d9f27c4866b1e13c1c95f37cf56b7da7af25" +checksum = "d388ad962e8854402734a7387af8790f6bdbc8d05349052dab16ca4a0def50f6" dependencies = [ "bstr", "filetime", @@ -1620,7 +1677,7 @@ version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d74589adefde59de1a0c4f4732695c32805624aec7b68d91503d4dba79afc" dependencies = [ - "aho-corasick", + "aho-corasick 0.7.20", "bstr", "fnv", "log", @@ -1761,9 +1818,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -1841,9 +1898,9 @@ dependencies = [ [[package]] name = "io-lifetimes" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.1", "libc", @@ -1894,9 +1951,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" dependencies = [ "wasm-bindgen", ] @@ -1930,15 +1987,15 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.146" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "f92be4933c13fd498862a9e02a3055f8a8d9c039ce33db97306fd5a6caa7f29b" [[package]] name = "libgit2-sys" -version = "0.15.1+1.6.4" +version = "0.15.2+1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb4577bde8cdfc7d6a2a4bcb7b049598597de33ffd337276e9c7db6cd4a2cee7" +checksum = "a80df2e11fb4a61f4ba2ab42dbe7f74468da143f1a75c74e11dee7c813f694fa" dependencies = [ "cc", "libc", @@ -1950,9 +2007,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" +checksum = "f7012b1bbb0719e1097c47611d3898568c546d597c2e74d66f6087edd5233ff4" [[package]] name = "libnghttp2-sys" @@ -1980,9 +2037,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.8" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +checksum = "56ee889ecc9568871456d42f603d6a0ce59ff328d291063a45cbdf0036baf6db" dependencies = [ "cc", "libc", @@ -1992,15 +2049,15 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.3.4" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36eb31c1778188ae1e64398743890d0877fef36d11521ac60406b42016e8c2cf" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "lock_api" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" dependencies = [ "autocfg", "scopeguard", @@ -2008,12 +2065,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" [[package]] name = "maybe-async" @@ -2056,9 +2110,9 @@ dependencies = [ [[package]] name = "memoffset" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" dependencies = [ "autocfg", ] @@ -2071,9 +2125,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" dependencies = [ "adler", ] @@ -2134,9 +2188,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "oorandom" @@ -2156,9 +2210,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.50" +version = "0.10.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e30d8bc91859781f0a943411186324d580f2bbeb71b452fe91ae344806af3f1" +checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" dependencies = [ "bitflags 1.3.2", "cfg-if", @@ -2177,7 +2231,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.18", ] [[package]] @@ -2188,18 +2242,18 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "111.25.3+1.1.1t" +version = "111.26.0+1.1.1u" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924757a6a226bf60da5f7dd0311a34d2b52283dd82ddeb103208ddc66362f80c" +checksum = "efc62c9f12b22b8f5208c23a7200a442b2e5999f8bdf80233852122b5a4f6f37" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.85" +version = "0.9.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d3d193fb1488ad46ffe3aaabc912cc931d02ee8518fe2959aea8ef52718b0c0" +checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" dependencies = [ "cc", "libc", @@ -2272,15 +2326,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall 0.3.5", "smallvec", - "windows-sys 0.45.0", + "windows-targets", ] [[package]] @@ -2305,9 +2359,9 @@ dependencies = [ [[package]] name = "pasetors" -version = "0.6.6" +version = "0.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824bf633b85dc1dece2eb07161627ba5d90a951597cd5dbf8d85f4d82b7aea69" +checksum = "ba765699a309908d55950919a3445e9491453e89b2587b1b2abe4143a48894c0" dependencies = [ "ct-codecs", "ed25519-compact", @@ -2341,15 +2395,15 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pest" -version = "2.5.7" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1403e8401ad5dedea73c626b99758535b342502f8d1e361f4a2dd952749122" +checksum = "e68e84bfb01f0507134eac1e9b410a12ba379d064eab48c50ba4ce329a527b70" dependencies = [ "thiserror", "ucd-trie", @@ -2357,9 +2411,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.5.7" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be99c4c1d2fc2769b1d00239431d711d08f6efedcecb8b6e30707160aee99c15" +checksum = "6b79d4c71c865a25a4322296122e3924d30bc8ee0834c8bfc8b95f7f054afbfb" dependencies = [ "pest", "pest_generator", @@ -2367,22 +2421,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.5.7" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56094789873daa36164de2e822b3888c6ae4b4f9da555a1103587658c805b1e" +checksum = "6c435bf1076437b851ebc8edc3a18442796b30f1728ffea6262d59bbe28b077e" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.18", ] [[package]] name = "pest_meta" -version = "2.5.7" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6733073c7cff3d8459fda0e42f13a047870242aed8b509fe98000928975f359e" +checksum = "745a452f8eb71e39ffd8ee32b3c5f51d03845f99786fa9b68db6ff509c505411" dependencies = [ "once_cell", "pest", @@ -2401,9 +2455,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.26" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "plotters" @@ -2463,47 +2517,46 @@ dependencies = [ [[package]] name = "primeorder" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf8d3875361e28f7753baefef104386e7aa47642c93023356d97fdef4003bfb5" +checksum = "3c2fcef82c0ec6eefcc179b978446c399b3cdf73c392c35604e399eee6df1ee3" dependencies = [ "elliptic-curve", ] [[package]] name = "proc-macro2" -version = "1.0.64" +version = "1.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" +checksum = "dec2b086b7a862cf4de201096214fa870344cf922b2b30c167badb3af3195406" dependencies = [ "unicode-ident", ] [[package]] name = "prodash" -version = "23.1.2" +version = "25.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9516b775656bc3e8985e19cd4b8c0c0de045095074e453d2c0a513b5f978392d" +checksum = "3236ce1618b6da4c7b618e0143c4d5b5dc190f75f81c49f248221382f7e9e9ae" dependencies = [ "parking_lot", ] [[package]] name = "proptest" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29f1b898011ce9595050a68e60f90bad083ff2987a695a42357134c8381fba70" +checksum = "4e35c06b98bf36aba164cc17cb25f7e232f5c4aeea73baa14b8a9f0d92dbfa65" dependencies = [ "bit-set", "bitflags 1.3.2", "byteorder", "lazy_static", "num-traits", - "quick-error 2.0.1", "rand", "rand_chacha", "rand_xorshift", - "regex-syntax", + "regex-syntax 0.6.29", "rusty-fork", "tempfile", "unarray", @@ -2511,9 +2564,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.9.2" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" +checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" dependencies = [ "bitflags 1.3.2", "memchr", @@ -2534,9 +2587,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quote" -version = "1.0.26" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488" dependencies = [ "proc-macro2", ] @@ -2631,13 +2684,13 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.3" +version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" dependencies = [ - "aho-corasick", + "aho-corasick 1.0.2", "memchr", - "regex-syntax", + "regex-syntax 0.7.2", ] [[package]] @@ -2653,12 +2706,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] +name = "regex-syntax" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436b050e76ed2903236f032a59761c1eb99e1b0aead2c257922771dab1fc8c78" + +[[package]] name = "resolver-tests" version = "0.0.0" dependencies = [ "cargo", "cargo-util", - "is-terminal", "lazy_static", "proptest", "varisat", @@ -2694,9 +2752,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.37.15" +version = "0.37.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0661814f891c57c930a610266415528da53c4933e6dea5fb350cbfe048a9ece" +checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" dependencies = [ "bitflags 1.3.2", "errno", @@ -2764,9 +2822,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.8.2" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" +checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -2777,9 +2835,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" +checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" dependencies = [ "core-foundation-sys", "libc", @@ -2803,9 +2861,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.160" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "9e8c8cf938e98f769bc164923b06dce91cea1751522f46f8466461af04c9027d" dependencies = [ "serde_derive", ] @@ -2832,13 +2890,13 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.160" +version = "1.0.164" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "d9735b638ccc51c28bf6914d90a2e9725b377144fc612c49a611fddd1b631d68" dependencies = [ "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.18", ] [[package]] @@ -2852,9 +2910,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa 1.0.6", "ryu", @@ -2863,9 +2921,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +checksum = "93107647184f6027e3b7dcb2e11034cf95ffa1e3a682c67951963ac69c1c007d" dependencies = [ "serde", ] @@ -2957,9 +3015,9 @@ checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "snapbox" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9615402f9cff539301119bdf2c2f328739cf2b45c2116666618fb6ac399f75bb" +checksum = "f6bccd62078347f89a914e3004d94582e13824d4e3d8a816317862884c423835" dependencies = [ "anstream", "anstyle", @@ -2975,9 +3033,9 @@ dependencies = [ [[package]] name = "snapbox-macros" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8e40c667388ed1cb5060f545d0013bf0a23efdfa6c5c3e9ef592de391cd860f" +checksum = "eaaf09df9f0eeae82be96290918520214530e738a7fe5a351b0f24cf77c0ca31" dependencies = [ "anstream", ] @@ -2994,9 +3052,9 @@ dependencies = [ [[package]] name = "spki" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a5be806ab6f127c3da44b7378837ebf01dadca8510a0e572460216b228bd0e" +checksum = "9d1e996ef02c474957d681f1b05213dfb0abab947b446a62d37770b23500184a" dependencies = [ "base64ct", "der", @@ -3025,9 +3083,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" @@ -3042,9 +3100,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.14" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5" +checksum = "32d41677bcbe24c20c52e7c70b0d8db04134c5d1066bf98662e2871ad200ea3e" dependencies = [ "proc-macro2", "quote", @@ -3075,15 +3133,16 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.5.0" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" dependencies = [ + "autocfg", "cfg-if", "fastrand", "redox_syscall 0.3.5", "rustix", - "windows-sys 0.45.0", + "windows-sys 0.48.0", ] [[package]] @@ -3131,7 +3190,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.14", + "syn 2.0.18", ] [[package]] @@ -3146,9 +3205,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +checksum = "ea9e1b3cf1243ae005d9e74085d4d542f3125458f3a81af210d901dcd7411efd" dependencies = [ "itoa 1.0.6", "libc", @@ -3160,15 +3219,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] name = "time-macros" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +checksum = "372950940a5f07bf38dbe211d7283c9e6d7327df53794992d293e534c733d09b" dependencies = [ "time-core", ] @@ -3200,9 +3259,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" +checksum = "d6135d499e69981f9ff0ef2167955a5333c35e36f6937d382974566b3d5b94ec" dependencies = [ "serde", "serde_spanned", @@ -3212,18 +3271,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +checksum = "5a76a9312f5ba4c2dec6b9161fdf25d87ad8a09256ccea5a556fef03c706a10f" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.8" +version = "0.19.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +checksum = "2380d56e8670370eee6566b0bfd4265f65b3f432e8c6d85623f728d4fa31f739" dependencies = [ "indexmap", "serde", @@ -3273,9 +3332,9 @@ checksum = "98e90c70c9f0d4d1ee6d0a7d04aa06cb9bbd53d8cfbdd62a0269a7c2eb640552" [[package]] name = "unicode-ident" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0" [[package]] name = "unicode-normalization" @@ -3300,9 +3359,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", @@ -3462,9 +3521,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -3472,24 +3531,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3497,28 +3556,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.18", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" dependencies = [ "js-sys", "wasm-bindgen", @@ -3561,7 +3620,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.48.0", + "windows-targets", ] [[package]] @@ -3581,35 +3640,11 @@ dependencies = [ [[package]] name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - -[[package]] -name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-targets", ] [[package]] @@ -3713,9 +3748,9 @@ checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" [[package]] name = "winnow" -version = "0.4.1" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +checksum = "61de7bac303dc551fe038e2b3cef0f571087a47571ea6e79a87692ac99b99699" dependencies = [ "memchr", ] @@ -3737,7 +3772,7 @@ version = "0.0.0" dependencies = [ "anyhow", "cargo", - "clap 4.2.1", + "clap 4.3.3", "env_logger 0.10.0", "log", ] diff --git a/src/tools/cargo/Cargo.toml b/src/tools/cargo/Cargo.toml index 05c95c727..7e383be69 100644 --- a/src/tools/cargo/Cargo.toml +++ b/src/tools/cargo/Cargo.toml @@ -19,22 +19,22 @@ cargo-credential = { version = "0.2.0", path = "credential/cargo-credential" } cargo-platform = { path = "crates/cargo-platform", version = "0.1.3" } cargo-test-macro = { path = "crates/cargo-test-macro" } cargo-test-support = { path = "crates/cargo-test-support" } -cargo-util = { version = "0.2.4", path = "crates/cargo-util" } +cargo-util = { version = "0.2.5", path = "crates/cargo-util" } cargo_metadata = "0.14.0" clap = "4.2.0" core-foundation = { version = "0.9.0", features = ["mac_os_10_7_support"] } crates-io = { version = "0.37.0", path = "crates/crates-io" } criterion = { version = "0.3.5", features = ["html_reports"] } curl = "0.4.44" -curl-sys = "0.4.61" +curl-sys = "0.4.63" env_logger = "0.10.0" filetime = "0.2.9" flate2 = { version = "1.0.3", default-features = false, features = ["zlib"] } fwdansi = "1.1.0" git2 = "0.17.1" git2-curl = "0.18.0" -gix = { version = "0.44.1", default-features = false, features = ["blocking-http-transport-curl", "progress-tree"] } -gix-features-for-configuration-only = { version = "0.29.0", package = "gix-features", features = [ "parallel" ] } +gix = { version = "0.45.1", default-features = false, features = ["blocking-http-transport-curl", "progress-tree"] } +gix-features-for-configuration-only = { version = "0.30.0", package = "gix-features", features = [ "parallel" ] } glob = "0.3.0" handlebars = { version = "3.2.1", features = ["dir_source"] } hex = "0.4.2" @@ -45,7 +45,6 @@ humantime = "2.0.0" ignore = "0.4.7" im-rc = "15.0.0" indexmap = "1" -is-terminal = "0.4.4" itertools = "0.10.0" jobserver = "0.1.26" lazy_static = "1.3.0" @@ -56,7 +55,7 @@ log = "0.4.17" memchr = "2.1.3" miow = "0.5.0" opener = "0.5" -openssl ="0.10.50" +openssl ="0.10.55" os_info = "3.5.0" pasetors = { version = "0.6.4", features = ["v3", "paserk", "std", "serde"] } pathdiff = "0.2" @@ -80,6 +79,7 @@ sha2 = "0.10.6" shell-escape = "0.1.4" snapbox = { version = "0.4.0", features = ["diff", "path"] } strip-ansi-escapes = "0.1.0" +syn = { version = "2.0.14", features = ["extra-traits", "full"] } tar = { version = "0.4.39", default-features = false } tempfile = "3.1.0" termcolor = "1.1.2" @@ -95,7 +95,7 @@ windows-sys = "0.48" [package] name = "cargo" -version = "0.72.1" +version = "0.73.0" edition = "2021" license = "MIT OR Apache-2.0" homepage = "https://crates.io" @@ -136,10 +136,8 @@ humantime.workspace = true ignore.workspace = true im-rc.workspace = true indexmap.workspace = true -is-terminal.workspace = true itertools.workspace = true jobserver.workspace = true -lazy_static.workspace = true lazycell.workspace = true libc.workspace = true libgit2-sys.workspace = true @@ -150,6 +148,7 @@ os_info.workspace = true pasetors.workspace = true pathdiff.workspace = true pretty_env_logger = { workspace = true, optional = true } +pulldown-cmark.workspace = true rand.workspace = true rustfix.workspace = true semver.workspace = true @@ -160,6 +159,7 @@ serde_json = { workspace = true, features = ["raw_value"] } sha1.workspace = true shell-escape.workspace = true strip-ansi-escapes.workspace = true +syn.workspace = true tar.workspace = true tempfile.workspace = true termcolor.workspace = true @@ -183,11 +183,8 @@ features = [ "Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_Console", - "Win32_System_IO", "Win32_System_Threading", "Win32_System_JobObjects", - "Win32_Security", - "Win32_System_SystemServices" ] [dev-dependencies] diff --git a/src/tools/cargo/README.md b/src/tools/cargo/README.md index 423555e62..1d806b978 100644 --- a/src/tools/cargo/README.md +++ b/src/tools/cargo/README.md @@ -13,7 +13,7 @@ Cargo downloads your Rust project’s dependencies and compiles your project. [![CI](https://github.com/rust-lang/cargo/actions/workflows/main.yml/badge.svg?branch=auto-cargo)](https://github.com/rust-lang/cargo/actions/workflows/main.yml) -Code documentation: https://docs.rs/cargo/ +Code documentation: <https://doc.rust-lang.org/nightly/nightly-rustc/cargo/> ## Installing Cargo diff --git a/src/tools/cargo/clippy.toml b/src/tools/cargo/clippy.toml index 4f9be8f9b..050cc8716 100644 --- a/src/tools/cargo/clippy.toml +++ b/src/tools/cargo/clippy.toml @@ -1,3 +1,5 @@ +allow-print-in-tests = true +allow-dbg-in-tests = true disallowed-methods = [ { path = "std::env::var", reason = "Use `Config::get_env` instead. See rust-lang/cargo#11588" }, { path = "std::env::var_os", reason = "Use `Config::get_env_os` instead. See rust-lang/cargo#11588" }, diff --git a/src/tools/cargo/crates/cargo-test-support/src/containers.rs b/src/tools/cargo/crates/cargo-test-support/src/containers.rs index 17040d82a..22fd5fd85 100644 --- a/src/tools/cargo/crates/cargo-test-support/src/containers.rs +++ b/src/tools/cargo/crates/cargo-test-support/src/containers.rs @@ -94,7 +94,9 @@ impl Container { let image_base = self.build_context.file_name().unwrap(); let image_name = format!("cargo-test-{}", image_base.to_str().unwrap()); - let _lock = BUILD_LOCK.lock().unwrap(); + let _lock = BUILD_LOCK + .lock() + .map_err(|_| panic!("previous docker build failed, unable to run test")); ProcessBuilder::new("docker") .args(&["build", "--tag", image_name.as_str()]) .arg(&self.build_context) 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 d27aab44f..a2fa54c60 100644 --- a/src/tools/cargo/crates/cargo-test-support/src/lib.rs +++ b/src/tools/cargo/crates/cargo-test-support/src/lib.rs @@ -110,7 +110,9 @@ impl FileBuilder { fn mk(&mut self) { if self.executable { - self.path.set_extension(env::consts::EXE_EXTENSION); + let mut path = self.path.clone().into_os_string(); + write!(path, "{}", env::consts::EXE_SUFFIX).unwrap(); + self.path = path.into(); } self.dirname().mkdir_p(); @@ -1259,6 +1261,8 @@ pub trait TestEnv: Sized { .env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "stable") // Keeps cargo within its sandbox. .env("__CARGO_TEST_DISABLE_GLOBAL_KNOWN_HOST", "1") + // Set retry sleep to 1 millisecond. + .env("__CARGO_TEST_FIXED_RETRY_SLEEP_MS", "1") // Incremental generates a huge amount of data per test, which we // don't particularly need. Tests that specifically need to check // the incremental behavior should turn this back on. 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 ef1fddb70..50040e1d4 100644 --- a/src/tools/cargo/crates/cargo-test-support/src/paths.rs +++ b/src/tools/cargo/crates/cargo-test-support/src/paths.rs @@ -1,7 +1,6 @@ use filetime::{self, FileTime}; -use lazy_static::lazy_static; + use std::cell::RefCell; -use std::collections::HashMap; use std::env; use std::fs; use std::io::{self, ErrorKind}; @@ -9,15 +8,11 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Mutex; +use std::sync::OnceLock; static CARGO_INTEGRATION_TEST_DIR: &str = "cit"; -lazy_static! { - // TODO: Use `SyncOnceCell` when stable - static ref GLOBAL_ROOT: Mutex<Option<PathBuf>> = Mutex::new(None); - - static ref TEST_ROOTS: Mutex<HashMap<String, PathBuf>> = Default::default(); -} +static GLOBAL_ROOT: OnceLock<Mutex<Option<PathBuf>>> = OnceLock::new(); /// This is used when running cargo is pre-CARGO_TARGET_TMPDIR /// TODO: Remove when CARGO_TARGET_TMPDIR grows old enough. @@ -31,7 +26,10 @@ fn global_root_legacy() -> PathBuf { } fn set_global_root(tmp_dir: Option<&'static str>) { - let mut lock = GLOBAL_ROOT.lock().unwrap(); + let mut lock = GLOBAL_ROOT + .get_or_init(|| Default::default()) + .lock() + .unwrap(); if lock.is_none() { let mut root = match tmp_dir { Some(tmp_dir) => PathBuf::from(tmp_dir), @@ -44,7 +42,10 @@ fn set_global_root(tmp_dir: Option<&'static str>) { } pub fn global_root() -> PathBuf { - let lock = GLOBAL_ROOT.lock().unwrap(); + let lock = GLOBAL_ROOT + .get_or_init(|| Default::default()) + .lock() + .unwrap(); match lock.as_ref() { Some(p) => p.clone(), None => unreachable!("GLOBAL_ROOT not set yet"), 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 0cf82cb70..910f95bfa 100644 --- a/src/tools/cargo/crates/cargo-test-support/src/registry.rs +++ b/src/tools/cargo/crates/cargo-test-support/src/registry.rs @@ -1342,7 +1342,7 @@ impl Package { /// Sets the index schema version for this package. /// - /// See `cargo::sources::registry::RegistryPackage` for more information. + /// See `cargo::sources::registry::IndexPackage` for more information. pub fn schema_version(&mut self, version: u32) -> &mut Package { self.v = Some(version); 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 7c056b6fa..2ce2849ae 100644 --- a/src/tools/cargo/crates/cargo-test-support/src/tools.rs +++ b/src/tools/cargo/crates/cargo-test-support/src/tools.rs @@ -1,20 +1,21 @@ //! Common executables that can be reused by various tests. use crate::{basic_manifest, paths, project, Project}; -use lazy_static::lazy_static; use std::path::{Path, PathBuf}; use std::sync::Mutex; +use std::sync::OnceLock; -lazy_static! { - static ref ECHO_WRAPPER: Mutex<Option<PathBuf>> = Mutex::new(None); - static ref ECHO: Mutex<Option<PathBuf>> = Mutex::new(None); -} +static ECHO_WRAPPER: OnceLock<Mutex<Option<PathBuf>>> = OnceLock::new(); +static ECHO: OnceLock<Mutex<Option<PathBuf>>> = OnceLock::new(); /// Returns the path to an executable that works as a wrapper around rustc. /// /// The wrapper will echo the command line it was called with to stderr. pub fn echo_wrapper() -> PathBuf { - let mut lock = ECHO_WRAPPER.lock().unwrap(); + let mut lock = ECHO_WRAPPER + .get_or_init(|| Default::default()) + .lock() + .unwrap(); if let Some(path) = &*lock { return path.clone(); } @@ -53,7 +54,7 @@ pub fn echo_wrapper() -> PathBuf { /// /// Do not expect this to be anything fancy. pub fn echo() -> PathBuf { - let mut lock = ECHO.lock().unwrap(); + let mut lock = ECHO.get_or_init(|| Default::default()).lock().unwrap(); if let Some(path) = &*lock { return path.clone(); } diff --git a/src/tools/cargo/crates/cargo-util/Cargo.toml b/src/tools/cargo/crates/cargo-util/Cargo.toml index f01705fca..614581037 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.4" +version = "0.2.5" edition = "2021" license = "MIT OR Apache-2.0" homepage = "https://github.com/rust-lang/cargo" diff --git a/src/tools/cargo/crates/cargo-util/src/paths.rs b/src/tools/cargo/crates/cargo-util/src/paths.rs index 69df7a209..4a917821b 100644 --- a/src/tools/cargo/crates/cargo-util/src/paths.rs +++ b/src/tools/cargo/crates/cargo-util/src/paths.rs @@ -55,6 +55,8 @@ pub fn dylib_path_envvar() -> &'static str { // penalty starting in 10.13. Cargo's testsuite ran more than twice as // slow with it on CI. "DYLD_FALLBACK_LIBRARY_PATH" + } else if cfg!(target_os = "aix") { + "LIBPATH" } else { "LD_LIBRARY_PATH" } @@ -411,11 +413,22 @@ fn _create_dir_all(p: &Path) -> Result<()> { Ok(()) } -/// Recursively remove all files and directories at the given directory. +/// Equivalent to [`std::fs::remove_dir_all`] with better error messages. /// /// This does *not* follow symlinks. pub fn remove_dir_all<P: AsRef<Path>>(p: P) -> Result<()> { - _remove_dir_all(p.as_ref()) + _remove_dir_all(p.as_ref()).or_else(|prev_err| { + // `std::fs::remove_dir_all` is highly specialized for different platforms + // and may be more reliable than a simple walk. We try the walk first in + // order to report more detailed errors. + fs::remove_dir_all(p.as_ref()).with_context(|| { + format!( + "{:?}\n\nError: failed to remove directory `{}`", + prev_err, + p.as_ref().display(), + ) + }) + }) } fn _remove_dir_all(p: &Path) -> Result<()> { diff --git a/src/tools/cargo/crates/resolver-tests/Cargo.toml b/src/tools/cargo/crates/resolver-tests/Cargo.toml index 8a7cab113..e0efb9b6d 100644 --- a/src/tools/cargo/crates/resolver-tests/Cargo.toml +++ b/src/tools/cargo/crates/resolver-tests/Cargo.toml @@ -7,7 +7,6 @@ publish = false [dependencies] cargo.workspace = true cargo-util.workspace = true -is-terminal.workspace = true lazy_static.workspace = true proptest.workspace = true varisat.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 01d9b5e6d..ab34e8663 100644 --- a/src/tools/cargo/crates/resolver-tests/src/lib.rs +++ b/src/tools/cargo/crates/resolver-tests/src/lib.rs @@ -179,7 +179,6 @@ pub fn resolve_with_config_raw( used: HashSet::new(), }; let summary = Summary::new( - config, pkg_id("root"), deps, &BTreeMap::new(), @@ -581,7 +580,6 @@ pub fn pkg_dep<T: ToPkgId>(name: T, dep: Vec<Dependency>) -> Summary { None }; Summary::new( - &Config::default().unwrap(), name.to_pkgid(), dep, &BTreeMap::new(), @@ -610,7 +608,6 @@ pub fn pkg_loc(name: &str, loc: &str) -> Summary { None }; Summary::new( - &Config::default().unwrap(), pkg_id_loc(name, loc), Vec::new(), &BTreeMap::new(), @@ -625,7 +622,6 @@ pub fn remove_dep(sum: &Summary, ind: usize) -> Summary { deps.remove(ind); // note: more things will need to be copied over in the future, but it works for now. Summary::new( - &Config::default().unwrap(), sum.package_id(), deps, &BTreeMap::new(), diff --git a/src/tools/cargo/crates/resolver-tests/tests/resolve.rs b/src/tools/cargo/crates/resolver-tests/tests/resolve.rs index df74826f0..02486bfb5 100644 --- a/src/tools/cargo/crates/resolver-tests/tests/resolve.rs +++ b/src/tools/cargo/crates/resolver-tests/tests/resolve.rs @@ -1,3 +1,5 @@ +use std::io::IsTerminal; + use cargo::core::dependency::DepKind; use cargo::core::Dependency; use cargo::util::Config; @@ -21,7 +23,7 @@ use proptest::prelude::*; proptest! { #![proptest_config(ProptestConfig { max_shrink_iters: - if is_ci() || !is_terminal::IsTerminal::is_terminal(&std::io::stderr()){ + if is_ci() || !std::io::stderr().is_terminal() { // This attempts to make sure that CI will fail fast, 0 } else { diff --git a/src/tools/cargo/credential/cargo-credential-1password/README.md b/src/tools/cargo/credential/cargo-credential-1password/README.md new file mode 100644 index 000000000..7cc15e05b --- /dev/null +++ b/src/tools/cargo/credential/cargo-credential-1password/README.md @@ -0,0 +1,7 @@ +# cargo-credential-1password + +This is the implementation for the Cargo credential helper for [1password]. +See the [credential-process] documentation for how to use this. + +[1password]: https://1password.com/ +[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process diff --git a/src/tools/cargo/credential/cargo-credential-gnome-secret/README.md b/src/tools/cargo/credential/cargo-credential-gnome-secret/README.md new file mode 100644 index 000000000..7a4b02838 --- /dev/null +++ b/src/tools/cargo/credential/cargo-credential-gnome-secret/README.md @@ -0,0 +1,7 @@ +# cargo-credential-gnome-secret + +This is the implementation for the Cargo credential helper for [GNOME libsecret]. +See the [credential-process] documentation for how to use this. + +[GNOME libsecret]: https://wiki.gnome.org/Projects/Libsecret +[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process diff --git a/src/tools/cargo/credential/cargo-credential-gnome-secret/build.rs b/src/tools/cargo/credential/cargo-credential-gnome-secret/build.rs index 9283535af..8bb86ee43 100644 --- a/src/tools/cargo/credential/cargo-credential-gnome-secret/build.rs +++ b/src/tools/cargo/credential/cargo-credential-gnome-secret/build.rs @@ -1,3 +1,8 @@ fn main() { - pkg_config::probe_library("libsecret-1").unwrap(); + if cfg!(target_os = "linux") { + // TODO: Consider ignoring errors when libsecret is not installed and + // switching the impl to UnsupportedCredential (possibly along with a + // warning?). + pkg_config::probe_library("libsecret-1").unwrap(); + } } diff --git a/src/tools/cargo/credential/cargo-credential-gnome-secret/src/libsecret.rs b/src/tools/cargo/credential/cargo-credential-gnome-secret/src/libsecret.rs new file mode 100644 index 000000000..c584eeecf --- /dev/null +++ b/src/tools/cargo/credential/cargo-credential-gnome-secret/src/libsecret.rs @@ -0,0 +1,190 @@ +//! Implementation of the libsecret credential helper. + +use cargo_credential::{Credential, Error}; +use std::ffi::{CStr, CString}; +use std::os::raw::{c_char, c_int}; +use std::ptr::{null, null_mut}; + +#[allow(non_camel_case_types)] +type gchar = c_char; + +#[allow(non_camel_case_types)] +type gboolean = c_int; + +type GQuark = u32; + +#[repr(C)] +struct GError { + domain: GQuark, + code: c_int, + message: *mut gchar, +} + +#[repr(C)] +struct GCancellable { + _private: [u8; 0], +} + +#[repr(C)] +struct SecretSchema { + name: *const gchar, + flags: SecretSchemaFlags, + attributes: [SecretSchemaAttribute; 32], +} + +#[repr(C)] +#[derive(Copy, Clone)] +struct SecretSchemaAttribute { + name: *const gchar, + attr_type: SecretSchemaAttributeType, +} + +#[repr(C)] +enum SecretSchemaFlags { + None = 0, +} + +#[repr(C)] +#[derive(Copy, Clone)] +enum SecretSchemaAttributeType { + String = 0, +} + +extern "C" { + fn secret_password_store_sync( + schema: *const SecretSchema, + collection: *const gchar, + label: *const gchar, + password: *const gchar, + cancellable: *mut GCancellable, + error: *mut *mut GError, + ... + ) -> gboolean; + fn secret_password_clear_sync( + schema: *const SecretSchema, + cancellable: *mut GCancellable, + error: *mut *mut GError, + ... + ) -> gboolean; + fn secret_password_lookup_sync( + schema: *const SecretSchema, + cancellable: *mut GCancellable, + error: *mut *mut GError, + ... + ) -> *mut gchar; +} + +pub struct GnomeSecret; + +fn label(index_url: &str) -> CString { + CString::new(format!("cargo-registry:{}", index_url)).unwrap() +} + +fn schema() -> SecretSchema { + let mut attributes = [SecretSchemaAttribute { + name: null(), + attr_type: SecretSchemaAttributeType::String, + }; 32]; + attributes[0] = SecretSchemaAttribute { + name: b"url\0".as_ptr() as *const gchar, + attr_type: SecretSchemaAttributeType::String, + }; + SecretSchema { + name: b"org.rust-lang.cargo.registry\0".as_ptr() as *const gchar, + flags: SecretSchemaFlags::None, + attributes, + } +} + +impl Credential for GnomeSecret { + fn name(&self) -> &'static str { + env!("CARGO_PKG_NAME") + } + + fn get(&self, index_url: &str) -> Result<String, Error> { + let mut error: *mut GError = null_mut(); + let attr_url = CString::new("url").unwrap(); + let index_url_c = CString::new(index_url).unwrap(); + let schema = schema(); + unsafe { + let token_c = secret_password_lookup_sync( + &schema, + null_mut(), + &mut error, + attr_url.as_ptr(), + index_url_c.as_ptr(), + null() as *const gchar, + ); + if !error.is_null() { + return Err(format!( + "failed to get token: {}", + CStr::from_ptr((*error).message).to_str()? + ) + .into()); + } + if token_c.is_null() { + return Err(format!("cannot find token for {}", index_url).into()); + } + let token = CStr::from_ptr(token_c) + .to_str() + .map_err(|e| format!("expected utf8 token: {}", e))? + .to_string(); + Ok(token) + } + } + + fn store(&self, index_url: &str, token: &str, name: Option<&str>) -> Result<(), Error> { + let label = label(name.unwrap_or(index_url)); + let token = CString::new(token).unwrap(); + let mut error: *mut GError = null_mut(); + let attr_url = CString::new("url").unwrap(); + let index_url_c = CString::new(index_url).unwrap(); + let schema = schema(); + unsafe { + secret_password_store_sync( + &schema, + b"default\0".as_ptr() as *const gchar, + label.as_ptr(), + token.as_ptr(), + null_mut(), + &mut error, + attr_url.as_ptr(), + index_url_c.as_ptr(), + null() as *const gchar, + ); + if !error.is_null() { + return Err(format!( + "failed to store token: {}", + CStr::from_ptr((*error).message).to_str()? + ) + .into()); + } + } + Ok(()) + } + + fn erase(&self, index_url: &str) -> Result<(), Error> { + let schema = schema(); + let mut error: *mut GError = null_mut(); + let attr_url = CString::new("url").unwrap(); + let index_url_c = CString::new(index_url).unwrap(); + unsafe { + secret_password_clear_sync( + &schema, + null_mut(), + &mut error, + attr_url.as_ptr(), + index_url_c.as_ptr(), + null() as *const gchar, + ); + if !error.is_null() { + return Err(format!( + "failed to erase token: {}", + CStr::from_ptr((*error).message).to_str()? + ) + .into()); + } + } + Ok(()) + } +} diff --git a/src/tools/cargo/credential/cargo-credential-gnome-secret/src/main.rs b/src/tools/cargo/credential/cargo-credential-gnome-secret/src/main.rs index 40972b05d..1d2ecc61f 100644 --- a/src/tools/cargo/credential/cargo-credential-gnome-secret/src/main.rs +++ b/src/tools/cargo/credential/cargo-credential-gnome-secret/src/main.rs @@ -1,193 +1,11 @@ //! Cargo registry gnome libsecret credential process. -use cargo_credential::{Credential, Error}; -use std::ffi::{CStr, CString}; -use std::os::raw::{c_char, c_int}; -use std::ptr::{null, null_mut}; - -#[allow(non_camel_case_types)] -type gchar = c_char; - -#[allow(non_camel_case_types)] -type gboolean = c_int; - -type GQuark = u32; - -#[repr(C)] -struct GError { - domain: GQuark, - code: c_int, - message: *mut gchar, -} - -#[repr(C)] -struct GCancellable { - _private: [u8; 0], -} - -#[repr(C)] -struct SecretSchema { - name: *const gchar, - flags: SecretSchemaFlags, - attributes: [SecretSchemaAttribute; 32], -} - -#[repr(C)] -#[derive(Copy, Clone)] -struct SecretSchemaAttribute { - name: *const gchar, - attr_type: SecretSchemaAttributeType, -} - -#[repr(C)] -enum SecretSchemaFlags { - None = 0, -} - -#[repr(C)] -#[derive(Copy, Clone)] -enum SecretSchemaAttributeType { - String = 0, -} - -extern "C" { - fn secret_password_store_sync( - schema: *const SecretSchema, - collection: *const gchar, - label: *const gchar, - password: *const gchar, - cancellable: *mut GCancellable, - error: *mut *mut GError, - ... - ) -> gboolean; - fn secret_password_clear_sync( - schema: *const SecretSchema, - cancellable: *mut GCancellable, - error: *mut *mut GError, - ... - ) -> gboolean; - fn secret_password_lookup_sync( - schema: *const SecretSchema, - cancellable: *mut GCancellable, - error: *mut *mut GError, - ... - ) -> *mut gchar; -} - -struct GnomeSecret; - -fn label(index_url: &str) -> CString { - CString::new(format!("cargo-registry:{}", index_url)).unwrap() -} - -fn schema() -> SecretSchema { - let mut attributes = [SecretSchemaAttribute { - name: null(), - attr_type: SecretSchemaAttributeType::String, - }; 32]; - attributes[0] = SecretSchemaAttribute { - name: b"url\0".as_ptr() as *const gchar, - attr_type: SecretSchemaAttributeType::String, - }; - SecretSchema { - name: b"org.rust-lang.cargo.registry\0".as_ptr() as *const gchar, - flags: SecretSchemaFlags::None, - attributes, - } -} - -impl Credential for GnomeSecret { - fn name(&self) -> &'static str { - env!("CARGO_PKG_NAME") - } - - fn get(&self, index_url: &str) -> Result<String, Error> { - let mut error: *mut GError = null_mut(); - let attr_url = CString::new("url").unwrap(); - let index_url_c = CString::new(index_url).unwrap(); - let schema = schema(); - unsafe { - let token_c = secret_password_lookup_sync( - &schema, - null_mut(), - &mut error, - attr_url.as_ptr(), - index_url_c.as_ptr(), - null() as *const gchar, - ); - if !error.is_null() { - return Err(format!( - "failed to get token: {}", - CStr::from_ptr((*error).message).to_str()? - ) - .into()); - } - if token_c.is_null() { - return Err(format!("cannot find token for {}", index_url).into()); - } - let token = CStr::from_ptr(token_c) - .to_str() - .map_err(|e| format!("expected utf8 token: {}", e))? - .to_string(); - Ok(token) - } - } - - fn store(&self, index_url: &str, token: &str, name: Option<&str>) -> Result<(), Error> { - let label = label(name.unwrap_or(index_url)); - let token = CString::new(token).unwrap(); - let mut error: *mut GError = null_mut(); - let attr_url = CString::new("url").unwrap(); - let index_url_c = CString::new(index_url).unwrap(); - let schema = schema(); - unsafe { - secret_password_store_sync( - &schema, - b"default\0".as_ptr() as *const gchar, - label.as_ptr(), - token.as_ptr(), - null_mut(), - &mut error, - attr_url.as_ptr(), - index_url_c.as_ptr(), - null() as *const gchar, - ); - if !error.is_null() { - return Err(format!( - "failed to store token: {}", - CStr::from_ptr((*error).message).to_str()? - ) - .into()); - } - } - Ok(()) - } - - fn erase(&self, index_url: &str) -> Result<(), Error> { - let schema = schema(); - let mut error: *mut GError = null_mut(); - let attr_url = CString::new("url").unwrap(); - let index_url_c = CString::new(index_url).unwrap(); - unsafe { - secret_password_clear_sync( - &schema, - null_mut(), - &mut error, - attr_url.as_ptr(), - index_url_c.as_ptr(), - null() as *const gchar, - ); - if !error.is_null() { - return Err(format!( - "failed to erase token: {}", - CStr::from_ptr((*error).message).to_str()? - ) - .into()); - } - } - Ok(()) - } -} +#[cfg(target_os = "linux")] +mod libsecret; +#[cfg(not(target_os = "linux"))] +use cargo_credential::UnsupportedCredential as GnomeSecret; +#[cfg(target_os = "linux")] +use libsecret::GnomeSecret; fn main() { cargo_credential::main(GnomeSecret); diff --git a/src/tools/cargo/credential/cargo-credential-macos-keychain/README.md b/src/tools/cargo/credential/cargo-credential-macos-keychain/README.md new file mode 100644 index 000000000..554116b55 --- /dev/null +++ b/src/tools/cargo/credential/cargo-credential-macos-keychain/README.md @@ -0,0 +1,7 @@ +# cargo-credential-macos-keychain + +This is the implementation for the Cargo credential helper for [macOS Keychain]. +See the [credential-process] documentation for how to use this. + +[macOS Keychain]: https://support.apple.com/guide/keychain-access/welcome/mac +[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process diff --git a/src/tools/cargo/credential/cargo-credential-wincred/README.md b/src/tools/cargo/credential/cargo-credential-wincred/README.md new file mode 100644 index 000000000..8c8d18789 --- /dev/null +++ b/src/tools/cargo/credential/cargo-credential-wincred/README.md @@ -0,0 +1,7 @@ +# cargo-credential-wincred + +This is the implementation for the Cargo credential helper for [Windows Credential Manager]. +See the [credential-process] documentation for how to use this. + +[Windows Credential Manager]: https://support.microsoft.com/en-us/windows/accessing-credential-manager-1b5c916a-6a16-889f-8581-fc16e8165ac0 +[credential-process]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#credential-process diff --git a/src/tools/cargo/src/bin/cargo/cli.rs b/src/tools/cargo/src/bin/cargo/cli.rs index 946816571..db52bc8f2 100644 --- a/src/tools/cargo/src/bin/cargo/cli.rs +++ b/src/tools/cargo/src/bin/cargo/cli.rs @@ -1,7 +1,7 @@ use anyhow::{anyhow, Context as _}; use cargo::core::shell::Shell; use cargo::core::{features, CliUnstable}; -use cargo::{self, drop_print, drop_println, CliResult, Config}; +use cargo::{self, drop_print, drop_println, CargoResult, CliResult, Config}; use clap::{Arg, ArgMatches}; use itertools::Itertools; use std::collections::HashMap; @@ -14,16 +14,6 @@ use super::list_commands; use crate::command_prelude::*; use cargo::core::features::HIDDEN; -lazy_static::lazy_static! { - // Maps from commonly known external commands (not builtin to cargo) to their - // description, for the help page. Reserved for external subcommands that are - // core within the rust ecosystem (esp ones that might become internal in the future). - static ref KNOWN_EXTERNAL_COMMAND_DESCRIPTIONS: HashMap<&'static str, &'static str> = HashMap::from([ - ("clippy", "Checks a package to catch common mistakes and improve your Rust code."), - ("fmt", "Formats all bin and lib files of the current crate using rustfmt."), - ]); -} - pub fn main(config: &mut LazyConfig) -> CliResult { let args = cli().try_get_matches()?; @@ -128,15 +118,28 @@ Run with 'cargo -Z [FLAG] [COMMAND]'", } if expanded_args.flag("list") { + // Maps from commonly known external commands (not builtin to cargo) + // to their description, for the help page. Reserved for external + // subcommands that are core within the rust ecosystem (esp ones that + // might become internal in the future). + let known_external_command_descriptions = HashMap::from([ + ( + "clippy", + "Checks a package to catch common mistakes and improve your Rust code.", + ), + ( + "fmt", + "Formats all bin and lib files of the current crate using rustfmt.", + ), + ]); drop_println!(config, "Installed Commands:"); for (name, command) in list_commands(config) { - let known_external_desc = KNOWN_EXTERNAL_COMMAND_DESCRIPTIONS.get(name.as_str()); + let known_external_desc = known_external_command_descriptions.get(name.as_str()); match command { CommandInfo::BuiltIn { about } => { assert!( known_external_desc.is_none(), - "KNOWN_EXTERNAL_COMMANDS shouldn't contain builtin \"{}\"", - name + "known_external_commands shouldn't contain builtin `{name}`", ); let summary = about.unwrap_or_default(); let summary = summary.lines().next().unwrap_or(&summary); // display only the first line @@ -172,10 +175,11 @@ Run with 'cargo -Z [FLAG] [COMMAND]'", return Ok(()); } }; - config_configure(config, &expanded_args, subcommand_args, global_args)?; + let exec = Exec::infer(cmd)?; + config_configure(config, &expanded_args, subcommand_args, global_args, &exec)?; super::init_git(config); - execute_subcommand(config, cmd, subcommand_args) + exec.exec(config, subcommand_args) } pub fn get_version_string(is_verbose: bool) -> String { @@ -255,7 +259,7 @@ fn expand_aliases( args: ArgMatches, mut already_expanded: Vec<String>, ) -> Result<(ArgMatches, GlobalArgs), CliError> { - if let Some((cmd, args)) = args.subcommand() { + if let Some((cmd, sub_args)) = args.subcommand() { let exec = commands::builtin_exec(cmd); let aliased_cmd = super::aliased_command(config, cmd); @@ -271,7 +275,7 @@ fn expand_aliases( // Here we ignore errors from aliasing as we already favor built-in command, // and alias doesn't involve in this context. - if let Some(values) = args.get_many::<OsString>("") { + if let Some(values) = sub_args.get_many::<OsString>("") { // Command is built-in and is not conflicting with alias, but contains ignored values. return Err(anyhow::format_err!( "\ @@ -302,17 +306,34 @@ For more information, see issue #10049 <https://github.com/rust-lang/cargo/issue ))?; } } + if commands::run::is_manifest_command(cmd) { + if config.cli_unstable().script { + return Ok((args, GlobalArgs::default())); + } else { + config.shell().warn(format_args!( + "\ +user-defined alias `{cmd}` has the appearance of a manfiest-command +This was previously accepted but will be phased out when `-Zscript` is stabilized. +For more information, see issue #12207 <https://github.com/rust-lang/cargo/issues/12207>." + ))?; + } + } let mut alias = alias .into_iter() .map(|s| OsString::from(s)) .collect::<Vec<_>>(); - alias.extend(args.get_many::<OsString>("").unwrap_or_default().cloned()); + alias.extend( + sub_args + .get_many::<OsString>("") + .unwrap_or_default() + .cloned(), + ); // new_args strips out everything before the subcommand, so // capture those global options now. // Note that an alias to an external command will not receive // these arguments. That may be confusing, but such is life. - let global_args = GlobalArgs::new(args); + let global_args = GlobalArgs::new(sub_args); let new_args = cli().no_binary_name(true).try_get_matches_from(alias)?; let new_cmd = new_args.subcommand_name().expect("subcommand is required"); @@ -343,12 +364,26 @@ fn config_configure( args: &ArgMatches, subcommand_args: &ArgMatches, global_args: GlobalArgs, + exec: &Exec, ) -> CliResult { let arg_target_dir = &subcommand_args.value_of_path("target-dir", config); - let verbose = global_args.verbose + args.verbose(); + let mut verbose = global_args.verbose + args.verbose(); // quiet is unusual because it is redefined in some subcommands in order // to provide custom help text. - let quiet = args.flag("quiet") || subcommand_args.flag("quiet") || global_args.quiet; + let mut quiet = args.flag("quiet") || subcommand_args.flag("quiet") || global_args.quiet; + if matches!(exec, Exec::Manifest(_)) && !quiet { + // Verbosity is shifted quieter for `Exec::Manifest` as it is can be used as if you ran + // `cargo install` and we especially shouldn't pollute programmatic output. + // + // For now, interactive output has the same default output as `cargo run` but that is + // subject to change. + if let Some(lower) = verbose.checked_sub(1) { + verbose = lower; + } else if !config.shell().is_err_tty() { + // Don't pollute potentially-scripted output + quiet = true; + } + } let global_color = global_args.color; // Extract so it can take reference. let color = args .get_one::<String>("color") @@ -379,19 +414,65 @@ fn config_configure( Ok(()) } -fn execute_subcommand(config: &mut Config, cmd: &str, subcommand_args: &ArgMatches) -> CliResult { - if let Some(exec) = commands::builtin_exec(cmd) { - return exec(config, subcommand_args); +enum Exec { + Builtin(commands::Exec), + Manifest(String), + External(String), +} + +impl Exec { + /// Precedence isn't the most obvious from this function because + /// - Some is determined by `expand_aliases` + /// - Some is enforced by `avoid_ambiguity_between_builtins_and_manifest_commands` + /// + /// In actuality, it is: + /// 1. built-ins xor manifest-command + /// 2. aliases + /// 3. external subcommands + fn infer(cmd: &str) -> CargoResult<Self> { + if let Some(exec) = commands::builtin_exec(cmd) { + Ok(Self::Builtin(exec)) + } else if commands::run::is_manifest_command(cmd) { + Ok(Self::Manifest(cmd.to_owned())) + } else { + Ok(Self::External(cmd.to_owned())) + } } - let mut ext_args: Vec<&OsStr> = vec![OsStr::new(cmd)]; - ext_args.extend( - subcommand_args - .get_many::<OsString>("") - .unwrap_or_default() - .map(OsString::as_os_str), - ); - super::execute_external_subcommand(config, cmd, &ext_args) + fn exec(self, config: &mut Config, subcommand_args: &ArgMatches) -> CliResult { + match self { + Self::Builtin(exec) => exec(config, subcommand_args), + Self::Manifest(cmd) => { + let ext_path = super::find_external_subcommand(config, &cmd); + if !config.cli_unstable().script && ext_path.is_some() { + config.shell().warn(format_args!( + "\ +external subcommand `{cmd}` has the appearance of a manfiest-command +This was previously accepted but will be phased out when `-Zscript` is stabilized. +For more information, see issue #12207 <https://github.com/rust-lang/cargo/issues/12207>.", + ))?; + Self::External(cmd).exec(config, subcommand_args) + } else { + let ext_args: Vec<OsString> = subcommand_args + .get_many::<OsString>("") + .unwrap_or_default() + .cloned() + .collect(); + commands::run::exec_manifest_command(config, &cmd, &ext_args) + } + } + Self::External(cmd) => { + let mut ext_args = vec![OsStr::new(&cmd)]; + ext_args.extend( + subcommand_args + .get_many::<OsString>("") + .unwrap_or_default() + .map(OsString::as_os_str), + ); + super::execute_external_subcommand(config, &cmd, &ext_args) + } + } + } } #[derive(Default)] @@ -435,9 +516,9 @@ pub fn cli() -> Command { #[allow(clippy::disallowed_methods)] let is_rustup = std::env::var_os("RUSTUP_HOME").is_some(); let usage = if is_rustup { - "cargo [+toolchain] [OPTIONS] [COMMAND]" + "cargo [+toolchain] [OPTIONS] [COMMAND]\n cargo [+toolchain] [OPTIONS] -Zscript <MANIFEST_RS> [ARGS]..." } else { - "cargo [OPTIONS] [COMMAND]" + "cargo [OPTIONS] [COMMAND]\n cargo [OPTIONS] -Zscript <MANIFEST> [ARGS]..." }; Command::new("cargo") // Subcommands all count their args' display order independently (from 0), @@ -567,3 +648,14 @@ impl LazyConfig { fn verify_cli() { cli().debug_assert(); } + +#[test] +fn avoid_ambiguity_between_builtins_and_manifest_commands() { + for cmd in commands::builtin() { + let name = cmd.get_name(); + assert!( + !commands::run::is_manifest_command(&name), + "built-in command {name} is ambiguous with manifest-commands" + ) + } +} diff --git a/src/tools/cargo/src/bin/cargo/commands/add.rs b/src/tools/cargo/src/bin/cargo/commands/add.rs index 90c4f4dd5..52fc38b74 100644 --- a/src/tools/cargo/src/bin/cargo/commands/add.rs +++ b/src/tools/cargo/src/bin/cargo/commands/add.rs @@ -242,7 +242,20 @@ fn parse_dependencies(config: &Config, matches: &ArgMatches) -> CargoResult<Vec< .flatten() .map(|c| (Some(c.clone()), None)) .collect::<IndexMap<_, _>>(); + let mut infer_crate_name = false; + + for (crate_name, _) in crates.iter() { + let crate_name = crate_name.as_ref().unwrap(); + + if let Some(toolchain) = crate_name.strip_prefix("+") { + anyhow::bail!( + "invalid character `+` in dependency name: `+{toolchain}` + Use `cargo +{toolchain} add` if you meant to use the `{toolchain}` toolchain." + ); + } + } + if crates.is_empty() { if path.is_some() || git.is_some() { crates.insert(None, None); diff --git a/src/tools/cargo/src/bin/cargo/commands/install.rs b/src/tools/cargo/src/bin/cargo/commands/install.rs index 8197a1690..3bb90c2d5 100644 --- a/src/tools/cargo/src/bin/cargo/commands/install.rs +++ b/src/tools/cargo/src/bin/cargo/commands/install.rs @@ -1,5 +1,6 @@ use crate::command_prelude::*; +use anyhow::anyhow; use cargo::core::{GitReference, SourceId, Workspace}; use cargo::ops; use cargo::util::IntoUrl; @@ -108,6 +109,16 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { .map(|k| resolve_crate(k, version)) .collect::<crate::CargoResult<Vec<_>>>()?; + for (crate_name, _) in krates.iter() { + if let Some(toolchain) = crate_name.strip_prefix("+") { + return Err(anyhow!( + "invalid character `+` in package name: `+{toolchain}` + Use `cargo +{toolchain} install` if you meant to use the `{toolchain}` toolchain." + ) + .into()); + } + } + let mut from_cwd = false; let source = if let Some(url) = args.get_one::<String>("git") { diff --git a/src/tools/cargo/src/bin/cargo/commands/mod.rs b/src/tools/cargo/src/bin/cargo/commands/mod.rs index da3109260..b9da0e5fb 100644 --- a/src/tools/cargo/src/bin/cargo/commands/mod.rs +++ b/src/tools/cargo/src/bin/cargo/commands/mod.rs @@ -43,7 +43,9 @@ pub fn builtin() -> Vec<Command> { ] } -pub fn builtin_exec(cmd: &str) -> Option<fn(&mut Config, &ArgMatches) -> CliResult> { +pub type Exec = fn(&mut Config, &ArgMatches) -> CliResult; + +pub fn builtin_exec(cmd: &str) -> Option<Exec> { let f = match cmd { "add" => add::exec, "bench" => bench::exec, diff --git a/src/tools/cargo/src/bin/cargo/commands/run.rs b/src/tools/cargo/src/bin/cargo/commands/run.rs index cde754c7a..366e19396 100644 --- a/src/tools/cargo/src/bin/cargo/commands/run.rs +++ b/src/tools/cargo/src/bin/cargo/commands/run.rs @@ -1,6 +1,11 @@ +use std::ffi::OsStr; +use std::ffi::OsString; +use std::path::Path; + use crate::command_prelude::*; use crate::util::restricted_names::is_glob_pattern; use cargo::core::Verbosity; +use cargo::core::Workspace; use cargo::ops::{self, CompileFilter, Packages}; use cargo_util::ProcessError; @@ -13,7 +18,7 @@ pub fn cli() -> Command { .arg( Arg::new("args") .help("Arguments for the binary or example to run") - .value_parser(value_parser!(std::ffi::OsString)) + .value_parser(value_parser!(OsString)) .num_args(0..) .trailing_var_arg(true), ) @@ -77,27 +82,64 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { } }; - ops::run(&ws, &compile_opts, &values_os(args, "args")).map_err(|err| { - let proc_err = match err.downcast_ref::<ProcessError>() { - Some(e) => e, - None => return CliError::new(err, 101), - }; - - // If we never actually spawned the process then that sounds pretty - // bad and we always want to forward that up. - let exit_code = match proc_err.code { - Some(exit) => exit, - None => return CliError::new(err, 101), - }; - - // If `-q` was passed then we suppress extra error information about - // a failed process, we assume the process itself printed out enough - // information about why it failed so we don't do so as well - let is_quiet = config.shell().verbosity() == Verbosity::Quiet; - if is_quiet { - CliError::code(exit_code) - } else { - CliError::new(err, exit_code) - } - }) + ops::run(&ws, &compile_opts, &values_os(args, "args")).map_err(|err| to_run_error(config, err)) +} + +/// See also `util/toml/mod.rs`s `is_embedded` +pub fn is_manifest_command(arg: &str) -> bool { + let path = Path::new(arg); + 1 < path.components().count() + || path.extension() == Some(OsStr::new("rs")) + || path.file_name() == Some(OsStr::new("Cargo.toml")) +} + +pub fn exec_manifest_command(config: &mut Config, cmd: &str, args: &[OsString]) -> CliResult { + if !config.cli_unstable().script { + return Err(anyhow::anyhow!("running `{cmd}` requires `-Zscript`").into()); + } + + let manifest_path = Path::new(cmd); + let manifest_path = root_manifest(Some(manifest_path), config)?; + + // Treat `cargo foo.rs` like `cargo install --path foo` and re-evaluate the config based on the + // location where the script resides, rather than the environment from where it's being run. + let parent_path = manifest_path + .parent() + .expect("a file should always have a parent"); + config.reload_rooted_at(parent_path)?; + + let mut ws = Workspace::new(&manifest_path, config)?; + if config.cli_unstable().avoid_dev_deps { + ws.set_require_optional_deps(false); + } + + let mut compile_opts = + cargo::ops::CompileOptions::new(config, cargo::core::compiler::CompileMode::Build)?; + compile_opts.spec = cargo::ops::Packages::Default; + + cargo::ops::run(&ws, &compile_opts, args).map_err(|err| to_run_error(config, err)) +} + +fn to_run_error(config: &cargo::util::Config, err: anyhow::Error) -> CliError { + let proc_err = match err.downcast_ref::<ProcessError>() { + Some(e) => e, + None => return CliError::new(err, 101), + }; + + // If we never actually spawned the process then that sounds pretty + // bad and we always want to forward that up. + let exit_code = match proc_err.code { + Some(exit) => exit, + None => return CliError::new(err, 101), + }; + + // If `-q` was passed then we suppress extra error information about + // a failed process, we assume the process itself printed out enough + // information about why it failed so we don't do so as well + let is_quiet = config.shell().verbosity() == Verbosity::Quiet; + if is_quiet { + CliError::code(exit_code) + } else { + CliError::new(err, exit_code) + } } diff --git a/src/tools/cargo/src/bin/cargo/main.rs b/src/tools/cargo/src/bin/cargo/main.rs index 9fb6635ea..462332fb7 100644 --- a/src/tools/cargo/src/bin/cargo/main.rs +++ b/src/tools/cargo/src/bin/cargo/main.rs @@ -2,6 +2,8 @@ #![allow(clippy::all)] #![warn(clippy::disallowed_methods)] +use cargo::util::network::http::http_handle; +use cargo::util::network::http::needs_custom_http_transport; use cargo::util::toml::StringOrVec; use cargo::util::CliError; use cargo::util::{self, closest_msg, command_prelude, CargoResult, CliResult, Config}; @@ -293,12 +295,12 @@ fn init_git(config: &Config) { /// configured to use libcurl instead of the built-in networking support so /// that those configuration settings can be used. fn init_git_transports(config: &Config) { - match cargo::ops::needs_custom_http_transport(config) { + match needs_custom_http_transport(config) { Ok(true) => {} _ => return, } - let handle = match cargo::ops::http_handle(config) { + let handle = match http_handle(config) { Ok(handle) => handle, Err(..) => return, }; diff --git a/src/tools/cargo/src/cargo/core/compiler/build_config.rs b/src/tools/cargo/src/cargo/core/compiler/build_config.rs index 885b124b9..5d4d754bf 100644 --- a/src/tools/cargo/src/cargo/core/compiler/build_config.rs +++ b/src/tools/cargo/src/cargo/core/compiler/build_config.rs @@ -1,4 +1,5 @@ use crate::core::compiler::CompileKind; +use crate::util::config::JobsConfig; use crate::util::interning::InternedString; use crate::util::{CargoResult, Config, RustfixDiagnosticServer}; use anyhow::{bail, Context as _}; @@ -64,7 +65,7 @@ impl BuildConfig { /// * `target.$target.libfoo.metadata` pub fn new( config: &Config, - jobs: Option<i32>, + jobs: Option<JobsConfig>, keep_going: bool, requested_targets: &[String], mode: CompileMode, @@ -78,11 +79,22 @@ impl BuildConfig { its environment, ignoring the `-j` parameter", )?; } - let jobs = match jobs.or(cfg.jobs) { + let jobs = match jobs.or(cfg.jobs.clone()) { None => default_parallelism()?, - Some(0) => anyhow::bail!("jobs may not be 0"), - Some(j) if j < 0 => (default_parallelism()? as i32 + j).max(1) as u32, - Some(j) => j as u32, + Some(value) => match value { + JobsConfig::Integer(j) => match j { + 0 => anyhow::bail!("jobs may not be 0"), + j if j < 0 => (default_parallelism()? as i32 + j).max(1) as u32, + j => j as u32, + }, + JobsConfig::String(j) => match j.as_str() { + "default" => default_parallelism()?, + _ => { + anyhow::bail!( + format!("could not parse `{j}`. Number of parallel jobs should be `default` or a number.")) + } + }, + }, }; if config.cli_unstable().build_std.is_some() && requested_kinds[0].is_host() { diff --git a/src/tools/cargo/src/cargo/core/compiler/custom_build.rs b/src/tools/cargo/src/cargo/core/compiler/custom_build.rs index 01890e542..d17462174 100644 --- a/src/tools/cargo/src/cargo/core/compiler/custom_build.rs +++ b/src/tools/cargo/src/cargo/core/compiler/custom_build.rs @@ -31,7 +31,7 @@ //! [`CompileMode::RunCustomBuild`]: super::CompileMode //! [instructions]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script -use super::{fingerprint, Context, Job, LinkType, Unit, Work}; +use super::{fingerprint, Context, Job, Unit, Work}; use crate::core::compiler::artifact; use crate::core::compiler::context::Metadata; use crate::core::compiler::job_queue::JobState; @@ -62,7 +62,7 @@ pub struct BuildOutput { /// Names and link kinds of libraries, suitable for the `-l` flag. pub library_links: Vec<String>, /// Linker arguments suitable to be passed to `-C link-arg=<args>` - pub linker_args: Vec<(LinkType, String)>, + pub linker_args: Vec<(LinkArgTarget, String)>, /// Various `--cfg` flags to pass to the compiler. pub cfgs: Vec<String>, /// Various `--check-cfg` flags to pass to the compiler. @@ -146,6 +146,47 @@ pub struct BuildDeps { pub rerun_if_env_changed: Vec<String>, } +/// Represents one of the instructions from `cargo:rustc-link-arg-*` build +/// script instruction family. +/// +/// In other words, indicates targets that custom linker arguments applies to. +/// +/// See the [build script documentation][1] for more. +/// +/// [1]: https://doc.rust-lang.org/nightly/cargo/reference/build-scripts.html#cargorustc-link-argflag +#[derive(Clone, Hash, Debug, PartialEq, Eq)] +pub enum LinkArgTarget { + /// Represents `cargo:rustc-link-arg=FLAG`. + All, + /// Represents `cargo:rustc-cdylib-link-arg=FLAG`. + Cdylib, + /// Represents `cargo:rustc-link-arg-bins=FLAG`. + Bin, + /// Represents `cargo:rustc-link-arg-bin=BIN=FLAG`. + SingleBin(String), + /// Represents `cargo:rustc-link-arg-tests=FLAG`. + Test, + /// Represents `cargo:rustc-link-arg-benches=FLAG`. + Bench, + /// Represents `cargo:rustc-link-arg-examples=FLAG`. + Example, +} + +impl LinkArgTarget { + /// Checks if this link type applies to a given [`Target`]. + pub fn applies_to(&self, target: &Target) -> bool { + match self { + LinkArgTarget::All => true, + LinkArgTarget::Cdylib => target.is_cdylib(), + LinkArgTarget::Bin => target.is_bin(), + LinkArgTarget::SingleBin(name) => target.is_bin() && target.name() == name, + LinkArgTarget::Test => target.is_test(), + LinkArgTarget::Bench => target.is_bench(), + LinkArgTarget::Example => target.is_exe_example(), + } + } +} + /// Prepares a `Work` that executes the target as a custom build script. pub fn prepare(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> { let _p = profile::start(format!( @@ -711,10 +752,10 @@ impl BuildOutput { key, pkg_descr )); } - linker_args.push((LinkType::Cdylib, value)) + linker_args.push((LinkArgTarget::Cdylib, value)) } "rustc-link-arg-bins" => { - check_and_add_target!("bin", Target::is_bin, LinkType::Bin); + check_and_add_target!("bin", Target::is_bin, LinkArgTarget::Bin); } "rustc-link-arg-bin" => { let mut parts = value.splitn(2, '='); @@ -742,19 +783,19 @@ impl BuildOutput { bin_name ); } - linker_args.push((LinkType::SingleBin(bin_name), arg.to_string())); + linker_args.push((LinkArgTarget::SingleBin(bin_name), arg.to_string())); } "rustc-link-arg-tests" => { - check_and_add_target!("test", Target::is_test, LinkType::Test); + check_and_add_target!("test", Target::is_test, LinkArgTarget::Test); } "rustc-link-arg-benches" => { - check_and_add_target!("benchmark", Target::is_bench, LinkType::Bench); + check_and_add_target!("benchmark", Target::is_bench, LinkArgTarget::Bench); } "rustc-link-arg-examples" => { - check_and_add_target!("example", Target::is_example, LinkType::Example); + check_and_add_target!("example", Target::is_example, LinkArgTarget::Example); } "rustc-link-arg" => { - linker_args.push((LinkType::All, value)); + linker_args.push((LinkArgTarget::All, value)); } "rustc-cfg" => cfgs.push(value.to_string()), "rustc-check-cfg" => { 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 a3523110b..aa8be50f7 100644 --- a/src/tools/cargo/src/cargo/core/compiler/fingerprint/mod.rs +++ b/src/tools/cargo/src/cargo/core/compiler/fingerprint/mod.rs @@ -1,8 +1,11 @@ -//! # Fingerprints +//! Tracks changes to determine if something needs to be recompiled. //! //! This module implements change-tracking so that Cargo can know whether or //! not something needs to be recompiled. A Cargo [`Unit`] can be either "dirty" //! (needs to be recompiled) or "fresh" (it does not need to be recompiled). +//! +//! ## Mechanisms affecting freshness +//! //! There are several mechanisms that influence a Unit's freshness: //! //! - The [`Fingerprint`] is a hash, saved to the filesystem in the diff --git a/src/tools/cargo/src/cargo/core/compiler/mod.rs b/src/tools/cargo/src/cargo/core/compiler/mod.rs index 7e49f0079..31e63c226 100644 --- a/src/tools/cargo/src/cargo/core/compiler/mod.rs +++ b/src/tools/cargo/src/cargo/core/compiler/mod.rs @@ -76,6 +76,7 @@ pub use self::compilation::{Compilation, Doctest, UnitOutput}; pub use self::compile_kind::{CompileKind, CompileTarget}; pub use self::context::{Context, Metadata}; pub use self::crate_type::CrateType; +pub use self::custom_build::LinkArgTarget; pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts}; pub(crate) use self::fingerprint::DirtyReason; pub use self::job_queue::Freshness; @@ -99,44 +100,6 @@ use rustfix::diagnostics::Applicability; const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version"; -// TODO: Rename this to `ExtraLinkArgFor` or else, and move to compiler/custom_build.rs? -/// Represents one of the instruction from `cargo:rustc-link-arg-*` build script -/// instruction family. -/// -/// In other words, indicates targets that custom linker arguments applies to. -#[derive(Clone, Hash, Debug, PartialEq, Eq)] -pub enum LinkType { - /// Represents `cargo:rustc-link-arg=FLAG`. - All, - /// Represents `cargo:rustc-cdylib-link-arg=FLAG`. - Cdylib, - /// Represents `cargo:rustc-link-arg-bins=FLAG`. - Bin, - /// Represents `cargo:rustc-link-arg-bin=BIN=FLAG`. - SingleBin(String), - /// Represents `cargo:rustc-link-arg-tests=FLAG`. - Test, - /// Represents `cargo:rustc-link-arg-benches=FLAG`. - Bench, - /// Represents `cargo:rustc-link-arg-examples=FLAG`. - Example, -} - -impl LinkType { - /// Checks if this link type applies to a given [`Target`]. - pub fn applies_to(&self, target: &Target) -> bool { - match self { - LinkType::All => true, - LinkType::Cdylib => target.is_cdylib(), - LinkType::Bin => target.is_bin(), - LinkType::SingleBin(name) => target.is_bin() && target.name() == name, - LinkType::Test => target.is_test(), - LinkType::Bench => target.is_bench(), - LinkType::Example => target.is_exe_example(), - } - } -} - /// A glorified callback for executing calls to rustc. Rather than calling rustc /// directly, we'll use an `Executor`, giving clients an opportunity to intercept /// the build calls. @@ -286,14 +249,12 @@ fn make_failed_scrape_diagnostic( /// Creates a unit of work invoking `rustc` for building the `unit`. fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> CargoResult<Work> { - let mut rustc = prepare_rustc(cx, &unit.target.rustc_crate_types(), unit)?; + let mut rustc = prepare_rustc(cx, unit)?; let build_plan = cx.bcx.build_config.build_plan; let name = unit.pkg.name().to_string(); let buildkey = unit.buildkey(); - add_cap_lints(cx.bcx, unit, &mut rustc); - let outputs = cx.outputs(unit)?; let root = cx.files().out_dir(unit); @@ -319,10 +280,6 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car let rustc_dep_info_loc = root.join(dep_info_name); let dep_info_loc = fingerprint::dep_info_loc(cx, unit); - rustc.args(cx.bcx.rustflags_args(unit)); - if cx.bcx.config.cli_unstable().binary_dep_depinfo { - rustc.arg("-Z").arg("binary-dep-depinfo"); - } let mut output_options = OutputOptions::new(cx, unit); let package_id = unit.pkg.package_id(); let target = Target::clone(&unit.target); @@ -544,7 +501,7 @@ fn rustc(cx: &mut Context<'_, '_>, unit: &Unit, exec: &Arc<dyn Executor>) -> Car // clause should have been kept in the `if` block above. For // now, continue allowing it for cdylib only. // See https://github.com/rust-lang/cargo/issues/9562 - if lt.applies_to(target) && (key.0 == current_id || *lt == LinkType::Cdylib) { + if lt.applies_to(target) && (key.0 == current_id || *lt == LinkArgTarget::Cdylib) { rustc.arg("-C").arg(format!("link-arg={}", arg)); } } @@ -604,7 +561,7 @@ fn link_targets(cx: &mut Context<'_, '_>, unit: &Unit, fresh: bool) -> CargoResu } if json_messages { - let debuginfo = profile.debuginfo.to_option().map(|d| match d { + let debuginfo = match profile.debuginfo.into_inner() { TomlDebugInfo::None => machine_message::ArtifactDebuginfo::Int(0), TomlDebugInfo::Limited => machine_message::ArtifactDebuginfo::Int(1), TomlDebugInfo::Full => machine_message::ArtifactDebuginfo::Int(2), @@ -614,10 +571,10 @@ fn link_targets(cx: &mut Context<'_, '_>, unit: &Unit, fresh: bool) -> CargoResu TomlDebugInfo::LineTablesOnly => { machine_message::ArtifactDebuginfo::Named("line-tables-only") } - }); + }; let art_profile = machine_message::ArtifactProfile { opt_level: profile.opt_level.as_str(), - debuginfo, + debuginfo: Some(debuginfo), debug_assertions: profile.debug_assertions, overflow_checks: profile.overflow_checks, test: unit_mode.is_any_test(), @@ -705,13 +662,13 @@ where search_path } -// TODO: do we really need this as a separate function? -// Maybe we should reorganize `rustc` fn to make it more traceable and readable. -fn prepare_rustc( - cx: &mut Context<'_, '_>, - crate_types: &[CrateType], - unit: &Unit, -) -> CargoResult<ProcessBuilder> { +/// Prepares flags and environments we can compute for a `rustc` invocation +/// before the job queue starts compiling any unit. +/// +/// This builds a static view of the invocation. Flags depending on the +/// completion of other units will be added later in runtime, such as flags +/// from build scripts. +fn prepare_rustc(cx: &Context<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> { let is_primary = cx.is_primary_package(unit); let is_workspace = cx.bcx.ws.is_member(&unit.pkg); @@ -729,13 +686,23 @@ fn prepare_rustc( } base.inherit_jobserver(&cx.jobserver); - build_base_args(cx, &mut base, unit, crate_types)?; + 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"); + } Ok(base) } -/// Creates a unit of work invoking `rustdoc` for documenting the `unit`. -fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> { +/// Prepares flags and environments we can compute for a `rustdoc` invocation +/// before the job queue starts compiling any unit. +/// +/// This builds a static view of the invocation. Flags depending on the +/// completion of other units will be added later in runtime, such as flags +/// from build scripts. +fn prepare_rustdoc(cx: &Context<'_, '_>, unit: &Unit) -> CargoResult<ProcessBuilder> { let bcx = cx.bcx; // script_metadata is not needed here, it is only for tests. let mut rustdoc = cx.compilation.rustdoc_process(unit, None)?; @@ -749,12 +716,6 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> { rustdoc.arg("--target").arg(target.rustc_target()); } let doc_dir = cx.files().out_dir(unit); - - // Create the documentation directory ahead of time as rustdoc currently has - // a bug where concurrent invocations will race to create this directory if - // it doesn't already exist. - paths::create_dir_all(&doc_dir)?; - rustdoc.arg("-o").arg(&doc_dir); rustdoc.args(&features_args(unit)); rustdoc.args(&check_cfg_args(cx, unit)); @@ -770,10 +731,6 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> { let metadata = cx.metadata_for_doc_units[unit]; rustdoc.arg("-C").arg(format!("metadata={}", metadata)); - let scrape_output_path = |unit: &Unit| -> CargoResult<PathBuf> { - cx.outputs(unit).map(|outputs| outputs[0].path.clone()) - }; - if unit.mode.is_doc_scrape() { debug_assert!(cx.bcx.scrape_units.contains(unit)); @@ -785,7 +742,7 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> { rustdoc .arg("--scrape-examples-output-path") - .arg(scrape_output_path(unit)?); + .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() { @@ -800,21 +757,9 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> { } } - let should_include_scrape_units = unit.mode.is_doc() - && cx.bcx.scrape_units.len() > 0 - && cx.bcx.ws.unit_needs_doc_scrape(unit); - let scrape_outputs = if should_include_scrape_units { + if should_include_scrape_units(cx.bcx, unit) { rustdoc.arg("-Zunstable-options"); - Some( - cx.bcx - .scrape_units - .iter() - .map(|unit| Ok((cx.files().metadata(unit), scrape_output_path(unit)?))) - .collect::<CargoResult<HashMap<_, _>>>()?, - ) - } else { - None - }; + } build_deps_args(&mut rustdoc, cx, unit)?; rustdoc::add_root_urls(cx, unit, &mut rustdoc)?; @@ -825,6 +770,20 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> { append_crate_version_flag(unit, &mut rustdoc); } + Ok(rustdoc) +} + +/// Creates a unit of work invoking `rustdoc` for documenting the `unit`. +fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> { + let mut rustdoc = prepare_rustdoc(cx, unit)?; + + let crate_name = unit.target.crate_name(); + let doc_dir = cx.files().out_dir(unit); + // Create the documentation directory ahead of time as rustdoc currently has + // a bug where concurrent invocations will race to create this directory if + // it doesn't already exist. + paths::create_dir_all(&doc_dir)?; + let target_desc = unit.target.description_named(); let name = unit.pkg.name().to_string(); let build_script_outputs = Arc::clone(&cx.build_script_outputs); @@ -833,6 +792,17 @@ fn rustdoc(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Work> { let target = Target::clone(&unit.target); let mut output_options = OutputOptions::new(cx, unit); let script_metadata = cx.find_build_script_metadata(unit); + let scrape_outputs = if should_include_scrape_units(cx.bcx, unit) { + Some( + cx.bcx + .scrape_units + .iter() + .map(|unit| Ok((cx.files().metadata(unit), scrape_output_path(cx, unit)?))) + .collect::<CargoResult<HashMap<_, _>>>()?, + ) + } else { + None + }; let failed_scrape_units = Arc::clone(&cx.failed_scrape_units); let hide_diagnostics_for_scrape_unit = cx.bcx.unit_can_fail_for_docscraping(unit) @@ -977,12 +947,7 @@ fn add_error_format_and_color(cx: &Context<'_, '_>, cmd: &mut ProcessBuilder) { } /// Adds essential rustc flags and environment variables to the command to execute. -fn build_base_args( - cx: &mut Context<'_, '_>, - cmd: &mut ProcessBuilder, - unit: &Unit, - crate_types: &[CrateType], -) -> CargoResult<()> { +fn build_base_args(cx: &Context<'_, '_>, cmd: &mut ProcessBuilder, unit: &Unit) -> CargoResult<()> { assert!(!unit.mode.is_run_custom_build()); let bcx = cx.bcx; @@ -998,7 +963,7 @@ fn build_base_args( ref panic, incremental, strip, - rustflags, + rustflags: profile_rustflags, .. } = unit.profile.clone(); let test = unit.mode.is_any_test(); @@ -1014,7 +979,7 @@ fn build_base_args( let mut contains_dy_lib = false; if !test { - for crate_type in crate_types { + for crate_type in &unit.target.rustc_crate_types() { cmd.arg("--crate-type").arg(crate_type.as_str()); contains_dy_lib |= crate_type == &CrateType::Dylib; } @@ -1047,22 +1012,6 @@ fn build_base_args( cmd.args(<o_args(cx, unit)); - // This is generally just an optimization on build time so if we don't pass - // it then it's ok. The values for the flag (off, packed, unpacked) may be supported - // or not depending on the platform, so availability is checked per-value. - // For example, at the time of writing this code, on Windows the only stable valid - // value for split-debuginfo is "packed", while on Linux "unpacked" is also stable. - if let Some(split) = split_debuginfo { - if cx - .bcx - .target_data - .info(unit.kind) - .supports_debuginfo_split(split) - { - cmd.arg("-C").arg(format!("split-debuginfo={}", split)); - } - } - if let Some(backend) = codegen_backend { cmd.arg("-Z").arg(&format!("codegen-backend={}", backend)); } @@ -1071,14 +1020,30 @@ fn build_base_args( cmd.arg("-C").arg(&format!("codegen-units={}", n)); } - if let Some(debuginfo) = debuginfo.to_option() { - cmd.arg("-C").arg(format!("debuginfo={}", debuginfo)); + let debuginfo = debuginfo.into_inner(); + // Shorten the number of arguments if possible. + if debuginfo != TomlDebugInfo::None { + cmd.arg("-C").arg(format!("debuginfo={debuginfo}")); + // This is generally just an optimization on build time so if we don't + // pass it then it's ok. The values for the flag (off, packed, unpacked) + // may be supported or not depending on the platform, so availability is + // checked per-value. For example, at the time of writing this code, on + // Windows the only stable valid value for split-debuginfo is "packed", + // while on Linux "unpacked" is also stable. + if let Some(split) = split_debuginfo { + if cx + .bcx + .target_data + .info(unit.kind) + .supports_debuginfo_split(split) + { + cmd.arg("-C").arg(format!("split-debuginfo={split}")); + } + } } cmd.args(unit.pkg.manifest().lint_rustflags()); - if !rustflags.is_empty() { - cmd.args(&rustflags); - } + cmd.args(&profile_rustflags); if let Some(args) = cx.bcx.extra_args_for(unit) { cmd.args(args); } @@ -1277,11 +1242,7 @@ fn lto_args(cx: &Context<'_, '_>, unit: &Unit) -> Vec<OsString> { /// /// [`-L`]: https://doc.rust-lang.org/nightly/rustc/command-line-arguments.html#-l-add-a-directory-to-the-library-search-path /// [`--extern`]: https://doc.rust-lang.org/nightly/rustc/command-line-arguments.html#--extern-specify-where-an-external-library-is-located -fn build_deps_args( - cmd: &mut ProcessBuilder, - cx: &mut Context<'_, '_>, - unit: &Unit, -) -> CargoResult<()> { +fn build_deps_args(cmd: &mut ProcessBuilder, cx: &Context<'_, '_>, unit: &Unit) -> CargoResult<()> { let bcx = cx.bcx; cmd.arg("-L").arg(&{ let mut deps = OsString::from("dependency="); @@ -1821,3 +1782,14 @@ fn apply_env_config(config: &crate::Config, cmd: &mut ProcessBuilder) -> CargoRe } Ok(()) } + +/// Checks if there are some scrape units waiting to be processed. +fn should_include_scrape_units(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool { + unit.mode.is_doc() && bcx.scrape_units.len() > 0 && bcx.ws.unit_needs_doc_scrape(unit) +} + +/// Gets the file path of function call information output from `rustdoc`. +fn scrape_output_path(cx: &Context<'_, '_>, unit: &Unit) -> CargoResult<PathBuf> { + assert!(unit.mode.is_doc() || unit.mode.is_doc_scrape()); + cx.outputs(unit).map(|outputs| outputs[0].path.clone()) +} diff --git a/src/tools/cargo/src/cargo/core/compiler/unit_dependencies.rs b/src/tools/cargo/src/cargo/core/compiler/unit_dependencies.rs index 3bf8b0c77..369fd8318 100644 --- a/src/tools/cargo/src/cargo/core/compiler/unit_dependencies.rs +++ b/src/tools/cargo/src/cargo/core/compiler/unit_dependencies.rs @@ -1,4 +1,4 @@ -//! # Constructs the dependency graph for compilation +//! Constructs the dependency graph for compilation. //! //! Rust code is typically organized as a set of Cargo packages. The //! dependencies between the packages themselves are stored in the diff --git a/src/tools/cargo/src/cargo/core/features.rs b/src/tools/cargo/src/cargo/core/features.rs index d56054a0a..9b99d5a15 100644 --- a/src/tools/cargo/src/cargo/core/features.rs +++ b/src/tools/cargo/src/cargo/core/features.rs @@ -687,6 +687,26 @@ macro_rules! unstable_cli_options { fields } } + + #[cfg(test)] + mod test { + #[test] + fn ensure_sorted() { + // This will be printed out if the fields are not sorted. + let location = std::panic::Location::caller(); + println!( + "\nTo fix this test, sort the features inside the macro at {}:{}\n", + location.file(), + location.line() + ); + let mut expected = vec![$(stringify!($element)),*]; + expected[2..].sort(); + snapbox::assert_eq( + format!("{:#?}", expected), + format!("{:#?}", vec![$(stringify!($element)),*]) + ); + } + } } } @@ -696,7 +716,7 @@ unstable_cli_options!( print_im_a_teapot: bool = (HIDDEN), // All other unstable features. - // Please keep this list lexiographically ordered. + // Please keep this list lexicographically ordered. advanced_env: bool = (HIDDEN), avoid_dev_deps: bool = ("Avoid installing dev-dependencies if possible"), binary_dep_depinfo: bool = ("Track changes to dependency artifacts"), @@ -704,34 +724,34 @@ unstable_cli_options!( #[serde(deserialize_with = "deserialize_build_std")] build_std: Option<Vec<String>> = ("Enable Cargo to compile the standard library itself as part of a crate graph compilation"), build_std_features: Option<Vec<String>> = ("Configure features enabled for the standard library itself when building the standard library"), + #[serde(deserialize_with = "deserialize_check_cfg")] + check_cfg: Option<(/*features:*/ bool, /*well_known_names:*/ bool, /*well_known_values:*/ bool, /*output:*/ bool)> = ("Specify scope of compile-time checking of `cfg` names/values"), codegen_backend: bool = ("Enable the `codegen-backend` option in profiles in .cargo/config.toml file"), config_include: bool = ("Enable the `include` key in config files"), credential_process: bool = ("Add a config setting to fetch registry authentication tokens by calling an external process"), - #[serde(deserialize_with = "deserialize_check_cfg")] - check_cfg: Option<(/*features:*/ bool, /*well_known_names:*/ bool, /*well_known_values:*/ bool, /*output:*/ bool)> = ("Specify scope of compile-time checking of `cfg` names/values"), - doctest_in_workspace: bool = ("Compile doctests with paths relative to the workspace root"), + direct_minimal_versions: bool = ("Resolve minimal dependency versions instead of maximum (direct dependencies only)"), 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), gitoxide: Option<GitoxideFeatures> = ("Use gitoxide for the given git interactions, or all of them if no argument is given"), - jobserver_per_rustc: bool = (HIDDEN), + host_config: bool = ("Enable the [host] section in the .cargo/config.toml file"), + lints: bool = ("Pass `[lints]` to the linting tools"), minimal_versions: bool = ("Resolve minimal dependency versions instead of maximum"), - direct_minimal_versions: bool = ("Resolve minimal dependency versions instead of maximum (direct dependencies only)"), + msrv_policy: bool = ("Enable rust-version aware policy within cargo"), mtime_on_use: bool = ("Configure Cargo to update the mtime of used files"), + next_lockfile_bump: bool = (HIDDEN), no_index_update: bool = ("Do not update the registry index even if the cache is outdated"), panic_abort_tests: bool = ("Enable support to run tests with -Cpanic=abort"), profile_rustflags: bool = ("Enable the `rustflags` option in profiles in .cargo/config.toml file"), - host_config: bool = ("Enable the [host] section in the .cargo/config.toml file"), + publish_timeout: bool = ("Enable the `publish.timeout` key in .cargo/config.toml file"), registry_auth: bool = ("Authentication for alternative registries, and generate registry authentication tokens using asymmetric cryptography"), - target_applies_to_host: bool = ("Enable the `target-applies-to-host` key in the .cargo/config.toml file"), rustdoc_map: bool = ("Allow passing external documentation mappings to rustdoc"), + rustdoc_scrape_examples: bool = ("Allows Rustdoc to scrape code examples from reverse-dependencies"), + script: bool = ("Enable support for single-file, `.rs` packages"), separate_nightlies: bool = (HIDDEN), - publish_timeout: bool = ("Enable the `publish.timeout` key in .cargo/config.toml file"), - unstable_options: bool = ("Allow the usage of unstable options"), skip_rustdoc_fingerprint: bool = (HIDDEN), - rustdoc_scrape_examples: bool = ("Allows Rustdoc to scrape code examples from reverse-dependencies"), - msrv_policy: bool = ("Enable rust-version aware policy within cargo"), - lints: bool = ("Pass `[lints]` to the linting tools"), + target_applies_to_host: bool = ("Enable the `target-applies-to-host` key in the .cargo/config.toml file"), + unstable_options: bool = ("Allow the usage of unstable options"), ); const STABILIZED_COMPILE_PROGRESS: &str = "The progress bar is now always \ @@ -779,6 +799,9 @@ const STABILIZED_NAMED_PROFILES: &str = "The named-profiles feature is now alway See https://doc.rust-lang.org/nightly/cargo/reference/profiles.html#custom-profiles \ for more information"; +const STABILIZED_DOCTEST_IN_WORKSPACE: &str = + "The doctest-in-workspace feature is now always enabled."; + const STABILIZED_FUTURE_INCOMPAT_REPORT: &str = "The future-incompat-report feature is now always enabled."; @@ -1011,41 +1034,19 @@ impl CliUnstable { } match k { - "print-im-a-teapot" => self.print_im_a_teapot = parse_bool(k, v)?, + // Permanently unstable features + // Sorted alphabetically: "allow-features" => self.allow_features = Some(parse_features(v).into_iter().collect()), - "unstable-options" => self.unstable_options = parse_empty(k, v)?, - "no-index-update" => self.no_index_update = parse_empty(k, v)?, - "avoid-dev-deps" => self.avoid_dev_deps = parse_empty(k, v)?, - "minimal-versions" => self.minimal_versions = parse_empty(k, v)?, - "direct-minimal-versions" => self.direct_minimal_versions = parse_empty(k, v)?, - "advanced-env" => self.advanced_env = parse_empty(k, v)?, - "config-include" => self.config_include = parse_empty(k, v)?, - "check-cfg" => { - self.check_cfg = v.map_or(Ok(None), |v| parse_check_cfg(v.split(',')))? - } - "dual-proc-macros" => self.dual_proc_macros = parse_empty(k, v)?, - // can also be set in .cargo/config or with and ENV - "mtime-on-use" => self.mtime_on_use = parse_empty(k, v)?, - "named-profiles" => stabilized_warn(k, "1.57", STABILIZED_NAMED_PROFILES), - "binary-dep-depinfo" => self.binary_dep_depinfo = parse_empty(k, v)?, - "bindeps" => self.bindeps = parse_empty(k, v)?, - "build-std" => { - self.build_std = Some(crate::core::compiler::standard_lib::parse_unstable_flag(v)) - } - "build-std-features" => self.build_std_features = Some(parse_features(v)), - "doctest-xcompile" => self.doctest_xcompile = parse_empty(k, v)?, - "doctest-in-workspace" => self.doctest_in_workspace = parse_empty(k, v)?, - "panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?, - "jobserver-per-rustc" => self.jobserver_per_rustc = parse_empty(k, v)?, - "gitoxide" => { - self.gitoxide = v.map_or_else( - || Ok(Some(GitoxideFeatures::all())), - |v| parse_gitoxide(v.split(',')), - )? - } - "host-config" => self.host_config = parse_empty(k, v)?, - "target-applies-to-host" => self.target_applies_to_host = parse_empty(k, v)?, - "publish-timeout" => self.publish_timeout = parse_empty(k, v)?, + "print-im-a-teapot" => self.print_im_a_teapot = parse_bool(k, v)?, + + // Stabilized features + // Sorted by version, then alphabetically: + "compile-progress" => stabilized_warn(k, "1.30", STABILIZED_COMPILE_PROGRESS), + "offline" => stabilized_err(k, "1.36", STABILIZED_OFFLINE)?, + "cache-messages" => stabilized_warn(k, "1.40", STABILIZED_CACHE_MESSAGES), + "install-upgrade" => stabilized_warn(k, "1.41", STABILIZED_INSTALL_UPGRADE), + "config-profile" => stabilized_warn(k, "1.43", STABILIZED_CONFIG_PROFILE), + "crate-versions" => stabilized_warn(k, "1.47", STABILIZED_CRATE_VERSIONS), "features" => { // `-Z features` has been stabilized since 1.51, // but `-Z features=compare` is still allowed for convenience @@ -1067,35 +1068,66 @@ impl CliUnstable { } self.features = Some(feats); } - "separate-nightlies" => self.separate_nightlies = parse_empty(k, v)?, - "multitarget" => stabilized_warn(k, "1.64", STABILISED_MULTITARGET), - "rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?, - "terminal-width" => stabilized_warn(k, "1.68", STABILIZED_TERMINAL_WIDTH), - "sparse-registry" => stabilized_warn(k, "1.68", STABILISED_SPARSE_REGISTRY), - "registry-auth" => self.registry_auth = parse_empty(k, v)?, - "namespaced-features" => stabilized_warn(k, "1.60", STABILISED_NAMESPACED_FEATURES), - "weak-dep-features" => stabilized_warn(k, "1.60", STABILIZED_WEAK_DEP_FEATURES), - "credential-process" => self.credential_process = parse_empty(k, v)?, - "rustdoc-scrape-examples" => self.rustdoc_scrape_examples = parse_empty(k, v)?, - "skip-rustdoc-fingerprint" => self.skip_rustdoc_fingerprint = parse_empty(k, v)?, - "compile-progress" => stabilized_warn(k, "1.30", STABILIZED_COMPILE_PROGRESS), - "offline" => stabilized_err(k, "1.36", STABILIZED_OFFLINE)?, - "cache-messages" => stabilized_warn(k, "1.40", STABILIZED_CACHE_MESSAGES), - "install-upgrade" => stabilized_warn(k, "1.41", STABILIZED_INSTALL_UPGRADE), - "config-profile" => stabilized_warn(k, "1.43", STABILIZED_CONFIG_PROFILE), - "crate-versions" => stabilized_warn(k, "1.47", STABILIZED_CRATE_VERSIONS), "package-features" => stabilized_warn(k, "1.51", STABILIZED_PACKAGE_FEATURES), - "extra-link-arg" => stabilized_warn(k, "1.56", STABILIZED_EXTRA_LINK_ARG), "configurable-env" => stabilized_warn(k, "1.56", STABILIZED_CONFIGURABLE_ENV), + "extra-link-arg" => stabilized_warn(k, "1.56", STABILIZED_EXTRA_LINK_ARG), "patch-in-config" => stabilized_warn(k, "1.56", STABILIZED_PATCH_IN_CONFIG), + "named-profiles" => stabilized_warn(k, "1.57", STABILIZED_NAMED_PROFILES), "future-incompat-report" => { stabilized_warn(k, "1.59.0", STABILIZED_FUTURE_INCOMPAT_REPORT) } + "namespaced-features" => stabilized_warn(k, "1.60", STABILISED_NAMESPACED_FEATURES), "timings" => stabilized_warn(k, "1.60", STABILIZED_TIMINGS), + "weak-dep-features" => stabilized_warn(k, "1.60", STABILIZED_WEAK_DEP_FEATURES), + "multitarget" => stabilized_warn(k, "1.64", STABILISED_MULTITARGET), + "sparse-registry" => stabilized_warn(k, "1.68", STABILISED_SPARSE_REGISTRY), + "terminal-width" => stabilized_warn(k, "1.68", STABILIZED_TERMINAL_WIDTH), + "doctest-in-workspace" => stabilized_warn(k, "1.72", STABILIZED_DOCTEST_IN_WORKSPACE), + + // Unstable features + // Sorted alphabetically: + "advanced-env" => self.advanced_env = parse_empty(k, v)?, + "avoid-dev-deps" => self.avoid_dev_deps = parse_empty(k, v)?, + "binary-dep-depinfo" => self.binary_dep_depinfo = parse_empty(k, v)?, + "bindeps" => self.bindeps = parse_empty(k, v)?, + "build-std" => { + self.build_std = Some(crate::core::compiler::standard_lib::parse_unstable_flag(v)) + } + "build-std-features" => self.build_std_features = Some(parse_features(v)), + "check-cfg" => { + self.check_cfg = v.map_or(Ok(None), |v| parse_check_cfg(v.split(',')))? + } "codegen-backend" => self.codegen_backend = parse_empty(k, v)?, - "profile-rustflags" => self.profile_rustflags = parse_empty(k, v)?, - "msrv-policy" => self.msrv_policy = parse_empty(k, v)?, + "config-include" => self.config_include = parse_empty(k, v)?, + "credential-process" => self.credential_process = parse_empty(k, v)?, + "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)?, + "gitoxide" => { + self.gitoxide = v.map_or_else( + || Ok(Some(GitoxideFeatures::all())), + |v| parse_gitoxide(v.split(',')), + )? + } + "host-config" => self.host_config = parse_empty(k, v)?, "lints" => self.lints = parse_empty(k, v)?, + "next-lockfile-bump" => self.next_lockfile_bump = parse_empty(k, v)?, + "minimal-versions" => self.minimal_versions = parse_empty(k, v)?, + "msrv-policy" => self.msrv_policy = parse_empty(k, v)?, + // can also be set in .cargo/config or with and ENV + "mtime-on-use" => self.mtime_on_use = parse_empty(k, v)?, + "no-index-update" => self.no_index_update = parse_empty(k, v)?, + "panic-abort-tests" => self.panic_abort_tests = parse_empty(k, v)?, + "profile-rustflags" => self.profile_rustflags = parse_empty(k, v)?, + "publish-timeout" => self.publish_timeout = parse_empty(k, v)?, + "registry-auth" => self.registry_auth = parse_empty(k, v)?, + "rustdoc-map" => self.rustdoc_map = parse_empty(k, v)?, + "rustdoc-scrape-examples" => self.rustdoc_scrape_examples = parse_empty(k, v)?, + "separate-nightlies" => self.separate_nightlies = parse_empty(k, v)?, + "skip-rustdoc-fingerprint" => self.skip_rustdoc_fingerprint = parse_empty(k, v)?, + "script" => self.script = parse_empty(k, v)?, + "target-applies-to-host" => self.target_applies_to_host = parse_empty(k, v)?, + "unstable-options" => self.unstable_options = parse_empty(k, v)?, _ => bail!("unknown `-Z` flag specified: {}", k), } diff --git a/src/tools/cargo/src/cargo/core/manifest.rs b/src/tools/cargo/src/cargo/core/manifest.rs index 98498ead8..5d46a7e06 100644 --- a/src/tools/cargo/src/cargo/core/manifest.rs +++ b/src/tools/cargo/src/cargo/core/manifest.rs @@ -64,6 +64,7 @@ pub struct Manifest { metabuild: Option<Vec<String>>, resolve_behavior: Option<ResolveBehavior>, lint_rustflags: Vec<String>, + embedded: bool, } /// When parsing `Cargo.toml`, some warnings should silenced @@ -407,6 +408,7 @@ impl Manifest { metabuild: Option<Vec<String>>, resolve_behavior: Option<ResolveBehavior>, lint_rustflags: Vec<String>, + embedded: bool, ) -> Manifest { Manifest { summary, @@ -433,6 +435,7 @@ impl Manifest { metabuild, resolve_behavior, lint_rustflags, + embedded, } } @@ -500,6 +503,9 @@ impl Manifest { pub fn links(&self) -> Option<&str> { self.links.as_deref() } + pub fn is_embedded(&self) -> bool { + self.embedded + } pub fn workspace_config(&self) -> &WorkspaceConfig { &self.workspace diff --git a/src/tools/cargo/src/cargo/core/package.rs b/src/tools/cargo/src/cargo/core/package.rs index 40ba9cdf8..f4ab448d2 100644 --- a/src/tools/cargo/src/cargo/core/package.rs +++ b/src/tools/cargo/src/cargo/core/package.rs @@ -10,10 +10,10 @@ use std::time::{Duration, Instant}; use anyhow::Context; use bytesize::ByteSize; -use curl::easy::{Easy, HttpVersion}; +use curl::easy::Easy; use curl::multi::{EasyHandle, Multi}; use lazycell::LazyCell; -use log::{debug, warn}; +use log::debug; use semver::Version; use serde::Serialize; @@ -24,10 +24,11 @@ use crate::core::resolver::{HasDevUnits, Resolve}; use crate::core::source::MaybePackage; use crate::core::{Dependency, Manifest, PackageId, SourceId, Target}; use crate::core::{SourceMap, Summary, Workspace}; -use crate::ops; use crate::util::config::PackageCacheLock; use crate::util::errors::{CargoResult, HttpNotSuccessful, DEBUG_HEADERS}; use crate::util::interning::InternedString; +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::{self, internal, Config, Progress, ProgressStyle}; @@ -348,7 +349,7 @@ pub struct Downloads<'a, 'cfg> { /// Note that timeout management is done manually here instead of in libcurl /// because we want to apply timeouts to an entire batch of operations, not /// any one particular single operation. - timeout: ops::HttpTimeout, + timeout: HttpTimeout, /// Last time bytes were received. updated_at: Cell<Instant>, /// This is a slow-speed check. It is reset to `now + timeout_duration` @@ -441,7 +442,7 @@ impl<'cfg> PackageSet<'cfg> { pub fn enable_download<'a>(&'a self) -> CargoResult<Downloads<'a, 'cfg>> { assert!(!self.downloading.replace(true)); - let timeout = ops::HttpTimeout::new(self.config)?; + let timeout = HttpTimeout::new(self.config)?; Ok(Downloads { start: Instant::now(), set: self, @@ -713,7 +714,7 @@ impl<'a, 'cfg> Downloads<'a, 'cfg> { debug!("downloading {} as {}", id, token); assert!(self.pending_ids.insert(id)); - let (mut handle, _timeout) = ops::http_handle_and_timeout(self.set.config)?; + let (mut handle, _timeout) = http_handle_and_timeout(self.set.config)?; handle.get(true)?; handle.url(&url)?; handle.follow_location(true)?; // follow redirects @@ -725,32 +726,8 @@ impl<'a, 'cfg> Downloads<'a, 'cfg> { handle.http_headers(headers)?; } - // Enable HTTP/2 to be used as it'll allow true multiplexing which makes - // downloads much faster. - // - // Currently Cargo requests the `http2` feature of the `curl` crate - // which means it should always be built in. On OSX, however, we ship - // cargo still linked against the system libcurl. Building curl with - // ALPN support for HTTP/2 requires newer versions of OSX (the - // SecureTransport API) than we want to ship Cargo for. By linking Cargo - // against the system libcurl then older curl installations won't use - // HTTP/2 but newer ones will. All that to basically say we ignore - // errors here on OSX, but consider this a fatal error to not activate - // HTTP/2 on all other platforms. - if self.set.multiplexing { - crate::try_old_curl!(handle.http_version(HttpVersion::V2), "HTTP2"); - } else { - handle.http_version(HttpVersion::V11)?; - } - - // This is an option to `libcurl` which indicates that if there's a - // bunch of parallel requests to the same host they all wait until the - // pipelining status of the host is known. This means that we won't - // initiate dozens of connections to crates.io, but rather only one. - // Once the main one is opened we realized that pipelining is possible - // and multiplexing is possible with static.crates.io. All in all this - // reduces the number of connections down to a more manageable state. - crate::try_old_curl!(handle.pipewait(true), "pipewait"); + // Enable HTTP/2 if possible. + crate::try_old_curl_http2_pipewait!(self.set.multiplexing, handle); handle.write_function(move |buf| { debug!("{} - {} bytes of data", token, buf.len()); diff --git a/src/tools/cargo/src/cargo/core/package_id.rs b/src/tools/cargo/src/cargo/core/package_id.rs index ee31e9c48..e17a73e68 100644 --- a/src/tools/cargo/src/cargo/core/package_id.rs +++ b/src/tools/cargo/src/cargo/core/package_id.rs @@ -5,6 +5,7 @@ use std::hash::Hash; use std::path::Path; use std::ptr; use std::sync::Mutex; +use std::sync::OnceLock; use serde::de; use serde::ser; @@ -13,10 +14,7 @@ use crate::core::source::SourceId; use crate::util::interning::InternedString; use crate::util::{CargoResult, ToSemver}; -lazy_static::lazy_static! { - static ref PACKAGE_ID_CACHE: Mutex<HashSet<&'static PackageIdInner>> = - Mutex::new(HashSet::new()); -} +static PACKAGE_ID_CACHE: OnceLock<Mutex<HashSet<&'static PackageIdInner>>> = OnceLock::new(); /// Identifier for a specific version of a package in a specific source. #[derive(Clone, Copy, Eq, PartialOrd, Ord)] @@ -147,7 +145,10 @@ impl PackageId { version, source_id, }; - let mut cache = PACKAGE_ID_CACHE.lock().unwrap(); + let mut cache = PACKAGE_ID_CACHE + .get_or_init(|| Default::default()) + .lock() + .unwrap(); let inner = cache.get(&inner).cloned().unwrap_or_else(|| { let inner = Box::leak(Box::new(inner)); cache.insert(inner); @@ -195,6 +196,11 @@ impl PackageId { pub fn stable_hash(self, workspace: &Path) -> PackageIdStableHash<'_> { PackageIdStableHash(self, workspace) } + + /// Filename of the `.crate` tarball, e.g., `once_cell-1.18.0.crate`. + pub fn tarball_name(&self) -> String { + format!("{}-{}.crate", self.name(), self.version()) + } } pub struct PackageIdStableHash<'a>(PackageId, &'a Path); diff --git a/src/tools/cargo/src/cargo/core/profiles.rs b/src/tools/cargo/src/cargo/core/profiles.rs index 3831f18c2..5c7d3e248 100644 --- a/src/tools/cargo/src/cargo/core/profiles.rs +++ b/src/tools/cargo/src/cargo/core/profiles.rs @@ -1,4 +1,4 @@ -//! # Profiles: built-in and customizable compiler flag presets +//! Handles built-in and customizable compiler flag presets. //! //! [`Profiles`] is a collections of built-in profiles, and profiles defined //! in the root manifest and configurations. @@ -449,9 +449,7 @@ impl ProfileMaker { // a unit is shared. If that's the case, we'll use the deferred value // below so the unit can be reused, otherwise we can avoid emitting // the unit's debuginfo. - if let Some(debuginfo) = profile.debuginfo.to_option() { - profile.debuginfo = DebugInfo::Deferred(debuginfo); - } + profile.debuginfo = DebugInfo::Deferred(profile.debuginfo.into_inner()); } // ... and next comes any other sorts of overrides specified in // profiles, such as `[profile.release.build-override]` or @@ -529,7 +527,7 @@ fn merge_profile(profile: &mut Profile, toml: &TomlProfile) { profile.codegen_units = toml.codegen_units; } if let Some(debuginfo) = toml.debug { - profile.debuginfo = DebugInfo::Explicit(debuginfo); + profile.debuginfo = DebugInfo::Resolved(debuginfo); } if let Some(debug_assertions) = toml.debug_assertions { profile.debug_assertions = debug_assertions; @@ -611,7 +609,7 @@ impl Default for Profile { lto: Lto::Bool(false), codegen_backend: None, codegen_units: None, - debuginfo: DebugInfo::None, + debuginfo: DebugInfo::Resolved(TomlDebugInfo::None), debug_assertions: false, split_debuginfo: None, overflow_checks: false, @@ -680,7 +678,7 @@ impl Profile { Profile { name: InternedString::new("dev"), root: ProfileRoot::Debug, - debuginfo: DebugInfo::Explicit(TomlDebugInfo::Full), + debuginfo: DebugInfo::Resolved(TomlDebugInfo::Full), debug_assertions: true, overflow_checks: true, incremental: true, @@ -720,11 +718,8 @@ impl Profile { /// The debuginfo level setting. /// -/// This is semantically an `Option<u32>`, and should be used as so via the -/// [DebugInfo::to_option] method for all intents and purposes: -/// - `DebugInfo::None` corresponds to `None` -/// - `DebugInfo::Explicit(u32)` and `DebugInfo::Deferred` correspond to -/// `Option<u32>::Some` +/// This is semantically a [`TomlDebugInfo`], and should be used as so via the +/// [`DebugInfo::into_inner`] method for all intents and purposes. /// /// Internally, it's used to model a debuginfo level whose value can be deferred /// for optimization purposes: host dependencies usually don't need the same @@ -736,35 +731,34 @@ impl Profile { #[derive(Debug, Copy, Clone, serde::Serialize)] #[serde(untagged)] pub enum DebugInfo { - /// No debuginfo level was set. - None, - /// A debuginfo level that is explicitly set, by a profile or a user. - Explicit(TomlDebugInfo), + /// A debuginfo level that is fixed and will not change. + /// + /// This can be set by a profile, user, or default value. + Resolved(TomlDebugInfo), /// For internal purposes: a deferred debuginfo level that can be optimized /// away, but has this value otherwise. /// - /// Behaves like `Explicit` in all situations except for the default build + /// Behaves like `Resolved` in all situations except for the default build /// dependencies profile: whenever a build dependency is not shared with /// runtime dependencies, this level is weakened to a lower level that is - /// faster to build (see [DebugInfo::weaken]). + /// faster to build (see [`DebugInfo::weaken`]). /// /// In all other situations, this level value will be the one to use. Deferred(TomlDebugInfo), } impl DebugInfo { - /// The main way to interact with this debuginfo level, turning it into an Option. - pub fn to_option(self) -> Option<TomlDebugInfo> { + /// The main way to interact with this debuginfo level, turning it into a [`TomlDebugInfo`]. + pub fn into_inner(self) -> TomlDebugInfo { match self { - DebugInfo::None => None, - DebugInfo::Explicit(v) | DebugInfo::Deferred(v) => Some(v), + DebugInfo::Resolved(v) | DebugInfo::Deferred(v) => v, } } /// Returns true if any debuginfo will be generated. Helper /// for a common operation on the usual `Option` representation. pub(crate) fn is_turned_on(&self) -> bool { - !matches!(self.to_option(), None | Some(TomlDebugInfo::None)) + !matches!(self.into_inner(), TomlDebugInfo::None) } pub(crate) fn is_deferred(&self) -> bool { @@ -774,24 +768,20 @@ impl DebugInfo { /// Force the deferred, preferred, debuginfo level to a finalized explicit value. pub(crate) fn finalize(self) -> Self { match self { - DebugInfo::Deferred(v) => DebugInfo::Explicit(v), + DebugInfo::Deferred(v) => DebugInfo::Resolved(v), _ => self, } } /// Reset to the lowest level: no debuginfo. - /// If it is explicitly set, keep it explicit. pub(crate) fn weaken(self) -> Self { - match self { - DebugInfo::None => DebugInfo::None, - _ => DebugInfo::Explicit(TomlDebugInfo::None), - } + DebugInfo::Resolved(TomlDebugInfo::None) } } impl PartialEq for DebugInfo { fn eq(&self, other: &DebugInfo) -> bool { - self.to_option().eq(&other.to_option()) + self.into_inner().eq(&other.into_inner()) } } @@ -799,19 +789,19 @@ impl Eq for DebugInfo {} impl Hash for DebugInfo { fn hash<H: std::hash::Hasher>(&self, state: &mut H) { - self.to_option().hash(state); + self.into_inner().hash(state); } } impl PartialOrd for DebugInfo { fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { - self.to_option().partial_cmp(&other.to_option()) + self.into_inner().partial_cmp(&other.into_inner()) } } impl Ord for DebugInfo { fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.to_option().cmp(&other.to_option()) + self.into_inner().cmp(&other.into_inner()) } } diff --git a/src/tools/cargo/src/cargo/core/resolver/encode.rs b/src/tools/cargo/src/cargo/core/resolver/encode.rs index 88d0d8296..f73d023b1 100644 --- a/src/tools/cargo/src/cargo/core/resolver/encode.rs +++ b/src/tools/cargo/src/cargo/core/resolver/encode.rs @@ -154,10 +154,18 @@ impl EncodableResolve { /// primary uses is to be used with `resolve_with_previous` to guide the /// resolver to create a complete Resolve. pub fn into_resolve(self, original: &str, ws: &Workspace<'_>) -> CargoResult<Resolve> { + let unstable_lockfile_version_allowed = ws.config().cli_unstable().next_lockfile_bump; let path_deps = build_path_deps(ws)?; let mut checksums = HashMap::new(); let mut version = match self.version { + Some(4) if ws.config().nightly_features_allowed => { + if unstable_lockfile_version_allowed { + ResolveVersion::V4 + } else { + anyhow::bail!("lock file version 4 requires `-Znext-lockfile-bump`"); + } + } Some(3) => ResolveVersion::V3, Some(n) => bail!( "lock file version `{}` was found, but this version of Cargo \ @@ -612,6 +620,7 @@ impl ser::Serialize for Resolve { metadata, patch, version: match self.version() { + ResolveVersion::V4 => Some(4), ResolveVersion::V3 => Some(3), ResolveVersion::V2 | ResolveVersion::V1 => None, }, diff --git a/src/tools/cargo/src/cargo/core/resolver/features.rs b/src/tools/cargo/src/cargo/core/resolver/features.rs index 6b79722ca..3670e8711 100644 --- a/src/tools/cargo/src/cargo/core/resolver/features.rs +++ b/src/tools/cargo/src/cargo/core/resolver/features.rs @@ -1,4 +1,4 @@ -//! # Feature resolver +//! Resolves conditional compilation for [`features` section] in the manifest. //! //! This is a [new feature resolver] that runs independently of the main //! dependency resolver. It has several options which can enable new feature @@ -34,6 +34,7 @@ //! //! There are probably other assumptions that I am forgetting. //! +//! [`features` section]: https://doc.rust-lang.org/nightly/cargo/reference/features.html //! [new feature resolver]: https://doc.rust-lang.org/nightly/cargo/reference/resolver.html#feature-resolver-version-2 //! [`resolve_ws_with_opts`]: crate::ops::resolve_ws_with_opts diff --git a/src/tools/cargo/src/cargo/core/resolver/resolve.rs b/src/tools/cargo/src/cargo/core/resolver/resolve.rs index 6ab957e92..8405a1245 100644 --- a/src/tools/cargo/src/cargo/core/resolver/resolve.rs +++ b/src/tools/cargo/src/cargo/core/resolver/resolve.rs @@ -80,6 +80,10 @@ pub enum ResolveVersion { /// V3 by default staring in 1.53. #[default] V3, + /// Unstable. Will collect a certain amount of changes and then go. + /// + /// Changes made: + V4, } impl Resolve { 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 002f11ff8..bf26d0498 100644 --- a/src/tools/cargo/src/cargo/core/resolver/version_prefs.rs +++ b/src/tools/cargo/src/cargo/core/resolver/version_prefs.rs @@ -81,7 +81,6 @@ impl VersionPreferences { mod test { use super::*; use crate::core::SourceId; - use crate::util::Config; use std::collections::BTreeMap; fn pkgid(name: &str, version: &str) -> PackageId { @@ -98,10 +97,8 @@ mod test { fn summ(name: &str, version: &str) -> Summary { let pkg_id = pkgid(name, version); - let config = Config::default().unwrap(); let features = BTreeMap::new(); Summary::new( - &config, pkg_id, Vec::new(), &features, diff --git a/src/tools/cargo/src/cargo/core/shell.rs b/src/tools/cargo/src/cargo/core/shell.rs index f74bde257..4d45e6098 100644 --- a/src/tools/cargo/src/cargo/core/shell.rs +++ b/src/tools/cargo/src/cargo/core/shell.rs @@ -1,7 +1,7 @@ use std::fmt; use std::io::prelude::*; +use std::io::IsTerminal; -use is_terminal::IsTerminal; use termcolor::Color::{Cyan, Green, Red, Yellow}; use termcolor::{self, Color, ColorSpec, StandardStream, WriteColor}; diff --git a/src/tools/cargo/src/cargo/core/source/mod.rs b/src/tools/cargo/src/cargo/core/source/mod.rs index 6ca614d34..2d1db1d53 100644 --- a/src/tools/cargo/src/cargo/core/source/mod.rs +++ b/src/tools/cargo/src/cargo/core/source/mod.rs @@ -87,10 +87,25 @@ pub trait Source { /// If quiet, the source should not display any progress or status messages. fn set_quiet(&mut self, quiet: bool); - /// Fetches the full package for each name and version specified. + /// Starts the process to fetch a [`Package`] for the given [`PackageId`]. + /// + /// If the source already has the package available on disk, then it + /// should return immediately with [`MaybePackage::Ready`] with the + /// [`Package`]. Otherwise it should return a [`MaybePackage::Download`] + /// to indicate the URL to download the package (this is for remote + /// registry sources only). + /// + /// In the case where [`MaybePackage::Download`] is returned, then the + /// package downloader will call [`Source::finish_download`] after the + /// download has finished. fn download(&mut self, package: PackageId) -> CargoResult<MaybePackage>; - /// Fetches the full package **immediately** for each name and version specified. + /// Convenience method used to **immediately** fetch a [`Package`] for the + /// given [`PackageId`]. + /// + /// This may trigger a download if necessary. This should only be used + /// when a single package is needed (as in the case for `cargo install`). + /// Otherwise downloads should be batched together via [`PackageSet`]. fn download_now(self: Box<Self>, package: PackageId, config: &Config) -> CargoResult<Package> where Self: std::marker::Sized, @@ -102,7 +117,13 @@ pub trait Source { Ok(Package::clone(pkg)) } - /// Finalizes the download contents of the given [`PackageId`] to a [`Package`]. + /// Gives the source the downloaded `.crate` file. + /// + /// When a source has returned [`MaybePackage::Download`] in the + /// [`Source::download`] method, then this function will be called with + /// the results of the download of the given URL. The source is + /// responsible for saving to disk, and returning the appropriate + /// [`Package`]. fn finish_download(&mut self, pkg_id: PackageId, contents: Vec<u8>) -> CargoResult<Package>; /// Generates a unique string which represents the fingerprint of the diff --git a/src/tools/cargo/src/cargo/core/source/source_id.rs b/src/tools/cargo/src/cargo/core/source/source_id.rs index c369dab16..4064364d5 100644 --- a/src/tools/cargo/src/cargo/core/source/source_id.rs +++ b/src/tools/cargo/src/cargo/core/source/source_id.rs @@ -13,11 +13,10 @@ use std::hash::{self, Hash}; use std::path::{Path, PathBuf}; use std::ptr; use std::sync::Mutex; +use std::sync::OnceLock; use url::Url; -lazy_static::lazy_static! { - static ref SOURCE_ID_CACHE: Mutex<HashSet<&'static SourceIdInner>> = Default::default(); -} +static SOURCE_ID_CACHE: OnceLock<Mutex<HashSet<&'static SourceIdInner>>> = OnceLock::new(); /// Unique identifier for a source of packages. /// @@ -118,7 +117,10 @@ impl SourceId { /// Interns the value and returns the wrapped type. fn wrap(inner: SourceIdInner) -> SourceId { - let mut cache = SOURCE_ID_CACHE.lock().unwrap(); + let mut cache = SOURCE_ID_CACHE + .get_or_init(|| Default::default()) + .lock() + .unwrap(); let inner = cache.get(&inner).cloned().unwrap_or_else(|| { let inner = Box::leak(Box::new(inner)); cache.insert(inner); diff --git a/src/tools/cargo/src/cargo/core/summary.rs b/src/tools/cargo/src/cargo/core/summary.rs index 2535c4482..1883df33b 100644 --- a/src/tools/cargo/src/cargo/core/summary.rs +++ b/src/tools/cargo/src/cargo/core/summary.rs @@ -1,6 +1,6 @@ use crate::core::{Dependency, PackageId, SourceId}; use crate::util::interning::InternedString; -use crate::util::{CargoResult, Config}; +use crate::util::CargoResult; use anyhow::bail; use semver::Version; use std::collections::{BTreeMap, HashMap, HashSet}; @@ -30,7 +30,6 @@ struct Inner { impl Summary { pub fn new( - config: &Config, pkg_id: PackageId, dependencies: Vec<Dependency>, features: &BTreeMap<InternedString, Vec<InternedString>>, @@ -49,7 +48,7 @@ impl Summary { ) } } - let feature_map = build_feature_map(config, pkg_id, features, &dependencies)?; + let feature_map = build_feature_map(pkg_id, features, &dependencies)?; Ok(Summary { inner: Rc::new(Inner { package_id: pkg_id, @@ -140,7 +139,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( - config: &Config, pkg_id: PackageId, features: &BTreeMap<InternedString, Vec<InternedString>>, dependencies: &[Dependency], @@ -204,7 +202,7 @@ fn build_feature_map( feature ); } - validate_feature_name(config, pkg_id, feature)?; + validate_feature_name(pkg_id, feature)?; for fv in fvs { // Find data for the referenced dependency... let dep_data = { @@ -431,33 +429,63 @@ impl fmt::Display for FeatureValue { pub type FeatureMap = BTreeMap<InternedString, Vec<FeatureValue>>; -fn validate_feature_name(config: &Config, pkg_id: PackageId, name: &str) -> CargoResult<()> { +fn validate_feature_name(pkg_id: PackageId, name: &str) -> CargoResult<()> { let mut chars = name.chars(); - const FUTURE: &str = "This was previously accepted but is being phased out; \ - it will become a hard error in a future release.\n\ - For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, \ - and please leave a comment if this will be a problem for your project."; if let Some(ch) = chars.next() { if !(unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' || ch.is_digit(10)) { - config.shell().warn(&format!( + 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`)\n\ - {}", - ch, name, pkg_id, FUTURE - ))?; + (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 == '.') { - config.shell().warn(&format!( + bail!( "invalid character `{}` in feature `{}` in package {}, \ characters must be Unicode XID characters, `+`, or `.` \ - (numbers, `+`, `-`, `_`, `.`, or most letters)\n\ - {}", - ch, name, pkg_id, FUTURE - ))?; + (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()); + } +} diff --git a/src/tools/cargo/src/cargo/core/workspace.rs b/src/tools/cargo/src/cargo/core/workspace.rs index 9922d6d33..db9c18010 100644 --- a/src/tools/cargo/src/cargo/core/workspace.rs +++ b/src/tools/cargo/src/cargo/core/workspace.rs @@ -15,7 +15,7 @@ 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, FeatureValue, PackageId, PackageIdSpec}; +use crate::core::{Dependency, Edition, FeatureValue, PackageId, PackageIdSpec}; use crate::core::{EitherManifest, Package, SourceId, VirtualManifest}; use crate::ops; use crate::sources::{PathSource, CRATES_IO_INDEX, CRATES_IO_REGISTRY}; @@ -381,7 +381,21 @@ impl<'cfg> Workspace<'cfg> { pub fn target_dir(&self) -> Filesystem { self.target_dir .clone() - .unwrap_or_else(|| Filesystem::new(self.root().join("target"))) + .unwrap_or_else(|| self.default_target_dir()) + } + + fn default_target_dir(&self) -> Filesystem { + if self.root_maybe().is_embedded() { + let hash = crate::util::hex::short_hash(&self.root_manifest().to_string_lossy()); + let mut rel_path = PathBuf::new(); + rel_path.push("target"); + rel_path.push(&hash[0..2]); + rel_path.push(&hash[2..]); + + self.config().home().join(rel_path) + } else { + Filesystem::new(self.root().join("target")) + } } /// Returns the root `[replace]` section of this workspace. @@ -726,6 +740,10 @@ impl<'cfg> Workspace<'cfg> { if self.members.contains(&manifest_path) { return Ok(()); } + if is_path_dep && self.root_maybe().is_embedded() { + // Embedded manifests cannot have workspace members + return Ok(()); + } if is_path_dep && !manifest_path.parent().unwrap().starts_with(self.root()) && self.find_root(&manifest_path)? != self.root_manifest @@ -993,6 +1011,24 @@ impl<'cfg> Workspace<'cfg> { } } } + if let MaybePackage::Virtual(vm) = self.root_maybe() { + if vm.resolve_behavior().is_none() { + if let Some(edition) = self + .members() + .filter(|p| p.manifest_path() != root_manifest) + .map(|p| p.manifest().edition()) + .filter(|&e| e >= Edition::Edition2021) + .max() + { + let resolver = edition.default_resolve_behavior().to_manifest(); + self.config.shell().warn(format_args!("some crates are on edition {edition} which defaults to `resolver = \"{resolver}\"`, but virtual workspaces default to `resolver = \"1\"`"))?; + self.config.shell().note( + "to keep the current resolver, specify `workspace.resolver = \"1\"` in the workspace root's manifest", + )?; + self.config.shell().note(format_args!("to use the edition {edition} resolver, specify `workspace.resolver = \"{resolver}\"` in the workspace root's manifest"))?; + } + } + } } Ok(()) } @@ -1562,6 +1598,14 @@ impl MaybePackage { MaybePackage::Virtual(ref vm) => vm.workspace_config(), } } + + /// Has an embedded manifest (single-file package) + pub fn is_embedded(&self) -> bool { + match self { + MaybePackage::Package(p) => p.manifest().is_embedded(), + MaybePackage::Virtual(_) => false, + } + } } impl WorkspaceRootConfig { diff --git a/src/tools/cargo/src/cargo/lib.rs b/src/tools/cargo/src/cargo/lib.rs index 31d03ad25..a03d51199 100644 --- a/src/tools/cargo/src/cargo/lib.rs +++ b/src/tools/cargo/src/cargo/lib.rs @@ -6,6 +6,9 @@ #![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)] //! # Cargo as a library diff --git a/src/tools/cargo/src/cargo/ops/cargo_clean.rs b/src/tools/cargo/src/cargo/ops/cargo_clean.rs index de3139c99..b9b33690e 100644 --- a/src/tools/cargo/src/cargo/ops/cargo_clean.rs +++ b/src/tools/cargo/src/cargo/ops/cargo_clean.rs @@ -297,7 +297,12 @@ fn rm_rf(path: &Path, config: &Config, progress: &mut dyn CleaningProgressBar) - let entry = entry?; progress.on_clean()?; if entry.file_type().is_dir() { - paths::remove_dir(entry.path()).with_context(|| "could not remove build directory")?; + // The contents should have been removed by now, but sometimes a race condition is hit + // where other files have been added by the OS. `paths::remove_dir_all` also falls back + // to `std::fs::remove_dir_all`, which may be more reliable than a simple walk in + // platform-specific edge cases. + paths::remove_dir_all(entry.path()) + .with_context(|| "could not remove build directory")?; } else { paths::remove_file(entry.path()).with_context(|| "failed to remove build artifact")?; } 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 3b6043d4f..f53a9e934 100644 --- a/src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs +++ b/src/tools/cargo/src/cargo/ops/cargo_compile/mod.rs @@ -1,7 +1,5 @@ -//! # The Cargo "compile" operation -//! -//! This module contains the entry point for starting the compilation process -//! for commands like `build`, `test`, `doc`, `rustc`, etc. +//! The entry point for starting the compilation process for commands like +//! `build`, `test`, `doc`, `rustc`, etc. //! //! The [`compile`] function will do all the work to compile a workspace. A //! rough outline is: diff --git a/src/tools/cargo/src/cargo/ops/cargo_fetch.rs b/src/tools/cargo/src/cargo/ops/cargo_fetch.rs index bac3f0278..273bce284 100644 --- a/src/tools/cargo/src/cargo/ops/cargo_fetch.rs +++ b/src/tools/cargo/src/cargo/ops/cargo_fetch.rs @@ -2,6 +2,7 @@ use crate::core::compiler::standard_lib; use crate::core::compiler::{BuildConfig, CompileMode, RustcTargetData}; use crate::core::{PackageSet, Resolve, Workspace}; use crate::ops; +use crate::util::config::JobsConfig; use crate::util::CargoResult; use crate::util::Config; use std::collections::HashSet; @@ -20,7 +21,7 @@ pub fn fetch<'a>( ws.emit_warnings()?; let (mut packages, resolve) = ops::resolve_ws(ws)?; - let jobs = Some(1); + let jobs = Some(JobsConfig::Integer(1)); let keep_going = false; let config = ws.config(); let build_config = BuildConfig::new( diff --git a/src/tools/cargo/src/cargo/ops/cargo_install.rs b/src/tools/cargo/src/cargo/ops/cargo_install.rs index 5f843e8c7..8ddfa4fab 100644 --- a/src/tools/cargo/src/cargo/ops/cargo_install.rs +++ b/src/tools/cargo/src/cargo/ops/cargo_install.rs @@ -320,7 +320,9 @@ impl<'cfg, 'a> InstallablePackage<'cfg, 'a> { format!( "failed to compile `{}`, intermediate artifacts can be \ - found at `{}`", + found at `{}`.\nTo reuse those artifacts with a future \ + compilation, set the environment variable \ + `CARGO_TARGET_DIR` to that path.", self.pkg, self.ws.target_dir().display() ) diff --git a/src/tools/cargo/src/cargo/ops/cargo_new.rs b/src/tools/cargo/src/cargo/ops/cargo_new.rs index 697798c0c..b113671b0 100644 --- a/src/tools/cargo/src/cargo/ops/cargo_new.rs +++ b/src/tools/cargo/src/cargo/ops/cargo_new.rs @@ -163,6 +163,7 @@ fn get_name<'a>(path: &'a Path, opts: &'a NewOptions) -> CargoResult<&'a str> { }) } +/// See also `util::toml::embedded::sanitize_name` fn check_name( name: &str, show_name_help: bool, @@ -820,6 +821,18 @@ fn mk(config: &Config, opts: &MkOptions<'_>) -> CargoResult<()> { workspace_package_keys, ) } + + // Try to inherit the workspace lints key if it exists. + if config.cli_unstable().lints + && workspace_document + .get("workspace") + .and_then(|workspace| workspace.get("lints")) + .is_some() + { + let mut table = toml_edit::Table::new(); + table["workspace"] = toml_edit::value(true); + manifest["lints"] = toml_edit::Item::Table(table); + } } } diff --git a/src/tools/cargo/src/cargo/ops/cargo_package.rs b/src/tools/cargo/src/cargo/ops/cargo_package.rs index f80848c75..a322afbb3 100644 --- a/src/tools/cargo/src/cargo/ops/cargo_package.rs +++ b/src/tools/cargo/src/cargo/ops/cargo_package.rs @@ -13,6 +13,7 @@ use crate::core::{registry::PackageRegistry, resolver::HasDevUnits}; use crate::core::{Feature, Shell, Verbosity, Workspace}; use crate::core::{Package, PackageId, PackageSet, Resolve, SourceId}; use crate::sources::PathSource; +use crate::util::config::JobsConfig; use crate::util::errors::CargoResult; use crate::util::toml::TomlManifest; use crate::util::{self, human_readable_bytes, restricted_names, Config, FileLock}; @@ -31,7 +32,7 @@ pub struct PackageOpts<'cfg> { pub check_metadata: bool, pub allow_dirty: bool, pub verify: bool, - pub jobs: Option<i32>, + pub jobs: Option<JobsConfig>, pub keep_going: bool, pub to_package: ops::Packages, pub targets: Vec<String>, @@ -126,7 +127,7 @@ pub fn package_one( super::check_dep_has_version(dep, false)?; } - let filename = format!("{}-{}.crate", pkg.name(), pkg.version()); + let filename = pkg.package_id().tarball_name(); let dir = ws.target_dir().join("package"); let mut dst = { let tmp = format!(".{}", filename); @@ -198,7 +199,7 @@ pub fn package(ws: &Workspace<'_>, opts: &PackageOpts<'_>) -> CargoResult<Option check_metadata: opts.check_metadata, allow_dirty: opts.allow_dirty, verify: opts.verify, - jobs: opts.jobs, + jobs: opts.jobs.clone(), keep_going: opts.keep_going, to_package: ops::Packages::Default, targets: opts.targets.clone(), @@ -392,7 +393,7 @@ fn build_lock(ws: &Workspace<'_>, orig_pkg: &Package) -> CargoResult<String> { 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, source_id, package_root, config)?; + TomlManifest::to_real_manifest(&toml_manifest, false, source_id, package_root, config)?; let new_pkg = Package::new(manifest, orig_pkg.manifest_path()); // Regenerate Cargo.lock using the old one as a guide. @@ -861,7 +862,7 @@ fn run_verify( &ops::CompileOptions { build_config: BuildConfig::new( config, - opts.jobs, + opts.jobs.clone(), opts.keep_going, &opts.targets, CompileMode::Build, diff --git a/src/tools/cargo/src/cargo/ops/cargo_test.rs b/src/tools/cargo/src/cargo/ops/cargo_test.rs index b7e61982d..1ddf7755f 100644 --- a/src/tools/cargo/src/cargo/ops/cargo_test.rs +++ b/src/tools/cargo/src/cargo/ops/cargo_test.rs @@ -172,7 +172,6 @@ fn run_doc_tests( let config = ws.config(); let mut errors = Vec::new(); let doctest_xcompile = config.cli_unstable().doctest_xcompile; - let doctest_in_workspace = config.cli_unstable().doctest_in_workspace; for doctest_info in &compilation.to_doc_test { let Doctest { @@ -215,15 +214,9 @@ fn run_doc_tests( p.arg("--crate-name").arg(&unit.target.crate_name()); p.arg("--test"); - if doctest_in_workspace { - add_path_args(ws, unit, &mut p); - // FIXME(swatinem): remove the `unstable-options` once rustdoc stabilizes the `test-run-directory` option - p.arg("-Z").arg("unstable-options"); - p.arg("--test-run-directory") - .arg(unit.pkg.root().to_path_buf()); - } else { - p.arg(unit.target.src_path().path().unwrap()); - } + add_path_args(ws, unit, &mut p); + p.arg("--test-run-directory") + .arg(unit.pkg.root().to_path_buf()); if let CompileKind::Target(target) = unit.kind { // use `rustc_target()` to properly handle JSON target paths diff --git a/src/tools/cargo/src/cargo/ops/lockfile.rs b/src/tools/cargo/src/cargo/ops/lockfile.rs index e11e492af..b27a7e742 100644 --- a/src/tools/cargo/src/cargo/ops/lockfile.rs +++ b/src/tools/cargo/src/cargo/ops/lockfile.rs @@ -8,12 +8,12 @@ use crate::util::Filesystem; use anyhow::Context as _; pub fn load_pkg_lockfile(ws: &Workspace<'_>) -> CargoResult<Option<Resolve>> { - if !ws.root().join("Cargo.lock").exists() { + let lock_root = lock_root(ws); + if !lock_root.as_path_unlocked().join("Cargo.lock").exists() { return Ok(None); } - let root = Filesystem::new(ws.root().to_path_buf()); - let mut f = root.open_ro("Cargo.lock", ws.config(), "Cargo.lock file")?; + let mut f = lock_root.open_ro("Cargo.lock", ws.config(), "Cargo.lock file")?; let mut s = String::new(); f.read_to_string(&mut s) @@ -30,12 +30,12 @@ pub fn load_pkg_lockfile(ws: &Workspace<'_>) -> CargoResult<Option<Resolve>> { /// Generate a toml String of Cargo.lock from a Resolve. pub fn resolve_to_string(ws: &Workspace<'_>, resolve: &mut Resolve) -> CargoResult<String> { - let (_orig, out, _ws_root) = resolve_to_string_orig(ws, resolve); + let (_orig, out, _lock_root) = resolve_to_string_orig(ws, resolve); Ok(out) } pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &mut Resolve) -> CargoResult<()> { - let (orig, mut out, ws_root) = resolve_to_string_orig(ws, resolve); + let (orig, mut out, lock_root) = resolve_to_string_orig(ws, resolve); // If the lock file contents haven't changed so don't rewrite it. This is // helpful on read-only filesystems. @@ -55,7 +55,7 @@ pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &mut Resolve) -> CargoRes "the lock file {} needs to be updated but {} was passed to prevent this\n\ If you want to try to generate the lock file without accessing the network, \ remove the {} flag and use --offline instead.", - ws.root().to_path_buf().join("Cargo.lock").display(), + lock_root.as_path_unlocked().join("Cargo.lock").display(), flag, flag ); @@ -69,17 +69,30 @@ pub fn write_pkg_lockfile(ws: &Workspace<'_>, resolve: &mut Resolve) -> CargoRes if resolve.version() < ResolveVersion::default() { resolve.set_version(ResolveVersion::default()); out = serialize_resolve(resolve, orig.as_deref()); + } else if resolve.version() > ResolveVersion::default() + && !ws.config().cli_unstable().next_lockfile_bump + { + // The next version hasn't yet stabilized. + anyhow::bail!( + "lock file version `{:?}` requires `-Znext-lockfile-bump`", + resolve.version() + ) } // Ok, if that didn't work just write it out - ws_root + lock_root .open_rw("Cargo.lock", ws.config(), "Cargo.lock file") .and_then(|mut f| { f.file().set_len(0)?; f.write_all(out.as_bytes())?; Ok(()) }) - .with_context(|| format!("failed to write {}", ws.root().join("Cargo.lock").display()))?; + .with_context(|| { + format!( + "failed to write {}", + lock_root.as_path_unlocked().join("Cargo.lock").display() + ) + })?; Ok(()) } @@ -88,15 +101,15 @@ fn resolve_to_string_orig( resolve: &mut Resolve, ) -> (Option<String>, String, Filesystem) { // Load the original lock file if it exists. - let ws_root = Filesystem::new(ws.root().to_path_buf()); - let orig = ws_root.open_ro("Cargo.lock", ws.config(), "Cargo.lock file"); + let lock_root = lock_root(ws); + let orig = lock_root.open_ro("Cargo.lock", ws.config(), "Cargo.lock file"); let orig = orig.and_then(|mut f| { let mut s = String::new(); f.read_to_string(&mut s)?; Ok(s) }); let out = serialize_resolve(resolve, orig.as_deref().ok()); - (orig.ok(), out, ws_root) + (orig.ok(), out, lock_root) } fn serialize_resolve(resolve: &Resolve, orig: Option<&str>) -> String { @@ -227,3 +240,11 @@ fn emit_package(dep: &toml::Table, out: &mut String) { out.push_str(&format!("replace = {}\n\n", &dep["replace"])); } } + +fn lock_root(ws: &Workspace<'_>) -> Filesystem { + if ws.root_maybe().is_embedded() { + ws.target_dir() + } else { + Filesystem::new(ws.root().to_owned()) + } +} diff --git a/src/tools/cargo/src/cargo/ops/mod.rs b/src/tools/cargo/src/cargo/ops/mod.rs index 4b6aea991..d4ec442dd 100644 --- a/src/tools/cargo/src/cargo/ops/mod.rs +++ b/src/tools/cargo/src/cargo/ops/mod.rs @@ -21,11 +21,15 @@ pub use self::cargo_test::{run_benches, run_tests, TestOptions}; pub use self::cargo_uninstall::uninstall; pub use self::fix::{fix, fix_exec_rustc, fix_get_proxy_lock_addr, FixOptions}; pub use self::lockfile::{load_pkg_lockfile, resolve_to_string, write_pkg_lockfile}; -pub use self::registry::HttpTimeout; -pub use self::registry::{configure_http_handle, http_handle, http_handle_and_timeout}; -pub use self::registry::{modify_owners, yank, OwnersOptions, PublishOpts}; -pub use self::registry::{needs_custom_http_transport, registry_login, registry_logout, search}; -pub use self::registry::{publish, RegistryCredentialConfig}; +pub use self::registry::modify_owners; +pub use self::registry::publish; +pub use self::registry::registry_login; +pub use self::registry::registry_logout; +pub use self::registry::search; +pub use self::registry::yank; +pub use self::registry::OwnersOptions; +pub use self::registry::PublishOpts; +pub use self::registry::RegistryCredentialConfig; pub use self::resolve::{ add_overrides, get_resolved_packages, resolve_with_previous, resolve_ws, resolve_ws_with_opts, WorkspaceResolve, diff --git a/src/tools/cargo/src/cargo/ops/registry.rs b/src/tools/cargo/src/cargo/ops/registry.rs deleted file mode 100644 index a8efb5492..000000000 --- a/src/tools/cargo/src/cargo/ops/registry.rs +++ /dev/null @@ -1,1227 +0,0 @@ -use std::cmp; -use std::collections::{BTreeMap, HashSet}; -use std::fs::File; -use std::io::{self, BufRead}; -use std::iter::repeat; -use std::path::PathBuf; -use std::str; -use std::task::Poll; -use std::time::Duration; - -use anyhow::{anyhow, bail, format_err, Context as _}; -use cargo_util::paths; -use crates_io::{self, NewCrate, NewCrateDependency, Registry}; -use curl::easy::{Easy, InfoType, SslOpt, SslVersion}; -use log::{log, Level}; -use pasetors::keys::{AsymmetricKeyPair, Generate}; -use pasetors::paserk::FormatAsPaserk; -use termcolor::Color::Green; -use termcolor::ColorSpec; -use url::Url; - -use crate::core::dependency::DepKind; -use crate::core::dependency::Dependency; -use crate::core::manifest::ManifestMetadata; -use crate::core::resolver::CliFeatures; -use crate::core::source::Source; -use crate::core::QueryKind; -use crate::core::{Package, SourceId, Workspace}; -use crate::ops; -use crate::ops::Packages; -use crate::sources::{RegistrySource, SourceConfigMap, CRATES_IO_DOMAIN, CRATES_IO_REGISTRY}; -use crate::util::auth::{ - paserk_public_from_paserk_secret, Secret, {self, AuthorizationError}, -}; -use crate::util::config::{Config, SslVersionConfig, SslVersionConfigRange}; -use crate::util::errors::CargoResult; -use crate::util::important_paths::find_root_manifest_for_wd; -use crate::util::network; -use crate::util::{truncate_with_ellipsis, IntoUrl}; -use crate::util::{Progress, ProgressStyle}; -use crate::{drop_print, drop_println, version}; - -/// Registry settings loaded from config files. -/// -/// This is loaded based on the `--registry` flag and the config settings. -#[derive(Debug, PartialEq)] -pub enum RegistryCredentialConfig { - None, - /// The authentication token. - Token(Secret<String>), - /// Process used for fetching a token. - Process((PathBuf, Vec<String>)), - /// Secret Key and subject for Asymmetric tokens. - AsymmetricKey((Secret<String>, Option<String>)), -} - -impl RegistryCredentialConfig { - /// Returns `true` if the credential is [`None`]. - /// - /// [`None`]: Self::None - pub fn is_none(&self) -> bool { - matches!(self, Self::None) - } - /// Returns `true` if the credential is [`Token`]. - /// - /// [`Token`]: Self::Token - pub fn is_token(&self) -> bool { - matches!(self, Self::Token(..)) - } - /// Returns `true` if the credential is [`AsymmetricKey`]. - /// - /// [`AsymmetricKey`]: RegistryCredentialConfig::AsymmetricKey - pub fn is_asymmetric_key(&self) -> bool { - matches!(self, Self::AsymmetricKey(..)) - } - pub fn as_token(&self) -> Option<Secret<&str>> { - if let Self::Token(v) = self { - Some(v.as_deref()) - } else { - None - } - } - pub fn as_process(&self) -> Option<&(PathBuf, Vec<String>)> { - if let Self::Process(v) = self { - Some(v) - } else { - None - } - } - pub fn as_asymmetric_key(&self) -> Option<&(Secret<String>, Option<String>)> { - if let Self::AsymmetricKey(v) = self { - Some(v) - } else { - None - } - } -} - -pub struct PublishOpts<'cfg> { - pub config: &'cfg Config, - pub token: Option<Secret<String>>, - pub index: Option<String>, - pub verify: bool, - pub allow_dirty: bool, - pub jobs: Option<i32>, - pub keep_going: bool, - pub to_publish: ops::Packages, - pub targets: Vec<String>, - pub dry_run: bool, - pub registry: Option<String>, - pub cli_features: CliFeatures, -} - -pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> { - let specs = opts.to_publish.to_package_id_specs(ws)?; - if specs.len() > 1 { - bail!("the `-p` argument must be specified to select a single package to publish") - } - if Packages::Default == opts.to_publish && ws.is_virtual() { - bail!("the `-p` argument must be specified in the root of a virtual workspace") - } - let member_ids = ws.members().map(|p| p.package_id()); - // Check that the spec matches exactly one member. - specs[0].query(member_ids)?; - let mut pkgs = ws.members_with_features(&specs, &opts.cli_features)?; - // In `members_with_features_old`, it will add "current" package (determined by the cwd) - // So we need filter - pkgs = pkgs - .into_iter() - .filter(|(m, _)| specs.iter().any(|spec| spec.matches(m.package_id()))) - .collect(); - // Double check. It is safe theoretically, unless logic has updated. - assert_eq!(pkgs.len(), 1); - - let (pkg, cli_features) = pkgs.pop().unwrap(); - - let mut publish_registry = opts.registry.clone(); - if let Some(ref allowed_registries) = *pkg.publish() { - if publish_registry.is_none() && allowed_registries.len() == 1 { - // If there is only one allowed registry, push to that one directly, - // even though there is no registry specified in the command. - let default_registry = &allowed_registries[0]; - if default_registry != CRATES_IO_REGISTRY { - // Don't change the registry for crates.io and don't warn the user. - // crates.io will be defaulted even without this. - opts.config.shell().note(&format!( - "Found `{}` as only allowed registry. Publishing to it automatically.", - default_registry - ))?; - publish_registry = Some(default_registry.clone()); - } - } - - let reg_name = publish_registry - .clone() - .unwrap_or_else(|| CRATES_IO_REGISTRY.to_string()); - if allowed_registries.is_empty() { - bail!( - "`{}` cannot be published.\n\ - `package.publish` is set to `false` or an empty list in Cargo.toml and prevents publishing.", - pkg.name(), - ); - } else if !allowed_registries.contains(®_name) { - bail!( - "`{}` cannot be published.\n\ - The registry `{}` is not listed in the `package.publish` value in Cargo.toml.", - pkg.name(), - reg_name - ); - } - } - // This is only used to confirm that we can create a token before we build the package. - // This causes the credential provider to be called an extra time, but keeps the same order of errors. - let ver = pkg.version().to_string(); - let mutation = auth::Mutation::PrePublish; - - let (mut registry, reg_ids) = registry( - opts.config, - opts.token.as_ref().map(Secret::as_deref), - opts.index.as_deref(), - publish_registry.as_deref(), - true, - Some(mutation).filter(|_| !opts.dry_run), - )?; - verify_dependencies(pkg, ®istry, reg_ids.original)?; - - // Prepare a tarball, with a non-suppressible warning if metadata - // is missing since this is being put online. - let tarball = ops::package_one( - ws, - pkg, - &ops::PackageOpts { - config: opts.config, - verify: opts.verify, - list: false, - check_metadata: true, - allow_dirty: opts.allow_dirty, - to_package: ops::Packages::Default, - targets: opts.targets.clone(), - jobs: opts.jobs, - keep_going: opts.keep_going, - cli_features: cli_features, - }, - )? - .unwrap(); - - if !opts.dry_run { - let hash = cargo_util::Sha256::new() - .update_file(tarball.file())? - .finish_hex(); - let mutation = Some(auth::Mutation::Publish { - name: pkg.name().as_str(), - vers: &ver, - cksum: &hash, - }); - registry.set_token(Some(auth::auth_token( - &opts.config, - ®_ids.original, - None, - mutation, - )?)); - } - - opts.config - .shell() - .status("Uploading", pkg.package_id().to_string())?; - transmit( - opts.config, - pkg, - tarball.file(), - &mut registry, - reg_ids.original, - opts.dry_run, - )?; - if !opts.dry_run { - const DEFAULT_TIMEOUT: u64 = 60; - let timeout = if opts.config.cli_unstable().publish_timeout { - let timeout: Option<u64> = opts.config.get("publish.timeout")?; - timeout.unwrap_or(DEFAULT_TIMEOUT) - } else { - DEFAULT_TIMEOUT - }; - if 0 < timeout { - let timeout = std::time::Duration::from_secs(timeout); - wait_for_publish(opts.config, reg_ids.original, pkg, timeout)?; - } - } - - Ok(()) -} - -fn verify_dependencies( - pkg: &Package, - registry: &Registry, - registry_src: SourceId, -) -> CargoResult<()> { - for dep in pkg.dependencies().iter() { - if super::check_dep_has_version(dep, true)? { - continue; - } - // TomlManifest::prepare_for_publish will rewrite the dependency - // to be just the `version` field. - if dep.source_id() != registry_src { - if !dep.source_id().is_registry() { - // Consider making SourceId::kind a public type that we can - // exhaustively match on. Using match can help ensure that - // every kind is properly handled. - panic!("unexpected source kind for dependency {:?}", dep); - } - // Block requests to send to crates.io with alt-registry deps. - // This extra hostname check is mostly to assist with testing, - // but also prevents someone using `--index` to specify - // something that points to crates.io. - if registry_src.is_crates_io() || registry.host_is_crates_io() { - bail!("crates cannot be published to crates.io with dependencies sourced from other\n\ - registries. `{}` needs to be published to crates.io before publishing this crate.\n\ - (crate `{}` is pulled from {})", - dep.package_name(), - dep.package_name(), - dep.source_id()); - } - } - } - Ok(()) -} - -fn transmit( - config: &Config, - pkg: &Package, - tarball: &File, - registry: &mut Registry, - registry_id: SourceId, - dry_run: bool, -) -> CargoResult<()> { - let deps = pkg - .dependencies() - .iter() - .filter(|dep| { - // Skip dev-dependency without version. - dep.is_transitive() || dep.specified_req() - }) - .map(|dep| { - // If the dependency is from a different registry, then include the - // registry in the dependency. - let dep_registry_id = match dep.registry_id() { - Some(id) => id, - None => SourceId::crates_io(config)?, - }; - // In the index and Web API, None means "from the same registry" - // whereas in Cargo.toml, it means "from crates.io". - let dep_registry = if dep_registry_id != registry_id { - Some(dep_registry_id.url().to_string()) - } else { - None - }; - - Ok(NewCrateDependency { - optional: dep.is_optional(), - default_features: dep.uses_default_features(), - name: dep.package_name().to_string(), - features: dep.features().iter().map(|s| s.to_string()).collect(), - version_req: dep.version_req().to_string(), - target: dep.platform().map(|s| s.to_string()), - kind: match dep.kind() { - DepKind::Normal => "normal", - DepKind::Build => "build", - DepKind::Development => "dev", - } - .to_string(), - registry: dep_registry, - explicit_name_in_toml: dep.explicit_name_in_toml().map(|s| s.to_string()), - }) - }) - .collect::<CargoResult<Vec<NewCrateDependency>>>()?; - let manifest = pkg.manifest(); - let ManifestMetadata { - ref authors, - ref description, - ref homepage, - ref documentation, - ref keywords, - ref readme, - ref repository, - ref license, - ref license_file, - ref categories, - ref badges, - ref links, - ref rust_version, - } = *manifest.metadata(); - let readme_content = readme - .as_ref() - .map(|readme| { - paths::read(&pkg.root().join(readme)) - .with_context(|| format!("failed to read `readme` file for package `{}`", pkg)) - }) - .transpose()?; - if let Some(ref file) = *license_file { - if !pkg.root().join(file).exists() { - bail!("the license file `{}` does not exist", file) - } - } - - // Do not upload if performing a dry run - if dry_run { - config.shell().warn("aborting upload due to dry run")?; - return Ok(()); - } - - let string_features = match manifest.original().features() { - Some(features) => features - .iter() - .map(|(feat, values)| { - ( - feat.to_string(), - values.iter().map(|fv| fv.to_string()).collect(), - ) - }) - .collect::<BTreeMap<String, Vec<String>>>(), - None => BTreeMap::new(), - }; - - let warnings = registry - .publish( - &NewCrate { - name: pkg.name().to_string(), - vers: pkg.version().to_string(), - deps, - features: string_features, - authors: authors.clone(), - description: description.clone(), - homepage: homepage.clone(), - documentation: documentation.clone(), - keywords: keywords.clone(), - categories: categories.clone(), - readme: readme_content, - readme_file: readme.clone(), - repository: repository.clone(), - license: license.clone(), - license_file: license_file.clone(), - badges: badges.clone(), - links: links.clone(), - rust_version: rust_version.clone(), - }, - tarball, - ) - .with_context(|| format!("failed to publish to registry at {}", registry.host()))?; - - if !warnings.invalid_categories.is_empty() { - let msg = format!( - "the following are not valid category slugs and were \ - ignored: {}. Please see https://crates.io/category_slugs \ - for the list of all category slugs. \ - ", - warnings.invalid_categories.join(", ") - ); - config.shell().warn(&msg)?; - } - - if !warnings.invalid_badges.is_empty() { - let msg = format!( - "the following are not valid badges and were ignored: {}. \ - Either the badge type specified is unknown or a required \ - attribute is missing. Please see \ - https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata \ - for valid badge types and their required attributes.", - warnings.invalid_badges.join(", ") - ); - config.shell().warn(&msg)?; - } - - if !warnings.other.is_empty() { - for msg in warnings.other { - config.shell().warn(&msg)?; - } - } - - Ok(()) -} - -fn wait_for_publish( - config: &Config, - registry_src: SourceId, - pkg: &Package, - timeout: std::time::Duration, -) -> CargoResult<()> { - let version_req = format!("={}", pkg.version()); - let mut source = SourceConfigMap::empty(config)?.load(registry_src, &HashSet::new())?; - // Disable the source's built-in progress bars. Repeatedly showing a bunch - // of independent progress bars can be a little confusing. There is an - // overall progress bar managed here. - source.set_quiet(true); - let source_description = source.source_id().to_string(); - let query = Dependency::parse(pkg.name(), Some(&version_req), registry_src)?; - - let now = std::time::Instant::now(); - let sleep_time = std::time::Duration::from_secs(1); - let max = timeout.as_secs() as usize; - // Short does not include the registry name. - let short_pkg_description = format!("{} v{}", pkg.name(), pkg.version()); - config.shell().status( - "Uploaded", - format!("{short_pkg_description} to {source_description}"), - )?; - config.shell().note(format!( - "Waiting for `{short_pkg_description}` to be available at {source_description}.\n\ - You may press ctrl-c to skip waiting; the crate should be available shortly." - ))?; - let mut progress = Progress::with_style("Waiting", ProgressStyle::Ratio, config); - progress.tick_now(0, max, "")?; - let is_available = loop { - { - let _lock = config.acquire_package_cache_lock()?; - // Force re-fetching the source - // - // As pulling from a git source is expensive, we track when we've done it within the - // process to only do it once, but we are one of the rare cases that needs to do it - // multiple times - config - .updated_sources() - .remove(&source.replaced_source_id()); - source.invalidate_cache(); - let summaries = loop { - // Exact to avoid returning all for path/git - match source.query_vec(&query, QueryKind::Exact) { - std::task::Poll::Ready(res) => { - break res?; - } - std::task::Poll::Pending => source.block_until_ready()?, - } - }; - if !summaries.is_empty() { - break true; - } - } - - let elapsed = now.elapsed(); - if timeout < elapsed { - config.shell().warn(format!( - "timed out waiting for `{short_pkg_description}` to be available in {source_description}", - ))?; - config.shell().note( - "The registry may have a backlog that is delaying making the \ - crate available. The crate should be available soon.", - )?; - break false; - } - - progress.tick_now(elapsed.as_secs() as usize, max, "")?; - std::thread::sleep(sleep_time); - }; - if is_available { - config.shell().status( - "Published", - format!("{short_pkg_description} at {source_description}"), - )?; - } - - Ok(()) -} - -/// Returns the `Registry` and `Source` based on command-line and config settings. -/// -/// * `token_from_cmdline`: The token from the command-line. If not set, uses the token -/// from the config. -/// * `index`: The index URL from the command-line. -/// * `registry`: The registry name from the command-line. If neither -/// `registry`, or `index` are set, then uses `crates-io`. -/// * `force_update`: If `true`, forces the index to be updated. -/// * `token_required`: If `true`, the token will be set. -fn registry( - config: &Config, - token_from_cmdline: Option<Secret<&str>>, - index: Option<&str>, - registry: Option<&str>, - force_update: bool, - token_required: Option<auth::Mutation<'_>>, -) -> CargoResult<(Registry, RegistrySourceIds)> { - let source_ids = get_source_id(config, index, registry)?; - - if token_required.is_some() && index.is_some() && token_from_cmdline.is_none() { - bail!("command-line argument --index requires --token to be specified"); - } - if let Some(token) = token_from_cmdline { - auth::cache_token(config, &source_ids.original, token); - } - - let cfg = { - let _lock = config.acquire_package_cache_lock()?; - let mut src = RegistrySource::remote(source_ids.replacement, &HashSet::new(), config)?; - // Only update the index if `force_update` is set. - if force_update { - src.invalidate_cache() - } - let cfg = loop { - match src.config()? { - Poll::Pending => src - .block_until_ready() - .with_context(|| format!("failed to update {}", source_ids.replacement))?, - Poll::Ready(cfg) => break cfg, - } - }; - cfg.expect("remote registries must have config") - }; - let api_host = cfg - .api - .ok_or_else(|| format_err!("{} does not support API commands", source_ids.replacement))?; - let token = if token_required.is_some() || cfg.auth_required { - Some(auth::auth_token( - config, - &source_ids.original, - None, - token_required, - )?) - } else { - None - }; - let handle = http_handle(config)?; - Ok(( - Registry::new_handle(api_host, token, handle, cfg.auth_required), - source_ids, - )) -} - -/// Creates a new HTTP handle with appropriate global configuration for cargo. -pub fn http_handle(config: &Config) -> CargoResult<Easy> { - let (mut handle, timeout) = http_handle_and_timeout(config)?; - timeout.configure(&mut handle)?; - Ok(handle) -} - -pub fn http_handle_and_timeout(config: &Config) -> CargoResult<(Easy, HttpTimeout)> { - if config.frozen() { - bail!( - "attempting to make an HTTP request, but --frozen was \ - specified" - ) - } - if config.offline() { - bail!( - "attempting to make an HTTP request, but --offline was \ - specified" - ) - } - - // The timeout option for libcurl by default times out the entire transfer, - // but we probably don't want this. Instead we only set timeouts for the - // connect phase as well as a "low speed" timeout so if we don't receive - // many bytes in a large-ish period of time then we time out. - let mut handle = Easy::new(); - let timeout = configure_http_handle(config, &mut handle)?; - Ok((handle, timeout)) -} - -// Only use a custom transport if any HTTP options are specified, -// such as proxies or custom certificate authorities. -// -// The custom transport, however, is not as well battle-tested. -pub fn needs_custom_http_transport(config: &Config) -> CargoResult<bool> { - Ok( - network::proxy::http_proxy_exists(config.http_config()?, config) - || *config.http_config()? != Default::default() - || config.get_env_os("HTTP_TIMEOUT").is_some(), - ) -} - -/// Configure a libcurl http handle with the defaults options for Cargo -pub fn configure_http_handle(config: &Config, handle: &mut Easy) -> CargoResult<HttpTimeout> { - let http = config.http_config()?; - if let Some(proxy) = network::proxy::http_proxy(http) { - handle.proxy(&proxy)?; - } - if let Some(cainfo) = &http.cainfo { - let cainfo = cainfo.resolve_path(config); - handle.cainfo(&cainfo)?; - } - if let Some(check) = http.check_revoke { - handle.ssl_options(SslOpt::new().no_revoke(!check))?; - } - - if let Some(user_agent) = &http.user_agent { - handle.useragent(user_agent)?; - } else { - handle.useragent(&format!("cargo {}", version()))?; - } - - fn to_ssl_version(s: &str) -> CargoResult<SslVersion> { - let version = match s { - "default" => SslVersion::Default, - "tlsv1" => SslVersion::Tlsv1, - "tlsv1.0" => SslVersion::Tlsv10, - "tlsv1.1" => SslVersion::Tlsv11, - "tlsv1.2" => SslVersion::Tlsv12, - "tlsv1.3" => SslVersion::Tlsv13, - _ => bail!( - "Invalid ssl version `{s}`,\ - choose from 'default', 'tlsv1', 'tlsv1.0', 'tlsv1.1', 'tlsv1.2', 'tlsv1.3'." - ), - }; - Ok(version) - } - - // Empty string accept encoding expands to the encodings supported by the current libcurl. - handle.accept_encoding("")?; - if let Some(ssl_version) = &http.ssl_version { - match ssl_version { - SslVersionConfig::Single(s) => { - let version = to_ssl_version(s.as_str())?; - handle.ssl_version(version)?; - } - SslVersionConfig::Range(SslVersionConfigRange { min, max }) => { - let min_version = min - .as_ref() - .map_or(Ok(SslVersion::Default), |s| to_ssl_version(s))?; - let max_version = max - .as_ref() - .map_or(Ok(SslVersion::Default), |s| to_ssl_version(s))?; - handle.ssl_min_max_version(min_version, max_version)?; - } - } - } else if cfg!(windows) { - // This is a temporary workaround for some bugs with libcurl and - // schannel and TLS 1.3. - // - // Our libcurl on Windows is usually built with schannel. - // On Windows 11 (or Windows Server 2022), libcurl recently (late - // 2022) gained support for TLS 1.3 with schannel, and it now defaults - // to 1.3. Unfortunately there have been some bugs with this. - // https://github.com/curl/curl/issues/9431 is the most recent. Once - // that has been fixed, and some time has passed where we can be more - // confident that the 1.3 support won't cause issues, this can be - // removed. - // - // Windows 10 is unaffected. libcurl does not support TLS 1.3 on - // Windows 10. (Windows 10 sorta had support, but it required enabling - // an advanced option in the registry which was buggy, and libcurl - // does runtime checks to prevent it.) - handle.ssl_min_max_version(SslVersion::Default, SslVersion::Tlsv12)?; - } - - if let Some(true) = http.debug { - handle.verbose(true)?; - log::debug!("{:#?}", curl::Version::get()); - handle.debug_function(|kind, data| { - let (prefix, level) = match kind { - InfoType::Text => ("*", Level::Debug), - InfoType::HeaderIn => ("<", Level::Debug), - InfoType::HeaderOut => (">", Level::Debug), - InfoType::DataIn => ("{", Level::Trace), - InfoType::DataOut => ("}", Level::Trace), - InfoType::SslDataIn | InfoType::SslDataOut => return, - _ => return, - }; - let starts_with_ignore_case = |line: &str, text: &str| -> bool { - line[..line.len().min(text.len())].eq_ignore_ascii_case(text) - }; - match str::from_utf8(data) { - Ok(s) => { - for mut line in s.lines() { - if starts_with_ignore_case(line, "authorization:") { - line = "Authorization: [REDACTED]"; - } else if starts_with_ignore_case(line, "h2h3 [authorization:") { - line = "h2h3 [Authorization: [REDACTED]]"; - } else if starts_with_ignore_case(line, "set-cookie") { - line = "set-cookie: [REDACTED]"; - } - log!(level, "http-debug: {} {}", prefix, line); - } - } - Err(_) => { - log!( - level, - "http-debug: {} ({} bytes of data)", - prefix, - data.len() - ); - } - } - })?; - } - - HttpTimeout::new(config) -} - -#[must_use] -pub struct HttpTimeout { - pub dur: Duration, - pub low_speed_limit: u32, -} - -impl HttpTimeout { - pub fn new(config: &Config) -> CargoResult<HttpTimeout> { - let http_config = config.http_config()?; - let low_speed_limit = http_config.low_speed_limit.unwrap_or(10); - let seconds = http_config - .timeout - .or_else(|| { - config - .get_env("HTTP_TIMEOUT") - .ok() - .and_then(|s| s.parse().ok()) - }) - .unwrap_or(30); - Ok(HttpTimeout { - dur: Duration::new(seconds, 0), - low_speed_limit, - }) - } - - pub fn configure(&self, handle: &mut Easy) -> CargoResult<()> { - // The timeout option for libcurl by default times out the entire - // transfer, but we probably don't want this. Instead we only set - // timeouts for the connect phase as well as a "low speed" timeout so - // if we don't receive many bytes in a large-ish period of time then we - // time out. - handle.connect_timeout(self.dur)?; - handle.low_speed_time(self.dur)?; - handle.low_speed_limit(self.low_speed_limit)?; - Ok(()) - } -} - -pub fn registry_login( - config: &Config, - token: Option<Secret<&str>>, - reg: Option<&str>, - generate_keypair: bool, - secret_key_required: bool, - key_subject: Option<&str>, -) -> CargoResult<()> { - let source_ids = get_source_id(config, None, reg)?; - let reg_cfg = auth::registry_credential_config(config, &source_ids.original)?; - - let login_url = match registry(config, token.clone(), None, reg, false, None) { - Ok((registry, _)) => Some(format!("{}/me", registry.host())), - Err(e) if e.is::<AuthorizationError>() => e - .downcast::<AuthorizationError>() - .unwrap() - .login_url - .map(|u| u.to_string()), - Err(e) => return Err(e), - }; - let new_token; - if generate_keypair || secret_key_required || key_subject.is_some() { - if !config.cli_unstable().registry_auth { - let flag = if generate_keypair { - "generate-keypair" - } else if secret_key_required { - "secret-key" - } else if key_subject.is_some() { - "key-subject" - } else { - unreachable!("how did we get here"); - }; - bail!( - "the `{flag}` flag is unstable, pass `-Z registry-auth` to enable it\n\ - See https://github.com/rust-lang/cargo/issues/10519 for more \ - information about the `{flag}` flag." - ); - } - assert!(token.is_none()); - // we are dealing with asymmetric tokens - let (old_secret_key, old_key_subject) = match ®_cfg { - RegistryCredentialConfig::AsymmetricKey((old_secret_key, old_key_subject)) => { - (Some(old_secret_key), old_key_subject.clone()) - } - _ => (None, None), - }; - let secret_key: Secret<String>; - if generate_keypair { - assert!(!secret_key_required); - let kp = AsymmetricKeyPair::<pasetors::version3::V3>::generate().unwrap(); - secret_key = Secret::default().map(|mut key| { - FormatAsPaserk::fmt(&kp.secret, &mut key).unwrap(); - key - }); - } else if secret_key_required { - assert!(!generate_keypair); - drop_println!(config, "please paste the API secret key below"); - secret_key = Secret::default() - .map(|mut line| { - let input = io::stdin(); - input - .lock() - .read_line(&mut line) - .with_context(|| "failed to read stdin") - .map(|_| line.trim().to_string()) - }) - .transpose()?; - } else { - secret_key = old_secret_key - .cloned() - .ok_or_else(|| anyhow!("need a secret_key to set a key_subject"))?; - } - if let Some(p) = paserk_public_from_paserk_secret(secret_key.as_deref()) { - drop_println!(config, "{}", &p); - } else { - bail!("not a validly formatted PASERK secret key"); - } - new_token = RegistryCredentialConfig::AsymmetricKey(( - secret_key, - match key_subject { - Some(key_subject) => Some(key_subject.to_string()), - None => old_key_subject, - }, - )); - } else { - new_token = RegistryCredentialConfig::Token(match token { - Some(token) => token.owned(), - None => { - if let Some(login_url) = login_url { - drop_println!( - config, - "please paste the token found on {} below", - login_url - ) - } else { - drop_println!( - config, - "please paste the token for {} below", - source_ids.original.display_registry_name() - ) - } - - let mut line = String::new(); - let input = io::stdin(); - input - .lock() - .read_line(&mut line) - .with_context(|| "failed to read stdin")?; - // Automatically remove `cargo login` from an inputted token to - // allow direct pastes from `registry.host()`/me. - Secret::from(line.replace("cargo login", "").trim().to_string()) - } - }); - - if let Some(tok) = new_token.as_token() { - crates_io::check_token(tok.as_ref().expose())?; - } - } - if ®_cfg == &new_token { - config.shell().status("Login", "already logged in")?; - return Ok(()); - } - - auth::login(config, &source_ids.original, new_token)?; - - config.shell().status( - "Login", - format!("token for `{}` saved", reg.unwrap_or(CRATES_IO_DOMAIN)), - )?; - Ok(()) -} - -pub fn registry_logout(config: &Config, reg: Option<&str>) -> CargoResult<()> { - let source_ids = get_source_id(config, None, reg)?; - let reg_cfg = auth::registry_credential_config(config, &source_ids.original)?; - let reg_name = source_ids.original.display_registry_name(); - if reg_cfg.is_none() { - config.shell().status( - "Logout", - format!("not currently logged in to `{}`", reg_name), - )?; - return Ok(()); - } - auth::logout(config, &source_ids.original)?; - config.shell().status( - "Logout", - format!( - "token for `{}` has been removed from local storage", - reg_name - ), - )?; - let location = if source_ids.original.is_crates_io() { - "<https://crates.io/me>".to_string() - } else { - // The URL for the source requires network access to load the config. - // That could be a fairly heavy operation to perform just to provide a - // help message, so for now this just provides some generic text. - // Perhaps in the future this could have an API to fetch the config if - // it is cached, but avoid network access otherwise? - format!("the `{reg_name}` website") - }; - config.shell().note(format!( - "This does not revoke the token on the registry server.\n \ - If you need to revoke the token, visit {location} and follow the instructions there." - ))?; - Ok(()) -} - -pub struct OwnersOptions { - pub krate: Option<String>, - pub token: Option<Secret<String>>, - pub index: Option<String>, - pub to_add: Option<Vec<String>>, - pub to_remove: Option<Vec<String>>, - pub list: bool, - pub registry: Option<String>, -} - -pub fn modify_owners(config: &Config, opts: &OwnersOptions) -> CargoResult<()> { - let name = match opts.krate { - Some(ref name) => name.clone(), - None => { - let manifest_path = find_root_manifest_for_wd(config.cwd())?; - let ws = Workspace::new(&manifest_path, config)?; - ws.current()?.package_id().name().to_string() - } - }; - - let mutation = auth::Mutation::Owners { name: &name }; - - let (mut registry, _) = registry( - config, - opts.token.as_ref().map(Secret::as_deref), - opts.index.as_deref(), - opts.registry.as_deref(), - true, - Some(mutation), - )?; - - if let Some(ref v) = opts.to_add { - let v = v.iter().map(|s| &s[..]).collect::<Vec<_>>(); - let msg = registry.add_owners(&name, &v).with_context(|| { - format!( - "failed to invite owners to crate `{}` on registry at {}", - name, - registry.host() - ) - })?; - - config.shell().status("Owner", msg)?; - } - - if let Some(ref v) = opts.to_remove { - let v = v.iter().map(|s| &s[..]).collect::<Vec<_>>(); - config - .shell() - .status("Owner", format!("removing {:?} from crate {}", v, name))?; - registry.remove_owners(&name, &v).with_context(|| { - format!( - "failed to remove owners from crate `{}` on registry at {}", - name, - registry.host() - ) - })?; - } - - if opts.list { - let owners = registry.list_owners(&name).with_context(|| { - format!( - "failed to list owners of crate `{}` on registry at {}", - name, - registry.host() - ) - })?; - for owner in owners.iter() { - drop_print!(config, "{}", owner.login); - match (owner.name.as_ref(), owner.email.as_ref()) { - (Some(name), Some(email)) => drop_println!(config, " ({} <{}>)", name, email), - (Some(s), None) | (None, Some(s)) => drop_println!(config, " ({})", s), - (None, None) => drop_println!(config), - } - } - } - - Ok(()) -} - -pub fn yank( - config: &Config, - krate: Option<String>, - version: Option<String>, - token: Option<Secret<String>>, - index: Option<String>, - undo: bool, - reg: Option<String>, -) -> CargoResult<()> { - let name = match krate { - Some(name) => name, - None => { - let manifest_path = find_root_manifest_for_wd(config.cwd())?; - let ws = Workspace::new(&manifest_path, config)?; - ws.current()?.package_id().name().to_string() - } - }; - let version = match version { - Some(v) => v, - None => bail!("a version must be specified to yank"), - }; - - let message = if undo { - auth::Mutation::Unyank { - name: &name, - vers: &version, - } - } else { - auth::Mutation::Yank { - name: &name, - vers: &version, - } - }; - - let (mut registry, _) = registry( - config, - token.as_ref().map(Secret::as_deref), - index.as_deref(), - reg.as_deref(), - true, - Some(message), - )?; - - let package_spec = format!("{}@{}", name, version); - if undo { - config.shell().status("Unyank", package_spec)?; - registry.unyank(&name, &version).with_context(|| { - format!( - "failed to undo a yank from the registry at {}", - registry.host() - ) - })?; - } else { - config.shell().status("Yank", package_spec)?; - registry - .yank(&name, &version) - .with_context(|| format!("failed to yank from the registry at {}", registry.host()))?; - } - - Ok(()) -} - -/// Gets the SourceId for an index or registry setting. -/// -/// The `index` and `reg` values are from the command-line or config settings. -/// If both are None, and no source-replacement is configured, returns the source for crates.io. -/// If both are None, and source replacement is configured, returns an error. -/// -/// The source for crates.io may be GitHub, index.crates.io, or a test-only registry depending -/// on configuration. -/// -/// If `reg` is set, source replacement is not followed. -/// -/// The return value is a pair of `SourceId`s: The first may be a built-in replacement of -/// crates.io (such as index.crates.io), while the second is always the original source. -fn get_source_id( - config: &Config, - index: Option<&str>, - reg: Option<&str>, -) -> CargoResult<RegistrySourceIds> { - let sid = match (reg, index) { - (None, None) => SourceId::crates_io(config)?, - (_, Some(i)) => SourceId::for_registry(&i.into_url()?)?, - (Some(r), None) => SourceId::alt_registry(config, r)?, - }; - // Load source replacements that are built-in to Cargo. - let builtin_replacement_sid = SourceConfigMap::empty(config)? - .load(sid, &HashSet::new())? - .replaced_source_id(); - let replacement_sid = SourceConfigMap::new(config)? - .load(sid, &HashSet::new())? - .replaced_source_id(); - if reg.is_none() && index.is_none() && replacement_sid != builtin_replacement_sid { - // Neither --registry nor --index was passed and the user has configured source-replacement. - if let Some(replacement_name) = replacement_sid.alt_registry_key() { - bail!("crates-io is replaced with remote registry {replacement_name};\ninclude `--registry {replacement_name}` or `--registry crates-io`"); - } else { - bail!("crates-io is replaced with non-remote-registry source {replacement_sid};\ninclude `--registry crates-io` to use crates.io"); - } - } else { - Ok(RegistrySourceIds { - original: sid, - replacement: builtin_replacement_sid, - }) - } -} - -struct RegistrySourceIds { - /// Use when looking up the auth token, or writing out `Cargo.lock` - original: SourceId, - /// Use when interacting with the source (querying / publishing , etc) - /// - /// The source for crates.io may be replaced by a built-in source for accessing crates.io with - /// the sparse protocol, or a source for the testing framework (when the replace_crates_io - /// function is used) - /// - /// User-defined source replacement is not applied. - replacement: SourceId, -} - -pub fn search( - query: &str, - config: &Config, - index: Option<String>, - limit: u32, - reg: Option<String>, -) -> CargoResult<()> { - let (mut registry, source_ids) = - registry(config, None, index.as_deref(), reg.as_deref(), false, None)?; - let (crates, total_crates) = registry.search(query, limit).with_context(|| { - format!( - "failed to retrieve search results from the registry at {}", - registry.host() - ) - })?; - - let names = crates - .iter() - .map(|krate| format!("{} = \"{}\"", krate.name, krate.max_version)) - .collect::<Vec<String>>(); - - let description_margin = names.iter().map(|s| s.len() + 4).max().unwrap_or_default(); - - let description_length = cmp::max(80, 128 - description_margin); - - let descriptions = crates.iter().map(|krate| { - krate - .description - .as_ref() - .map(|desc| truncate_with_ellipsis(&desc.replace("\n", " "), description_length)) - }); - - for (name, description) in names.into_iter().zip(descriptions) { - let line = match description { - Some(desc) => { - let space = repeat(' ') - .take(description_margin - name.len()) - .collect::<String>(); - name + &space + "# " + &desc - } - None => name, - }; - let mut fragments = line.split(query).peekable(); - while let Some(fragment) = fragments.next() { - let _ = config.shell().write_stdout(fragment, &ColorSpec::new()); - if fragments.peek().is_some() { - let _ = config - .shell() - .write_stdout(query, &ColorSpec::new().set_bold(true).set_fg(Some(Green))); - } - } - let _ = config.shell().write_stdout("\n", &ColorSpec::new()); - } - - let search_max_limit = 100; - if total_crates > limit && limit < search_max_limit { - let _ = config.shell().write_stdout( - format_args!( - "... and {} crates more (use --limit N to see more)\n", - total_crates - limit - ), - &ColorSpec::new(), - ); - } else if total_crates > limit && limit >= search_max_limit { - let extra = if source_ids.original.is_crates_io() { - let url = Url::parse_with_params("https://crates.io/search", &[("q", query)])?; - format!(" (go to {url} to see more)") - } else { - String::new() - }; - let _ = config.shell().write_stdout( - format_args!("... and {} crates more{}\n", total_crates - limit, extra), - &ColorSpec::new(), - ); - } - - Ok(()) -} diff --git a/src/tools/cargo/src/cargo/ops/registry/login.rs b/src/tools/cargo/src/cargo/ops/registry/login.rs new file mode 100644 index 000000000..1e2b3a87b --- /dev/null +++ b/src/tools/cargo/src/cargo/ops/registry/login.rs @@ -0,0 +1,160 @@ +//! Interacts with the registry [login API][1]. +//! +//! This doesn't really call any web API at this moment. Instead, it's just an +//! operation for `cargo login`. +//! +//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html#login + +use std::io; +use std::io::BufRead; + +use anyhow::anyhow; +use anyhow::bail; +use anyhow::Context as _; +use pasetors::keys::AsymmetricKeyPair; +use pasetors::keys::Generate as _; +use pasetors::paserk::FormatAsPaserk; + +use crate::drop_println; +use crate::ops::RegistryCredentialConfig; +use crate::sources::CRATES_IO_DOMAIN; +use crate::util::auth; +use crate::util::auth::paserk_public_from_paserk_secret; +use crate::util::auth::AuthorizationError; +use crate::util::auth::Secret; +use crate::CargoResult; +use crate::Config; + +use super::get_source_id; + +pub fn registry_login( + config: &Config, + token: Option<Secret<&str>>, + reg: Option<&str>, + generate_keypair: bool, + secret_key_required: bool, + key_subject: Option<&str>, +) -> CargoResult<()> { + let source_ids = get_source_id(config, None, reg)?; + let reg_cfg = auth::registry_credential_config(config, &source_ids.original)?; + + let login_url = match super::registry(config, token.clone(), None, reg, false, None) { + Ok((registry, _)) => Some(format!("{}/me", registry.host())), + Err(e) if e.is::<AuthorizationError>() => e + .downcast::<AuthorizationError>() + .unwrap() + .login_url + .map(|u| u.to_string()), + Err(e) => return Err(e), + }; + let new_token; + if generate_keypair || secret_key_required || key_subject.is_some() { + if !config.cli_unstable().registry_auth { + let flag = if generate_keypair { + "generate-keypair" + } else if secret_key_required { + "secret-key" + } else if key_subject.is_some() { + "key-subject" + } else { + unreachable!("how did we get here"); + }; + bail!( + "the `{flag}` flag is unstable, pass `-Z registry-auth` to enable it\n\ + See https://github.com/rust-lang/cargo/issues/10519 for more \ + information about the `{flag}` flag." + ); + } + assert!(token.is_none()); + // we are dealing with asymmetric tokens + let (old_secret_key, old_key_subject) = match ®_cfg { + RegistryCredentialConfig::AsymmetricKey((old_secret_key, old_key_subject)) => { + (Some(old_secret_key), old_key_subject.clone()) + } + _ => (None, None), + }; + let secret_key: Secret<String>; + if generate_keypair { + assert!(!secret_key_required); + let kp = AsymmetricKeyPair::<pasetors::version3::V3>::generate().unwrap(); + secret_key = Secret::default().map(|mut key| { + FormatAsPaserk::fmt(&kp.secret, &mut key).unwrap(); + key + }); + } else if secret_key_required { + assert!(!generate_keypair); + drop_println!(config, "please paste the API secret key below"); + secret_key = Secret::default() + .map(|mut line| { + let input = io::stdin(); + input + .lock() + .read_line(&mut line) + .with_context(|| "failed to read stdin") + .map(|_| line.trim().to_string()) + }) + .transpose()?; + } else { + secret_key = old_secret_key + .cloned() + .ok_or_else(|| anyhow!("need a secret_key to set a key_subject"))?; + } + if let Some(p) = paserk_public_from_paserk_secret(secret_key.as_deref()) { + drop_println!(config, "{}", &p); + } else { + bail!("not a validly formatted PASERK secret key"); + } + new_token = RegistryCredentialConfig::AsymmetricKey(( + secret_key, + match key_subject { + Some(key_subject) => Some(key_subject.to_string()), + None => old_key_subject, + }, + )); + } else { + new_token = RegistryCredentialConfig::Token(match token { + Some(token) => token.owned(), + None => { + if let Some(login_url) = login_url { + drop_println!( + config, + "please paste the token found on {} below", + login_url + ) + } else { + drop_println!( + config, + "please paste the token for {} below", + source_ids.original.display_registry_name() + ) + } + + let mut line = String::new(); + let input = io::stdin(); + input + .lock() + .read_line(&mut line) + .with_context(|| "failed to read stdin")?; + // Automatically remove `cargo login` from an inputted token to + // allow direct pastes from `registry.host()`/me. + Secret::from(line.replace("cargo login", "").trim().to_string()) + } + }); + + if let Some(tok) = new_token.as_token() { + crates_io::check_token(tok.as_ref().expose())?; + } + } + if ®_cfg == &new_token { + config.shell().status("Login", "already logged in")?; + return Ok(()); + } + + auth::login(config, &source_ids.original, new_token)?; + + config.shell().status( + "Login", + format!("token for `{}` saved", reg.unwrap_or(CRATES_IO_DOMAIN)), + )?; + Ok(()) +} diff --git a/src/tools/cargo/src/cargo/ops/registry/logout.rs b/src/tools/cargo/src/cargo/ops/registry/logout.rs new file mode 100644 index 000000000..59f2d9261 --- /dev/null +++ b/src/tools/cargo/src/cargo/ops/registry/logout.rs @@ -0,0 +1,42 @@ +//! Interacts with the registry logout. +//! +//! There is no web API for logout at this moment. Instead, it's just an +//! operation for `cargo logout`. + +use crate::util::auth; +use crate::CargoResult; +use crate::Config; + +use super::get_source_id; + +pub fn registry_logout(config: &Config, reg: Option<&str>) -> CargoResult<()> { + let source_ids = get_source_id(config, None, reg)?; + let reg_cfg = auth::registry_credential_config(config, &source_ids.original)?; + let reg_name = source_ids.original.display_registry_name(); + if reg_cfg.is_none() { + config + .shell() + .status("Logout", format!("not currently logged in to `{reg_name}`"))?; + return Ok(()); + } + auth::logout(config, &source_ids.original)?; + config.shell().status( + "Logout", + format!("token for `{reg_name}` has been removed from local storage"), + )?; + let location = if source_ids.original.is_crates_io() { + "<https://crates.io/me>".to_string() + } else { + // The URL for the source requires network access to load the config. + // That could be a fairly heavy operation to perform just to provide a + // help message, so for now this just provides some generic text. + // Perhaps in the future this could have an API to fetch the config if + // it is cached, but avoid network access otherwise? + format!("the `{reg_name}` website") + }; + config.shell().note(format!( + "This does not revoke the token on the registry server.\n \ + If you need to revoke the token, visit {location} and follow the instructions there." + ))?; + Ok(()) +} diff --git a/src/tools/cargo/src/cargo/ops/registry/mod.rs b/src/tools/cargo/src/cargo/ops/registry/mod.rs new file mode 100644 index 000000000..ecb610ddd --- /dev/null +++ b/src/tools/cargo/src/cargo/ops/registry/mod.rs @@ -0,0 +1,213 @@ +//! Operations that interact with the [registry web API][1]. +//! +//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html + +mod login; +mod logout; +mod owner; +mod publish; +mod search; +mod yank; + +use std::collections::HashSet; +use std::path::PathBuf; +use std::str; +use std::task::Poll; + +use anyhow::{bail, format_err, Context as _}; +use crates_io::{self, Registry}; + +use crate::core::source::Source; +use crate::core::SourceId; +use crate::sources::{RegistrySource, SourceConfigMap}; +use crate::util::auth::{self, Secret}; +use crate::util::config::Config; +use crate::util::errors::CargoResult; +use crate::util::network::http::http_handle; +use crate::util::IntoUrl; + +pub use self::login::registry_login; +pub use self::logout::registry_logout; +pub use self::owner::modify_owners; +pub use self::owner::OwnersOptions; +pub use self::publish::publish; +pub use self::publish::PublishOpts; +pub use self::search::search; +pub use self::yank::yank; + +/// Registry settings loaded from config files. +/// +/// This is loaded based on the `--registry` flag and the config settings. +#[derive(Debug, PartialEq)] +pub enum RegistryCredentialConfig { + None, + /// The authentication token. + Token(Secret<String>), + /// Process used for fetching a token. + Process((PathBuf, Vec<String>)), + /// Secret Key and subject for Asymmetric tokens. + AsymmetricKey((Secret<String>, Option<String>)), +} + +impl RegistryCredentialConfig { + /// Returns `true` if the credential is [`None`]. + /// + /// [`None`]: Self::None + pub fn is_none(&self) -> bool { + matches!(self, Self::None) + } + /// Returns `true` if the credential is [`Token`]. + /// + /// [`Token`]: Self::Token + pub fn is_token(&self) -> bool { + matches!(self, Self::Token(..)) + } + /// Returns `true` if the credential is [`AsymmetricKey`]. + /// + /// [`AsymmetricKey`]: RegistryCredentialConfig::AsymmetricKey + pub fn is_asymmetric_key(&self) -> bool { + matches!(self, Self::AsymmetricKey(..)) + } + pub fn as_token(&self) -> Option<Secret<&str>> { + if let Self::Token(v) = self { + Some(v.as_deref()) + } else { + None + } + } + pub fn as_process(&self) -> Option<&(PathBuf, Vec<String>)> { + if let Self::Process(v) = self { + Some(v) + } else { + None + } + } + pub fn as_asymmetric_key(&self) -> Option<&(Secret<String>, Option<String>)> { + if let Self::AsymmetricKey(v) = self { + Some(v) + } else { + None + } + } +} + +/// Returns the `Registry` and `Source` based on command-line and config settings. +/// +/// * `token_from_cmdline`: The token from the command-line. If not set, uses the token +/// from the config. +/// * `index`: The index URL from the command-line. +/// * `registry`: The registry name from the command-line. If neither +/// `registry`, or `index` are set, then uses `crates-io`. +/// * `force_update`: If `true`, forces the index to be updated. +/// * `token_required`: If `true`, the token will be set. +fn registry( + config: &Config, + token_from_cmdline: Option<Secret<&str>>, + index: Option<&str>, + registry: Option<&str>, + force_update: bool, + token_required: Option<auth::Mutation<'_>>, +) -> CargoResult<(Registry, RegistrySourceIds)> { + let source_ids = get_source_id(config, index, registry)?; + + if token_required.is_some() && index.is_some() && token_from_cmdline.is_none() { + bail!("command-line argument --index requires --token to be specified"); + } + if let Some(token) = token_from_cmdline { + auth::cache_token(config, &source_ids.original, token); + } + + let cfg = { + let _lock = config.acquire_package_cache_lock()?; + let mut src = RegistrySource::remote(source_ids.replacement, &HashSet::new(), config)?; + // Only update the index if `force_update` is set. + if force_update { + src.invalidate_cache() + } + let cfg = loop { + match src.config()? { + Poll::Pending => src + .block_until_ready() + .with_context(|| format!("failed to update {}", source_ids.replacement))?, + Poll::Ready(cfg) => break cfg, + } + }; + cfg.expect("remote registries must have config") + }; + let api_host = cfg + .api + .ok_or_else(|| format_err!("{} does not support API commands", source_ids.replacement))?; + let token = if token_required.is_some() || cfg.auth_required { + Some(auth::auth_token( + config, + &source_ids.original, + None, + token_required, + )?) + } else { + None + }; + let handle = http_handle(config)?; + Ok(( + Registry::new_handle(api_host, token, handle, cfg.auth_required), + source_ids, + )) +} + +/// Gets the SourceId for an index or registry setting. +/// +/// The `index` and `reg` values are from the command-line or config settings. +/// If both are None, and no source-replacement is configured, returns the source for crates.io. +/// If both are None, and source replacement is configured, returns an error. +/// +/// The source for crates.io may be GitHub, index.crates.io, or a test-only registry depending +/// on configuration. +/// +/// If `reg` is set, source replacement is not followed. +/// +/// The return value is a pair of `SourceId`s: The first may be a built-in replacement of +/// crates.io (such as index.crates.io), while the second is always the original source. +fn get_source_id( + config: &Config, + index: Option<&str>, + reg: Option<&str>, +) -> CargoResult<RegistrySourceIds> { + let sid = match (reg, index) { + (None, None) => SourceId::crates_io(config)?, + (_, Some(i)) => SourceId::for_registry(&i.into_url()?)?, + (Some(r), None) => SourceId::alt_registry(config, r)?, + }; + // Load source replacements that are built-in to Cargo. + let builtin_replacement_sid = SourceConfigMap::empty(config)? + .load(sid, &HashSet::new())? + .replaced_source_id(); + let replacement_sid = SourceConfigMap::new(config)? + .load(sid, &HashSet::new())? + .replaced_source_id(); + if reg.is_none() && index.is_none() && replacement_sid != builtin_replacement_sid { + // Neither --registry nor --index was passed and the user has configured source-replacement. + if let Some(replacement_name) = replacement_sid.alt_registry_key() { + bail!("crates-io is replaced with remote registry {replacement_name};\ninclude `--registry {replacement_name}` or `--registry crates-io`"); + } else { + bail!("crates-io is replaced with non-remote-registry source {replacement_sid};\ninclude `--registry crates-io` to use crates.io"); + } + } else { + Ok(RegistrySourceIds { + original: sid, + replacement: builtin_replacement_sid, + }) + } +} + +struct RegistrySourceIds { + /// Use when looking up the auth token, or writing out `Cargo.lock` + original: SourceId, + /// Use when interacting with the source (querying / publishing , etc) + /// + /// The source for crates.io may be replaced by a built-in source for accessing crates.io with + /// the sparse protocol, or a source for the testing framework (when the replace_crates_io + /// function is used) + /// + /// User-defined source replacement is not applied. + replacement: SourceId, +} diff --git a/src/tools/cargo/src/cargo/ops/registry/owner.rs b/src/tools/cargo/src/cargo/ops/registry/owner.rs new file mode 100644 index 000000000..e53e07cb8 --- /dev/null +++ b/src/tools/cargo/src/cargo/ops/registry/owner.rs @@ -0,0 +1,93 @@ +//! Interacts with the registry [owners API][1]. +//! +//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html#owners + +use anyhow::Context as _; + +use crate::core::Workspace; +use crate::drop_print; +use crate::drop_println; +use crate::util::auth; +use crate::util::auth::Secret; +use crate::util::important_paths::find_root_manifest_for_wd; +use crate::CargoResult; +use crate::Config; + +pub struct OwnersOptions { + pub krate: Option<String>, + pub token: Option<Secret<String>>, + pub index: Option<String>, + pub to_add: Option<Vec<String>>, + pub to_remove: Option<Vec<String>>, + pub list: bool, + pub registry: Option<String>, +} + +pub fn modify_owners(config: &Config, opts: &OwnersOptions) -> CargoResult<()> { + let name = match opts.krate { + Some(ref name) => name.clone(), + None => { + let manifest_path = find_root_manifest_for_wd(config.cwd())?; + let ws = Workspace::new(&manifest_path, config)?; + ws.current()?.package_id().name().to_string() + } + }; + + let mutation = auth::Mutation::Owners { name: &name }; + + let (mut registry, _) = super::registry( + config, + opts.token.as_ref().map(Secret::as_deref), + opts.index.as_deref(), + opts.registry.as_deref(), + true, + Some(mutation), + )?; + + if let Some(ref v) = opts.to_add { + let v = v.iter().map(|s| &s[..]).collect::<Vec<_>>(); + let msg = registry.add_owners(&name, &v).with_context(|| { + format!( + "failed to invite owners to crate `{}` on registry at {}", + name, + registry.host() + ) + })?; + + config.shell().status("Owner", msg)?; + } + + if let Some(ref v) = opts.to_remove { + let v = v.iter().map(|s| &s[..]).collect::<Vec<_>>(); + config + .shell() + .status("Owner", format!("removing {:?} from crate {}", v, name))?; + registry.remove_owners(&name, &v).with_context(|| { + format!( + "failed to remove owners from crate `{}` on registry at {}", + name, + registry.host() + ) + })?; + } + + if opts.list { + let owners = registry.list_owners(&name).with_context(|| { + format!( + "failed to list owners of crate `{}` on registry at {}", + name, + registry.host() + ) + })?; + for owner in owners.iter() { + drop_print!(config, "{}", owner.login); + match (owner.name.as_ref(), owner.email.as_ref()) { + (Some(name), Some(email)) => drop_println!(config, " ({} <{}>)", name, email), + (Some(s), None) | (None, Some(s)) => drop_println!(config, " ({})", s), + (None, None) => drop_println!(config), + } + } + } + + Ok(()) +} diff --git a/src/tools/cargo/src/cargo/ops/registry/publish.rs b/src/tools/cargo/src/cargo/ops/registry/publish.rs new file mode 100644 index 000000000..7f4fbbae2 --- /dev/null +++ b/src/tools/cargo/src/cargo/ops/registry/publish.rs @@ -0,0 +1,461 @@ +//! Interacts with the registry [publish API][1]. +//! +//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html#publish + +use std::collections::BTreeMap; +use std::collections::HashSet; +use std::fs::File; +use std::time::Duration; + +use anyhow::bail; +use anyhow::Context as _; +use cargo_util::paths; +use crates_io::NewCrate; +use crates_io::NewCrateDependency; +use crates_io::Registry; + +use crate::core::dependency::DepKind; +use crate::core::manifest::ManifestMetadata; +use crate::core::resolver::CliFeatures; +use crate::core::Dependency; +use crate::core::Package; +use crate::core::QueryKind; +use crate::core::SourceId; +use crate::core::Workspace; +use crate::ops; +use crate::ops::PackageOpts; +use crate::ops::Packages; +use crate::sources::SourceConfigMap; +use crate::sources::CRATES_IO_REGISTRY; +use crate::util::auth; +use crate::util::auth::Secret; +use crate::util::config::JobsConfig; +use crate::util::Progress; +use crate::util::ProgressStyle; +use crate::CargoResult; +use crate::Config; + +use super::super::check_dep_has_version; + +pub struct PublishOpts<'cfg> { + pub config: &'cfg Config, + pub token: Option<Secret<String>>, + pub index: Option<String>, + pub verify: bool, + pub allow_dirty: bool, + pub jobs: Option<JobsConfig>, + pub keep_going: bool, + pub to_publish: ops::Packages, + pub targets: Vec<String>, + pub dry_run: bool, + pub registry: Option<String>, + pub cli_features: CliFeatures, +} + +pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> { + let specs = opts.to_publish.to_package_id_specs(ws)?; + if specs.len() > 1 { + bail!("the `-p` argument must be specified to select a single package to publish") + } + if Packages::Default == opts.to_publish && ws.is_virtual() { + bail!("the `-p` argument must be specified in the root of a virtual workspace") + } + let member_ids = ws.members().map(|p| p.package_id()); + // Check that the spec matches exactly one member. + specs[0].query(member_ids)?; + let mut pkgs = ws.members_with_features(&specs, &opts.cli_features)?; + // In `members_with_features_old`, it will add "current" package (determined by the cwd) + // So we need filter + pkgs = pkgs + .into_iter() + .filter(|(m, _)| specs.iter().any(|spec| spec.matches(m.package_id()))) + .collect(); + // Double check. It is safe theoretically, unless logic has updated. + assert_eq!(pkgs.len(), 1); + + let (pkg, cli_features) = pkgs.pop().unwrap(); + + let mut publish_registry = opts.registry.clone(); + if let Some(ref allowed_registries) = *pkg.publish() { + if publish_registry.is_none() && allowed_registries.len() == 1 { + // If there is only one allowed registry, push to that one directly, + // even though there is no registry specified in the command. + let default_registry = &allowed_registries[0]; + if default_registry != CRATES_IO_REGISTRY { + // Don't change the registry for crates.io and don't warn the user. + // crates.io will be defaulted even without this. + opts.config.shell().note(&format!( + "Found `{}` as only allowed registry. Publishing to it automatically.", + default_registry + ))?; + publish_registry = Some(default_registry.clone()); + } + } + + let reg_name = publish_registry + .clone() + .unwrap_or_else(|| CRATES_IO_REGISTRY.to_string()); + if allowed_registries.is_empty() { + bail!( + "`{}` cannot be published.\n\ + `package.publish` is set to `false` or an empty list in Cargo.toml and prevents publishing.", + pkg.name(), + ); + } else if !allowed_registries.contains(®_name) { + bail!( + "`{}` cannot be published.\n\ + The registry `{}` is not listed in the `package.publish` value in Cargo.toml.", + pkg.name(), + reg_name + ); + } + } + // This is only used to confirm that we can create a token before we build the package. + // This causes the credential provider to be called an extra time, but keeps the same order of errors. + let ver = pkg.version().to_string(); + let mutation = auth::Mutation::PrePublish; + + let (mut registry, reg_ids) = super::registry( + opts.config, + opts.token.as_ref().map(Secret::as_deref), + opts.index.as_deref(), + publish_registry.as_deref(), + true, + Some(mutation).filter(|_| !opts.dry_run), + )?; + verify_dependencies(pkg, ®istry, reg_ids.original)?; + + // Prepare a tarball, with a non-suppressible warning if metadata + // is missing since this is being put online. + let tarball = ops::package_one( + ws, + pkg, + &PackageOpts { + config: opts.config, + verify: opts.verify, + list: false, + check_metadata: true, + allow_dirty: opts.allow_dirty, + to_package: Packages::Default, + targets: opts.targets.clone(), + jobs: opts.jobs.clone(), + keep_going: opts.keep_going, + cli_features, + }, + )? + .unwrap(); + + if !opts.dry_run { + let hash = cargo_util::Sha256::new() + .update_file(tarball.file())? + .finish_hex(); + let mutation = Some(auth::Mutation::Publish { + name: pkg.name().as_str(), + vers: &ver, + cksum: &hash, + }); + registry.set_token(Some(auth::auth_token( + &opts.config, + ®_ids.original, + None, + mutation, + )?)); + } + + opts.config + .shell() + .status("Uploading", pkg.package_id().to_string())?; + transmit( + opts.config, + pkg, + tarball.file(), + &mut registry, + reg_ids.original, + opts.dry_run, + )?; + if !opts.dry_run { + const DEFAULT_TIMEOUT: u64 = 60; + let timeout = if opts.config.cli_unstable().publish_timeout { + let timeout: Option<u64> = opts.config.get("publish.timeout")?; + timeout.unwrap_or(DEFAULT_TIMEOUT) + } else { + DEFAULT_TIMEOUT + }; + if 0 < timeout { + let timeout = Duration::from_secs(timeout); + wait_for_publish(opts.config, reg_ids.original, pkg, timeout)?; + } + } + + Ok(()) +} + +fn wait_for_publish( + config: &Config, + registry_src: SourceId, + pkg: &Package, + timeout: Duration, +) -> CargoResult<()> { + let version_req = format!("={}", pkg.version()); + let mut source = SourceConfigMap::empty(config)?.load(registry_src, &HashSet::new())?; + // Disable the source's built-in progress bars. Repeatedly showing a bunch + // of independent progress bars can be a little confusing. There is an + // overall progress bar managed here. + source.set_quiet(true); + let source_description = source.source_id().to_string(); + let query = Dependency::parse(pkg.name(), Some(&version_req), registry_src)?; + + let now = std::time::Instant::now(); + let sleep_time = Duration::from_secs(1); + let max = timeout.as_secs() as usize; + // Short does not include the registry name. + let short_pkg_description = format!("{} v{}", pkg.name(), pkg.version()); + config.shell().status( + "Uploaded", + format!("{short_pkg_description} to {source_description}"), + )?; + config.shell().note(format!( + "Waiting for `{short_pkg_description}` to be available at {source_description}.\n\ + You may press ctrl-c to skip waiting; the crate should be available shortly." + ))?; + let mut progress = Progress::with_style("Waiting", ProgressStyle::Ratio, config); + progress.tick_now(0, max, "")?; + let is_available = loop { + { + let _lock = config.acquire_package_cache_lock()?; + // Force re-fetching the source + // + // As pulling from a git source is expensive, we track when we've done it within the + // process to only do it once, but we are one of the rare cases that needs to do it + // multiple times + config + .updated_sources() + .remove(&source.replaced_source_id()); + source.invalidate_cache(); + let summaries = loop { + // Exact to avoid returning all for path/git + match source.query_vec(&query, QueryKind::Exact) { + std::task::Poll::Ready(res) => { + break res?; + } + std::task::Poll::Pending => source.block_until_ready()?, + } + }; + if !summaries.is_empty() { + break true; + } + } + + let elapsed = now.elapsed(); + if timeout < elapsed { + config.shell().warn(format!( + "timed out waiting for `{short_pkg_description}` to be available in {source_description}", + ))?; + config.shell().note( + "The registry may have a backlog that is delaying making the \ + crate available. The crate should be available soon.", + )?; + break false; + } + + progress.tick_now(elapsed.as_secs() as usize, max, "")?; + std::thread::sleep(sleep_time); + }; + if is_available { + config.shell().status( + "Published", + format!("{short_pkg_description} at {source_description}"), + )?; + } + + Ok(()) +} + +fn verify_dependencies( + pkg: &Package, + registry: &Registry, + registry_src: SourceId, +) -> CargoResult<()> { + for dep in pkg.dependencies().iter() { + if check_dep_has_version(dep, true)? { + continue; + } + // TomlManifest::prepare_for_publish will rewrite the dependency + // to be just the `version` field. + if dep.source_id() != registry_src { + if !dep.source_id().is_registry() { + // Consider making SourceId::kind a public type that we can + // exhaustively match on. Using match can help ensure that + // every kind is properly handled. + panic!("unexpected source kind for dependency {:?}", dep); + } + // Block requests to send to crates.io with alt-registry deps. + // This extra hostname check is mostly to assist with testing, + // but also prevents someone using `--index` to specify + // something that points to crates.io. + if registry_src.is_crates_io() || registry.host_is_crates_io() { + bail!("crates cannot be published to crates.io with dependencies sourced from other\n\ + registries. `{}` needs to be published to crates.io before publishing this crate.\n\ + (crate `{}` is pulled from {})", + dep.package_name(), + dep.package_name(), + dep.source_id()); + } + } + } + Ok(()) +} + +fn transmit( + config: &Config, + pkg: &Package, + tarball: &File, + registry: &mut Registry, + registry_id: SourceId, + dry_run: bool, +) -> CargoResult<()> { + let deps = pkg + .dependencies() + .iter() + .filter(|dep| { + // Skip dev-dependency without version. + dep.is_transitive() || dep.specified_req() + }) + .map(|dep| { + // If the dependency is from a different registry, then include the + // registry in the dependency. + let dep_registry_id = match dep.registry_id() { + Some(id) => id, + None => SourceId::crates_io(config)?, + }; + // In the index and Web API, None means "from the same registry" + // whereas in Cargo.toml, it means "from crates.io". + let dep_registry = if dep_registry_id != registry_id { + Some(dep_registry_id.url().to_string()) + } else { + None + }; + + Ok(NewCrateDependency { + optional: dep.is_optional(), + default_features: dep.uses_default_features(), + name: dep.package_name().to_string(), + features: dep.features().iter().map(|s| s.to_string()).collect(), + version_req: dep.version_req().to_string(), + target: dep.platform().map(|s| s.to_string()), + kind: match dep.kind() { + DepKind::Normal => "normal", + DepKind::Build => "build", + DepKind::Development => "dev", + } + .to_string(), + registry: dep_registry, + explicit_name_in_toml: dep.explicit_name_in_toml().map(|s| s.to_string()), + }) + }) + .collect::<CargoResult<Vec<NewCrateDependency>>>()?; + let manifest = pkg.manifest(); + let ManifestMetadata { + ref authors, + ref description, + ref homepage, + ref documentation, + ref keywords, + ref readme, + ref repository, + ref license, + ref license_file, + ref categories, + ref badges, + ref links, + ref rust_version, + } = *manifest.metadata(); + let readme_content = readme + .as_ref() + .map(|readme| { + paths::read(&pkg.root().join(readme)) + .with_context(|| format!("failed to read `readme` file for package `{}`", pkg)) + }) + .transpose()?; + if let Some(ref file) = *license_file { + if !pkg.root().join(file).exists() { + bail!("the license file `{}` does not exist", file) + } + } + + // Do not upload if performing a dry run + if dry_run { + config.shell().warn("aborting upload due to dry run")?; + return Ok(()); + } + + let string_features = match manifest.original().features() { + Some(features) => features + .iter() + .map(|(feat, values)| { + ( + feat.to_string(), + values.iter().map(|fv| fv.to_string()).collect(), + ) + }) + .collect::<BTreeMap<String, Vec<String>>>(), + None => BTreeMap::new(), + }; + + let warnings = registry + .publish( + &NewCrate { + name: pkg.name().to_string(), + vers: pkg.version().to_string(), + deps, + features: string_features, + authors: authors.clone(), + description: description.clone(), + homepage: homepage.clone(), + documentation: documentation.clone(), + keywords: keywords.clone(), + categories: categories.clone(), + readme: readme_content, + readme_file: readme.clone(), + repository: repository.clone(), + license: license.clone(), + license_file: license_file.clone(), + badges: badges.clone(), + links: links.clone(), + rust_version: rust_version.clone(), + }, + tarball, + ) + .with_context(|| format!("failed to publish to registry at {}", registry.host()))?; + + if !warnings.invalid_categories.is_empty() { + let msg = format!( + "the following are not valid category slugs and were \ + ignored: {}. Please see https://crates.io/category_slugs \ + for the list of all category slugs. \ + ", + warnings.invalid_categories.join(", ") + ); + config.shell().warn(&msg)?; + } + + if !warnings.invalid_badges.is_empty() { + let msg = format!( + "the following are not valid badges and were ignored: {}. \ + Either the badge type specified is unknown or a required \ + attribute is missing. Please see \ + https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata \ + for valid badge types and their required attributes.", + warnings.invalid_badges.join(", ") + ); + config.shell().warn(&msg)?; + } + + if !warnings.other.is_empty() { + for msg in warnings.other { + config.shell().warn(&msg)?; + } + } + + Ok(()) +} diff --git a/src/tools/cargo/src/cargo/ops/registry/search.rs b/src/tools/cargo/src/cargo/ops/registry/search.rs new file mode 100644 index 000000000..5e01ea98f --- /dev/null +++ b/src/tools/cargo/src/cargo/ops/registry/search.rs @@ -0,0 +1,95 @@ +//! Interacts with the registry [search API][1]. +//! +//! [1]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html#search + +use std::cmp; +use std::iter::repeat; + +use anyhow::Context as _; +use termcolor::Color; +use termcolor::ColorSpec; +use url::Url; + +use crate::util::truncate_with_ellipsis; +use crate::CargoResult; +use crate::Config; + +pub fn search( + query: &str, + config: &Config, + index: Option<String>, + limit: u32, + reg: Option<String>, +) -> CargoResult<()> { + let (mut registry, source_ids) = + super::registry(config, None, index.as_deref(), reg.as_deref(), false, None)?; + let (crates, total_crates) = registry.search(query, limit).with_context(|| { + format!( + "failed to retrieve search results from the registry at {}", + registry.host() + ) + })?; + + let names = crates + .iter() + .map(|krate| format!("{} = \"{}\"", krate.name, krate.max_version)) + .collect::<Vec<String>>(); + + let description_margin = names.iter().map(|s| s.len() + 4).max().unwrap_or_default(); + + let description_length = cmp::max(80, 128 - description_margin); + + let descriptions = crates.iter().map(|krate| { + krate + .description + .as_ref() + .map(|desc| truncate_with_ellipsis(&desc.replace("\n", " "), description_length)) + }); + + for (name, description) in names.into_iter().zip(descriptions) { + let line = match description { + Some(desc) => { + let space = repeat(' ') + .take(description_margin - name.len()) + .collect::<String>(); + name + &space + "# " + &desc + } + None => name, + }; + let mut fragments = line.split(query).peekable(); + while let Some(fragment) = fragments.next() { + let _ = config.shell().write_stdout(fragment, &ColorSpec::new()); + if fragments.peek().is_some() { + let _ = config.shell().write_stdout( + query, + &ColorSpec::new().set_bold(true).set_fg(Some(Color::Green)), + ); + } + } + let _ = config.shell().write_stdout("\n", &ColorSpec::new()); + } + + let search_max_limit = 100; + if total_crates > limit && limit < search_max_limit { + let _ = config.shell().write_stdout( + format_args!( + "... and {} crates more (use --limit N to see more)\n", + total_crates - limit + ), + &ColorSpec::new(), + ); + } else if total_crates > limit && limit >= search_max_limit { + let extra = if source_ids.original.is_crates_io() { + let url = Url::parse_with_params("https://crates.io/search", &[("q", query)])?; + format!(" (go to {url} to see more)") + } else { + String::new() + }; + let _ = config.shell().write_stdout( + format_args!("... and {} crates more{}\n", total_crates - limit, extra), + &ColorSpec::new(), + ); + } + + Ok(()) +} diff --git a/src/tools/cargo/src/cargo/ops/registry/yank.rs b/src/tools/cargo/src/cargo/ops/registry/yank.rs new file mode 100644 index 000000000..7f087570a --- /dev/null +++ b/src/tools/cargo/src/cargo/ops/registry/yank.rs @@ -0,0 +1,76 @@ +//! Interacts with the registry [yank] and [unyank] API. +//! +//! [yank]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html#yank +//! [unyank]: https://doc.rust-lang.org/nightly/cargo/reference/registry-web-api.html#unyank + +use anyhow::bail; +use anyhow::Context as _; + +use crate::core::Workspace; +use crate::util::auth; +use crate::util::auth::Secret; +use crate::util::config::Config; +use crate::util::errors::CargoResult; +use crate::util::important_paths::find_root_manifest_for_wd; + +pub fn yank( + config: &Config, + krate: Option<String>, + version: Option<String>, + token: Option<Secret<String>>, + index: Option<String>, + undo: bool, + reg: Option<String>, +) -> CargoResult<()> { + let name = match krate { + Some(name) => name, + None => { + let manifest_path = find_root_manifest_for_wd(config.cwd())?; + let ws = Workspace::new(&manifest_path, config)?; + ws.current()?.package_id().name().to_string() + } + }; + let version = match version { + Some(v) => v, + None => bail!("a version must be specified to yank"), + }; + + let message = if undo { + auth::Mutation::Unyank { + name: &name, + vers: &version, + } + } else { + auth::Mutation::Yank { + name: &name, + vers: &version, + } + }; + + let (mut registry, _) = super::registry( + config, + token.as_ref().map(Secret::as_deref), + index.as_deref(), + reg.as_deref(), + true, + Some(message), + )?; + + let package_spec = format!("{}@{}", name, version); + if undo { + config.shell().status("Unyank", package_spec)?; + registry.unyank(&name, &version).with_context(|| { + format!( + "failed to undo a yank from the registry at {}", + registry.host() + ) + })?; + } else { + config.shell().status("Yank", package_spec)?; + registry + .yank(&name, &version) + .with_context(|| format!("failed to yank from the registry at {}", registry.host()))?; + } + + Ok(()) +} diff --git a/src/tools/cargo/src/cargo/sources/config.rs b/src/tools/cargo/src/cargo/sources/config.rs index 5d5a4e8db..4097567bb 100644 --- a/src/tools/cargo/src/cargo/sources/config.rs +++ b/src/tools/cargo/src/cargo/sources/config.rs @@ -14,7 +14,9 @@ use log::debug; use std::collections::{HashMap, HashSet}; use url::Url; -/// Represents the entire `[source]` table in Cargo configuration. +/// Represents the entire [`[source]` replacement table][1] in Cargo configuration. +/// +/// [1]: https://doc.rust-lang.org/nightly/cargo/reference/config.html#source #[derive(Clone)] pub struct SourceConfigMap<'cfg> { /// Mapping of source name to the toml configuration. diff --git a/src/tools/cargo/src/cargo/sources/git/known_hosts.rs b/src/tools/cargo/src/cargo/sources/git/known_hosts.rs index 9a623151e..7b013f99c 100644 --- a/src/tools/cargo/src/cargo/sources/git/known_hosts.rs +++ b/src/tools/cargo/src/cargo/sources/git/known_hosts.rs @@ -1,5 +1,8 @@ //! SSH host key validation support. //! +//! The only public item in this module is [`certificate_check`], +//! which provides a callback to [`git2::RemoteCallbacks::certificate_check`]. +//! //! A primary goal with this implementation is to provide user-friendly error //! messages, guiding them to understand the issue and how to resolve it. //! @@ -408,7 +411,7 @@ fn check_ssh_known_hosts_loaded( // fingerprints (see FingerprintHash ssh config option). Here we only // support SHA256. let mut remote_fingerprint = cargo_util::Sha256::new(); - remote_fingerprint.update(remote_host_key.clone()); + remote_fingerprint.update(remote_host_key); let remote_fingerprint = STANDARD_NO_PAD.encode(remote_fingerprint.finish()); let remote_host_key_encoded = STANDARD.encode(remote_host_key); diff --git a/src/tools/cargo/src/cargo/sources/git/mod.rs b/src/tools/cargo/src/cargo/sources/git/mod.rs index 47827f267..5d9d2886e 100644 --- a/src/tools/cargo/src/cargo/sources/git/mod.rs +++ b/src/tools/cargo/src/cargo/sources/git/mod.rs @@ -1,3 +1,12 @@ +//! Home of the [`GitSource`]. +//! +//! Apparently, the most important type in this module is [`GitSource`]. +//! [`utils`] provides libgit2 utilities like fetch and checkout, whereas +//! [`oxide`] is the couterpart for gitoxide integration. [`known_hosts`] +//! is the mitigation of [CVE-2022-46176]. +//! +//! [CVE-2022-46176]: https://blog.rust-lang.org/2023/01/10/cve-2022-46176.html + pub use self::source::GitSource; pub use self::utils::{fetch, GitCheckout, GitDatabase, GitRemote}; mod known_hosts; @@ -5,6 +14,7 @@ mod oxide; mod source; mod utils; +/// For `-Zgitoxide` integration. pub mod fetch { use crate::core::features::GitoxideFeatures; use crate::Config; diff --git a/src/tools/cargo/src/cargo/sources/git/oxide.rs b/src/tools/cargo/src/cargo/sources/git/oxide.rs index c8d0a4ecd..e86c63e8e 100644 --- a/src/tools/cargo/src/cargo/sources/git/oxide.rs +++ b/src/tools/cargo/src/cargo/sources/git/oxide.rs @@ -1,7 +1,7 @@ //! This module contains all code sporting `gitoxide` for operations on `git` repositories and it mirrors //! `utils` closely for now. One day it can be renamed into `utils` once `git2` isn't required anymore. -use crate::ops::HttpTimeout; +use crate::util::network::http::HttpTimeout; use crate::util::{human_readable_bytes, network, MetricsCounter, Progress}; use crate::{CargoResult, Config}; use cargo_util::paths; @@ -353,6 +353,8 @@ pub fn cargo_config_to_gitoxide_overrides(config: &Config) -> CargoResult<Vec<BS Ok(values) } +/// Reinitializes a given Git repository. This is useful when a Git repoistory +/// seems corrupted and we want to start over. pub fn reinitialize(git_dir: &Path) -> CargoResult<()> { fn init(path: &Path, bare: bool) -> CargoResult<()> { let mut opts = git2::RepositoryInitOptions::new(); diff --git a/src/tools/cargo/src/cargo/sources/git/source.rs b/src/tools/cargo/src/cargo/sources/git/source.rs index 4cdb8983b..b021d23a0 100644 --- a/src/tools/cargo/src/cargo/sources/git/source.rs +++ b/src/tools/cargo/src/cargo/sources/git/source.rs @@ -1,3 +1,5 @@ +//! See [GitSource]. + use crate::core::source::{MaybePackage, QueryKind, Source, SourceId}; use crate::core::GitReference; use crate::core::{Dependency, Package, PackageId, Summary}; @@ -13,18 +15,71 @@ use std::fmt::{self, Debug, Formatter}; use std::task::Poll; use url::Url; +/// `GitSource` contains one or more packages gathering from a Git repository. +/// Under the hood it uses [`PathSource`] to discover packages inside the +/// repository. +/// +/// ## Filesystem layout +/// +/// During a successful `GitSource` download, at least two Git repositories are +/// created: one is the shared Git database of this remote, and the other is the +/// Git checkout to a specific revision, which contains the actual files to be +/// compiled. Multiple checkouts can be cloned from a single Git database. +/// +/// Those repositories are located at Cargo's Git cache directory +/// `$CARGO_HOME/git`. The file tree of the cache directory roughly looks like: +/// +/// ```text +/// $CARGO_HOME/git/ +/// ├── checkouts/ +/// │ ├── gimli-a0d193bd15a5ed96/ +/// │ │ ├── 8e73ef0/ # Git short ID for a certain revision +/// │ │ ├── a2a4b78/ +/// │ │ └── e33d1ac/ +/// │ ├── log-c58e1db3de7c154d-shallow/ +/// │ │ └── 11eda98/ +/// └── db/ +/// ├── gimli-a0d193bd15a5ed96/ +/// └── log-c58e1db3de7c154d-shallow/ +/// ``` +/// +/// For more on Git cache directory, see ["Cargo Home"] in The Cargo Book. +/// +/// For more on the directory format `<pkg>-<hash>[-shallow]`, see [`ident`] +/// and [`ident_shallow`]. +/// +/// ## Locked to a revision +/// +/// Once a `GitSource` is fetched, it will resolve to a specific commit revision. +/// This is often mentioned as "locked revision" (`locked_rev`) throughout the +/// codebase. The revision is written into `Cargo.lock`. This is essential since +/// we want to ensure a package can compiles with the same set of files when +/// a `Cargo.lock` is present. With the `locked_rev` provided, `GitSource` can +/// precisely fetch the same revision from the Git repository. +/// +/// ["Cargo Home"]: https://doc.rust-lang.org/nightly/cargo/guide/cargo-home.html#directories pub struct GitSource<'cfg> { + /// The git remote which we're going to fetch from. remote: GitRemote, + /// The Git reference from the manifest file. manifest_reference: GitReference, + /// The revision which a git source is locked to. + /// This is expected to be set after the Git repository is fetched. locked_rev: Option<git2::Oid>, + /// The unique identifier of this source. source_id: SourceId, + /// The underlying path source to discover packages inside the Git repository. path_source: Option<PathSource<'cfg>>, + /// The identifer of this source for Cargo's Git cache directory. + /// See [`ident`] for more. ident: String, config: &'cfg Config, + /// Disables status messages. quiet: bool, } impl<'cfg> GitSource<'cfg> { + /// Creates a git source for the given [`SourceId`]. pub fn new(source_id: SourceId, config: &'cfg Config) -> CargoResult<GitSource<'cfg>> { assert!(source_id.is_git(), "id is not git, id={}", source_id); @@ -59,10 +114,14 @@ impl<'cfg> GitSource<'cfg> { Ok(source) } + /// Gets the remote repository URL. pub fn url(&self) -> &Url { self.remote.url() } + /// Returns the packages discovered by this source. It may fetch the Git + /// repository as well as walk the filesystem if package informations + /// haven't yet updated. pub fn read_packages(&mut self) -> CargoResult<Vec<Package>> { if self.path_source.is_none() { self.invalidate_cache(); @@ -72,7 +131,8 @@ impl<'cfg> GitSource<'cfg> { } } -/// Create an identifier from a URL, essentially turning `proto://host/path/repo` into `repo-<hash-of-url>`. +/// Create an identifier from a URL, +/// essentially turning `proto://host/path/repo` into `repo-<hash-of-url>`. fn ident(id: &SourceId) -> String { let ident = id .canonical_url() @@ -86,10 +146,12 @@ fn ident(id: &SourceId) -> String { format!("{}-{}", ident, short_hash(id.canonical_url())) } -/// Like `ident()`, but appends `-shallow` to it, turning `proto://host/path/repo` into `repo-<hash-of-url>-shallow`. +/// Like [`ident()`], but appends `-shallow` to it, turning +/// `proto://host/path/repo` into `repo-<hash-of-url>-shallow`. /// -/// It's important to separate shallow from non-shallow clones for reasons of backwards compatibility - older -/// cargo's aren't necessarily handling shallow clones correctly. +/// It's important to separate shallow from non-shallow clones for reasons of +/// backwards compatibility --- older cargo's aren't necessarily handling +/// shallow clones correctly. fn ident_shallow(id: &SourceId, is_shallow: bool) -> String { let mut ident = ident(id); if is_shallow { @@ -217,8 +279,7 @@ impl<'cfg> Source for GitSource<'cfg> { .join("checkouts") .join(&self.ident) .join(short_id.as_str()); - let parent_remote_url = self.url(); - db.copy_to(actual_rev, &checkout_path, self.config, parent_remote_url)?; + db.copy_to(actual_rev, &checkout_path, self.config)?; let source_id = self.source_id.with_precise(Some(actual_rev.to_string())); let path_source = PathSource::new_recursive(&checkout_path, source_id, self.config); diff --git a/src/tools/cargo/src/cargo/sources/git/utils.rs b/src/tools/cargo/src/cargo/sources/git/utils.rs index c14d1daf3..0c7ce8b64 100644 --- a/src/tools/cargo/src/cargo/sources/git/utils.rs +++ b/src/tools/cargo/src/cargo/sources/git/utils.rs @@ -23,6 +23,10 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::time::{Duration, Instant}; use url::Url; +/// A file indicates that if present, `git reset` has been done and a repo +/// checkout is ready to go. See [`GitCheckout::reset`] for why we need this. +const CHECKOUT_READY_LOCK: &str = ".cargo-ok"; + fn serialize_str<T, S>(t: &T, s: S) -> Result<S::Ok, S::Error> where T: fmt::Display, @@ -31,60 +35,72 @@ where s.collect_str(t) } +/// A short abbreviated OID. +/// +/// Exists for avoiding extra allocations in [`GitDatabase::to_short_id`]. pub struct GitShortID(git2::Buf); impl GitShortID { + /// Views the short ID as a `str`. pub fn as_str(&self) -> &str { self.0.as_str().unwrap() } } -/// `GitRemote` represents a remote repository. It gets cloned into a local -/// `GitDatabase`. +/// A remote repository. It gets cloned into a local [`GitDatabase`]. #[derive(PartialEq, Clone, Debug, Serialize)] pub struct GitRemote { + /// URL to a remote repository. #[serde(serialize_with = "serialize_str")] url: Url, } -/// `GitDatabase` is a local clone of a remote repository's database. Multiple -/// `GitCheckouts` can be cloned from this `GitDatabase`. -#[derive(Serialize)] +/// A local clone of a remote repository's database. Multiple [`GitCheckout`]s +/// can be cloned from a single [`GitDatabase`]. pub struct GitDatabase { + /// The remote repository where this database is fetched from. remote: GitRemote, + /// Path to the root of the underlying Git repository on the local filesystem. path: PathBuf, - #[serde(skip_serializing)] + /// Underlying Git repository instance for this database. repo: git2::Repository, } -/// `GitCheckout` is a local checkout of a particular revision. Calling -/// `clone_into` with a reference will resolve the reference into a revision, -/// and return an `anyhow::Error` if no revision for that reference was found. -#[derive(Serialize)] +/// A local checkout of a particular revision from a [`GitDatabase`]. pub struct GitCheckout<'a> { + /// The git database where this checkout is cloned from. database: &'a GitDatabase, - location: PathBuf, - #[serde(serialize_with = "serialize_str")] + /// Path to the root of the underlying Git repository on the local filesystem. + path: PathBuf, + /// The git revision this checkout is for. revision: git2::Oid, - #[serde(skip_serializing)] + /// Underlying Git repository instance for this checkout. repo: git2::Repository, } -// Implementations - impl GitRemote { + /// Creates an instance for a remote repository URL. pub fn new(url: &Url) -> GitRemote { GitRemote { url: url.clone() } } + /// Gets the remote repository URL. pub fn url(&self) -> &Url { &self.url } - pub fn rev_for(&self, path: &Path, reference: &GitReference) -> CargoResult<git2::Oid> { - reference.resolve(&self.db_at(path)?.repo) - } - + /// Fetches and checkouts to a reference or a revision from this remote + /// into a local path. + /// + /// This ensures that it gets the up-to-date commit when a named reference + /// is given (tag, branch, refs/*). Thus, network connection is involved. + /// + /// When `locked_rev` is provided, it takes precedence over `reference`. + /// + /// If we have a previous instance of [`GitDatabase`] then fetch into that + /// if we can. If that can successfully load our revision then we've + /// populated the database with the latest version of `reference`, so + /// return that database and the rev we resolve to. pub fn checkout( &self, into: &Path, @@ -93,10 +109,6 @@ impl GitRemote { locked_rev: Option<git2::Oid>, cargo_config: &Config, ) -> CargoResult<(GitDatabase, git2::Oid)> { - // If we have a previous instance of `GitDatabase` then fetch into that - // if we can. If that can successfully load our revision then we've - // populated the database with the latest version of `reference`, so - // return that database and the rev we resolve to. let locked_ref = locked_rev.map(|oid| GitReference::Rev(oid.to_string())); let reference = locked_ref.as_ref().unwrap_or(reference); if let Some(mut db) = db { @@ -149,6 +161,7 @@ impl GitRemote { )) } + /// Creates a [`GitDatabase`] of this remote at `db_path`. pub fn db_at(&self, db_path: &Path) -> CargoResult<GitDatabase> { let repo = git2::Repository::open(db_path)?; Ok(GitDatabase { @@ -160,12 +173,12 @@ impl GitRemote { } impl GitDatabase { + /// Checkouts to a revision at `dest`ination from this database. pub fn copy_to( &self, rev: git2::Oid, dest: &Path, cargo_config: &Config, - parent_remote_url: &Url, ) -> CargoResult<GitCheckout<'_>> { // If the existing checkout exists, and it is fresh, use it. // A non-fresh checkout can happen if the checkout operation was @@ -173,31 +186,35 @@ impl GitDatabase { // clone is created. let checkout = match git2::Repository::open(dest) .ok() - .map(|repo| GitCheckout::new(dest, self, rev, repo)) + .map(|repo| GitCheckout::new(self, rev, repo)) .filter(|co| co.is_fresh()) { Some(co) => co, None => GitCheckout::clone_into(dest, self, rev, cargo_config)?, }; - checkout.update_submodules(cargo_config, parent_remote_url)?; + checkout.update_submodules(cargo_config)?; Ok(checkout) } + /// Get a short OID for a `revision`, usually 7 chars or more if ambiguous. pub fn to_short_id(&self, revision: git2::Oid) -> CargoResult<GitShortID> { let obj = self.repo.find_object(revision, None)?; Ok(GitShortID(obj.short_id()?)) } + /// Checks if the database contains the object of this `oid`.. pub fn contains(&self, oid: git2::Oid) -> bool { self.repo.revparse_single(&oid.to_string()).is_ok() } + /// [`GitReference::resolve`]s this reference with this database. pub fn resolve(&self, r: &GitReference) -> CargoResult<git2::Oid> { r.resolve(&self.repo) } } impl GitReference { + /// Resolves self to an object ID with objects the `repo` currently has. pub fn resolve(&self, repo: &git2::Repository) -> CargoResult<git2::Oid> { let id = match self { // Note that we resolve the named tag here in sync with where it's @@ -243,20 +260,32 @@ impl GitReference { } impl<'a> GitCheckout<'a> { + /// Creates an instance of [`GitCheckout`]. This doesn't imply the checkout + /// is done. Use [`GitCheckout::is_fresh`] to check. + /// + /// * The `database` is where this checkout is from. + /// * The `repo` will be the checked out Git repoistory. fn new( - path: &Path, database: &'a GitDatabase, revision: git2::Oid, repo: git2::Repository, ) -> GitCheckout<'a> { + let path = repo.workdir().unwrap_or_else(|| repo.path()); GitCheckout { - location: path.to_path_buf(), + path: path.to_path_buf(), database, revision, repo, } } + /// Gets the remote repository URL. + fn remote_url(&self) -> &Url { + &self.database.remote.url() + } + + /// Clone a repo for a `revision` into a local path from a `datatabase`. + /// This is a filesystem-to-filesystem clone. fn clone_into( into: &Path, database: &'a GitDatabase, @@ -293,10 +322,12 @@ impl<'a> GitCheckout<'a> { .with_checkout(checkout) .fetch_options(fopts) .clone(url.as_str(), into)?; - // `git2` doesn't seem to handle shallow repos correctly when doing a local clone. - // Fortunately all that's needed is the copy of the one file that defines the - // shallow boundary, the commits which have their parents omitted as part of the - // shallow clone. + // `git2` doesn't seem to handle shallow repos correctly when doing + // a local clone. Fortunately all that's needed is the copy of the + // one file that defines the shallow boundary, the commits which + // have their parents omitted as part of the shallow clone. + // + // TODO(git2): remove this when git2 supports shallow clone correctly if database.repo.is_shallow() { std::fs::copy( database.repo.path().join("shallow"), @@ -308,31 +339,38 @@ impl<'a> GitCheckout<'a> { })?; let repo = repo.unwrap(); - let checkout = GitCheckout::new(into, database, revision, repo); + let checkout = GitCheckout::new(database, revision, repo); checkout.reset(config)?; Ok(checkout) } + /// Checks if the `HEAD` of this checkout points to the expected revision. fn is_fresh(&self) -> bool { match self.repo.revparse_single("HEAD") { Ok(ref head) if head.id() == self.revision => { // See comments in reset() for why we check this - self.location.join(".cargo-ok").exists() + self.path.join(CHECKOUT_READY_LOCK).exists() } _ => false, } } + /// Similar to [`reset()`]. This roughly performs `git reset --hard` to the + /// revision of this checkout, with additional interrupt protection by a + /// dummy file [`CHECKOUT_READY_LOCK`]. + /// + /// If we're interrupted while performing a `git reset` (e.g., we die + /// because of a signal) Cargo needs to be sure to try to check out this + /// repo again on the next go-round. + /// + /// To enable this we have a dummy file in our checkout, [`.cargo-ok`], + /// which if present means that the repo has been successfully reset and is + /// ready to go. Hence if we start to do a reset, we make sure this file + /// *doesn't* exist, and then once we're done we create the file. + /// + /// [`.cargo-ok`]: CHECKOUT_READY_LOCK fn reset(&self, config: &Config) -> CargoResult<()> { - // If we're interrupted while performing this reset (e.g., we die because - // of a signal) Cargo needs to be sure to try to check out this repo - // again on the next go-round. - // - // To enable this we have a dummy file in our checkout, .cargo-ok, which - // if present means that the repo has been successfully reset and is - // ready to go. Hence if we start to do a reset, we make sure this file - // *doesn't* exist, and then once we're done we create the file. - let ok_file = self.location.join(".cargo-ok"); + let ok_file = self.path.join(CHECKOUT_READY_LOCK); let _ = paths::remove_file(&ok_file); info!("reset {} to {}", self.repo.path().display(), self.revision); @@ -347,13 +385,20 @@ impl<'a> GitCheckout<'a> { Ok(()) } - fn update_submodules(&self, cargo_config: &Config, parent_remote_url: &Url) -> CargoResult<()> { - return update_submodules(&self.repo, cargo_config, parent_remote_url); + /// Like `git submodule update --recursive` but for this git checkout. + /// + /// This function respects `submodule.<name>.update = none`[^1] git config. + /// Submodules set to `none` won't be fetched. + /// + /// [^1]: <https://git-scm.com/docs/git-submodule#Documentation/git-submodule.txt-none> + fn update_submodules(&self, cargo_config: &Config) -> CargoResult<()> { + return update_submodules(&self.repo, cargo_config, self.remote_url().as_str()); + /// Recusive helper for [`GitCheckout::update_submodules`]. fn update_submodules( repo: &git2::Repository, cargo_config: &Config, - parent_remote_url: &Url, + parent_remote_url: &str, ) -> CargoResult<()> { debug!("update submodules for: {:?}", repo.workdir().unwrap()); @@ -370,11 +415,12 @@ impl<'a> GitCheckout<'a> { Ok(()) } + /// Update a single Git submodule, and recurse into its submodules. fn update_submodule( parent: &git2::Repository, child: &mut git2::Submodule<'_>, cargo_config: &Config, - parent_remote_url: &Url, + parent_remote_url: &str, ) -> CargoResult<()> { child.init(false)?; @@ -394,31 +440,7 @@ impl<'a> GitCheckout<'a> { return Ok(()); } - // Git only assumes a URL is a relative path if it starts with `./` or `../`. - // See [`git submodule add`] documentation. - // - // [`git submodule add`]: https://git-scm.com/docs/git-submodule - let url = if child_url_str.starts_with("./") || child_url_str.starts_with("../") { - let mut new_parent_remote_url = parent_remote_url.clone(); - - let mut new_path = Cow::from(parent_remote_url.path()); - if !new_path.ends_with('/') { - new_path.to_mut().push('/'); - } - new_parent_remote_url.set_path(&new_path); - - match new_parent_remote_url.join(child_url_str) { - Ok(x) => x.to_string(), - Err(err) => Err(err).with_context(|| { - format!( - "failed to parse relative child submodule url `{}` using parent base url `{}`", - child_url_str, new_parent_remote_url - ) - })?, - } - } else { - child_url_str.to_string() - }; + let child_remote_url = absolute_submodule_url(parent_remote_url, child_url_str)?; // A submodule which is listed in .gitmodules but not actually // checked out will not have a head id, so we should ignore it. @@ -438,7 +460,7 @@ impl<'a> GitCheckout<'a> { let mut repo = match head_and_repo { Ok((head, repo)) => { if child.head_id() == head { - return update_submodules(&repo, cargo_config, parent_remote_url); + return update_submodules(&repo, cargo_config, &child_remote_url); } repo } @@ -452,29 +474,78 @@ impl<'a> GitCheckout<'a> { let reference = GitReference::Rev(head.to_string()); cargo_config .shell() - .status("Updating", format!("git submodule `{}`", url))?; + .status("Updating", format!("git submodule `{child_remote_url}`"))?; fetch( &mut repo, - &url, + &child_remote_url, &reference, cargo_config, RemoteKind::GitDependency, ) .with_context(|| { - format!( - "failed to fetch submodule `{}` from {}", - child.name().unwrap_or(""), - url - ) + let name = child.name().unwrap_or(""); + format!("failed to fetch submodule `{name}` from {child_remote_url}",) })?; let obj = repo.find_object(head, None)?; reset(&repo, &obj, cargo_config)?; - update_submodules(&repo, cargo_config, parent_remote_url) + update_submodules(&repo, cargo_config, &child_remote_url) } } } +/// Constructs an absolute URL for a child submodule URL with its parent base URL. +/// +/// Git only assumes a submodule URL is a relative path if it starts with `./` +/// or `../` [^1]. To fetch the correct repo, we need to construct an absolute +/// submodule URL. +/// +/// At this moment it comes with some limitations: +/// +/// * GitHub doesn't accept non-normalized URLs with relative paths. +/// (`ssh://git@github.com/rust-lang/cargo.git/relative/..` is invalid) +/// * `url` crate cannot parse SCP-like URLs. +/// (`git@github.com:rust-lang/cargo.git` is not a valid WHATWG URL) +/// +/// To overcome these, this patch always tries [`Url::parse`] first to normalize +/// the path. If it couldn't, append the relative path as the last resort and +/// pray the remote git service supports non-normalized URLs. +/// +/// See also rust-lang/cargo#12404 and rust-lang/cargo#12295. +/// +/// [^1]: <https://git-scm.com/docs/git-submodule> +fn absolute_submodule_url<'s>(base_url: &str, submodule_url: &'s str) -> CargoResult<Cow<'s, str>> { + let absolute_url = if ["./", "../"].iter().any(|p| submodule_url.starts_with(p)) { + match Url::parse(base_url) { + Ok(mut base_url) => { + let path = base_url.path(); + if !path.ends_with('/') { + base_url.set_path(&format!("{path}/")); + } + let absolute_url = base_url.join(submodule_url).with_context(|| { + format!( + "failed to parse relative child submodule url `{submodule_url}` \ + using parent base url `{base_url}`" + ) + })?; + Cow::from(absolute_url.to_string()) + } + Err(_) => { + let mut absolute_url = base_url.to_string(); + if !absolute_url.ends_with('/') { + absolute_url.push('/'); + } + absolute_url.push_str(submodule_url); + Cow::from(absolute_url) + } + } + } else { + Cow::from(submodule_url) + }; + + Ok(absolute_url) +} + /// Prepare the authentication callbacks for cloning a git repository. /// /// The main purpose of this function is to construct the "authentication @@ -745,6 +816,9 @@ where Err(err) } +/// `git reset --hard` to the given `obj` for the `repo`. +/// +/// The `obj` is a commit-ish to which the head should be moved. fn reset(repo: &git2::Repository, obj: &git2::Object<'_>, config: &Config) -> CargoResult<()> { let mut pb = Progress::new("Checkout", config); let mut opts = git2::build::CheckoutBuilder::new(); @@ -757,6 +831,14 @@ fn reset(repo: &git2::Repository, obj: &git2::Object<'_>, config: &Config) -> Ca Ok(()) } +/// Prepares the callbacks for fetching a git repository. +/// +/// The main purpose of this function is to construct everything before a fetch. +/// This will attempt to setup a progress bar, the authentication for git, +/// ssh known hosts check, and the network retry mechanism. +/// +/// The callback is provided a fetch options, which can be used by the actual +/// git fetch. pub fn with_fetch_options( git_config: &git2::Config, url: &str, @@ -831,12 +913,22 @@ pub fn with_fetch_options( }) } -/// Note that `history` is a complex computed value to determine whether it's acceptable to perform shallow clones -/// at all. It's needed to allow the caller to determine the correct position of the destination repository or move it -/// into place should its position change. +/// Attempts to fetch the given git `reference` for a Git repository. +/// +/// This is the main entry for git clone/fetch. It does the followings: +/// +/// * Turns [`GitReference`] into refspecs accordingly. +/// * Dispatches `git fetch` using libgit2, gitoxide, or git CLI. +/// +/// The `remote_url` argument is the git remote URL where we want to fetch from. +/// +/// The `remote_kind` argument is a thing for [`-Zgitoxide`] shallow clones +/// at this time. It could be extended when libgit2 supports shallow clones. +/// +/// [`-Zgitoxide`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#gitoxide pub fn fetch( repo: &mut git2::Repository, - orig_url: &str, + remote_url: &str, reference: &GitReference, config: &Config, remote_kind: RemoteKind, @@ -853,9 +945,7 @@ pub fn fetch( let shallow = remote_kind.to_shallow_setting(repo.is_shallow(), config); - // If we're fetching from GitHub, attempt GitHub's special fast path for - // testing if we've already got an up-to-date copy of the repository. - let oid_to_fetch = match github_fast_path(repo, orig_url, reference, config) { + let oid_to_fetch = match github_fast_path(repo, remote_url, reference, config) { Ok(FastPathRev::UpToDate) => return Ok(()), Ok(FastPathRev::NeedsFetch(rev)) => Some(rev), Ok(FastPathRev::Indeterminate) => None, @@ -865,10 +955,6 @@ pub fn fetch( } }; - // We reuse repositories quite a lot, so before we go through and update the - // repo check to see if it's a little too old and could benefit from a gc. - // In theory this shouldn't be too expensive compared to the network - // request we're about to issue. maybe_gc_repo(repo, config)?; clean_repo_temp_files(repo); @@ -921,15 +1007,10 @@ pub fn fetch( } } - // Unfortunately `libgit2` is notably lacking in the realm of authentication - // when compared to the `git` command line. As a result, allow an escape - // hatch for users that would prefer to use `git`-the-CLI for fetching - // repositories instead of `libgit2`-the-library. This should make more - // flavors of authentication possible while also still giving us all the - // speed and portability of using `libgit2`. if let Some(true) = config.net_config()?.git_fetch_with_cli { - return fetch_with_cli(repo, orig_url, &refspecs, tags, config); + return fetch_with_cli(repo, remote_url, &refspecs, tags, config); } + if config .cli_unstable() .gitoxide @@ -963,10 +1044,10 @@ pub fn fetch( ) .map_err(crate::sources::git::fetch::Error::from) .and_then(|repo| { - debug!("initiating fetch of {:?} from {}", refspecs, orig_url); + debug!("initiating fetch of {refspecs:?} from {remote_url}"); let url_for_authentication = &mut *url_for_authentication; let remote = repo - .remote_at(orig_url)? + .remote_at(remote_url)? .with_fetch_tags(if tags { gix::remote::fetch::Tags::All } else { @@ -985,10 +1066,9 @@ pub fn fetch( let mut authenticate = connection.configured_credentials(url)?; let connection = connection.with_credentials( move |action: gix::protocol::credentials::helper::Action| { - if let Some(url) = action - .context() - .and_then(|ctx| ctx.url.as_ref().filter(|url| *url != orig_url)) - { + if let Some(url) = action.context().and_then(|ctx| { + ctx.url.as_ref().filter(|url| *url != remote_url) + }) { url_for_authentication(url.as_ref()); } authenticate(action) @@ -1034,9 +1114,9 @@ pub fn fetch( } res } else { - debug!("doing a fetch for {}", orig_url); + debug!("doing a fetch for {remote_url}"); let git_config = git2::Config::open_default()?; - with_fetch_options(&git_config, orig_url, config, &mut |mut opts| { + with_fetch_options(&git_config, remote_url, config, &mut |mut opts| { if tags { opts.download_tags(git2::AutotagOption::All); } @@ -1052,10 +1132,10 @@ pub fn fetch( // blown away the repository, then we want to return the error as-is. let mut repo_reinitialized = false; loop { - debug!("initiating fetch of {:?} from {}", refspecs, orig_url); - let res = repo - .remote_anonymous(orig_url)? - .fetch(&refspecs, Some(&mut opts), None); + debug!("initiating fetch of {refspecs:?} from {remote_url}"); + let res = + repo.remote_anonymous(remote_url)? + .fetch(&refspecs, Some(&mut opts), None); let err = match res { Ok(()) => break, Err(e) => e, @@ -1093,6 +1173,17 @@ fn has_shallow_lock_file(err: &crate::sources::git::fetch::Error) -> bool { ) } +/// Attempts to use `git` CLI installed on the system to fetch a repository, +/// when the config value [`net.git-fetch-with-cli`][1] is set. +/// +/// Unfortunately `libgit2` is notably lacking in the realm of authentication +/// when compared to the `git` command line. As a result, allow an escape +/// hatch for users that would prefer to use `git`-the-CLI for fetching +/// repositories instead of `libgit2`-the-library. This should make more +/// flavors of authentication possible while also still giving us all the +/// speed and portability of using `libgit2`. +/// +/// [1]: https://doc.rust-lang.org/nightly/cargo/reference/config.html#netgit-fetch-with-cli fn fetch_with_cli( repo: &mut git2::Repository, url: &str, @@ -1137,6 +1228,8 @@ fn fetch_with_cli( Ok(()) } +/// Attempts to `git gc` a repository. +/// /// Cargo has a bunch of long-lived git repositories in its global cache and /// some, like the index, are updated very frequently. Right now each update /// creates a new "pack file" inside the git database, and over time this can @@ -1144,12 +1237,17 @@ fn fetch_with_cli( /// /// One pathological use case today is where libgit2 opens hundreds of file /// descriptors, getting us dangerously close to blowing out the OS limits of -/// how many fds we can have open. This is detailed in #4403. +/// how many fds we can have open. This is detailed in [#4403]. /// /// To try to combat this problem we attempt a `git gc` here. Note, though, that /// we may not even have `git` installed on the system! As a result we /// opportunistically try a `git gc` when the pack directory looks too big, and /// failing that we just blow away the repository and start over. +/// +/// In theory this shouldn't be too expensive compared to the network request +/// we're about to issue. +/// +/// [#4403]: https://github.com/rust-lang/cargo/issues/4403 fn maybe_gc_repo(repo: &mut git2::Repository, config: &Config) -> CargoResult<()> { // Here we arbitrarily declare that if you have more than 100 files in your // `pack` folder that we need to do a gc. @@ -1236,6 +1334,8 @@ fn clean_repo_temp_files(repo: &git2::Repository) { } } +/// Reinitializes a given Git repository. This is useful when a Git repoistory +/// seems corrupted and we want to start over. fn reinitialize(repo: &mut git2::Repository) -> CargoResult<()> { // Here we want to drop the current repository object pointed to by `repo`, // so we initialize temporary repository in a sub-folder, blow away the @@ -1259,6 +1359,7 @@ fn reinitialize(repo: &mut git2::Repository) -> CargoResult<()> { Ok(()) } +/// Initializes a Git repository at `path`. fn init(path: &Path, bare: bool) -> CargoResult<git2::Repository> { let mut opts = git2::RepositoryInitOptions::new(); // Skip anything related to templates, they just call all sorts of issues as @@ -1269,6 +1370,7 @@ fn init(path: &Path, bare: bool) -> CargoResult<git2::Repository> { Ok(git2::Repository::init_opts(&path, &opts)?) } +/// The result of GitHub fast path check. See [`github_fast_path`] for more. enum FastPathRev { /// The local rev (determined by `reference.resolve(repo)`) is already up to /// date with what this rev resolves to on GitHub's server. @@ -1281,20 +1383,19 @@ enum FastPathRev { Indeterminate, } +/// Attempts GitHub's special fast path for testing if we've already got an +/// up-to-date copy of the repository. +/// /// Updating the index is done pretty regularly so we want it to be as fast as /// possible. For registries hosted on GitHub (like the crates.io index) there's -/// a fast path available to use [1] to tell us that there's no updates to be +/// a fast path available to use[^1] to tell us that there's no updates to be /// made. /// -/// This function will attempt to hit that fast path and verify that the `oid` -/// is actually the current branch of the repository. -/// -/// [1]: https://developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-reference -/// /// Note that this function should never cause an actual failure because it's -/// just a fast path. As a result all errors are ignored in this function and we -/// just return a `bool`. Any real errors will be reported through the normal -/// update path above. +/// just a fast path. As a result, a caller should ignore `Err` returned from +/// this function and move forward on the normal path. +/// +/// [^1]: <https://developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-reference> fn github_fast_path( repo: &mut git2::Repository, url: &str, @@ -1406,14 +1507,17 @@ fn github_fast_path( } } +/// Whether a `url` is one from GitHub. fn is_github(url: &Url) -> bool { url.host_str() == Some("github.com") } +/// Whether a `rev` looks like a commit hash (ASCII hex digits). fn looks_like_commit_hash(rev: &str) -> bool { rev.len() >= 7 && rev.chars().all(|ch| ch.is_ascii_hexdigit()) } +/// Whether `rev` is a shorter hash of `oid`. fn is_short_hash_of(rev: &str, oid: Oid) -> bool { let long_hash = oid.to_string(); match long_hash.get(..rev.len()) { @@ -1421,3 +1525,102 @@ fn is_short_hash_of(rev: &str, oid: Oid) -> bool { None => false, } } + +#[cfg(test)] +mod tests { + use super::absolute_submodule_url; + + #[test] + fn test_absolute_submodule_url() { + let cases = [ + ( + "ssh://git@gitub.com/rust-lang/cargo", + "git@github.com:rust-lang/cargo.git", + "git@github.com:rust-lang/cargo.git", + ), + ( + "ssh://git@gitub.com/rust-lang/cargo", + "./", + "ssh://git@gitub.com/rust-lang/cargo/", + ), + ( + "ssh://git@gitub.com/rust-lang/cargo", + "../", + "ssh://git@gitub.com/rust-lang/", + ), + ( + "ssh://git@gitub.com/rust-lang/cargo", + "./foo", + "ssh://git@gitub.com/rust-lang/cargo/foo", + ), + ( + "ssh://git@gitub.com/rust-lang/cargo/", + "./foo", + "ssh://git@gitub.com/rust-lang/cargo/foo", + ), + ( + "ssh://git@gitub.com/rust-lang/cargo/", + "../foo", + "ssh://git@gitub.com/rust-lang/foo", + ), + ( + "ssh://git@gitub.com/rust-lang/cargo", + "../foo", + "ssh://git@gitub.com/rust-lang/foo", + ), + ( + "ssh://git@gitub.com/rust-lang/cargo", + "../foo/bar/../baz", + "ssh://git@gitub.com/rust-lang/foo/baz", + ), + ( + "git@github.com:rust-lang/cargo.git", + "ssh://git@gitub.com/rust-lang/cargo", + "ssh://git@gitub.com/rust-lang/cargo", + ), + ( + "git@github.com:rust-lang/cargo.git", + "./", + "git@github.com:rust-lang/cargo.git/./", + ), + ( + "git@github.com:rust-lang/cargo.git", + "../", + "git@github.com:rust-lang/cargo.git/../", + ), + ( + "git@github.com:rust-lang/cargo.git", + "./foo", + "git@github.com:rust-lang/cargo.git/./foo", + ), + ( + "git@github.com:rust-lang/cargo.git/", + "./foo", + "git@github.com:rust-lang/cargo.git/./foo", + ), + ( + "git@github.com:rust-lang/cargo.git", + "../foo", + "git@github.com:rust-lang/cargo.git/../foo", + ), + ( + "git@github.com:rust-lang/cargo.git/", + "../foo", + "git@github.com:rust-lang/cargo.git/../foo", + ), + ( + "git@github.com:rust-lang/cargo.git", + "../foo/bar/../baz", + "git@github.com:rust-lang/cargo.git/../foo/bar/../baz", + ), + ]; + + for (base_url, submodule_url, expected) in cases { + let url = absolute_submodule_url(base_url, submodule_url).unwrap(); + assert_eq!( + expected, url, + "base `{base_url}`; submodule `{submodule_url}`" + ); + } + } +} diff --git a/src/tools/cargo/src/cargo/sources/path.rs b/src/tools/cargo/src/cargo/sources/path.rs index 2f147b19e..bb40ec9b1 100644 --- a/src/tools/cargo/src/cargo/sources/path.rs +++ b/src/tools/cargo/src/cargo/sources/path.rs @@ -95,7 +95,7 @@ impl<'cfg> PathSource<'cfg> { } /// Returns the packages discovered by this source. It may walk the - /// the filesystem if package informations haven't yet updated. + /// filesystem if package informations haven't yet updated. pub fn read_packages(&self) -> CargoResult<Vec<Package>> { if self.updated { Ok(self.packages.clone()) diff --git a/src/tools/cargo/src/cargo/sources/registry/download.rs b/src/tools/cargo/src/cargo/sources/registry/download.rs index 723c55ffd..a85d87177 100644 --- a/src/tools/cargo/src/cargo/sources/registry/download.rs +++ b/src/tools/cargo/src/cargo/sources/registry/download.rs @@ -1,13 +1,15 @@ +//! Shared download logic between [`HttpRegistry`] and [`RemoteRegistry`]. +//! +//! [`HttpRegistry`]: super::http_remote::HttpRegistry +//! [`RemoteRegistry`]: super::remote::RemoteRegistry + use anyhow::Context; +use cargo_util::registry::make_dep_path; use cargo_util::Sha256; use crate::core::PackageId; -use crate::sources::registry::make_dep_prefix; use crate::sources::registry::MaybeLock; -use crate::sources::registry::{ - RegistryConfig, CHECKSUM_TEMPLATE, CRATE_TEMPLATE, LOWER_PREFIX_TEMPLATE, PREFIX_TEMPLATE, - VERSION_TEMPLATE, -}; +use crate::sources::registry::RegistryConfig; use crate::util::auth; use crate::util::errors::CargoResult; use crate::util::{Config, Filesystem}; @@ -17,10 +19,16 @@ use std::io::prelude::*; use std::io::SeekFrom; use std::str; -pub(super) fn filename(pkg: PackageId) -> String { - format!("{}-{}.crate", pkg.name(), pkg.version()) -} +const CRATE_TEMPLATE: &str = "{crate}"; +const VERSION_TEMPLATE: &str = "{version}"; +const PREFIX_TEMPLATE: &str = "{prefix}"; +const LOWER_PREFIX_TEMPLATE: &str = "{lowerprefix}"; +const CHECKSUM_TEMPLATE: &str = "{sha256-checksum}"; +/// Checks if `pkg` is downloaded and ready under the directory at `cache_path`. +/// If not, returns a URL to download it from. +/// +/// This is primarily called by [`RegistryData::download`](super::RegistryData::download). pub(super) fn download( cache_path: &Filesystem, config: &Config, @@ -28,8 +36,7 @@ pub(super) fn download( checksum: &str, registry_config: RegistryConfig, ) -> CargoResult<MaybeLock> { - let filename = filename(pkg); - let path = cache_path.join(&filename); + let path = cache_path.join(&pkg.tarball_name()); let path = config.assert_package_cache_locked(&path); // Attempt to open a read-only copy first to avoid an exclusive write @@ -61,7 +68,7 @@ pub(super) fn download( ) .unwrap(); } else { - let prefix = make_dep_prefix(&*pkg.name()); + let prefix = make_dep_path(&pkg.name(), true); url = url .replace(CRATE_TEMPLATE, &*pkg.name()) .replace(VERSION_TEMPLATE, &pkg.version().to_string()) @@ -83,6 +90,10 @@ pub(super) fn download( }) } +/// Verifies the integrity of `data` with `checksum` and persists it under the +/// directory at `cache_path`. +/// +/// This is primarily called by [`RegistryData::finish_download`](super::RegistryData::finish_download). pub(super) fn finish_download( cache_path: &Filesystem, config: &Config, @@ -96,9 +107,8 @@ pub(super) fn finish_download( anyhow::bail!("failed to verify the checksum of `{}`", pkg) } - let filename = filename(pkg); cache_path.create_dir()?; - let path = cache_path.join(&filename); + let path = cache_path.join(&pkg.tarball_name()); let path = config.assert_package_cache_locked(&path); let mut dst = OpenOptions::new() .create(true) @@ -116,12 +126,16 @@ pub(super) fn finish_download( Ok(dst) } +/// Checks if a tarball of `pkg` has been already downloaded under the +/// directory at `cache_path`. +/// +/// This is primarily called by [`RegistryData::is_crate_downloaded`](super::RegistryData::is_crate_downloaded). pub(super) fn is_crate_downloaded( cache_path: &Filesystem, config: &Config, pkg: PackageId, ) -> bool { - let path = cache_path.join(filename(pkg)); + let path = cache_path.join(pkg.tarball_name()); let path = config.assert_package_cache_locked(&path); if let Ok(meta) = fs::metadata(path) { return meta.len() > 0; diff --git a/src/tools/cargo/src/cargo/sources/registry/http_remote.rs b/src/tools/cargo/src/cargo/sources/registry/http_remote.rs index 7e1f2a587..c69ef8f9b 100644 --- a/src/tools/cargo/src/cargo/sources/registry/http_remote.rs +++ b/src/tools/cargo/src/cargo/sources/registry/http_remote.rs @@ -1,21 +1,19 @@ -//! Access to a HTTP-based crate registry. -//! -//! See [`HttpRegistry`] for details. +//! Access to a HTTP-based crate registry. See [`HttpRegistry`] for details. use crate::core::{PackageId, SourceId}; -use crate::ops::{self}; use crate::sources::registry::download; use crate::sources::registry::MaybeLock; use crate::sources::registry::{LoadResponse, RegistryConfig, RegistryData}; use crate::util::errors::{CargoResult, HttpNotSuccessful, DEBUG_HEADERS}; +use crate::util::network::http::http_handle; use crate::util::network::retry::{Retry, RetryResult}; use crate::util::network::sleep::SleepTracker; use crate::util::{auth, Config, Filesystem, IntoUrl, Progress, ProgressStyle}; use anyhow::Context; use cargo_util::paths; -use curl::easy::{Easy, HttpVersion, List}; +use curl::easy::{Easy, List}; use curl::multi::{EasyHandle, Multi}; -use log::{debug, trace, warn}; +use log::{debug, trace}; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::fs::{self, File}; @@ -52,8 +50,15 @@ const UNKNOWN: &'static str = "Unknown"; /// /// [RFC 2789]: https://github.com/rust-lang/rfcs/pull/2789 pub struct HttpRegistry<'cfg> { + /// Path to the registry index (`$CARGO_HOME/registry/index/$REG-HASH`). + /// + /// To be fair, `HttpRegistry` doesn't store the registry index it + /// downloads on the file system, but other cached data like registry + /// configuration could be stored here. index_path: Filesystem, + /// Path to the cache of `.crate` files (`$CARGO_HOME/registry/cache/$REG-HASH`). cache_path: Filesystem, + /// The unique identifier of this registry source. source_id: SourceId, config: &'cfg Config, @@ -95,20 +100,20 @@ pub struct HttpRegistry<'cfg> { quiet: bool, } -/// Helper for downloading crates. +/// State for currently pending index file downloads. struct Downloads<'cfg> { /// When a download is started, it is added to this map. The key is a - /// "token" (see `Download::token`). It is removed once the download is + /// "token" (see [`Download::token`]). It is removed once the download is /// finished. pending: HashMap<usize, (Download<'cfg>, EasyHandle)>, /// Set of paths currently being downloaded. - /// This should stay in sync with `pending`. + /// This should stay in sync with the `pending` field. pending_paths: HashSet<PathBuf>, /// Downloads that have failed and are waiting to retry again later. sleeping: SleepTracker<(Download<'cfg>, Easy)>, /// The final result of each download. results: HashMap<PathBuf, CargoResult<CompletedDownload>>, - /// The next ID to use for creating a token (see `Download::token`). + /// The next ID to use for creating a token (see [`Download::token`]). next: usize, /// Progress bar. progress: RefCell<Option<Progress<'cfg>>>, @@ -119,9 +124,10 @@ struct Downloads<'cfg> { blocking_calls: usize, } +/// Represents a single index file download, including its progress and retry. struct Download<'cfg> { - /// The token for this download, used as the key of the `Downloads::pending` map - /// and stored in `EasyHandle` as well. + /// The token for this download, used as the key of the + /// [`Downloads::pending`] map and stored in [`EasyHandle`] as well. token: usize, /// The path of the package that we're downloading. @@ -137,14 +143,17 @@ struct Download<'cfg> { retry: Retry<'cfg>, } +/// HTTPS headers [`HttpRegistry`] cares about. #[derive(Default)] struct Headers { last_modified: Option<String>, etag: Option<String>, www_authenticate: Vec<String>, + /// We don't care about these headers. Put them here for debugging purpose. others: Vec<String>, } +/// HTTP status code [`HttpRegistry`] cares about. enum StatusCode { Success, NotModified, @@ -152,6 +161,10 @@ enum StatusCode { Unauthorized, } +/// Represents a complete [`Download`] from an HTTP request. +/// +/// Usually it is constructed in [`HttpRegistry::handle_completed_downloads`], +/// and then returns to the caller of [`HttpRegistry::load()`]. struct CompletedDownload { response_code: StatusCode, data: Vec<u8>, @@ -159,6 +172,10 @@ struct CompletedDownload { } impl<'cfg> HttpRegistry<'cfg> { + /// Creates a HTTP-rebased remote registry for `source_id`. + /// + /// * `name` --- Name of a path segment where `.crate` tarballs and the + /// registry index are stored. Expect to be unique. pub fn new( source_id: SourceId, config: &'cfg Config, @@ -208,6 +225,7 @@ impl<'cfg> HttpRegistry<'cfg> { }) } + /// Splits HTTP `HEADER: VALUE` to a tuple. fn handle_http_header(buf: &[u8]) -> Option<(&str, &str)> { if buf.is_empty() { return None; @@ -222,6 +240,9 @@ impl<'cfg> HttpRegistry<'cfg> { Some((tag, value)) } + /// Setup the necessary works before the first fetch gets started. + /// + /// This is a no-op if called more than one time. fn start_fetch(&mut self) -> CargoResult<()> { if self.fetch_started { // We only need to run the setup code once. @@ -249,6 +270,8 @@ impl<'cfg> HttpRegistry<'cfg> { Ok(()) } + /// Checks the results inside the [`HttpRegistry::multi`] handle, and + /// updates relevant state in [`HttpRegistry::downloads`] accordingly. fn handle_completed_downloads(&mut self) -> CargoResult<()> { assert_eq!( self.downloads.pending.len(), @@ -322,11 +345,15 @@ impl<'cfg> HttpRegistry<'cfg> { Ok(()) } + /// Constructs the full URL to download a index file. fn full_url(&self, path: &Path) -> String { // self.url always ends with a slash. format!("{}{}", self.url, path.display()) } + /// Check if an index file of `path` is up-to-date. + /// + /// The `path` argument is the same as in [`RegistryData::load`]. fn is_fresh(&self, path: &Path) -> bool { if !self.requested_update { trace!( @@ -356,7 +383,7 @@ impl<'cfg> HttpRegistry<'cfg> { } let config_json_path = self .assert_index_locked(&self.index_path) - .join("config.json"); + .join(RegistryConfig::NAME); match fs::read(&config_json_path) { Ok(raw_data) => match serde_json::from_slice(&raw_data) { Ok(json) => { @@ -373,16 +400,16 @@ impl<'cfg> HttpRegistry<'cfg> { Ok(self.registry_config.as_ref()) } - /// Get the registry configuration. + /// Get the registry configuration from either cache or remote. fn config(&mut self) -> Poll<CargoResult<&RegistryConfig>> { debug!("loading config"); let index_path = self.assert_index_locked(&self.index_path); - let config_json_path = index_path.join("config.json"); - if self.is_fresh(Path::new("config.json")) && self.config_cached()?.is_some() { + let config_json_path = index_path.join(RegistryConfig::NAME); + if self.is_fresh(Path::new(RegistryConfig::NAME)) && self.config_cached()?.is_some() { return Poll::Ready(Ok(self.registry_config.as_ref().unwrap())); } - match ready!(self.load(Path::new(""), Path::new("config.json"), None)?) { + match ready!(self.load(Path::new(""), Path::new(RegistryConfig::NAME), None)?) { LoadResponse::Data { raw_data, index_version: _, @@ -405,6 +432,7 @@ impl<'cfg> HttpRegistry<'cfg> { } } + /// Moves failed [`Download`]s that are ready to retry to the pending queue. fn add_sleepers(&mut self) -> CargoResult<()> { for (dl, handle) in self.downloads.sleeping.to_retry() { let mut handle = self.multi.add(handle)?; @@ -515,7 +543,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> { } StatusCode::Unauthorized if !self.auth_required - && path == Path::new("config.json") + && path == Path::new(RegistryConfig::NAME) && self.config.cli_unstable().registry_auth => { debug!("re-attempting request for config.json with authorization included."); @@ -565,7 +593,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> { } } - if path != Path::new("config.json") { + if path != Path::new(RegistryConfig::NAME) { self.auth_required = ready!(self.config()?).auth_required; } else if !self.auth_required { // Check if there's a cached config that says auth is required. @@ -582,7 +610,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> { // Looks like we're going to have to do a network request. self.start_fetch()?; - let mut handle = ops::http_handle(self.config)?; + let mut handle = http_handle(self.config)?; let full_url = self.full_url(path); debug!("fetch {}", full_url); handle.get(true)?; @@ -590,20 +618,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> { handle.follow_location(true)?; // Enable HTTP/2 if possible. - if self.multiplexing { - crate::try_old_curl!(handle.http_version(HttpVersion::V2), "HTTP2"); - } else { - handle.http_version(HttpVersion::V11)?; - } - - // This is an option to `libcurl` which indicates that if there's a - // bunch of parallel requests to the same host they all wait until the - // pipelining status of the host is known. This means that we won't - // initiate dozens of connections to crates.io, but rather only one. - // Once the main one is opened we realized that pipelining is possible - // and multiplexing is possible with static.crates.io. All in all this - // reduces the number of connections done to a more manageable state. - crate::try_old_curl!(handle.pipewait(true), "pipewait"); + crate::try_old_curl_http2_pipewait!(self.multiplexing, handle); let mut headers = List::new(); // Include a header to identify the protocol. This allows the server to @@ -790,6 +805,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> { } impl<'cfg> Downloads<'cfg> { + /// Updates the state of the progress bar for downloads. fn tick(&self) -> CargoResult<()> { let mut progress = self.progress.borrow_mut(); let Some(progress) = progress.as_mut() else { return Ok(()); }; diff --git a/src/tools/cargo/src/cargo/sources/registry/index.rs b/src/tools/cargo/src/cargo/sources/registry/index.rs index d857a053e..6d565da8f 100644 --- a/src/tools/cargo/src/cargo/sources/registry/index.rs +++ b/src/tools/cargo/src/cargo/sources/registry/index.rs @@ -1,11 +1,26 @@ -//! Management of the index of a registry source +//! Management of the index of a registry source. //! //! This module contains management of the index and various operations, such as //! actually parsing the index, looking for crates, etc. This is intended to be -//! abstract over remote indices (downloaded via git) and local registry indices -//! (which are all just present on the filesystem). +//! abstract over remote indices (downloaded via Git or HTTP) and local registry +//! indices (which are all just present on the filesystem). //! -//! ## Index Performance +//! ## How the index works +//! +//! Here is a simple flow when loading a [`Summary`] (metadata) from the index: +//! +//! 1. A query is fired via [`RegistryIndex::query_inner`]. +//! 2. Tries loading all summaries via [`RegistryIndex::load_summaries`], and +//! under the hood calling [`Summaries::parse`] to parse an index file. +//! 1. If an on-disk index cache is present, loads it via +//! [`Summaries::parse_cache`]. +//! 2. Otherwise goes to the slower path [`RegistryData::load`] to get the +//! specific index file. +//! 3. A [`Summary`] is now ready in callback `f` in [`RegistryIndex::query_inner`]. +//! +//! This is just an overview. To know the rationale behind, continue reading. +//! +//! ## A layer of on-disk index cache for performance //! //! One important aspect of the index is that we want to optimize the "happy //! path" as much as possible. Whenever you type `cargo build` Cargo will @@ -20,19 +35,20 @@ //! don't need them. Most secondary optimizations are centered around removing //! allocations and such, but avoiding parsing JSON is the #1 optimization. //! -//! When we get queries from the resolver we're given a `Dependency`. This +//! When we get queries from the resolver we're given a [`Dependency`]. This //! dependency in turn has a version requirement, and with lock files that //! already exist these version requirements are exact version requirements //! `=a.b.c`. This means that we in theory only need to parse one line of JSON //! per query in the registry, the one that matches version `a.b.c`. //! //! The crates.io index, however, is not amenable to this form of query. Instead -//! the crates.io index simply is a file where each line is a JSON blob. To -//! learn about the versions in each JSON blob we would need to parse the JSON, -//! defeating the purpose of trying to parse as little as possible. +//! the crates.io index simply is a file where each line is a JSON blob, aka +//! [`IndexPackage`]. To learn about the versions in each JSON blob we would +//! need to parse the JSON via [`IndexSummary::parse`], defeating the purpose +//! of trying to parse as little as possible. //! //! > Note that as a small aside even *loading* the JSON from the registry is -//! > actually pretty slow. For crates.io and remote registries we don't +//! > actually pretty slow. For crates.io and [`RemoteRegistry`] we don't //! > actually check out the git index on disk because that takes quite some //! > time and is quite large. Instead we use `libgit2` to read the JSON from //! > the raw git objects. This in turn can be slow (aka show up high in @@ -43,14 +59,14 @@ //! (first time being for an entire computer) Cargo will load the contents //! (slowly via libgit2) from the registry. It will then (slowly) parse every //! single line to learn about its versions. Afterwards, however, Cargo will -//! emit a new file (a cache) which is amenable for speedily parsing in future -//! invocations. +//! emit a new file (a cache, representing as [`SummariesCache`]) which is +//! amenable for speedily parsing in future invocations. //! //! This cache file is currently organized by basically having the semver -//! version extracted from each JSON blob. That way Cargo can quickly and easily -//! parse all versions contained and which JSON blob they're associated with. -//! The JSON blob then doesn't actually need to get parsed unless the version is -//! parsed. +//! version extracted from each JSON blob. That way Cargo can quickly and +//! easily parse all versions contained and which JSON blob they're associated +//! with. The JSON blob then doesn't actually need to get parsed unless the +//! version is parsed. //! //! Altogether the initial measurements of this shows a massive improvement for //! Cargo null build performance. It's expected that the improvements earned @@ -65,15 +81,24 @@ //! Note that this is just a high-level overview, there's of course lots of //! details like invalidating caches and whatnot which are handled below, but //! hopefully those are more obvious inline in the code itself. +//! +//! [`RemoteRegistry`]: super::remote::RemoteRegistry +//! [`Dependency`]: crate::core::Dependency +use crate::core::dependency::DepKind; +use crate::core::Dependency; use crate::core::{PackageId, SourceId, Summary}; -use crate::sources::registry::{LoadResponse, RegistryData, RegistryPackage, INDEX_V_MAX}; +use crate::sources::registry::{LoadResponse, RegistryData}; use crate::util::interning::InternedString; +use crate::util::IntoUrl; use crate::util::{internal, CargoResult, Config, Filesystem, OptVersionReq, ToSemver}; use anyhow::bail; use cargo_util::{paths, registry::make_dep_path}; use log::{debug, info}; use semver::Version; +use serde::Deserialize; +use std::borrow::Cow; +use std::collections::BTreeMap; use std::collections::{HashMap, HashSet}; use std::fs; use std::io::ErrorKind; @@ -81,19 +106,33 @@ use std::path::Path; use std::str; use std::task::{ready, Poll}; +/// The current version of [`SummariesCache`]. +const CURRENT_CACHE_VERSION: u8 = 3; + +/// The maximum schema version of the `v` field in the index this version of +/// cargo understands. See [`IndexPackage::v`] for the detail. +const INDEX_V_MAX: u32 = 2; + /// Manager for handling the on-disk index. /// -/// Note that local and remote registries store the index differently. Local -/// is a simple on-disk tree of files of the raw index. Remote registries are -/// stored as a raw git repository. The different means of access are handled -/// via the [`RegistryData`] trait abstraction. +/// Different kinds of registries store the index differently: /// +/// * [`LocalRegistry`]` is a simple on-disk tree of files of the raw index. +/// * [`RemoteRegistry`] is stored as a raw git repository. +/// * [`HttpRegistry`] fills the on-disk index cache directly without keeping +/// any raw index. +/// +/// These means of access are handled via the [`RegistryData`] trait abstraction. /// This transparently handles caching of the index in a more efficient format. +/// +/// [`LocalRegistry`]: super::local::LocalRegistry +/// [`RemoteRegistry`]: super::remote::RemoteRegistry +/// [`HttpRegistry`]: super::http_remote::HttpRegistry pub struct RegistryIndex<'cfg> { source_id: SourceId, /// Root directory of the index for the registry. path: Filesystem, - /// Cache of summary data. + /// In-memory cache of summary data. /// /// This is keyed off the package name. The [`Summaries`] value handles /// loading the summary data. It keeps an optimized on-disk representation @@ -110,14 +149,16 @@ pub struct RegistryIndex<'cfg> { /// /// A list of summaries are loaded from disk via one of two methods: /// -/// 1. Primarily Cargo will parse the corresponding file for a crate in the -/// upstream crates.io registry. That's just a JSON blob per line which we -/// can parse, extract the version, and then store here. +/// 1. From raw registry index --- Primarily Cargo will parse the corresponding +/// file for a crate in the upstream crates.io registry. That's just a JSON +/// blob per line which we can parse, extract the version, and then store here. +/// See [`IndexPackage`] and [`IndexSummary::parse`]. /// -/// 2. Alternatively, if Cargo has previously run, we'll have a cached index of -/// dependencies for the upstream index. This is a file that Cargo maintains -/// lazily on the local filesystem and is much faster to parse since it -/// doesn't involve parsing all of the JSON. +/// 2. From on-disk index cache --- If Cargo has previously run, we'll have a +/// cached index of dependencies for the upstream index. This is a file that +/// Cargo maintains lazily on the local filesystem and is much faster to +/// parse since it doesn't involve parsing all of the JSON. +/// See [`SummariesCache`]. /// /// The outward-facing interface of this doesn't matter too much where it's /// loaded from, but it's important when reading the implementation to note that @@ -134,37 +175,191 @@ struct Summaries { versions: HashMap<Version, MaybeIndexSummary>, } -/// A lazily parsed `IndexSummary`. +/// A lazily parsed [`IndexSummary`]. enum MaybeIndexSummary { /// A summary which has not been parsed, The `start` and `end` are pointers - /// into `Summaries::raw_data` which this is an entry of. + /// into [`Summaries::raw_data`] which this is an entry of. Unparsed { start: usize, end: usize }, /// An actually parsed summary. Parsed(IndexSummary), } -/// A parsed representation of a summary from the index. +/// A parsed representation of a summary from the index. This is usually parsed +/// from a line from a raw index file, or a JSON blob from on-disk index cache. /// -/// In addition to a full `Summary` we have information on whether it is `yanked`. +/// In addition to a full [`Summary`], we have information on whether it is `yanked`. pub struct IndexSummary { pub summary: Summary, pub yanked: bool, - /// Schema version, see [`RegistryPackage`]. + /// Schema version, see [`IndexPackage::v`]. v: u32, } /// A representation of the cache on disk that Cargo maintains of summaries. +/// /// Cargo will initially parse all summaries in the registry and will then /// serialize that into this form and place it in a new location on disk, /// ensuring that access in the future is much speedier. +/// +/// For serialization and deserialization of this on-disk index cache of +/// summaries, see [`SummariesCache::serialize`] and [`SummariesCache::parse`]. +/// +/// # The format of the index cache +/// +/// The idea of this format is that it's a very easy file for Cargo to parse in +/// future invocations. The read from disk should be fast and then afterwards +/// all we need to know is what versions correspond to which JSON blob. +/// +/// Currently the format looks like: +/// +/// ```text +/// +---------------+----------------------+--------------------+---+ +/// | cache version | index schema version | index file version | 0 | +/// +---------------+----------------------+--------------------+---+ +/// ``` +/// +/// followed by one or more (version + JSON blob) pairs... +/// +/// ```text +/// +----------------+---+-----------+---+ +/// | semver version | 0 | JSON blob | 0 | ... +/// +----------------+---+-----------+---+ +/// ``` +/// +/// Each field represents: +/// +/// * _cache version_ --- Intended to ensure that there's some level of +/// future compatibility against changes to this cache format so if different +/// versions of Cargo share the same cache they don't get too confused. +/// * _index schema version_ --- The schema version of the raw index file. +/// See [`IndexPackage::v`] for the detail. +/// * _index file version_ --- Tracks when a cache needs to be regenerated. +/// A cache regeneration is required whenever the index file itself updates. +/// * _semver version_ --- The version for each JSON blob. Extracted from the +/// blob for fast queries without parsing the entire blob. +/// * _JSON blob_ --- The actual metadata for each version of the package. It +/// has the same representation as [`IndexPackage`]. +/// +/// # Changes between each cache version +/// +/// * `1`: The original version. +/// * `2`: Added the "index schema version" field so that if the index schema +/// changes, different versions of cargo won't get confused reading each +/// other's caches. +/// * `3`: Bumped the version to work around an issue where multiple versions of +/// a package were published that differ only by semver metadata. For +/// example, openssl-src 110.0.0 and 110.0.0+1.1.0f. Previously, the cache +/// would be incorrectly populated with two entries, both 110.0.0. After +/// this, the metadata will be correctly included. This isn't really a format +/// change, just a version bump to clear the incorrect cache entries. Note: +/// the index shouldn't allow these, but unfortunately crates.io doesn't +/// check it. +/// +/// See [`CURRENT_CACHE_VERSION`] for the current cache version. #[derive(Default)] struct SummariesCache<'a> { + /// JSON blobs of the summaries. Each JSON blob has a [`Version`] beside, + /// so that Cargo can query a version without full JSON parsing. versions: Vec<(Version, &'a [u8])>, + /// For cache invalidation, we tracks the index file version to determine + /// when to regenerate the cache itself. index_version: &'a str, } +/// A single line in the index representing a single version of a package. +#[derive(Deserialize)] +pub struct IndexPackage<'a> { + /// Name of the pacakge. + name: InternedString, + /// The version of this dependency. + vers: Version, + /// All kinds of direct dependencies of the package, including dev and + /// build dependencies. + #[serde(borrow)] + deps: Vec<RegistryDependency<'a>>, + /// Set of features defined for the package, i.e., `[features]` table. + features: BTreeMap<InternedString, Vec<InternedString>>, + /// This field contains features with new, extended syntax. Specifically, + /// namespaced features (`dep:`) and weak dependencies (`pkg?/feat`). + /// + /// This is separated from `features` because versions older than 1.19 + /// will fail to load due to not being able to parse the new syntax, even + /// with a `Cargo.lock` file. + features2: Option<BTreeMap<InternedString, Vec<InternedString>>>, + /// Checksum for verifying the integrity of the corresponding downloaded package. + cksum: String, + /// If `true`, Cargo will skip this version when resolving. + /// + /// This was added in 2014. Everything in the crates.io index has this set + /// now, so this probably doesn't need to be an option anymore. + yanked: Option<bool>, + /// Native library name this package links to. + /// + /// Added early 2018 (see <https://github.com/rust-lang/cargo/pull/4978>), + /// can be `None` if published before then. + links: Option<InternedString>, + /// Required version of rust + /// + /// Corresponds to `package.rust-version`. + /// + /// Added in 2023 (see <https://github.com/rust-lang/crates.io/pull/6267>), + /// can be `None` if published before then or if not set in the manifest. + rust_version: Option<InternedString>, + /// The schema version for this entry. + /// + /// If this is None, it defaults to version `1`. Entries with unknown + /// versions are ignored. + /// + /// Version `2` schema adds the `features2` field. + /// + /// This provides a method to safely introduce changes to index entries + /// and allow older versions of cargo to ignore newer entries it doesn't + /// understand. This is honored as of 1.51, so unfortunately older + /// versions will ignore it, and potentially misinterpret version 2 and + /// newer entries. + /// + /// The intent is that versions older than 1.51 will work with a + /// pre-existing `Cargo.lock`, but they may not correctly process `cargo + /// update` or build a lock from scratch. In that case, cargo may + /// incorrectly select a new package that uses a new index schema. A + /// workaround is to downgrade any packages that are incompatible with the + /// `--precise` flag of `cargo update`. + v: Option<u32>, +} + +/// A dependency as encoded in the [`IndexPackage`] index JSON. +#[derive(Deserialize)] +struct RegistryDependency<'a> { + /// Name of the dependency. If the dependency is renamed, the original + /// would be stored in [`RegistryDependency::package`]. + name: InternedString, + /// The SemVer requirement for this dependency. + #[serde(borrow)] + req: Cow<'a, str>, + /// Set of features enabled for this dependency. + features: Vec<InternedString>, + /// Whether or not this is an optional dependency. + optional: bool, + /// Whether or not default features are enabled. + default_features: bool, + /// The target platform for this dependency. + target: Option<Cow<'a, str>>, + /// The dependency kind. "dev", "build", and "normal". + kind: Option<Cow<'a, str>>, + // The URL of the index of the registry where this dependency is from. + // `None` if it is from the same index. + registry: Option<Cow<'a, str>>, + /// The original name if the dependency is renamed. + package: Option<InternedString>, + /// Whether or not this is a public dependency. Unstable. See [RFC 1977]. + /// + /// [RFC 1977]: https://rust-lang.github.io/rfcs/1977-public-private-dependencies.html + public: Option<bool>, +} + impl<'cfg> RegistryIndex<'cfg> { + /// Creates an empty registry index at `path`. pub fn new( source_id: SourceId, path: &Filesystem, @@ -178,7 +373,9 @@ impl<'cfg> RegistryIndex<'cfg> { } } - /// Returns the hash listed for a specified `PackageId`. + /// Returns the hash listed for a specified `PackageId`. Primarily for + /// checking the integrity of a downloaded package matching the checksum in + /// the index file, aka [`IndexSummary`]. pub fn hash(&mut self, pkg: PackageId, load: &mut dyn RegistryData) -> Poll<CargoResult<&str>> { let req = OptVersionReq::exact(pkg.version()); let summary = self.summaries(&pkg.name(), &req, load)?; @@ -191,10 +388,14 @@ impl<'cfg> RegistryIndex<'cfg> { } /// Load a list of summaries for `name` package in this registry which - /// match `req` + /// match `req`. + /// + /// This function will semantically + /// + /// 1. parse the index file (either raw or cache), + /// 2. match all versions, + /// 3. and then return an iterator over all summaries which matched. /// - /// This function will semantically parse the on-disk index, match all - /// versions, and then return an iterator over all summaries which matched. /// Internally there's quite a few layer of caching to amortize this cost /// though since this method is called quite a lot on null builds in Cargo. pub fn summaries<'a, 'b>( @@ -207,12 +408,8 @@ impl<'cfg> RegistryIndex<'cfg> { 'a: 'b, { let source_id = self.source_id; - let config = self.config; - // First up actually parse what summaries we have available. If Cargo - // has run previously this will parse a Cargo-specific cache file rather - // than the registry itself. In effect this is intended to be a quite - // cheap operation. + // First up parse what summaries we have available. let name = InternedString::new(name); let summaries = ready!(self.load_summaries(name, load)?); @@ -227,15 +424,13 @@ impl<'cfg> RegistryIndex<'cfg> { .versions .iter_mut() .filter_map(move |(k, v)| if req.matches(k) { Some(v) } else { None }) - .filter_map( - move |maybe| match maybe.parse(config, raw_data, source_id) { - Ok(summary) => Some(summary), - Err(e) => { - info!("failed to parse `{}` registry package: {}", name, e); - None - } - }, - ) + .filter_map(move |maybe| match maybe.parse(raw_data, source_id) { + Ok(summary) => Some(summary), + Err(e) => { + info!("failed to parse `{}` registry package: {}", name, e); + None + } + }) .filter(move |is| { if is.v > INDEX_V_MAX { debug!( @@ -251,13 +446,28 @@ impl<'cfg> RegistryIndex<'cfg> { }))) } + /// Actually parses what summaries we have available. + /// + /// If Cargo has run previously, this tries in this order: + /// + /// 1. Returns from in-memory cache, aka [`RegistryIndex::summaries_cache`]. + /// 2. If missing, hands over to [`Summaries::parse`] to parse an index file. + /// + /// The actual kind index file being parsed depends on which kind of + /// [`RegistryData`] the `load` argument is given. For example, a + /// Git-based [`RemoteRegistry`] will first try a on-disk index cache + /// file, and then try parsing registry raw index fomr Git repository. + /// + /// In effect, this is intended to be a quite cheap operation. + /// + /// [`RemoteRegistry`]: super::remote::RemoteRegistry fn load_summaries( &mut self, name: InternedString, load: &mut dyn RegistryData, ) -> Poll<CargoResult<&mut Summaries>> { // If we've previously loaded what versions are present for `name`, just - // return that since our cache should still be valid. + // return that since our in-memory cache should still be valid. if self.summaries_cache.contains_key(&name) { return Poll::Ready(Ok(self.summaries_cache.get_mut(&name).unwrap())); } @@ -271,12 +481,7 @@ impl<'cfg> RegistryIndex<'cfg> { // See module comment in `registry/mod.rs` for why this is structured // the way it is. - let fs_name = name - .chars() - .flat_map(|c| c.to_lowercase()) - .collect::<String>(); - - let path = make_dep_path(&fs_name, false); + let path = make_dep_path(&name.to_lowercase(), false); let summaries = ready!(Summaries::parse( root, &cache_root, @@ -295,6 +500,9 @@ impl<'cfg> RegistryIndex<'cfg> { self.summaries_cache.clear(); } + /// Attempts to find the packages that match a `name` and a version `req`. + /// + /// This is primarily used by [`Source::query`](super::Source). pub fn query_inner( &mut self, name: &str, @@ -324,6 +532,10 @@ impl<'cfg> RegistryIndex<'cfg> { .map_ok(|_| ()) } + /// Inner implementation of [`Self::query_inner`]. Returns the number of + /// summaries we've got. + /// + /// The `online` controls whether Cargo can access the network when needed. fn query_inner_with_online( &mut self, name: &str, @@ -404,6 +616,7 @@ impl<'cfg> RegistryIndex<'cfg> { Poll::Ready(Ok(count)) } + /// Looks into the summaries to check if a package has been yanked. pub fn is_yanked( &mut self, pkg: PackageId, @@ -418,23 +631,26 @@ impl<'cfg> RegistryIndex<'cfg> { } impl Summaries { - /// Parse out a `Summaries` instances from on-disk state. + /// Parse out a [`Summaries`] instances from on-disk state. /// - /// This will attempt to prefer parsing a previous cache file that already - /// exists from a previous invocation of Cargo (aka you're typing `cargo - /// build` again after typing it previously). If parsing fails or the cache - /// isn't found, then we take a slower path which loads the full descriptor - /// for `relative` from the underlying index (aka typically libgit2 with - /// crates.io) and then parse everything in there. + /// This will do the followings in order: /// - /// * `root` - this is the root argument passed to `load` - /// * `cache_root` - this is the root on the filesystem itself of where to - /// store cache files. - /// * `relative` - this is the file we're loading from cache or the index + /// 1. Attempt to prefer parsing a previous index cache file that already + /// exists from a previous invocation of Cargo (aka you're typing `cargo + /// build` again after typing it previously). + /// 2. If parsing fails, or the cache isn't found or is invalid, we then + /// take a slower path which loads the full descriptor for `relative` + /// from the underlying index (aka libgit2 with crates.io, or from a + /// remote HTTP index) and then parse everything in there. + /// + /// * `root` --- this is the root argument passed to `load` + /// * `cache_root` --- this is the root on the filesystem itself of where + /// to store cache files. + /// * `relative` --- this is the file we're loading from cache or the index /// data - /// * `source_id` - the registry's SourceId used when parsing JSON blobs to - /// create summaries. - /// * `load` - the actual index implementation which may be very slow to + /// * `source_id` --- the registry's SourceId used when parsing JSON blobs + /// to create summaries. + /// * `load` --- the actual index implementation which may be very slow to /// call. We avoid this if we can. pub fn parse( root: &Path, @@ -495,7 +711,7 @@ impl Summaries { // allow future cargo implementations to break the // interpretation of each line here and older cargo will simply // ignore the new lines. - let summary = match IndexSummary::parse(config, line, source_id) { + let summary = match IndexSummary::parse(line, source_id) { Ok(summary) => summary, Err(e) => { // This should only happen when there is an index @@ -549,8 +765,8 @@ impl Summaries { } } - /// Parses an open `File` which represents information previously cached by - /// Cargo. + /// Parses the contents of an on-disk cache, aka [`SummariesCache`], which + /// represents information previously cached by Cargo. pub fn parse_cache(contents: Vec<u8>) -> CargoResult<(Summaries, InternedString)> { let cache = SummariesCache::parse(&contents)?; let index_version = InternedString::new(cache.index_version); @@ -577,46 +793,8 @@ impl Summaries { } } -// Implementation of serializing/deserializing the cache of summaries on disk. -// Currently the format looks like: -// -// +--------------------+----------------------+-------------+---+ -// | cache version byte | index format version | git sha rev | 0 | -// +--------------------+----------------------+-------------+---+ -// -// followed by... -// -// +----------------+---+------------+---+ -// | semver version | 0 | JSON blob | 0 | ... -// +----------------+---+------------+---+ -// -// The idea is that this is a very easy file for Cargo to parse in future -// invocations. The read from disk should be quite fast and then afterwards all -// we need to know is what versions correspond to which JSON blob. -// -// The leading version byte is intended to ensure that there's some level of -// future compatibility against changes to this cache format so if different -// versions of Cargo share the same cache they don't get too confused. The git -// sha lets us know when the file needs to be regenerated (it needs regeneration -// whenever the index itself updates). -// -// Cache versions: -// * `1`: The original version. -// * `2`: Added the "index format version" field so that if the index format -// changes, different versions of cargo won't get confused reading each -// other's caches. -// * `3`: Bumped the version to work around an issue where multiple versions of -// a package were published that differ only by semver metadata. For -// example, openssl-src 110.0.0 and 110.0.0+1.1.0f. Previously, the cache -// would be incorrectly populated with two entries, both 110.0.0. After -// this, the metadata will be correctly included. This isn't really a format -// change, just a version bump to clear the incorrect cache entries. Note: -// the index shouldn't allow these, but unfortunately crates.io doesn't -// check it. - -const CURRENT_CACHE_VERSION: u8 = 3; - impl<'a> SummariesCache<'a> { + /// Deserializes an on-disk cache. fn parse(data: &'a [u8]) -> CargoResult<SummariesCache<'a>> { // NB: keep this method in sync with `serialize` below let (first_byte, rest) = data @@ -627,13 +805,11 @@ impl<'a> SummariesCache<'a> { } let index_v_bytes = rest .get(..4) - .ok_or_else(|| anyhow::anyhow!("cache expected 4 bytes for index version"))?; + .ok_or_else(|| anyhow::anyhow!("cache expected 4 bytes for index schema version"))?; let index_v = u32::from_le_bytes(index_v_bytes.try_into().unwrap()); if index_v != INDEX_V_MAX { bail!( - "index format version {} doesn't match the version I know ({})", - index_v, - INDEX_V_MAX + "index schema version {index_v} doesn't match the version I know ({INDEX_V_MAX})", ); } let rest = &rest[4..]; @@ -655,6 +831,7 @@ impl<'a> SummariesCache<'a> { Ok(ret) } + /// Serializes itself with a given `index_version`. fn serialize(&self, index_version: &str) -> Vec<u8> { // NB: keep this method in sync with `parse` above let size = self @@ -683,17 +860,12 @@ impl MaybeIndexSummary { /// Does nothing if this is already `Parsed`, and otherwise the `raw_data` /// passed in is sliced with the bounds in `Unparsed` and then actually /// parsed. - fn parse( - &mut self, - config: &Config, - raw_data: &[u8], - source_id: SourceId, - ) -> CargoResult<&IndexSummary> { + fn parse(&mut self, raw_data: &[u8], source_id: SourceId) -> CargoResult<&IndexSummary> { let (start, end) = match self { MaybeIndexSummary::Unparsed { start, end } => (*start, *end), MaybeIndexSummary::Parsed(summary) => return Ok(summary), }; - let summary = IndexSummary::parse(config, &raw_data[start..end], source_id)?; + let summary = IndexSummary::parse(&raw_data[start..end], source_id)?; *self = MaybeIndexSummary::Parsed(summary); match self { MaybeIndexSummary::Unparsed { .. } => unreachable!(), @@ -709,18 +881,19 @@ impl From<IndexSummary> for MaybeIndexSummary { } impl IndexSummary { - /// Parses a line from the registry's index file into an `IndexSummary` for - /// a package. + /// Parses a line from the registry's index file into an [`IndexSummary`] + /// for a package. /// - /// The `line` provided is expected to be valid JSON. - fn parse(config: &Config, line: &[u8], source_id: SourceId) -> CargoResult<IndexSummary> { + /// The `line` provided is expected to be valid JSON. It is supposed to be + /// a [`IndexPackage`]. + fn parse(line: &[u8], source_id: SourceId) -> CargoResult<IndexSummary> { // ****CAUTION**** Please be extremely careful with returning errors // from this function. Entries that error are not included in the // index cache, and can cause cargo to get confused when switching // between different versions that understand the index differently. // Make sure to consider the INDEX_V_MAX and CURRENT_CACHE_VERSION // values carefully when making changes here. - let RegistryPackage { + let IndexPackage { name, vers, cksum, @@ -744,7 +917,7 @@ impl IndexSummary { features.entry(name).or_default().extend(values); } } - let mut summary = Summary::new(config, pkgid, deps, &features, links, rust_version)?; + let mut summary = Summary::new(pkgid, deps, &features, links, rust_version)?; summary.set_checksum(cksum); Ok(IndexSummary { summary, @@ -754,6 +927,71 @@ impl IndexSummary { } } +impl<'a> RegistryDependency<'a> { + /// Converts an encoded dependency in the registry to a cargo dependency + pub fn into_dep(self, default: SourceId) -> CargoResult<Dependency> { + let RegistryDependency { + name, + req, + mut features, + optional, + default_features, + target, + kind, + registry, + package, + public, + } = self; + + let id = if let Some(registry) = ®istry { + SourceId::for_registry(®istry.into_url()?)? + } else { + default + }; + + let mut dep = Dependency::parse(package.unwrap_or(name), Some(&req), id)?; + if package.is_some() { + dep.set_explicit_name_in_toml(name); + } + let kind = match kind.as_deref().unwrap_or("") { + "dev" => DepKind::Development, + "build" => DepKind::Build, + _ => DepKind::Normal, + }; + + let platform = match target { + Some(target) => Some(target.parse()?), + None => None, + }; + + // All dependencies are private by default + let public = public.unwrap_or(false); + + // Unfortunately older versions of cargo and/or the registry ended up + // publishing lots of entries where the features array contained the + // empty feature, "", inside. This confuses the resolution process much + // later on and these features aren't actually valid, so filter them all + // out here. + features.retain(|s| !s.is_empty()); + + // In index, "registry" is null if it is from the same index. + // In Cargo.toml, "registry" is None if it is from the default + if !id.is_crates_io() { + dep.set_registry_id(id); + } + + dep.set_optional(optional) + .set_default_features(default_features) + .set_features(features) + .set_platform(platform) + .set_kind(kind) + .set_public(public); + + Ok(dep) + } +} + +/// Like [`slice::split`] but is optimized by [`memchr`]. fn split(haystack: &[u8], needle: u8) -> impl Iterator<Item = &[u8]> { struct Split<'a> { haystack: &'a [u8], @@ -778,3 +1016,36 @@ fn split(haystack: &[u8], needle: u8) -> impl Iterator<Item = &[u8]> { Split { haystack, needle } } + +#[test] +fn escaped_char_in_index_json_blob() { + let _: IndexPackage<'_> = serde_json::from_str( + r#"{"name":"a","vers":"0.0.1","deps":[],"cksum":"bae3","features":{}}"#, + ) + .unwrap(); + let _: IndexPackage<'_> = serde_json::from_str( + r#"{"name":"a","vers":"0.0.1","deps":[],"cksum":"bae3","features":{"test":["k","q"]},"links":"a-sys"}"# + ).unwrap(); + + // Now we add escaped cher all the places they can go + // these are not valid, but it should error later than json parsing + let _: IndexPackage<'_> = serde_json::from_str( + r#"{ + "name":"This name has a escaped cher in it \n\t\" ", + "vers":"0.0.1", + "deps":[{ + "name": " \n\t\" ", + "req": " \n\t\" ", + "features": [" \n\t\" "], + "optional": true, + "default_features": true, + "target": " \n\t\" ", + "kind": " \n\t\" ", + "registry": " \n\t\" " + }], + "cksum":"bae3", + "features":{"test \n\t\" ":["k \n\t\" ","q \n\t\" "]}, + "links":" \n\t\" "}"#, + ) + .unwrap(); +} diff --git a/src/tools/cargo/src/cargo/sources/registry/local.rs b/src/tools/cargo/src/cargo/sources/registry/local.rs index 89419191f..68fc61a6c 100644 --- a/src/tools/cargo/src/cargo/sources/registry/local.rs +++ b/src/tools/cargo/src/cargo/sources/registry/local.rs @@ -1,3 +1,5 @@ +//! Access to a regstiry on the local filesystem. See [`LocalRegistry`] for more. + use crate::core::PackageId; use crate::sources::registry::{LoadResponse, MaybeLock, RegistryConfig, RegistryData}; use crate::util::errors::CargoResult; @@ -10,18 +12,68 @@ use std::path::Path; use std::task::Poll; /// A local registry is a registry that lives on the filesystem as a set of -/// `.crate` files with an `index` directory in the same format as a remote +/// `.crate` files with an `index` directory in the [same format] as a remote /// registry. +/// +/// This type is primarily accessed through the [`RegistryData`] trait. +/// +/// When a local registry is requested for a package, it simply looks into what +/// its index has under the `index` directory. When [`LocalRegistry::download`] +/// is called, a local registry verifies the checksum of the requested `.crate` +/// tarball and then unpacks it to `$CARGO_HOME/.registry/src`. +/// +/// > Note that there is a third-party subcommand [`cargo-local-registry`], +/// > which happened to be developed by a former Cargo team member when local +/// > registry was introduced. The tool is to ease the burden of maintaining +/// > local registries. However, in general the Cargo team avoids recommending +/// > any specific third-party crate. Just FYI. +/// +/// [same format]: super#the-format-of-the-index +/// [`cargo-local-registry`]: https://crates.io/crates/cargo-local-registry +/// +/// # Filesystem hierarchy +/// +/// Here is an example layout of a local registry on a local filesystem: +/// +/// ```text +/// [registry root]/ +/// ├── index/ # registry index +/// │ ├── an/ +/// │ │ └── yh/ +/// │ │ └── anyhow +/// │ ├── ru/ +/// │ │ └── st/ +/// │ │ ├── rustls +/// │ │ └── rustls-ffi +/// │ └── se/ +/// │ └── mv/ +/// │ └── semver +/// ├── anyhow-1.0.71.crate # pre-downloaded crate tarballs +/// ├── rustls-0.20.8.crate +/// ├── rustls-ffi-0.8.2.crate +/// └── semver-1.0.17.crate +/// ``` +/// +/// For general concepts of registries, see the [module-level documentation](crate::sources::registry). pub struct LocalRegistry<'cfg> { + /// Path to the registry index. index_path: Filesystem, + /// Root path of this local registry. root: Filesystem, + /// Path where this local registry extract `.crate` tarballs to. src_path: Filesystem, config: &'cfg Config, + /// Whether this source has updated all package informations it may contain. updated: bool, + /// Disables status messages. quiet: bool, } impl<'cfg> LocalRegistry<'cfg> { + /// Creates a local registry at `root`. + /// + /// * `name` --- Name of a path segment where `.crate` tarballs are stored. + /// Expect to be unique. pub fn new(root: &Path, config: &'cfg Config, name: &str) -> LocalRegistry<'cfg> { LocalRegistry { src_path: config.registry_source_path().join(name), @@ -115,17 +167,15 @@ impl<'cfg> RegistryData for LocalRegistry<'cfg> { } fn download(&mut self, pkg: PackageId, checksum: &str) -> CargoResult<MaybeLock> { - let crate_file = format!("{}-{}.crate", pkg.name(), pkg.version()); - // Note that the usage of `into_path_unlocked` here is because the local // crate files here never change in that we're not the one writing them, // so it's not our responsibility to synchronize access to them. - let path = self.root.join(&crate_file).into_path_unlocked(); + let path = self.root.join(&pkg.tarball_name()).into_path_unlocked(); let mut crate_file = paths::open(&path)?; // If we've already got an unpacked version of this crate, then skip the // checksum below as it is in theory already verified. - let dst = format!("{}-{}", pkg.name(), pkg.version()); + let dst = path.file_stem().unwrap(); if self.src_path.join(dst).into_path_unlocked().exists() { return Ok(MaybeLock::Ready(crate_file)); } diff --git a/src/tools/cargo/src/cargo/sources/registry/mod.rs b/src/tools/cargo/src/cargo/sources/registry/mod.rs index c76ee6142..a0178db55 100644 --- a/src/tools/cargo/src/cargo/sources/registry/mod.rs +++ b/src/tools/cargo/src/cargo/sources/registry/mod.rs @@ -2,13 +2,47 @@ //! //! # What's a Registry? //! -//! Registries are central locations where packages can be uploaded to, +//! [Registries] are central locations where packages can be uploaded to, //! discovered, and searched for. The purpose of a registry is to have a //! location that serves as permanent storage for versions of a crate over time. //! -//! Compared to git sources, a registry provides many packages as well as many -//! versions simultaneously. Git sources can also have commits deleted through -//! rebasings where registries cannot have their versions deleted. +//! Compared to git sources (see [`GitSource`]), a registry provides many +//! packages as well as many versions simultaneously. Git sources can also +//! have commits deleted through rebasings where registries cannot have their +//! versions deleted. +//! +//! In Cargo, [`RegistryData`] is an abstraction over each kind of actual +//! registry, and [`RegistrySource`] connects those implementations to +//! [`Source`] trait. Two prominent features these abstractions provide are +//! +//! * A way to query the metadata of a package from a registry. The metadata +//! comes from the index. +//! * A way to download package contents (a.k.a source files) that are required +//! when building the package itself. +//! +//! We'll cover each functionality later. +//! +//! [Registries]: https://doc.rust-lang.org/nightly/cargo/reference/registries.html +//! [`GitSource`]: super::GitSource +//! +//! # Different Kinds of Registries +//! +//! Cargo provides multiple kinds of registries. Each of them serves the index +//! and package contents in a slightly different way. Namely, +//! +//! * [`LocalRegistry`] --- Serves the index and package contents entirely on +//! a local filesystem. +//! * [`RemoteRegistry`] --- Serves the index ahead of time from a Git +//! repository, and package contents are downloaded as needed. +//! * [`HttpRegistry`] --- Serves both the index and package contents on demand +//! over a HTTP-based registry API. This is the default starting from 1.70.0. +//! +//! Each registry has its own [`RegistryData`] implementation, and can be +//! created from either [`RegistrySource::local`] or [`RegistrySource::remote`]. +//! +//! [`LocalRegistry`]: local::LocalRegistry +//! [`RemoteRegistry`]: remote::RemoteRegistry +//! [`HttpRegistry`]: http_remote::HttpRegistry //! //! # The Index of a Registry //! @@ -20,36 +54,16 @@ //! available on a registry, what versions are available, and what the //! dependencies for each version is. //! -//! One method of doing so would be having the registry expose an HTTP endpoint -//! which can be queried with a list of packages and a response of their -//! dependencies and versions is returned. This is somewhat inefficient however -//! as we may have to hit the endpoint many times and we may have already -//! queried for much of the data locally already (for other packages, for -//! example). This also involves inventing a transport format between the -//! registry and Cargo itself, so this route was not taken. -//! -//! Instead, Cargo communicates with registries through a git repository -//! referred to as the Index. The Index of a registry is essentially an easily -//! query-able version of the registry's database for a list of versions of a -//! package as well as a list of dependencies for each version. +//! To solve the problem, a registry must provide an index of package metadata. +//! The index of a registry is essentially an easily query-able version of the +//! registry's database for a list of versions of a package as well as a list +//! of dependencies for each version. The exact format of the index is +//! described later. //! -//! Using git to host this index provides a number of benefits: +//! See the [`index`] module for topics about the management, parsing, caching, +//! and versioning for the on-disk index. //! -//! * The entire index can be stored efficiently locally on disk. This means -//! that all queries of a registry can happen locally and don't need to touch -//! the network. -//! -//! * Updates of the index are quite efficient. Using git buys incremental -//! updates, compressed transmission, etc for free. The index must be updated -//! each time we need fresh information from a registry, but this is one -//! update of a git repository that probably hasn't changed a whole lot so -//! it shouldn't be too expensive. -//! -//! Additionally, each modification to the index is just appending a line at -//! the end of a file (the exact format is described later). This means that -//! the commits for an index are quite small and easily applied/compressible. -//! -//! ## The format of the Index +//! ## The Format of The Index //! //! The index is a store for the list of versions for all packages known, so its //! format on disk is optimized slightly to ensure that `ls registry` doesn't @@ -59,9 +73,12 @@ //! about the format of the registry: //! //! 1. Each crate will have one file corresponding to it. Each version for a -//! crate will just be a line in this file. +//! crate will just be a line in this file (see [`IndexPackage`] for its +//! representation). //! 2. There will be two tiers of directories for crate names, under which //! crates corresponding to those tiers will be located. +//! (See [`cargo_util::registry::make_dep_path`] for the implementation of +//! this layout hierarchy.) //! //! As an example, this is an example hierarchy of an index: //! @@ -99,26 +116,30 @@ //! The purpose of this layout is to hopefully cut down on `ls` sizes as well as //! efficient lookup based on the crate name itself. //! -//! ## Crate files +//! See [The Cargo Book: Registry Index][registry-index] for the public +//! interface on the index format. +//! +//! [registry-index]: https://doc.rust-lang.org/nightly/cargo/reference/registry-index.html +//! +//! ## The Index Files //! //! Each file in the index is the history of one crate over time. Each line in //! the file corresponds to one version of a crate, stored in JSON format (see -//! the `RegistryPackage` structure below). +//! the [`IndexPackage`] structure). //! -//! As new versions are published, new lines are appended to this file. The only -//! modifications to this file that should happen over time are yanks of a -//! particular version. +//! As new versions are published, new lines are appended to this file. **The +//! only modifications to this file that should happen over time are yanks of a +//! particular version.** //! //! # Downloading Packages //! -//! The purpose of the Index was to provide an efficient method to resolve the -//! dependency graph for a package. So far we only required one network -//! interaction to update the registry's repository (yay!). After resolution has -//! been performed, however we need to download the contents of packages so we -//! can read the full manifest and build the source code. +//! The purpose of the index was to provide an efficient method to resolve the +//! dependency graph for a package. After resolution has been performed, we need +//! to download the contents of packages so we can read the full manifest and +//! build the source code. //! -//! To accomplish this, this source's `download` method will make an HTTP -//! request per-package requested to download tarballs into a local cache. These +//! To accomplish this, [`RegistryData::download`] will "make" an HTTP request +//! per-package requested to download tarballs into a local cache. These //! tarballs will then be unpacked into a destination folder. //! //! Note that because versions uploaded to the registry are frozen forever that @@ -128,7 +149,8 @@ //! //! # Filesystem Hierarchy //! -//! Overall, the `$HOME/.cargo` looks like this when talking about the registry: +//! Overall, the `$HOME/.cargo` looks like this when talking about the registry +//! (remote registries, specifically): //! //! ```notrust //! # A folder under which all registry metadata is hosted (similar to @@ -144,8 +166,8 @@ //! registry2-<hash>/ //! ... //! -//! # This folder is a cache for all downloaded tarballs from a registry. -//! # Once downloaded and verified, a tarball never changes. +//! # This folder is a cache for all downloaded tarballs (`.crate` file) +//! # from a registry. Once downloaded and verified, a tarball never changes. //! cache/ //! registry1-<hash>/<pkg>-<version>.crate //! ... @@ -153,13 +175,14 @@ //! # Location in which all tarballs are unpacked. Each tarball is known to //! # be frozen after downloading, so transitively this folder is also //! # frozen once its unpacked (it's never unpacked again) +//! # CAVEAT: They are not read-only. See rust-lang/cargo#9455. //! src/ //! registry1-<hash>/<pkg>-<version>/... //! ... //! ``` +//! +//! [`IndexPackage`]: index::IndexPackage -use std::borrow::Cow; -use std::collections::BTreeMap; use std::collections::HashSet; use std::fs; use std::fs::{File, OpenOptions}; @@ -173,35 +196,30 @@ use anyhow::Context as _; use cargo_util::paths::{self, exclude_from_backups_and_indexing}; use flate2::read::GzDecoder; use log::debug; -use semver::Version; use serde::Deserialize; use serde::Serialize; use tar::Archive; -use crate::core::dependency::{DepKind, Dependency}; +use crate::core::dependency::Dependency; use crate::core::source::MaybePackage; use crate::core::{Package, PackageId, QueryKind, Source, SourceId, Summary}; use crate::sources::PathSource; use crate::util::hex; -use crate::util::interning::InternedString; -use crate::util::into_url::IntoUrl; use crate::util::network::PollExt; use crate::util::{ restricted_names, CargoResult, Config, Filesystem, LimitErrorReader, OptVersionReq, }; +/// The `.cargo-ok` file is used to track if the source is already unpacked. +/// See [`RegistrySource::unpack_package`] for more. +/// +/// Not to be confused with `.cargo-ok` file in git sources. const PACKAGE_SOURCE_LOCK: &str = ".cargo-ok"; + pub const CRATES_IO_INDEX: &str = "https://github.com/rust-lang/crates.io-index"; pub const CRATES_IO_HTTP_INDEX: &str = "sparse+https://index.crates.io/"; pub const CRATES_IO_REGISTRY: &str = "crates-io"; pub const CRATES_IO_DOMAIN: &str = "crates.io"; -const CRATE_TEMPLATE: &str = "{crate}"; -const VERSION_TEMPLATE: &str = "{version}"; -const PREFIX_TEMPLATE: &str = "{prefix}"; -const LOWER_PREFIX_TEMPLATE: &str = "{lowerprefix}"; -const CHECKSUM_TEMPLATE: &str = "{sha256-checksum}"; -const MAX_UNPACK_SIZE: u64 = 512 * 1024 * 1024; -const MAX_COMPRESSION_RATIO: usize = 20; // 20:1 /// The content inside `.cargo-ok`. /// See [`RegistrySource::unpack_package`] for more. @@ -211,13 +229,15 @@ struct LockMetadata { v: u32, } -/// A "source" for a local (see `local::LocalRegistry`) or remote (see -/// `remote::RemoteRegistry`) registry. +/// A [`Source`] implementation for a local or a remote registry. /// -/// This contains common functionality that is shared between the two registry -/// kinds, with the registry-specific logic implemented as part of the +/// This contains common functionality that is shared between each registry +/// kind, with the registry-specific logic implemented as part of the /// [`RegistryData`] trait referenced via the `ops` field. +/// +/// For general concepts of registries, see the [module-level documentation](crate::sources::registry). pub struct RegistrySource<'cfg> { + /// The unique identifier of this source. source_id: SourceId, /// The path where crate files are extracted (`$CARGO_HOME/registry/src/$REG-HASH`). src_path: Filesystem, @@ -237,7 +257,19 @@ pub struct RegistrySource<'cfg> { yanked_whitelist: HashSet<PackageId>, } -/// The `config.json` file stored in the index. +/// The [`config.json`] file stored in the index. +/// +/// The config file may look like: +/// +/// ```json +/// { +/// "dl": "https://example.com/api/{crate}/{version}/download", +/// "api": "https://example.com/api", +/// "auth-required": false # unstable feature (RFC 3139) +/// } +/// ``` +/// +/// [`config.json`]: https://doc.rust-lang.org/nightly/cargo/reference/registry-index.html#index-configuration #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "kebab-case")] pub struct RegistryConfig { @@ -257,6 +289,9 @@ pub struct RegistryConfig { /// will be extended with `/{crate}/{version}/download` to /// support registries like crates.io which were created before the /// templating setup was created. + /// + /// For more on the template of the download URL, see [Index Configuration]( + /// https://doc.rust-lang.org/nightly/cargo/reference/registry-index.html#index-configuration). pub dl: String, /// API endpoint for the registry. This is what's actually hit to perform @@ -264,183 +299,13 @@ pub struct RegistryConfig { /// If this is None, the registry does not support API commands. pub api: Option<String>, - /// Whether all operations require authentication. + /// Whether all operations require authentication. See [RFC 3139]. + /// + /// [RFC 3139]: https://rust-lang.github.io/rfcs/3139-cargo-alternative-registry-auth.html #[serde(default)] pub auth_required: bool, } -/// The maximum version of the `v` field in the index this version of cargo -/// understands. -pub(crate) const INDEX_V_MAX: u32 = 2; - -/// A single line in the index representing a single version of a package. -#[derive(Deserialize)] -pub struct RegistryPackage<'a> { - name: InternedString, - vers: Version, - #[serde(borrow)] - deps: Vec<RegistryDependency<'a>>, - features: BTreeMap<InternedString, Vec<InternedString>>, - /// This field contains features with new, extended syntax. Specifically, - /// namespaced features (`dep:`) and weak dependencies (`pkg?/feat`). - /// - /// This is separated from `features` because versions older than 1.19 - /// will fail to load due to not being able to parse the new syntax, even - /// with a `Cargo.lock` file. - features2: Option<BTreeMap<InternedString, Vec<InternedString>>>, - cksum: String, - /// If `true`, Cargo will skip this version when resolving. - /// - /// This was added in 2014. Everything in the crates.io index has this set - /// now, so this probably doesn't need to be an option anymore. - yanked: Option<bool>, - /// Native library name this package links to. - /// - /// Added early 2018 (see <https://github.com/rust-lang/cargo/pull/4978>), - /// can be `None` if published before then. - links: Option<InternedString>, - /// Required version of rust - /// - /// Corresponds to `package.rust-version`. - /// - /// Added in 2023 (see <https://github.com/rust-lang/crates.io/pull/6267>), - /// can be `None` if published before then or if not set in the manifest. - rust_version: Option<InternedString>, - /// The schema version for this entry. - /// - /// If this is None, it defaults to version 1. Entries with unknown - /// versions are ignored. - /// - /// Version `2` format adds the `features2` field. - /// - /// This provides a method to safely introduce changes to index entries - /// and allow older versions of cargo to ignore newer entries it doesn't - /// understand. This is honored as of 1.51, so unfortunately older - /// versions will ignore it, and potentially misinterpret version 2 and - /// newer entries. - /// - /// The intent is that versions older than 1.51 will work with a - /// pre-existing `Cargo.lock`, but they may not correctly process `cargo - /// update` or build a lock from scratch. In that case, cargo may - /// incorrectly select a new package that uses a new index format. A - /// workaround is to downgrade any packages that are incompatible with the - /// `--precise` flag of `cargo update`. - v: Option<u32>, -} - -#[test] -fn escaped_char_in_json() { - let _: RegistryPackage<'_> = serde_json::from_str( - r#"{"name":"a","vers":"0.0.1","deps":[],"cksum":"bae3","features":{}}"#, - ) - .unwrap(); - let _: RegistryPackage<'_> = serde_json::from_str( - r#"{"name":"a","vers":"0.0.1","deps":[],"cksum":"bae3","features":{"test":["k","q"]},"links":"a-sys"}"# - ).unwrap(); - - // Now we add escaped cher all the places they can go - // these are not valid, but it should error later than json parsing - let _: RegistryPackage<'_> = serde_json::from_str( - r#"{ - "name":"This name has a escaped cher in it \n\t\" ", - "vers":"0.0.1", - "deps":[{ - "name": " \n\t\" ", - "req": " \n\t\" ", - "features": [" \n\t\" "], - "optional": true, - "default_features": true, - "target": " \n\t\" ", - "kind": " \n\t\" ", - "registry": " \n\t\" " - }], - "cksum":"bae3", - "features":{"test \n\t\" ":["k \n\t\" ","q \n\t\" "]}, - "links":" \n\t\" "}"#, - ) - .unwrap(); -} - -/// A dependency as encoded in the index JSON. -#[derive(Deserialize)] -struct RegistryDependency<'a> { - name: InternedString, - #[serde(borrow)] - req: Cow<'a, str>, - features: Vec<InternedString>, - optional: bool, - default_features: bool, - target: Option<Cow<'a, str>>, - kind: Option<Cow<'a, str>>, - registry: Option<Cow<'a, str>>, - package: Option<InternedString>, - public: Option<bool>, -} - -impl<'a> RegistryDependency<'a> { - /// Converts an encoded dependency in the registry to a cargo dependency - pub fn into_dep(self, default: SourceId) -> CargoResult<Dependency> { - let RegistryDependency { - name, - req, - mut features, - optional, - default_features, - target, - kind, - registry, - package, - public, - } = self; - - let id = if let Some(registry) = ®istry { - SourceId::for_registry(®istry.into_url()?)? - } else { - default - }; - - let mut dep = Dependency::parse(package.unwrap_or(name), Some(&req), id)?; - if package.is_some() { - dep.set_explicit_name_in_toml(name); - } - let kind = match kind.as_deref().unwrap_or("") { - "dev" => DepKind::Development, - "build" => DepKind::Build, - _ => DepKind::Normal, - }; - - let platform = match target { - Some(target) => Some(target.parse()?), - None => None, - }; - - // All dependencies are private by default - let public = public.unwrap_or(false); - - // Unfortunately older versions of cargo and/or the registry ended up - // publishing lots of entries where the features array contained the - // empty feature, "", inside. This confuses the resolution process much - // later on and these features aren't actually valid, so filter them all - // out here. - features.retain(|s| !s.is_empty()); - - // In index, "registry" is null if it is from the same index. - // In Cargo.toml, "registry" is None if it is from the default - if !id.is_crates_io() { - dep.set_registry_id(id); - } - - dep.set_optional(optional) - .set_default_features(default_features) - .set_features(features) - .set_platform(platform) - .set_kind(kind) - .set_public(public); - - Ok(dep) - } -} - /// Result from loading data from a registry. pub enum LoadResponse { /// The cache is valid. The cached data should be used. @@ -449,6 +314,7 @@ pub enum LoadResponse { /// The cache is out of date. Returned data should be used. Data { raw_data: Vec<u8>, + /// Version of this data to determine whether it is out of date. index_version: Option<String>, }, @@ -456,10 +322,11 @@ pub enum LoadResponse { NotFound, } -/// An abstract interface to handle both a local (see `local::LocalRegistry`) -/// and remote (see `remote::RemoteRegistry`) registry. +/// An abstract interface to handle both a local and and remote registry. /// -/// This allows [`RegistrySource`] to abstractly handle both registry kinds. +/// This allows [`RegistrySource`] to abstractly handle each registry kind. +/// +/// For general concepts of registries, see the [module-level documentation](crate::sources::registry). pub trait RegistryData { /// Performs initialization for the registry. /// @@ -470,14 +337,15 @@ pub trait RegistryData { /// Returns the path to the index. /// /// Note that different registries store the index in different formats - /// (remote=git, local=files). + /// (remote = git, http & local = files). fn index_path(&self) -> &Filesystem; /// Loads the JSON for a specific named package from the index. /// /// * `root` is the root path to the index. /// * `path` is the relative path to the package to load (like `ca/rg/cargo`). - /// * `index_version` is the version of the requested crate data currently in cache. + /// * `index_version` is the version of the requested crate data currently + /// in cache. This is useful for checking if a local cache is outdated. fn load( &mut self, root: &Path, @@ -568,6 +436,8 @@ mod index; 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 { let hash = hex::short_hash(&id); let ident = id.url().host_str().unwrap_or("").to_string(); @@ -579,6 +449,11 @@ fn short_name(id: SourceId, is_shallow: bool) -> String { } impl<'cfg> RegistrySource<'cfg> { + /// Creates a [`Source`] of a "remote" registry. + /// It could be either an HTTP-based [`http_remote::HttpRegistry`] or + /// a Git-based [`remote::RemoteRegistry`]. + /// + /// * `yanked_whitelist` --- Packages allowed to be used, even if they are yanked. pub fn remote( source_id: SourceId, yanked_whitelist: &HashSet<PackageId>, @@ -608,6 +483,10 @@ impl<'cfg> RegistrySource<'cfg> { )) } + /// Creates a [`Source`] of a local registry, with [`local::LocalRegistry`] under the hood. + /// + /// * `path` --- The root path of a local registry on the file system. + /// * `yanked_whitelist` --- Packages allowed to be used, even if they are yanked. pub fn local( source_id: SourceId, path: &Path, @@ -619,6 +498,12 @@ impl<'cfg> RegistrySource<'cfg> { RegistrySource::new(source_id, config, &name, Box::new(ops), yanked_whitelist) } + /// Creates a source of a registry. This is a inner helper function. + /// + /// * `name` --- Name of a path segment which may affect where `.crate` + /// tarballs, the registry index and cache are stored. Expect to be unique. + /// * `ops` --- The underlying [`RegistryData`] type. + /// * `yanked_whitelist` --- Packages allowed to be used, even if they are yanked. fn new( source_id: SourceId, config: &'cfg Config, @@ -636,7 +521,7 @@ impl<'cfg> RegistrySource<'cfg> { } } - /// Decode the configuration stored within the registry. + /// Decode the [configuration](RegistryConfig) stored within the registry. /// /// This requires that the index has been at least checked out. pub fn config(&mut self) -> Poll<CargoResult<Option<RegistryConfig>>> { @@ -692,8 +577,6 @@ impl<'cfg> RegistrySource<'cfg> { /// /// [CVE-2022-36113]: https://blog.rust-lang.org/2022/09/14/cargo-cves.html#arbitrary-file-corruption-cve-2022-36113 fn unpack_package(&self, pkg: PackageId, tarball: &File) -> CargoResult<PathBuf> { - // The `.cargo-ok` file is used to track if the source is already - // unpacked. let package_dir = format!("{}-{}", pkg.name(), pkg.version()); let dst = self.src_path.join(&package_dir); let path = dst.join(PACKAGE_SOURCE_LOCK); @@ -786,6 +669,12 @@ impl<'cfg> RegistrySource<'cfg> { Ok(unpack_dir.to_path_buf()) } + /// Turns the downloaded `.crate` tarball file into a [`Package`]. + /// + /// This unconditionally sets checksum for the returned package, so it + /// should only be called after doing integrity check. That is to say, + /// you need to call either [`RegistryData::download`] or + /// [`RegistryData::finish_download`] before calling this method. fn get_pkg(&mut self, package: PackageId, path: &File) -> CargoResult<Package> { let path = self .unpack_package(package, path) @@ -996,6 +885,11 @@ impl<'cfg> Source for RegistrySource<'cfg> { } } +impl RegistryConfig { + /// File name of [`RegistryConfig`]. + const NAME: &str = "config.json"; +} + /// Get the maximum upack size that Cargo permits /// based on a given `size` of your compressed file. /// @@ -1017,6 +911,9 @@ impl<'cfg> Source for RegistrySource<'cfg> { fn max_unpack_size(config: &Config, size: u64) -> u64 { const SIZE_VAR: &str = "__CARGO_TEST_MAX_UNPACK_SIZE"; const RATIO_VAR: &str = "__CARGO_TEST_MAX_UNPACK_RATIO"; + const MAX_UNPACK_SIZE: u64 = 512 * 1024 * 1024; // 512 MiB + const MAX_COMPRESSION_RATIO: usize = 20; // 20:1 + let max_unpack_size = if cfg!(debug_assertions) && config.get_env(SIZE_VAR).is_ok() { // For integration test only. config @@ -1041,30 +938,6 @@ fn max_unpack_size(config: &Config, size: u64) -> u64 { u64::max(max_unpack_size, size * max_compression_ratio as u64) } -fn make_dep_prefix(name: &str) -> String { - match name.len() { - 1 => String::from("1"), - 2 => String::from("2"), - 3 => format!("3/{}", &name[..1]), - _ => format!("{}/{}", &name[0..2], &name[2..4]), - } -} - -#[cfg(test)] -mod tests { - use super::make_dep_prefix; - - #[test] - fn dep_prefix() { - assert_eq!(make_dep_prefix("a"), "1"); - assert_eq!(make_dep_prefix("ab"), "2"); - assert_eq!(make_dep_prefix("abc"), "3/a"); - assert_eq!(make_dep_prefix("Abc"), "3/A"); - assert_eq!(make_dep_prefix("AbCd"), "Ab/Cd"); - assert_eq!(make_dep_prefix("aBcDe"), "aB/cD"); - } -} - /// Set the current [`umask`] value for the given tarball. No-op on non-Unix /// platforms. /// diff --git a/src/tools/cargo/src/cargo/sources/registry/remote.rs b/src/tools/cargo/src/cargo/sources/registry/remote.rs index f4df4e86b..4223b0303 100644 --- a/src/tools/cargo/src/cargo/sources/registry/remote.rs +++ b/src/tools/cargo/src/cargo/sources/registry/remote.rs @@ -1,3 +1,5 @@ +//! Access to a Git index based registry. See [`RemoteRegistry`] for details. + use crate::core::{GitReference, PackageId, SourceId}; use crate::sources::git; use crate::sources::git::fetch::RemoteKind; @@ -21,29 +23,73 @@ use std::task::{ready, Poll}; /// A remote registry is a registry that lives at a remote URL (such as /// crates.io). The git index is cloned locally, and `.crate` files are /// downloaded as needed and cached locally. +/// +/// This type is primarily accessed through the [`RegistryData`] trait. +/// +/// See the [module-level documentation](super) for the index format and layout. +/// +/// ## History of Git-based index registry +/// +/// Using Git to host this index used to be quite efficient. The full index can +/// be stored efficiently locally on disk, and once it is downloaded, all +/// queries of a registry can happen locally and needn't touch the network. +/// Git-based index was a reasonable design choice at the time when HTTP/2 +/// was just introduced. +/// +/// However, the full index keeps growing as crates.io grows. It becomes +/// relatively big and slows down the first use of Cargo. Git (specifically +/// libgit2) is not efficient at handling huge amounts of small files either. +/// On the other hand, newer protocols like HTTP/2 are prevalent and capable to +/// serve a bunch of tiny files. Today, it is encouraged to use [`HttpRegistry`], +/// which is the default from 1.70.0. That being said, Cargo will continue +/// supporting Git-based index for a pretty long while. +/// +/// [`HttpRegistry`]: super::http_remote::HttpRegistry pub struct RemoteRegistry<'cfg> { + /// Path to the registry index (`$CARGO_HOME/registry/index/$REG-HASH`). index_path: Filesystem, - /// Path to the cache of `.crate` files (`$CARGO_HOME/registry/path/$REG-HASH`). + /// Path to the cache of `.crate` files (`$CARGO_HOME/registry/cache/$REG-HASH`). cache_path: Filesystem, + /// The unique identifier of this registry source. source_id: SourceId, + /// This reference is stored so that when a registry needs update, it knows + /// where to fetch from. index_git_ref: GitReference, config: &'cfg Config, + /// A Git [tree object] to help this registry find crate metadata from the + /// underlying Git repository. + /// + /// This is stored here to prevent Git from repeatly creating a tree object + /// during each call into `load()`. + /// + /// [tree object]: https://git-scm.com/book/en/v2/Git-Internals-Git-Objects#_tree_objects tree: RefCell<Option<git2::Tree<'static>>>, + /// A Git repository that contains the actual index we want. repo: LazyCell<git2::Repository>, + /// The current HEAD commit of the underlying Git repository. head: Cell<Option<git2::Oid>>, + /// This stores sha value of the current HEAD commit for convenience. current_sha: Cell<Option<InternedString>>, - needs_update: bool, // Does this registry need to be updated? + /// Whether this registry needs to update package informations. + /// + /// See [`RemoteRegistry::mark_updated`] on how to make sure a registry + /// index is updated only once per session. + needs_update: bool, + /// Disables status messages. quiet: bool, } impl<'cfg> RemoteRegistry<'cfg> { + /// Creates a Git-rebased remote registry for `source_id`. + /// + /// * `name` --- Name of a path segment where `.crate` tarballs and the + /// registry index are stored. Expect to be unique. pub fn new(source_id: SourceId, config: &'cfg Config, name: &str) -> RemoteRegistry<'cfg> { RemoteRegistry { index_path: config.registry_index_path().join(name), cache_path: config.registry_cache_path().join(name), source_id, config, - // TODO: we should probably make this configurable index_git_ref: GitReference::DefaultBranch, tree: RefCell::new(None), repo: LazyCell::new(), @@ -54,18 +100,12 @@ impl<'cfg> RemoteRegistry<'cfg> { } } + /// Creates intermediate dirs and initialize the repository. fn repo(&self) -> CargoResult<&git2::Repository> { self.repo.try_borrow_with(|| { + trace!("acquiring registry index lock"); let path = self.config.assert_package_cache_locked(&self.index_path); - // Fast path without a lock - if let Ok(repo) = git2::Repository::open(&path) { - trace!("opened a repo without a lock"); - return Ok(repo); - } - - // Ok, now we need to lock and try the whole thing over again. - trace!("acquiring registry index lock"); match git2::Repository::open(&path) { Ok(repo) => Ok(repo), Err(_) => { @@ -97,6 +137,7 @@ impl<'cfg> RemoteRegistry<'cfg> { }) } + /// Get the object ID of the HEAD commit from the underlying Git repository. fn head(&self) -> CargoResult<git2::Oid> { if self.head.get().is_none() { let repo = self.repo()?; @@ -106,6 +147,8 @@ impl<'cfg> RemoteRegistry<'cfg> { Ok(self.head.get().unwrap()) } + /// Returns a [`git2::Tree`] object of the current HEAD commit of the + /// underlying Git repository. fn tree(&self) -> CargoResult<Ref<'_, git2::Tree<'_>>> { { let tree = self.tree.borrow(); @@ -117,6 +160,7 @@ impl<'cfg> RemoteRegistry<'cfg> { let commit = repo.find_commit(self.head()?)?; let tree = commit.tree()?; + // SAFETY: // Unfortunately in libgit2 the tree objects look like they've got a // reference to the repository object which means that a tree cannot // outlive the repository that it came from. Here we want to cache this @@ -134,6 +178,9 @@ impl<'cfg> RemoteRegistry<'cfg> { Ok(Ref::map(self.tree.borrow(), |s| s.as_ref().unwrap())) } + /// Gets the current version of the registry index. + /// + /// It is usually sha of the HEAD commit from the underlying Git repository. fn current_version(&self) -> Option<InternedString> { if let Some(sha) = self.current_sha.get() { return Some(sha); @@ -143,20 +190,24 @@ impl<'cfg> RemoteRegistry<'cfg> { Some(sha) } + /// Whether the registry is up-to-date. See [`Self::mark_updated`] for more. fn is_updated(&self) -> bool { self.config.updated_sources().contains(&self.source_id) } + /// Marks this registry as up-to-date. + /// + /// This makes sure the index is only updated once per session since it is + /// an expensive operation. This generally only happens when the resolver + /// is run multiple times, such as during `cargo publish`. fn mark_updated(&self) { self.config.updated_sources().insert(self.source_id); } } -const LAST_UPDATED_FILE: &str = ".last-updated"; - impl<'cfg> RegistryData for RemoteRegistry<'cfg> { fn prepare(&self) -> CargoResult<()> { - self.repo()?; // create intermediate dirs and initialize the repo + self.repo()?; Ok(()) } @@ -168,13 +219,20 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> { self.config.assert_package_cache_locked(path) } - // `index_version` Is a string representing the version of the file used to construct the cached copy. - // Older versions of Cargo used the single value of the hash of the HEAD commit as a `index_version`. - // This is technically correct but a little too conservative. If a new commit is fetched all cached - // files need to be regenerated even if a particular file was not changed. - // However if an old cargo has written such a file we still know how to read it, as long as we check for that hash value. - // - // Cargo now uses a hash of the file's contents as provided by git. + /// Read the general concept for `load()` on [`RegistryData::load`]. + /// + /// `index_version` is a string representing the version of the file used + /// to construct the cached copy. + /// + /// Older versions of Cargo used the single value of the hash of the HEAD + /// commit as a `index_version`. This is technically correct but a little + /// too conservative. If a new commit is fetched all cached files need to + /// be regenerated even if a particular file was not changed. + /// + /// However if an old cargo has written such a file we still know how to + /// read it, as long as we check for that hash value. + /// + /// Cargo now uses a hash of the file's contents as provided by git. fn load( &mut self, _root: &Path, @@ -187,7 +245,8 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> { // Check if the cache is valid. let git_commit_hash = self.current_version(); if index_version.is_some() && index_version == git_commit_hash.as_deref() { - // This file was written by an old version of cargo, but it is still up-to-date. + // This file was written by an old version of cargo, but it is + // still up-to-date. return Poll::Ready(Ok(LoadResponse::CacheValid)); } // Note that the index calls this method and the filesystem is locked @@ -224,8 +283,8 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> { match load_helper(&self, path, index_version) { Ok(result) => Poll::Ready(Ok(result)), Err(_) if !self.is_updated() => { - // If git returns an error and we haven't updated the repo, return - // pending to allow an update to try again. + // If git returns an error and we haven't updated the repo, + // return pending to allow an update to try again. self.needs_update = true; Poll::Pending } @@ -245,7 +304,7 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> { debug!("loading config"); self.prepare()?; self.config.assert_package_cache_locked(&self.index_path); - match ready!(self.load(Path::new(""), Path::new("config.json"), None)?) { + match ready!(self.load(Path::new(""), Path::new(RegistryConfig::NAME), None)?) { LoadResponse::Data { raw_data, .. } => { trace!("config loaded"); let mut cfg: RegistryConfig = serde_json::from_slice(&raw_data)?; @@ -265,9 +324,6 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> { self.needs_update = false; - // Make sure the index is only updated once per session since it is an - // expensive operation. This generally only happens when the resolver - // is run multiple times, such as during `cargo publish`. if self.is_updated() { return Ok(()); } @@ -294,7 +350,7 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> { self.head.set(None); *self.tree.borrow_mut() = None; self.current_sha.set(None); - let path = self.config.assert_package_cache_locked(&self.index_path); + let _path = self.config.assert_package_cache_locked(&self.index_path); if !self.quiet { self.config .shell() @@ -314,15 +370,14 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> { ) .with_context(|| format!("failed to fetch `{}`", url))?; - // Create a dummy file to record the mtime for when we updated the - // index. - paths::create(&path.join(LAST_UPDATED_FILE))?; - Ok(()) } + /// Read the general concept for `invalidate_cache()` on + /// [`RegistryData::invalidate_cache`]. + /// + /// To fully invalidate, undo [`RemoteRegistry::mark_updated`]'s work. fn invalidate_cache(&mut self) { - // To fully invalidate, undo `mark_updated`s work self.needs_update = true; } @@ -365,9 +420,10 @@ impl<'cfg> RegistryData for RemoteRegistry<'cfg> { } } +/// Implemented to just be sure to drop `tree` field before our other fields. +/// See SAFETY inside [`RemoteRegistry::tree()`] for more. impl<'cfg> Drop for RemoteRegistry<'cfg> { fn drop(&mut self) { - // Just be sure to drop this before our other fields self.tree.borrow_mut().take(); } } diff --git a/src/tools/cargo/src/cargo/util/auth/asymmetric.rs b/src/tools/cargo/src/cargo/util/auth/asymmetric.rs new file mode 100644 index 000000000..50882a745 --- /dev/null +++ b/src/tools/cargo/src/cargo/util/auth/asymmetric.rs @@ -0,0 +1,155 @@ +//! Registry asymmetric authentication support. See [RFC 3231] for more. +//! +//! [RFC 3231]: https://rust-lang.github.io/rfcs/3231-cargo-asymmetric-tokens.html + +use pasetors::keys::AsymmetricPublicKey; +use pasetors::keys::AsymmetricSecretKey; +use pasetors::paserk; +use pasetors::paserk::FormatAsPaserk; +use pasetors::version3; +use pasetors::version3::PublicToken; +use time::format_description::well_known::Rfc3339; +use time::OffsetDateTime; + +use crate::core::SourceId; +use crate::ops::RegistryCredentialConfig; +use crate::CargoResult; + +use super::Mutation; +use super::Secret; + +/// The main body of an asymmetric token as describe in RFC 3231. +#[derive(serde::Serialize)] +struct Message<'a> { + iat: &'a str, + #[serde(skip_serializing_if = "Option::is_none")] + sub: Option<&'a str>, + #[serde(skip_serializing_if = "Option::is_none")] + mutation: Option<&'a str>, + #[serde(skip_serializing_if = "Option::is_none")] + name: Option<&'a str>, + #[serde(skip_serializing_if = "Option::is_none")] + vers: Option<&'a str>, + #[serde(skip_serializing_if = "Option::is_none")] + cksum: Option<&'a str>, + #[serde(skip_serializing_if = "Option::is_none")] + challenge: Option<&'a str>, + /// This field is not yet used. This field can be set to a value >1 to + /// indicate a breaking change in the token format. + #[serde(skip_serializing_if = "Option::is_none")] + v: Option<u8>, +} + +/// The footer of an asymmetric token as describe in RFC 3231. +#[derive(serde::Serialize)] +struct Footer<'a> { + url: &'a str, + kip: paserk::Id, +} + +/// Checks that a secret key is valid, and returns the associated public key in +/// Paserk format. +pub fn paserk_public_from_paserk_secret(secret_key: Secret<&str>) -> Option<String> { + let secret: Secret<AsymmetricSecretKey<version3::V3>> = + secret_key.map(|key| key.try_into()).transpose().ok()?; + let public: AsymmetricPublicKey<version3::V3> = secret + .as_ref() + .map(|key| key.try_into()) + .transpose() + .ok()? + .expose(); + let mut paserk_pub_key = String::new(); + FormatAsPaserk::fmt(&public, &mut paserk_pub_key).unwrap(); + Some(paserk_pub_key) +} + +/// Generates a public token from a registry's `credential` configuration for +/// authenticating to a `source_id` +/// +/// An optional `mutation` for authenticating a mutation operation aganist the +/// registry. +pub fn public_token_from_credential( + credential: RegistryCredentialConfig, + source_id: &SourceId, + mutation: Option<&'_ Mutation<'_>>, +) -> CargoResult<Secret<String>> { + let RegistryCredentialConfig::AsymmetricKey((secret_key, secret_key_subject)) = credential else { + anyhow::bail!("credential must be an asymmetric secret key") + }; + + let secret: Secret<AsymmetricSecretKey<version3::V3>> = + secret_key.map(|key| key.as_str().try_into()).transpose()?; + let public: AsymmetricPublicKey<version3::V3> = secret + .as_ref() + .map(|key| key.try_into()) + .transpose()? + .expose(); + let kip = (&public).try_into()?; + let iat = OffsetDateTime::now_utc(); + + let message = Message { + iat: &iat.format(&Rfc3339)?, + sub: secret_key_subject.as_deref(), + mutation: mutation.and_then(|m| { + Some(match m { + Mutation::PrePublish => return None, + Mutation::Publish { .. } => "publish", + Mutation::Yank { .. } => "yank", + Mutation::Unyank { .. } => "unyank", + Mutation::Owners { .. } => "owners", + }) + }), + name: mutation.and_then(|m| { + Some(match m { + Mutation::PrePublish => return None, + Mutation::Publish { name, .. } + | Mutation::Yank { name, .. } + | Mutation::Unyank { name, .. } + | Mutation::Owners { name, .. } => *name, + }) + }), + vers: mutation.and_then(|m| { + Some(match m { + Mutation::PrePublish | Mutation::Owners { .. } => return None, + Mutation::Publish { vers, .. } + | Mutation::Yank { vers, .. } + | Mutation::Unyank { vers, .. } => *vers, + }) + }), + cksum: mutation.and_then(|m| { + Some(match m { + Mutation::PrePublish + | Mutation::Yank { .. } + | Mutation::Unyank { .. } + | Mutation::Owners { .. } => return None, + Mutation::Publish { cksum, .. } => *cksum, + }) + }), + challenge: None, // todo: PASETO with challenges + v: None, + }; + + let footer = Footer { + url: &source_id.url().to_string(), + kip, + }; + + let secret = secret + .map(|secret| { + PublicToken::sign( + &secret, + serde_json::to_string(&message) + .expect("cannot serialize") + .as_bytes(), + Some( + serde_json::to_string(&footer) + .expect("cannot serialize") + .as_bytes(), + ), + None, + ) + }) + .transpose()?; + + Ok(secret) +} diff --git a/src/tools/cargo/src/cargo/util/auth.rs b/src/tools/cargo/src/cargo/util/auth/mod.rs index f19acaebe..58309964f 100644 --- a/src/tools/cargo/src/cargo/util/auth.rs +++ b/src/tools/cargo/src/cargo/util/auth/mod.rs @@ -1,11 +1,11 @@ //! Registry authentication support. +mod asymmetric; + use crate::util::{config, config::ConfigKey, CanonicalUrl, CargoResult, Config, IntoUrl}; use anyhow::{bail, format_err, Context as _}; use cargo_util::ProcessError; use core::fmt; -use pasetors::keys::{AsymmetricPublicKey, AsymmetricSecretKey}; -use pasetors::paserk::FormatAsPaserk; use serde::Deserialize; use std::collections::HashMap; use std::error::Error; @@ -13,13 +13,13 @@ use std::io::{Read, Write}; use std::ops::Deref; use std::path::PathBuf; use std::process::{Command, Stdio}; -use time::format_description::well_known::Rfc3339; -use time::OffsetDateTime; use url::Url; use crate::core::SourceId; use crate::ops::RegistryCredentialConfig; +pub use self::asymmetric::paserk_public_from_paserk_secret; + use super::config::CredentialCacheValue; /// A wrapper for values that should not be printed. @@ -466,82 +466,9 @@ fn auth_token_optional( run_command(config, &process, sid, Action::Get)?.unwrap(); (independent_of_endpoint, Secret::from(token)) } - RegistryCredentialConfig::AsymmetricKey((secret_key, secret_key_subject)) => { - let secret: Secret<AsymmetricSecretKey<pasetors::version3::V3>> = - secret_key.map(|key| key.as_str().try_into()).transpose()?; - let public: AsymmetricPublicKey<pasetors::version3::V3> = secret - .as_ref() - .map(|key| key.try_into()) - .transpose()? - .expose(); - let kip: pasetors::paserk::Id = (&public).try_into()?; - let iat = OffsetDateTime::now_utc(); - - let message = Message { - iat: &iat.format(&Rfc3339)?, - sub: secret_key_subject.as_deref(), - mutation: mutation.and_then(|m| { - Some(match m { - Mutation::PrePublish => return None, - Mutation::Publish { .. } => "publish", - Mutation::Yank { .. } => "yank", - Mutation::Unyank { .. } => "unyank", - Mutation::Owners { .. } => "owners", - }) - }), - name: mutation.and_then(|m| { - Some(match m { - Mutation::PrePublish => return None, - Mutation::Publish { name, .. } - | Mutation::Yank { name, .. } - | Mutation::Unyank { name, .. } - | Mutation::Owners { name, .. } => *name, - }) - }), - vers: mutation.and_then(|m| { - Some(match m { - Mutation::PrePublish | Mutation::Owners { .. } => return None, - Mutation::Publish { vers, .. } - | Mutation::Yank { vers, .. } - | Mutation::Unyank { vers, .. } => *vers, - }) - }), - cksum: mutation.and_then(|m| { - Some(match m { - Mutation::PrePublish - | Mutation::Yank { .. } - | Mutation::Unyank { .. } - | Mutation::Owners { .. } => return None, - Mutation::Publish { cksum, .. } => *cksum, - }) - }), - challenge: None, // todo: PASETO with challenges - v: None, - }; - let footer = Footer { - url: &sid.url().to_string(), - kip, - }; - - ( - false, - secret - .map(|secret| { - pasetors::version3::PublicToken::sign( - &secret, - serde_json::to_string(&message) - .expect("cannot serialize") - .as_bytes(), - Some( - serde_json::to_string(&footer) - .expect("cannot serialize") - .as_bytes(), - ), - None, - ) - }) - .transpose()?, - ) + cred @ RegistryCredentialConfig::AsymmetricKey(..) => { + let token = asymmetric::public_token_from_credential(cred, sid, mutation)?; + (false, token) } }; @@ -595,33 +522,6 @@ pub enum Mutation<'a> { }, } -/// The main body of an asymmetric token as describe in RFC 3231. -#[derive(serde::Serialize)] -struct Message<'a> { - iat: &'a str, - #[serde(skip_serializing_if = "Option::is_none")] - sub: Option<&'a str>, - #[serde(skip_serializing_if = "Option::is_none")] - mutation: Option<&'a str>, - #[serde(skip_serializing_if = "Option::is_none")] - name: Option<&'a str>, - #[serde(skip_serializing_if = "Option::is_none")] - vers: Option<&'a str>, - #[serde(skip_serializing_if = "Option::is_none")] - cksum: Option<&'a str>, - #[serde(skip_serializing_if = "Option::is_none")] - challenge: Option<&'a str>, - /// This field is not yet used. This field can be set to a value >1 to indicate a breaking change in the token format. - #[serde(skip_serializing_if = "Option::is_none")] - v: Option<u8>, -} -/// The footer of an asymmetric token as describe in RFC 3231. -#[derive(serde::Serialize)] -struct Footer<'a> { - url: &'a str, - kip: pasetors::paserk::Id, -} - enum Action { Get, Store(String), @@ -646,21 +546,6 @@ pub fn login(config: &Config, sid: &SourceId, token: RegistryCredentialConfig) - Ok(()) } -/// Checks that a secret key is valid, and returns the associated public key in Paserk format. -pub(crate) fn paserk_public_from_paserk_secret(secret_key: Secret<&str>) -> Option<String> { - let secret: Secret<AsymmetricSecretKey<pasetors::version3::V3>> = - secret_key.map(|key| key.try_into()).transpose().ok()?; - let public: AsymmetricPublicKey<pasetors::version3::V3> = secret - .as_ref() - .map(|key| key.try_into()) - .transpose() - .ok()? - .expose(); - let mut paserk_pub_key = String::new(); - FormatAsPaserk::fmt(&public, &mut paserk_pub_key).unwrap(); - Some(paserk_pub_key) -} - /// Removes the token for the given registry. pub fn logout(config: &Config, sid: &SourceId) -> CargoResult<()> { match registry_credential_config(config, sid)? { diff --git a/src/tools/cargo/src/cargo/util/command_prelude.rs b/src/tools/cargo/src/cargo/util/command_prelude.rs index c18785c66..46ed7dd7c 100644 --- a/src/tools/cargo/src/cargo/util/command_prelude.rs +++ b/src/tools/cargo/src/cargo/util/command_prelude.rs @@ -15,6 +15,7 @@ use crate::CargoResult; use anyhow::bail; use cargo_util::paths; use std::ffi::{OsStr, OsString}; +use std::path::Path; use std::path::PathBuf; pub use crate::core::compiler::CompileMode; @@ -23,6 +24,8 @@ pub use clap::{value_parser, Arg, ArgAction, ArgMatches}; pub use clap::Command; +use super::config::JobsConfig; + pub trait CommandExt: Sized { fn _arg(self, arg: Arg) -> Self; @@ -66,7 +69,7 @@ pub trait CommandExt: Sized { fn arg_jobs(self) -> Self { self._arg( - opt("jobs", "Number of parallel jobs, defaults to # of CPUs") + opt("jobs", "Number of parallel jobs, defaults to # of CPUs.") .short('j') .value_name("N") .allow_hyphen_values(true), @@ -337,22 +340,7 @@ pub trait ArgMatchesExt { } fn root_manifest(&self, config: &Config) -> CargoResult<PathBuf> { - if let Some(path) = self.value_of_path("manifest-path", config) { - // In general, we try to avoid normalizing paths in Cargo, - // but in this particular case we need it to fix #3586. - let path = paths::normalize_path(&path); - if !path.ends_with("Cargo.toml") { - anyhow::bail!("the manifest-path must be a path to a Cargo.toml file") - } - if !path.exists() { - anyhow::bail!( - "manifest path `{}` does not exist", - self._value_of("manifest-path").unwrap() - ) - } - return Ok(path); - } - find_root_manifest_for_wd(config.cwd()) + root_manifest(self._value_of("manifest-path").map(Path::new), config) } fn workspace<'a>(&self, config: &'a Config) -> CargoResult<Workspace<'a>> { @@ -364,8 +352,16 @@ pub trait ArgMatchesExt { Ok(ws) } - fn jobs(&self) -> CargoResult<Option<i32>> { - self.value_of_i32("jobs") + fn jobs(&self) -> CargoResult<Option<JobsConfig>> { + let arg = match self._value_of("jobs") { + None => None, + Some(arg) => match arg.parse::<i32>() { + Ok(j) => Some(JobsConfig::Integer(j)), + Err(_) => Some(JobsConfig::String(arg.to_string())), + }, + }; + + Ok(arg) } fn verbose(&self) -> u32 { @@ -782,6 +778,33 @@ pub fn values_os(args: &ArgMatches, name: &str) -> Vec<OsString> { args._values_of_os(name) } +pub fn root_manifest(manifest_path: Option<&Path>, config: &Config) -> CargoResult<PathBuf> { + if let Some(manifest_path) = manifest_path { + let path = config.cwd().join(manifest_path); + // In general, we try to avoid normalizing paths in Cargo, + // but in this particular case we need it to fix #3586. + let path = paths::normalize_path(&path); + if !path.ends_with("Cargo.toml") && !crate::util::toml::is_embedded(&path) { + anyhow::bail!("the manifest-path must be a path to a Cargo.toml file") + } + if !path.exists() { + anyhow::bail!("manifest path `{}` does not exist", manifest_path.display()) + } + if path.is_dir() { + anyhow::bail!( + "manifest path `{}` is a directory but expected a file", + manifest_path.display() + ) + } + if crate::util::toml::is_embedded(&path) && !config.cli_unstable().script { + anyhow::bail!("embedded manifest `{}` requires `-Zscript`", path.display()) + } + Ok(path) + } else { + find_root_manifest_for_wd(config.cwd()) + } +} + #[track_caller] pub fn ignore_unknown<T: Default>(r: Result<T, clap::parser::MatchesError>) -> T { match r { diff --git a/src/tools/cargo/src/cargo/util/config/mod.rs b/src/tools/cargo/src/cargo/util/config/mod.rs index 076b78299..4e6bca302 100644 --- a/src/tools/cargo/src/cargo/util/config/mod.rs +++ b/src/tools/cargo/src/cargo/util/config/mod.rs @@ -69,9 +69,11 @@ use self::ConfigValue as CV; use crate::core::compiler::rustdoc::RustdocExternMap; use crate::core::shell::Verbosity; use crate::core::{features, CliUnstable, Shell, SourceId, Workspace, WorkspaceRootConfig}; -use crate::ops::{self, RegistryCredentialConfig}; +use crate::ops::RegistryCredentialConfig; use crate::util::auth::Secret; use crate::util::errors::CargoResult; +use crate::util::network::http::configure_http_handle; +use crate::util::network::http::http_handle; use crate::util::CanonicalUrl; use crate::util::{internal, toml as cargo_toml}; use crate::util::{try_canonicalize, validate_package_name}; @@ -363,7 +365,7 @@ impl Config { self.registry_base_path().join("index") } - /// Gets the Cargo registry cache directory (`<cargo_home>/registry/path`). + /// Gets the Cargo registry cache directory (`<cargo_home>/registry/cache`). pub fn registry_cache_path(&self) -> Filesystem { self.registry_base_path().join("cache") } @@ -1273,6 +1275,16 @@ impl Config { return Ok(Vec::new()); } }; + + for (path, abs_path, def) in &includes { + if abs_path.extension() != Some(OsStr::new("toml")) { + bail!( + "expected a config include path ending with `.toml`, \ + but found `{path}` from `{def}`", + ) + } + } + Ok(includes) } @@ -1706,11 +1718,11 @@ impl Config { pub fn http(&self) -> CargoResult<&RefCell<Easy>> { let http = self .easy - .try_borrow_with(|| ops::http_handle(self).map(RefCell::new))?; + .try_borrow_with(|| http_handle(self).map(RefCell::new))?; { let mut http = http.borrow_mut(); http.reset(); - let timeout = ops::configure_http_handle(self, &mut http)?; + let timeout = configure_http_handle(self, &mut http)?; timeout.configure(&mut http)?; } Ok(http) @@ -2453,6 +2465,25 @@ pub struct CargoSshConfig { pub known_hosts: Option<Vec<Value<String>>>, } +/// Configuration for `jobs` in `build` section. There are two +/// ways to configure: An integer or a simple string expression. +/// +/// ```toml +/// [build] +/// jobs = 1 +/// ``` +/// +/// ```toml +/// [build] +/// jobs = "default" # Currently only support "default". +/// ``` +#[derive(Debug, Deserialize, Clone)] +#[serde(untagged)] +pub enum JobsConfig { + Integer(i32), + String(String), +} + #[derive(Debug, Deserialize)] #[serde(rename_all = "kebab-case")] pub struct CargoBuildConfig { @@ -2462,7 +2493,7 @@ pub struct CargoBuildConfig { pub target_dir: Option<ConfigRelativePath>, pub incremental: Option<bool>, pub target: Option<BuildTargetConfig>, - pub jobs: Option<i32>, + pub jobs: Option<JobsConfig>, pub rustflags: Option<StringList>, pub rustdocflags: Option<StringList>, pub rustc_wrapper: Option<ConfigRelativePath>, diff --git a/src/tools/cargo/src/cargo/util/config/target.rs b/src/tools/cargo/src/cargo/util/config/target.rs index a7f2f3ef2..cdafe73dd 100644 --- a/src/tools/cargo/src/cargo/util/config/target.rs +++ b/src/tools/cargo/src/cargo/util/config/target.rs @@ -1,5 +1,5 @@ use super::{Config, ConfigKey, ConfigRelativePath, OptValue, PathAndArgs, StringList, CV}; -use crate::core::compiler::{BuildOutput, LinkType}; +use crate::core::compiler::{BuildOutput, LinkArgTarget}; use crate::util::CargoResult; use serde::Deserialize; use std::collections::{BTreeMap, HashMap}; @@ -178,27 +178,27 @@ fn parse_links_overrides( .extend(list.iter().map(|v| PathBuf::from(&v.0))); } "rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => { - let args = extra_link_args(LinkType::Cdylib, key, value)?; + let args = extra_link_args(LinkArgTarget::Cdylib, key, value)?; output.linker_args.extend(args); } "rustc-link-arg-bins" => { - let args = extra_link_args(LinkType::Bin, key, value)?; + let args = extra_link_args(LinkArgTarget::Bin, key, value)?; output.linker_args.extend(args); } "rustc-link-arg" => { - let args = extra_link_args(LinkType::All, key, value)?; + let args = extra_link_args(LinkArgTarget::All, key, value)?; output.linker_args.extend(args); } "rustc-link-arg-tests" => { - let args = extra_link_args(LinkType::Test, key, value)?; + let args = extra_link_args(LinkArgTarget::Test, key, value)?; output.linker_args.extend(args); } "rustc-link-arg-benches" => { - let args = extra_link_args(LinkType::Bench, key, value)?; + let args = extra_link_args(LinkArgTarget::Bench, key, value)?; output.linker_args.extend(args); } "rustc-link-arg-examples" => { - let args = extra_link_args(LinkType::Example, key, value)?; + let args = extra_link_args(LinkArgTarget::Example, key, value)?; output.linker_args.extend(args); } "rustc-cfg" => { @@ -237,10 +237,10 @@ fn parse_links_overrides( } fn extra_link_args<'a>( - link_type: LinkType, + link_type: LinkArgTarget, key: &str, value: &'a CV, -) -> CargoResult<impl Iterator<Item = (LinkType, String)> + 'a> { +) -> CargoResult<impl Iterator<Item = (LinkArgTarget, String)> + 'a> { let args = value.list(key)?; Ok(args.iter().map(move |v| (link_type.clone(), v.0.clone()))) } diff --git a/src/tools/cargo/src/cargo/util/interning.rs b/src/tools/cargo/src/cargo/util/interning.rs index bbec12942..584fdf623 100644 --- a/src/tools/cargo/src/cargo/util/interning.rs +++ b/src/tools/cargo/src/cargo/util/interning.rs @@ -10,14 +10,13 @@ use std::path::Path; use std::ptr; use std::str; use std::sync::Mutex; +use std::sync::OnceLock; fn leak(s: String) -> &'static str { Box::leak(s.into_boxed_str()) } -lazy_static::lazy_static! { - static ref STRING_CACHE: Mutex<HashSet<&'static str>> = Mutex::new(HashSet::new()); -} +static STRING_CACHE: OnceLock<Mutex<HashSet<&'static str>>> = OnceLock::new(); #[derive(Clone, Copy)] pub struct InternedString { @@ -64,7 +63,10 @@ impl Eq for InternedString {} impl InternedString { pub fn new(str: &str) -> InternedString { - let mut cache = STRING_CACHE.lock().unwrap(); + let mut cache = STRING_CACHE + .get_or_init(|| Default::default()) + .lock() + .unwrap(); let s = cache.get(str).cloned().unwrap_or_else(|| { let s = leak(str.to_string()); cache.insert(s); diff --git a/src/tools/cargo/src/cargo/util/network/http.rs b/src/tools/cargo/src/cargo/util/network/http.rs new file mode 100644 index 000000000..f077ce2b6 --- /dev/null +++ b/src/tools/cargo/src/cargo/util/network/http.rs @@ -0,0 +1,216 @@ +//! Configures libcurl's http handles. + +use std::str; +use std::time::Duration; + +use anyhow::bail; +use curl::easy::Easy; +use curl::easy::InfoType; +use curl::easy::SslOpt; +use curl::easy::SslVersion; +use log::log; +use log::Level; + +use crate::util::config::SslVersionConfig; +use crate::util::config::SslVersionConfigRange; +use crate::version; +use crate::CargoResult; +use crate::Config; + +/// Creates a new HTTP handle with appropriate global configuration for cargo. +pub fn http_handle(config: &Config) -> CargoResult<Easy> { + let (mut handle, timeout) = http_handle_and_timeout(config)?; + timeout.configure(&mut handle)?; + Ok(handle) +} + +pub fn http_handle_and_timeout(config: &Config) -> CargoResult<(Easy, HttpTimeout)> { + if config.frozen() { + bail!( + "attempting to make an HTTP request, but --frozen was \ + specified" + ) + } + if config.offline() { + bail!( + "attempting to make an HTTP request, but --offline was \ + specified" + ) + } + + // The timeout option for libcurl by default times out the entire transfer, + // but we probably don't want this. Instead we only set timeouts for the + // connect phase as well as a "low speed" timeout so if we don't receive + // many bytes in a large-ish period of time then we time out. + let mut handle = Easy::new(); + let timeout = configure_http_handle(config, &mut handle)?; + Ok((handle, timeout)) +} + +// Only use a custom transport if any HTTP options are specified, +// such as proxies or custom certificate authorities. +// +// The custom transport, however, is not as well battle-tested. +pub fn needs_custom_http_transport(config: &Config) -> CargoResult<bool> { + Ok( + super::proxy::http_proxy_exists(config.http_config()?, config) + || *config.http_config()? != Default::default() + || config.get_env_os("HTTP_TIMEOUT").is_some(), + ) +} + +/// Configure a libcurl http handle with the defaults options for Cargo +pub fn configure_http_handle(config: &Config, handle: &mut Easy) -> CargoResult<HttpTimeout> { + let http = config.http_config()?; + if let Some(proxy) = super::proxy::http_proxy(http) { + handle.proxy(&proxy)?; + } + if let Some(cainfo) = &http.cainfo { + let cainfo = cainfo.resolve_path(config); + handle.cainfo(&cainfo)?; + } + if let Some(check) = http.check_revoke { + handle.ssl_options(SslOpt::new().no_revoke(!check))?; + } + + if let Some(user_agent) = &http.user_agent { + handle.useragent(user_agent)?; + } else { + handle.useragent(&format!("cargo {}", version()))?; + } + + fn to_ssl_version(s: &str) -> CargoResult<SslVersion> { + let version = match s { + "default" => SslVersion::Default, + "tlsv1" => SslVersion::Tlsv1, + "tlsv1.0" => SslVersion::Tlsv10, + "tlsv1.1" => SslVersion::Tlsv11, + "tlsv1.2" => SslVersion::Tlsv12, + "tlsv1.3" => SslVersion::Tlsv13, + _ => bail!( + "Invalid ssl version `{s}`,\ + choose from 'default', 'tlsv1', 'tlsv1.0', 'tlsv1.1', 'tlsv1.2', 'tlsv1.3'." + ), + }; + Ok(version) + } + + // Empty string accept encoding expands to the encodings supported by the current libcurl. + handle.accept_encoding("")?; + if let Some(ssl_version) = &http.ssl_version { + match ssl_version { + SslVersionConfig::Single(s) => { + let version = to_ssl_version(s.as_str())?; + handle.ssl_version(version)?; + } + SslVersionConfig::Range(SslVersionConfigRange { min, max }) => { + let min_version = min + .as_ref() + .map_or(Ok(SslVersion::Default), |s| to_ssl_version(s))?; + let max_version = max + .as_ref() + .map_or(Ok(SslVersion::Default), |s| to_ssl_version(s))?; + handle.ssl_min_max_version(min_version, max_version)?; + } + } + } else if cfg!(windows) { + // This is a temporary workaround for some bugs with libcurl and + // schannel and TLS 1.3. + // + // Our libcurl on Windows is usually built with schannel. + // On Windows 11 (or Windows Server 2022), libcurl recently (late + // 2022) gained support for TLS 1.3 with schannel, and it now defaults + // to 1.3. Unfortunately there have been some bugs with this. + // https://github.com/curl/curl/issues/9431 is the most recent. Once + // that has been fixed, and some time has passed where we can be more + // confident that the 1.3 support won't cause issues, this can be + // removed. + // + // Windows 10 is unaffected. libcurl does not support TLS 1.3 on + // Windows 10. (Windows 10 sorta had support, but it required enabling + // an advanced option in the registry which was buggy, and libcurl + // does runtime checks to prevent it.) + handle.ssl_min_max_version(SslVersion::Default, SslVersion::Tlsv12)?; + } + + if let Some(true) = http.debug { + handle.verbose(true)?; + log::debug!("{:#?}", curl::Version::get()); + handle.debug_function(|kind, data| { + let (prefix, level) = match kind { + InfoType::Text => ("*", Level::Debug), + InfoType::HeaderIn => ("<", Level::Debug), + InfoType::HeaderOut => (">", Level::Debug), + InfoType::DataIn => ("{", Level::Trace), + InfoType::DataOut => ("}", Level::Trace), + InfoType::SslDataIn | InfoType::SslDataOut => return, + _ => return, + }; + let starts_with_ignore_case = |line: &str, text: &str| -> bool { + line[..line.len().min(text.len())].eq_ignore_ascii_case(text) + }; + match str::from_utf8(data) { + Ok(s) => { + for mut line in s.lines() { + if starts_with_ignore_case(line, "authorization:") { + line = "Authorization: [REDACTED]"; + } else if starts_with_ignore_case(line, "h2h3 [authorization:") { + line = "h2h3 [Authorization: [REDACTED]]"; + } else if starts_with_ignore_case(line, "set-cookie") { + line = "set-cookie: [REDACTED]"; + } + log!(level, "http-debug: {} {}", prefix, line); + } + } + Err(_) => { + log!( + level, + "http-debug: {} ({} bytes of data)", + prefix, + data.len() + ); + } + } + })?; + } + + HttpTimeout::new(config) +} + +#[must_use] +pub struct HttpTimeout { + pub dur: Duration, + pub low_speed_limit: u32, +} + +impl HttpTimeout { + pub fn new(config: &Config) -> CargoResult<HttpTimeout> { + let http_config = config.http_config()?; + let low_speed_limit = http_config.low_speed_limit.unwrap_or(10); + let seconds = http_config + .timeout + .or_else(|| { + config + .get_env("HTTP_TIMEOUT") + .ok() + .and_then(|s| s.parse().ok()) + }) + .unwrap_or(30); + Ok(HttpTimeout { + dur: Duration::new(seconds, 0), + low_speed_limit, + }) + } + + pub fn configure(&self, handle: &mut Easy) -> CargoResult<()> { + // The timeout option for libcurl by default times out the entire + // transfer, but we probably don't want this. Instead we only set + // timeouts for the connect phase as well as a "low speed" timeout so + // if we don't receive many bytes in a large-ish period of time then we + // time out. + handle.connect_timeout(self.dur)?; + handle.low_speed_time(self.dur)?; + handle.low_speed_limit(self.low_speed_limit)?; + Ok(()) + } +} diff --git a/src/tools/cargo/src/cargo/util/network/mod.rs b/src/tools/cargo/src/cargo/util/network/mod.rs index 2006bb65f..b078fa352 100644 --- a/src/tools/cargo/src/cargo/util/network/mod.rs +++ b/src/tools/cargo/src/cargo/util/network/mod.rs @@ -2,6 +2,7 @@ use std::task::Poll; +pub mod http; pub mod proxy; pub mod retry; pub mod sleep; @@ -20,20 +21,51 @@ impl<T> PollExt<T> for Poll<T> { } } -// When dynamically linked against libcurl, we want to ignore some failures -// when using old versions that don't support certain features. +/// When dynamically linked against libcurl, we want to ignore some failures +/// when using old versions that don't support certain features. #[macro_export] macro_rules! try_old_curl { ($e:expr, $msg:expr) => { let result = $e; if cfg!(target_os = "macos") { if let Err(e) = result { - warn!("ignoring libcurl {} error: {}", $msg, e); + ::log::warn!("ignoring libcurl {} error: {}", $msg, e); } } else { + use ::anyhow::Context; result.with_context(|| { - anyhow::format_err!("failed to enable {}, is curl not built right?", $msg) + ::anyhow::format_err!("failed to enable {}, is curl not built right?", $msg) })?; } }; } + +/// Enable HTTP/2 and pipewait to be used as it'll allow true multiplexing +/// which makes downloads much faster. +/// +/// Currently Cargo requests the `http2` feature of the `curl` crate which +/// means it should always be built in. On OSX, however, we ship cargo still +/// linked against the system libcurl. Building curl with ALPN support for +/// HTTP/2 requires newer versions of OSX (the SecureTransport API) than we +/// want to ship Cargo for. By linking Cargo against the system libcurl then +/// older curl installations won't use HTTP/2 but newer ones will. All that to +/// basically say we ignore errors here on OSX, but consider this a fatal error +/// to not activate HTTP/2 on all other platforms. +/// +/// `pipewait` is an option which indicates that if there's a bunch of parallel +/// requests to the same host they all wait until the pipelining status of the +/// host is known. This means that we won't initiate dozens of connections but +/// rather only one. Once the main one is opened we realized that pipelining is +/// possible and multiplexing is possible. All in all this reduces the number +/// of connections down to a more manageable state. +#[macro_export] +macro_rules! try_old_curl_http2_pipewait { + ($multiplexing:expr, $handle:expr) => { + if $multiplexing { + $crate::try_old_curl!($handle.http_version(curl::easy::HttpVersion::V2), "HTTP/2"); + } else { + $handle.http_version(curl::easy::HttpVersion::V11)?; + } + $crate::try_old_curl!($handle.pipewait(true), "pipewait"); + }; +} diff --git a/src/tools/cargo/src/cargo/util/network/retry.rs b/src/tools/cargo/src/cargo/util/network/retry.rs index 42c38ab9f..5cb7d1e4f 100644 --- a/src/tools/cargo/src/cargo/util/network/retry.rs +++ b/src/tools/cargo/src/cargo/util/network/retry.rs @@ -56,21 +56,29 @@ impl<'a> Retry<'a> { return RetryResult::Err(e); } self.retries += 1; - let sleep = if self.retries == 1 { - let mut rng = rand::thread_rng(); - INITIAL_RETRY_SLEEP_BASE_MS + rng.gen_range(0..INITIAL_RETRY_JITTER_MS) - } else { - min( - ((self.retries - 1) * 3) * 1000 + INITIAL_RETRY_SLEEP_BASE_MS, - MAX_RETRY_SLEEP_MS, - ) - }; - RetryResult::Retry(sleep) + RetryResult::Retry(self.next_sleep_ms()) } Err(e) => RetryResult::Err(e), Ok(r) => RetryResult::Success(r), } } + + /// Gets the next sleep duration in milliseconds. + fn next_sleep_ms(&self) -> u64 { + if let Ok(sleep) = self.config.get_env("__CARGO_TEST_FIXED_RETRY_SLEEP_MS") { + return sleep.parse().expect("a u64"); + } + + if self.retries == 1 { + let mut rng = rand::thread_rng(); + INITIAL_RETRY_SLEEP_BASE_MS + rng.gen_range(0..INITIAL_RETRY_JITTER_MS) + } else { + min( + ((self.retries - 1) * 3) * 1000 + INITIAL_RETRY_SLEEP_BASE_MS, + MAX_RETRY_SLEEP_MS, + ) + } + } } fn maybe_spurious(err: &Error) -> bool { diff --git a/src/tools/cargo/src/cargo/util/profile.rs b/src/tools/cargo/src/cargo/util/profile.rs index 79b544d98..29b110492 100644 --- a/src/tools/cargo/src/cargo/util/profile.rs +++ b/src/tools/cargo/src/cargo/util/profile.rs @@ -1,4 +1,4 @@ -//! # An internal profiler for Cargo itself +//! An internal performance profiler for Cargo itself. //! //! > **Note**: This might not be the module you are looking for. //! > For information about how Cargo handles compiler flags with profiles, diff --git a/src/tools/cargo/src/cargo/util/restricted_names.rs b/src/tools/cargo/src/cargo/util/restricted_names.rs index 650ae2330..be1811a88 100644 --- a/src/tools/cargo/src/cargo/util/restricted_names.rs +++ b/src/tools/cargo/src/cargo/util/restricted_names.rs @@ -83,6 +83,30 @@ pub fn validate_package_name(name: &str, what: &str, help: &str) -> CargoResult< 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(); + if let Some(ch) = chars.next() { + if ch.is_digit(10) { + slug.push(placeholder); + slug.push(ch); + } else if unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '_' { + slug.push(ch); + } else { + slug.push(placeholder); + } + } + for ch in chars { + if unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '-' { + slug.push(ch); + } else { + slug.push(placeholder); + } + } + slug +} + /// Check the entire path for names reserved in Windows. pub fn is_windows_reserved_path(path: &Path) -> bool { path.iter() diff --git a/src/tools/cargo/src/cargo/util/toml/embedded.rs b/src/tools/cargo/src/cargo/util/toml/embedded.rs new file mode 100644 index 000000000..8e41010b4 --- /dev/null +++ b/src/tools/cargo/src/cargo/util/toml/embedded.rs @@ -0,0 +1,895 @@ +use anyhow::Context as _; + +use crate::util::restricted_names; +use crate::CargoResult; +use crate::Config; + +const DEFAULT_EDITION: crate::core::features::Edition = + crate::core::features::Edition::LATEST_STABLE; +const DEFAULT_VERSION: &str = "0.0.0"; +const DEFAULT_PUBLISH: bool = false; +const AUTO_FIELDS: &[&str] = &["autobins", "autoexamples", "autotests", "autobenches"]; + +pub fn expand_manifest( + content: &str, + path: &std::path::Path, + config: &Config, +) -> CargoResult<String> { + let comment = match extract_comment(content) { + Ok(comment) => Some(comment), + Err(err) => { + log::trace!("failed to extract doc comment: {err}"); + None + } + } + .unwrap_or_default(); + let manifest = match extract_manifest(&comment)? { + Some(manifest) => Some(manifest), + None => { + log::trace!("failed to extract manifest"); + None + } + } + .unwrap_or_default(); + let manifest = expand_manifest_(&manifest, path, config) + .with_context(|| format!("failed to parse manifest at {}", path.display()))?; + let manifest = toml::to_string_pretty(&manifest)?; + Ok(manifest) +} + +fn expand_manifest_( + manifest: &str, + path: &std::path::Path, + config: &Config, +) -> CargoResult<toml::Table> { + let mut manifest: toml::Table = toml::from_str(&manifest)?; + + for key in ["workspace", "lib", "bin", "example", "test", "bench"] { + if manifest.contains_key(key) { + anyhow::bail!("`{key}` is not allowed in embedded manifests") + } + } + + // Prevent looking for a workspace by `read_manifest_from_str` + manifest.insert("workspace".to_owned(), toml::Table::new().into()); + + let package = manifest + .entry("package".to_owned()) + .or_insert_with(|| toml::Table::new().into()) + .as_table_mut() + .ok_or_else(|| anyhow::format_err!("`package` must be a table"))?; + for key in ["workspace", "build", "links"] + .iter() + .chain(AUTO_FIELDS.iter()) + { + if package.contains_key(*key) { + anyhow::bail!("`package.{key}` is not allowed in embedded manifests") + } + } + let file_name = path + .file_name() + .ok_or_else(|| anyhow::format_err!("no file name"))? + .to_string_lossy(); + let file_stem = path + .file_stem() + .ok_or_else(|| anyhow::format_err!("no file name"))? + .to_string_lossy(); + let name = sanitize_name(file_stem.as_ref()); + let bin_name = name.clone(); + package + .entry("name".to_owned()) + .or_insert(toml::Value::String(name)); + package + .entry("version".to_owned()) + .or_insert_with(|| toml::Value::String(DEFAULT_VERSION.to_owned())); + package.entry("edition".to_owned()).or_insert_with(|| { + let _ = config.shell().warn(format_args!( + "`package.edition` is unspecifiead, defaulting to `{}`", + DEFAULT_EDITION + )); + toml::Value::String(DEFAULT_EDITION.to_string()) + }); + package + .entry("build".to_owned()) + .or_insert_with(|| toml::Value::Boolean(false)); + package + .entry("publish".to_owned()) + .or_insert_with(|| toml::Value::Boolean(DEFAULT_PUBLISH)); + for field in AUTO_FIELDS { + package + .entry(field.to_owned()) + .or_insert_with(|| toml::Value::Boolean(false)); + } + + let mut bin = toml::Table::new(); + bin.insert("name".to_owned(), toml::Value::String(bin_name)); + bin.insert( + "path".to_owned(), + toml::Value::String(file_name.into_owned()), + ); + manifest.insert( + "bin".to_owned(), + toml::Value::Array(vec![toml::Value::Table(bin)]), + ); + + let release = manifest + .entry("profile".to_owned()) + .or_insert_with(|| toml::Value::Table(Default::default())) + .as_table_mut() + .ok_or_else(|| anyhow::format_err!("`profile` must be a table"))? + .entry("release".to_owned()) + .or_insert_with(|| toml::Value::Table(Default::default())) + .as_table_mut() + .ok_or_else(|| anyhow::format_err!("`profile.release` must be a table"))?; + release + .entry("strip".to_owned()) + .or_insert_with(|| toml::Value::Boolean(true)); + + Ok(manifest) +} + +/// Ensure the package name matches the validation from `ops::cargo_new::check_name` +fn sanitize_name(name: &str) -> String { + let placeholder = if name.contains('_') { + '_' + } else { + // Since embedded manifests only support `[[bin]]`s, prefer arrow-case as that is the + // more common convention for CLIs + '-' + }; + + let mut name = restricted_names::sanitize_package_name(name, placeholder); + + loop { + if restricted_names::is_keyword(&name) { + name.push(placeholder); + } else if restricted_names::is_conflicting_artifact_name(&name) { + // Being an embedded manifest, we always assume it is a `[[bin]]` + name.push(placeholder); + } else if name == "test" { + name.push(placeholder); + } else if restricted_names::is_windows_reserved(&name) { + // Go ahead and be consistent across platforms + name.push(placeholder); + } else { + break; + } + } + + name +} + +/// Locates a "code block manifest" in Rust source. +fn extract_comment(input: &str) -> CargoResult<String> { + let mut doc_fragments = Vec::new(); + let file = syn::parse_file(input)?; + // HACK: `syn` doesn't tell us what kind of comment was used, so infer it from how many + // attributes were used + let kind = if 1 < file + .attrs + .iter() + .filter(|attr| attr.meta.path().is_ident("doc")) + .count() + { + CommentKind::Line + } else { + CommentKind::Block + }; + for attr in &file.attrs { + if attr.meta.path().is_ident("doc") { + doc_fragments.push(DocFragment::new(attr, kind)?); + } + } + if doc_fragments.is_empty() { + anyhow::bail!("no doc-comment found"); + } + unindent_doc_fragments(&mut doc_fragments); + + let mut doc_comment = String::new(); + for frag in &doc_fragments { + add_doc_fragment(&mut doc_comment, frag); + } + + Ok(doc_comment) +} + +/// A `#[doc]` +#[derive(Clone, Debug)] +struct DocFragment { + /// The attribute value + doc: String, + /// Indentation used within `doc + indent: usize, +} + +impl DocFragment { + fn new(attr: &syn::Attribute, kind: CommentKind) -> CargoResult<Self> { + let syn::Meta::NameValue(nv) = &attr.meta else { + anyhow::bail!("unsupported attr meta for {:?}", attr.meta.path()) + }; + let syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Str(lit), .. }) = &nv.value else { + anyhow::bail!("only string literals are supported") + }; + Ok(Self { + doc: beautify_doc_string(lit.value(), kind), + indent: 0, + }) + } +} + +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum CommentKind { + Line, + Block, +} + +/// Makes a doc string more presentable to users. +/// Used by rustdoc and perhaps other tools, but not by rustc. +/// +/// See `rustc_ast/util/comments.rs` +fn beautify_doc_string(data: String, kind: CommentKind) -> String { + fn get_vertical_trim(lines: &[&str]) -> Option<(usize, usize)> { + let mut i = 0; + let mut j = lines.len(); + // first line of all-stars should be omitted + if !lines.is_empty() && lines[0].chars().all(|c| c == '*') { + i += 1; + } + + // like the first, a last line of all stars should be omitted + if j > i && !lines[j - 1].is_empty() && lines[j - 1].chars().all(|c| c == '*') { + j -= 1; + } + + if i != 0 || j != lines.len() { + Some((i, j)) + } else { + None + } + } + + fn get_horizontal_trim(lines: &[&str], kind: CommentKind) -> Option<String> { + let mut i = usize::MAX; + let mut first = true; + + // In case we have doc comments like `/**` or `/*!`, we want to remove stars if they are + // present. However, we first need to strip the empty lines so they don't get in the middle + // when we try to compute the "horizontal trim". + let lines = match kind { + CommentKind::Block => { + // Whatever happens, we skip the first line. + let mut i = lines + .get(0) + .map(|l| { + if l.trim_start().starts_with('*') { + 0 + } else { + 1 + } + }) + .unwrap_or(0); + let mut j = lines.len(); + + while i < j && lines[i].trim().is_empty() { + i += 1; + } + while j > i && lines[j - 1].trim().is_empty() { + j -= 1; + } + &lines[i..j] + } + CommentKind::Line => lines, + }; + + for line in lines { + for (j, c) in line.chars().enumerate() { + if j > i || !"* \t".contains(c) { + return None; + } + if c == '*' { + if first { + i = j; + first = false; + } else if i != j { + return None; + } + break; + } + } + if i >= line.len() { + return None; + } + } + if lines.is_empty() { + None + } else { + Some(lines[0][..i].into()) + } + } + + let data_s = data.as_str(); + if data_s.contains('\n') { + let mut lines = data_s.lines().collect::<Vec<&str>>(); + let mut changes = false; + let lines = if let Some((i, j)) = get_vertical_trim(&lines) { + changes = true; + // remove whitespace-only lines from the start/end of lines + &mut lines[i..j] + } else { + &mut lines + }; + if let Some(horizontal) = get_horizontal_trim(lines, kind) { + changes = true; + // remove a "[ \t]*\*" block from each line, if possible + for line in lines.iter_mut() { + if let Some(tmp) = line.strip_prefix(&horizontal) { + *line = tmp; + if kind == CommentKind::Block + && (*line == "*" || line.starts_with("* ") || line.starts_with("**")) + { + *line = &line[1..]; + } + } + } + } + if changes { + return lines.join("\n"); + } + } + data +} + +/// Removes excess indentation on comments in order for the Markdown +/// to be parsed correctly. This is necessary because the convention for +/// writing documentation is to provide a space between the /// or //! marker +/// and the doc text, but Markdown is whitespace-sensitive. For example, +/// a block of text with four-space indentation is parsed as a code block, +/// so if we didn't unindent comments, these list items +/// +/// /// A list: +/// /// +/// /// - Foo +/// /// - Bar +/// +/// would be parsed as if they were in a code block, which is likely not what the user intended. +/// +/// See also `rustc_resolve/rustdoc.rs` +fn unindent_doc_fragments(docs: &mut [DocFragment]) { + // HACK: We can't tell the difference between `#[doc]` and doc-comments, so we can't specialize + // the indentation like rustodc does + let add = 0; + + // `min_indent` is used to know how much whitespaces from the start of each lines must be + // removed. Example: + // + // ``` + // /// hello! + // #[doc = "another"] + // ``` + // + // In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum + // 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4 + // (5 - 1) whitespaces. + let Some(min_indent) = docs + .iter() + .map(|fragment| { + fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| { + if line.chars().all(|c| c.is_whitespace()) { + min_indent + } else { + // Compare against either space or tab, ignoring whether they are + // mixed or not. + let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count(); + min_indent.min(whitespace) + } + }) + }) + .min() + else { + return; + }; + + for fragment in docs { + if fragment.doc.is_empty() { + continue; + } + + let min_indent = if min_indent > 0 { + min_indent - add + } else { + min_indent + }; + + fragment.indent = min_indent; + } +} + +/// The goal of this function is to apply the `DocFragment` transformation that is required when +/// transforming into the final Markdown, which is applying the computed indent to each line in +/// each doc fragment (a `DocFragment` can contain multiple lines in case of `#[doc = ""]`). +/// +/// Note: remove the trailing newline where appropriate +/// +/// See also `rustc_resolve/rustdoc.rs` +fn add_doc_fragment(out: &mut String, frag: &DocFragment) { + let s = frag.doc.as_str(); + let mut iter = s.lines(); + if s.is_empty() { + out.push('\n'); + return; + } + while let Some(line) = iter.next() { + if line.chars().any(|c| !c.is_whitespace()) { + assert!(line.len() >= frag.indent); + out.push_str(&line[frag.indent..]); + } else { + out.push_str(line); + } + out.push('\n'); + } +} + +/// Extracts the first `Cargo` fenced code block from a chunk of Markdown. +fn extract_manifest(comment: &str) -> CargoResult<Option<String>> { + use pulldown_cmark::{CodeBlockKind, Event, Options, Parser, Tag}; + + // To match librustdoc/html/markdown.rs, opts. + let exts = Options::ENABLE_TABLES | Options::ENABLE_FOOTNOTES; + + let md = Parser::new_ext(comment, exts); + + let mut inside = false; + let mut output = None; + + for item in md { + match item { + Event::Start(Tag::CodeBlock(CodeBlockKind::Fenced(ref info))) + if info.to_lowercase() == "cargo" => + { + if output.is_some() { + anyhow::bail!("multiple `cargo` manifests present") + } else { + output = Some(String::new()); + } + inside = true; + } + Event::Text(ref text) if inside => { + let s = output.get_or_insert(String::new()); + s.push_str(text); + } + Event::End(Tag::CodeBlock(_)) if inside => { + inside = false; + } + _ => (), + } + } + + Ok(output) +} + +#[cfg(test)] +mod test_expand { + use super::*; + + macro_rules! si { + ($i:expr) => {{ + expand_manifest( + $i, + std::path::Path::new("/home/me/test.rs"), + &Config::default().unwrap(), + ) + .unwrap_or_else(|err| panic!("{}", err)) + }}; + } + + #[test] + fn test_default() { + snapbox::assert_eq( + r#"[[bin]] +name = "test-" +path = "test.rs" + +[package] +autobenches = false +autobins = false +autoexamples = false +autotests = false +build = false +edition = "2021" +name = "test-" +publish = false +version = "0.0.0" + +[profile.release] +strip = true + +[workspace] +"#, + si!(r#"fn main() {}"#), + ); + } + + #[test] + fn test_dependencies() { + snapbox::assert_eq( + r#"[[bin]] +name = "test-" +path = "test.rs" + +[dependencies] +time = "0.1.25" + +[package] +autobenches = false +autobins = false +autoexamples = false +autotests = false +build = false +edition = "2021" +name = "test-" +publish = false +version = "0.0.0" + +[profile.release] +strip = true + +[workspace] +"#, + si!(r#" +//! ```cargo +//! [dependencies] +//! time="0.1.25" +//! ``` +fn main() {} +"#), + ); + } +} + +#[cfg(test)] +mod test_comment { + use super::*; + + macro_rules! ec { + ($s:expr) => { + extract_comment($s).unwrap_or_else(|err| panic!("{}", err)) + }; + } + + #[test] + fn test_no_comment() { + snapbox::assert_eq( + "no doc-comment found", + extract_comment( + r#" +fn main () { +} +"#, + ) + .unwrap_err() + .to_string(), + ); + } + + #[test] + fn test_no_comment_she_bang() { + snapbox::assert_eq( + "no doc-comment found", + extract_comment( + r#"#!/usr/bin/env cargo-eval + +fn main () { +} +"#, + ) + .unwrap_err() + .to_string(), + ); + } + + #[test] + fn test_comment() { + snapbox::assert_eq( + r#"Here is a manifest: + +```cargo +[dependencies] +time = "*" +``` +"#, + ec!(r#"//! Here is a manifest: +//! +//! ```cargo +//! [dependencies] +//! time = "*" +//! ``` +fn main() {} +"#), + ); + } + + #[test] + fn test_comment_shebang() { + snapbox::assert_eq( + r#"Here is a manifest: + +```cargo +[dependencies] +time = "*" +``` +"#, + ec!(r#"#!/usr/bin/env cargo-eval + +//! Here is a manifest: +//! +//! ```cargo +//! [dependencies] +//! time = "*" +//! ``` +fn main() {} +"#), + ); + } + + #[test] + fn test_multiline_comment() { + snapbox::assert_eq( + r#"Here is a manifest: + +```cargo +[dependencies] +time = "*" +``` +"#, + ec!(r#"/*! +Here is a manifest: + +```cargo +[dependencies] +time = "*" +``` +*/ + +fn main() { +} +"#), + ); + } + + #[test] + fn test_multiline_comment_shebang() { + snapbox::assert_eq( + r#"Here is a manifest: + +```cargo +[dependencies] +time = "*" +``` +"#, + ec!(r#"#!/usr/bin/env cargo-eval + +/*! +Here is a manifest: + +```cargo +[dependencies] +time = "*" +``` +*/ + +fn main() { +} +"#), + ); + } + + #[test] + fn test_multiline_block_comment() { + snapbox::assert_eq( + r#"Here is a manifest: + +```cargo +[dependencies] +time = "*" +``` +"#, + ec!(r#"/*! + * Here is a manifest: + * + * ```cargo + * [dependencies] + * time = "*" + * ``` + */ +fn main() {} +"#), + ); + } + + #[test] + fn test_multiline_block_comment_shebang() { + snapbox::assert_eq( + r#"Here is a manifest: + +```cargo +[dependencies] +time = "*" +``` +"#, + ec!(r#"#!/usr/bin/env cargo-eval + +/*! + * Here is a manifest: + * + * ```cargo + * [dependencies] + * time = "*" + * ``` + */ +fn main() {} +"#), + ); + } + + #[test] + fn test_adjacent_comments() { + snapbox::assert_eq( + r#"Here is a manifest: + +```cargo +[dependencies] +time = "*" +``` +"#, + ec!(r#"#!/usr/bin/env cargo-eval + +// I am a normal comment +//! Here is a manifest: +//! +//! ```cargo +//! [dependencies] +//! time = "*" +//! ``` + +fn main () { +} +"#), + ); + } + + #[test] + fn test_doc_attrib() { + snapbox::assert_eq( + r#"Here is a manifest: + +```cargo +[dependencies] +time = "*" +``` +"#, + ec!(r###"#!/usr/bin/env cargo-eval + +#![doc = r#"Here is a manifest: + +```cargo +[dependencies] +time = "*" +``` +"#] + +fn main () { +} +"###), + ); + } +} + +#[cfg(test)] +mod test_manifest { + use super::*; + + macro_rules! smm { + ($c:expr) => { + extract_manifest($c) + }; + } + + #[test] + fn test_no_code_fence() { + assert_eq!( + smm!( + r#"There is no manifest in this comment. +"# + ) + .unwrap(), + None + ); + } + + #[test] + fn test_no_cargo_code_fence() { + assert_eq!( + smm!( + r#"There is no manifest in this comment. + +``` +This is not a manifest. +``` + +```rust +println!("Nor is this."); +``` + + Or this. +"# + ) + .unwrap(), + None + ); + } + + #[test] + fn test_cargo_code_fence() { + assert_eq!( + smm!( + r#"This is a manifest: + +```cargo +dependencies = { time = "*" } +``` +"# + ) + .unwrap(), + Some( + r#"dependencies = { time = "*" } +"# + .into() + ) + ); + } + + #[test] + fn test_mixed_code_fence() { + assert_eq!( + smm!( + r#"This is *not* a manifest: + +``` +He's lying, I'm *totally* a manifest! +``` + +This *is*: + +```cargo +dependencies = { time = "*" } +``` +"# + ) + .unwrap(), + Some( + r#"dependencies = { time = "*" } +"# + .into() + ) + ); + } + + #[test] + fn test_two_cargo_code_fence() { + assert!(smm!( + r#"This is a manifest: + +```cargo +dependencies = { time = "*" } +``` + +So is this, but it doesn't count: + +```cargo +dependencies = { explode = true } +``` +"# + ) + .is_err()); + } +} diff --git a/src/tools/cargo/src/cargo/util/toml/mod.rs b/src/tools/cargo/src/cargo/util/toml/mod.rs index 2c213b7f5..2202f6b3b 100644 --- a/src/tools/cargo/src/cargo/util/toml/mod.rs +++ b/src/tools/cargo/src/cargo/util/toml/mod.rs @@ -1,4 +1,5 @@ use std::collections::{BTreeMap, BTreeSet, HashMap}; +use std::ffi::OsStr; use std::fmt::{self, Display, Write}; use std::marker::PhantomData; use std::path::{Path, PathBuf}; @@ -33,6 +34,7 @@ use crate::util::{ self, config::ConfigRelativePath, validate_package_name, Config, IntoUrl, VersionReqExt, }; +pub mod embedded; mod targets; use self::targets::targets; @@ -54,13 +56,32 @@ pub fn read_manifest( path.display(), source_id ); - let contents = paths::read(path).map_err(|err| ManifestError::new(err, path.into()))?; + let mut contents = paths::read(path).map_err(|err| ManifestError::new(err, path.into()))?; + let embedded = is_embedded(path); + if embedded { + if !config.cli_unstable().script { + return Err(ManifestError::new( + anyhow::anyhow!("parsing `{}` requires `-Zscript`", path.display()), + path.into(), + )); + } + contents = embedded::expand_manifest(&contents, path, config) + .map_err(|err| ManifestError::new(err, path.into()))?; + } - read_manifest_from_str(&contents, path, source_id, config) + read_manifest_from_str(&contents, path, embedded, source_id, config) .with_context(|| format!("failed to parse manifest at `{}`", path.display())) .map_err(|err| ManifestError::new(err, path.into())) } +/// See also `bin/cargo/commands/run.rs`s `is_manifest_command` +pub fn is_embedded(path: &Path) -> bool { + let ext = path.extension(); + ext == Some(OsStr::new("rs")) || + // Provide better errors by not considering directories to be embedded manifests + (ext.is_none() && path.is_file()) +} + /// Parse an already-loaded `Cargo.toml` as a Cargo manifest. /// /// This could result in a real or virtual manifest being returned. @@ -69,9 +90,10 @@ pub fn read_manifest( /// within the manifest. For virtual manifests, these paths can only /// come from patched or replaced dependencies. These paths are not /// canonicalized. -pub fn read_manifest_from_str( +fn read_manifest_from_str( contents: &str, manifest_file: &Path, + embedded: bool, source_id: SourceId, config: &Config, ) -> CargoResult<(EitherManifest, Vec<PathBuf>)> { @@ -127,7 +149,7 @@ pub fn read_manifest_from_str( } return if manifest.project.is_some() || manifest.package.is_some() { let (mut manifest, paths) = - TomlManifest::to_real_manifest(&manifest, source_id, package_root, config)?; + TomlManifest::to_real_manifest(&manifest, embedded, source_id, package_root, config)?; add_unused(manifest.warnings_mut()); if manifest.targets().iter().all(|t| t.is_custom_build()) { bail!( @@ -1573,178 +1595,89 @@ pub struct InheritableFields { ws_root: PathBuf, } -impl InheritableFields { - pub fn update_deps(&mut self, deps: Option<BTreeMap<String, TomlDependency>>) { - self.dependencies = deps; - } - - pub fn update_lints(&mut self, lints: Option<TomlLints>) { - self.lints = lints; - } - - pub fn update_ws_path(&mut self, ws_root: PathBuf) { - self.ws_root = ws_root; - } - - pub fn dependencies(&self) -> CargoResult<BTreeMap<String, TomlDependency>> { - self.dependencies.clone().map_or( - Err(anyhow!("`workspace.dependencies` was not defined")), - |d| Ok(d), - ) - } - - pub fn lints(&self) -> CargoResult<TomlLints> { - self.lints - .clone() - .map_or(Err(anyhow!("`workspace.lints` was not defined")), |d| Ok(d)) - } +/// Defines simple getter methods for inheritable fields. +macro_rules! inheritable_field_getter { + ( $(($key:literal, $field:ident -> $ret:ty),)* ) => ( + $( + #[doc = concat!("Gets the field `workspace.", $key, "`.")] + pub fn $field(&self) -> CargoResult<$ret> { + let Some(val) = &self.$field else { + bail!("`workspace.{}` was not defined", $key); + }; + Ok(val.clone()) + } + )* + ) +} +impl InheritableFields { + inheritable_field_getter! { + // Please keep this list lexicographically ordered. + ("dependencies", dependencies -> BTreeMap<String, TomlDependency>), + ("lints", lints -> TomlLints), + ("package.authors", authors -> Vec<String>), + ("package.badges", badges -> BTreeMap<String, BTreeMap<String, String>>), + ("package.categories", categories -> Vec<String>), + ("package.description", description -> String), + ("package.documentation", documentation -> String), + ("package.edition", edition -> String), + ("package.exclude", exclude -> Vec<String>), + ("package.homepage", homepage -> String), + ("package.include", include -> Vec<String>), + ("package.keywords", keywords -> Vec<String>), + ("package.license", license -> String), + ("package.publish", publish -> VecStringOrBool), + ("package.repository", repository -> String), + ("package.rust-version", rust_version -> String), + ("package.version", version -> semver::Version), + } + + /// Gets a workspace dependency with the `name`. pub fn get_dependency(&self, name: &str, package_root: &Path) -> CargoResult<TomlDependency> { - self.dependencies.clone().map_or( - Err(anyhow!("`workspace.dependencies` was not defined")), - |deps| { - deps.get(name).map_or( - Err(anyhow!( - "`dependency.{}` was not found in `workspace.dependencies`", - name - )), - |dep| { - let mut dep = dep.clone(); - if let TomlDependency::Detailed(detailed) = &mut dep { - detailed.resolve_path(name, self.ws_root(), package_root)? - } - Ok(dep) - }, - ) - }, - ) - } - - pub fn version(&self) -> CargoResult<semver::Version> { - self.version.clone().map_or( - Err(anyhow!("`workspace.package.version` was not defined")), - |d| Ok(d), - ) - } - - pub fn authors(&self) -> CargoResult<Vec<String>> { - self.authors.clone().map_or( - Err(anyhow!("`workspace.package.authors` was not defined")), - |d| Ok(d), - ) - } - - pub fn description(&self) -> CargoResult<String> { - self.description.clone().map_or( - Err(anyhow!("`workspace.package.description` was not defined")), - |d| Ok(d), - ) - } - - pub fn homepage(&self) -> CargoResult<String> { - self.homepage.clone().map_or( - Err(anyhow!("`workspace.package.homepage` was not defined")), - |d| Ok(d), - ) - } - - pub fn documentation(&self) -> CargoResult<String> { - self.documentation.clone().map_or( - Err(anyhow!("`workspace.package.documentation` was not defined")), - |d| Ok(d), - ) - } - - pub fn readme(&self, package_root: &Path) -> CargoResult<StringOrBool> { - readme_for_package(self.ws_root.as_path(), self.readme.clone()).map_or( - Err(anyhow!("`workspace.package.readme` was not defined")), - |readme| { - let rel_path = - resolve_relative_path("readme", &self.ws_root, package_root, &readme)?; - Ok(StringOrBool::String(rel_path)) - }, - ) - } - - pub fn keywords(&self) -> CargoResult<Vec<String>> { - self.keywords.clone().map_or( - Err(anyhow!("`workspace.package.keywords` was not defined")), - |d| Ok(d), - ) - } - - pub fn categories(&self) -> CargoResult<Vec<String>> { - self.categories.clone().map_or( - Err(anyhow!("`workspace.package.categories` was not defined")), - |d| Ok(d), - ) - } - - pub fn license(&self) -> CargoResult<String> { - self.license.clone().map_or( - Err(anyhow!("`workspace.package.license` was not defined")), - |d| Ok(d), - ) + let Some(deps) = &self.dependencies else { + bail!("`workspace.dependencies` was not defined"); + }; + let Some(dep) = deps.get(name) else { + bail!("`dependency.{name}` was not found in `workspace.dependencies`"); + }; + let mut dep = dep.clone(); + if let TomlDependency::Detailed(detailed) = &mut dep { + detailed.resolve_path(name, self.ws_root(), package_root)?; + } + Ok(dep) } + /// Gets the field `workspace.package.license-file`. pub fn license_file(&self, package_root: &Path) -> CargoResult<String> { - self.license_file.clone().map_or( - Err(anyhow!("`workspace.package.license_file` was not defined")), - |d| resolve_relative_path("license-file", &self.ws_root, package_root, &d), - ) - } - - pub fn repository(&self) -> CargoResult<String> { - self.repository.clone().map_or( - Err(anyhow!("`workspace.package.repository` was not defined")), - |d| Ok(d), - ) - } - - pub fn publish(&self) -> CargoResult<VecStringOrBool> { - self.publish.clone().map_or( - Err(anyhow!("`workspace.package.publish` was not defined")), - |d| Ok(d), - ) - } - - pub fn edition(&self) -> CargoResult<String> { - self.edition.clone().map_or( - Err(anyhow!("`workspace.package.edition` was not defined")), - |d| Ok(d), - ) + let Some(license_file) = &self.license_file else { + bail!("`workspace.package.license-file` was not defined"); + }; + resolve_relative_path("license-file", &self.ws_root, package_root, license_file) } - pub fn rust_version(&self) -> CargoResult<String> { - self.rust_version.clone().map_or( - Err(anyhow!("`workspace.package.rust-version` was not defined")), - |d| Ok(d), - ) + /// Gets the field `workspace.package.readme`. + pub fn readme(&self, package_root: &Path) -> CargoResult<StringOrBool> { + let Some(readme) = readme_for_package(self.ws_root.as_path(), self.readme.as_ref()) else { + bail!("`workspace.package.readme` was not defined"); + }; + resolve_relative_path("readme", &self.ws_root, package_root, &readme) + .map(StringOrBool::String) } - pub fn badges(&self) -> CargoResult<BTreeMap<String, BTreeMap<String, String>>> { - self.badges.clone().map_or( - Err(anyhow!("`workspace.package.badges` was not defined")), - |d| Ok(d), - ) + pub fn ws_root(&self) -> &PathBuf { + &self.ws_root } - pub fn exclude(&self) -> CargoResult<Vec<String>> { - self.exclude.clone().map_or( - Err(anyhow!("`workspace.package.exclude` was not defined")), - |d| Ok(d), - ) + pub fn update_deps(&mut self, deps: Option<BTreeMap<String, TomlDependency>>) { + self.dependencies = deps; } - pub fn include(&self) -> CargoResult<Vec<String>> { - self.include.clone().map_or( - Err(anyhow!("`workspace.package.include` was not defined")), - |d| Ok(d), - ) + pub fn update_lints(&mut self, lints: Option<TomlLints>) { + self.lints = lints; } - pub fn ws_root(&self) -> &PathBuf { - &self.ws_root + pub fn update_ws_path(&mut self, ws_root: PathBuf) { + self.ws_root = ws_root; } } @@ -1975,6 +1908,7 @@ impl TomlManifest { pub fn to_real_manifest( me: &Rc<TomlManifest>, + embedded: bool, source_id: SourceId, package_root: &Path, config: &Config, @@ -2412,7 +2346,6 @@ impl TomlManifest { let empty_features = BTreeMap::new(); let summary = Summary::new( - config, pkgid, deps, me.features.as_ref().unwrap_or(&empty_features), @@ -2442,7 +2375,8 @@ impl TomlManifest { .readme .clone() .map(|mw| mw.resolve("readme", || inherit()?.readme(package_root))) - .transpose()?, + .transpose()? + .as_ref(), ), authors: package .authors @@ -2643,6 +2577,7 @@ impl TomlManifest { 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( @@ -3039,7 +2974,7 @@ fn inheritable_from_path( } /// Returns the name of the README file for a [`TomlPackage`]. -pub fn readme_for_package(package_root: &Path, readme: Option<StringOrBool>) -> Option<String> { +pub fn readme_for_package(package_root: &Path, readme: Option<&StringOrBool>) -> Option<String> { match &readme { None => default_readme_from_package_root(package_root), Some(value) => match value { 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 f3fc150e1..5529b8029 100644 --- a/src/tools/cargo/src/cargo/util/toml_mut/manifest.rs +++ b/src/tools/cargo/src/cargo/util/toml_mut/manifest.rs @@ -496,6 +496,11 @@ fn fix_feature_activations( for idx in remove_list.iter().rev() { feature_values.remove(*idx); } + if !remove_list.is_empty() { + // HACK: Instead of cleaning up the users formatting from having removed a feature, we just + // re-format the whole feature list + feature_values.fmt(); + } if status == DependencyStatus::Required { for value in feature_values.iter_mut() { @@ -511,13 +516,13 @@ fn fix_feature_activations( } = parsed_value { if dep_name == dep_key && weak { - *value = format!("{dep_name}/{dep_feature}").into(); + let mut new_value = toml_edit::Value::from(format!("{dep_name}/{dep_feature}")); + *new_value.decor_mut() = value.decor().clone(); + *value = new_value; } } } } - - feature_values.fmt(); } pub fn str_or_1_len_table(item: &toml_edit::Item) -> bool { diff --git a/src/tools/cargo/src/doc/contrib/src/process/index.md b/src/tools/cargo/src/doc/contrib/src/process/index.md index 63f07ca55..c9dae918c 100644 --- a/src/tools/cargo/src/doc/contrib/src/process/index.md +++ b/src/tools/cargo/src/doc/contrib/src/process/index.md @@ -90,7 +90,7 @@ implemented. The Cargo project uses several bots: * [GitHub Actions] are used to automatically run all tests for each PR. -* [triagebot] automatically assigns reviewers for PRs, see [Assignment] for +* [triagebot] automatically assigns reviewers for PRs, see [PR Assignment] for how to configure. * [bors] is used to merge PRs. See [The merging process]. * [triagebot] is used for assigning issues to non-members, see [Issue @@ -100,20 +100,20 @@ The Cargo project uses several bots: [bors]: https://buildbot2.rust-lang.org/homu/ [The merging process]: working-on-cargo.md#the-merging-process [GitHub Actions]: https://github.com/features/actions -[triagebot]: https://github.com/rust-lang/triagebot/wiki +[triagebot]: https://forge.rust-lang.org/triagebot/index.html [rfcbot]: https://github.com/rust-lang/rfcbot-rs -[Assignment]: https://github.com/rust-lang/triagebot/wiki/Assignment +[PR Assignment]: https://forge.rust-lang.org/triagebot/pr-assignment.html ## Issue assignment Normally, if you plan to work on an issue that has been marked with the [S-accepted] label, it is sufficient just to leave a comment that you are working on it. We also have a bot that allows you to formally claim an issue -by entering the text `@rustbot claim` in a comment. See the [Assignment] docs +by entering the text `@rustbot claim` in a comment. See the [Issue Assignment] docs on how this works. -[Assignment]: https://github.com/rust-lang/triagebot/wiki/Assignment +[Issue Assignment]: https://forge.rust-lang.org/triagebot/issue-assignment.html [team]: https://www.rust-lang.org/governance/teams/dev-tools#cargo [Zulip]: https://rust-lang.zulipchat.com/#narrow/stream/246057-t-cargo [issue-feature-request]: https://github.com/rust-lang/cargo/labels/C-feature-request diff --git a/src/tools/cargo/src/doc/contrib/src/process/working-on-cargo.md b/src/tools/cargo/src/doc/contrib/src/process/working-on-cargo.md index e90bb8588..1567197c4 100644 --- a/src/tools/cargo/src/doc/contrib/src/process/working-on-cargo.md +++ b/src/tools/cargo/src/doc/contrib/src/process/working-on-cargo.md @@ -135,7 +135,7 @@ More information about these commands can be found at the [shortcuts documentati [`S-waiting-on-review`]: https://github.com/rust-lang/cargo/labels/S-waiting-on-review [`S-waiting-on-author`]: https://github.com/rust-lang/cargo/labels/S-waiting-on-author [`@rustbot`]: https://github.com/rustbot -[shortcuts documentation]: https://github.com/rust-lang/triagebot/wiki/Shortcuts +[shortcuts documentation]: https://forge.rust-lang.org/triagebot/shortcuts.html ## The merging process diff --git a/src/tools/cargo/src/doc/man/cargo-install.md b/src/tools/cargo/src/doc/man/cargo-install.md index 385ed27c2..e6786318f 100644 --- a/src/tools/cargo/src/doc/man/cargo-install.md +++ b/src/tools/cargo/src/doc/man/cargo-install.md @@ -18,7 +18,7 @@ cargo-install --- Build and install a Rust binary This command manages Cargo's local set of installed binary crates. Only packages which have executable `[[bin]]` or `[[example]]` targets can be installed, and all executables are installed into the installation root's -`bin` folder. +`bin` folder. By default only binaries, not examples, are installed. {{> description-install-root }} @@ -141,7 +141,7 @@ Install only the specified binary. {{/option}} {{#option "`--bins`" }} -Install all binaries. +Install all binaries. This is the default behavior. {{/option}} {{#option "`--example` _name_..." }} diff --git a/src/tools/cargo/src/doc/man/cargo-test.md b/src/tools/cargo/src/doc/man/cargo-test.md index 49792d6be..75bf72b30 100644 --- a/src/tools/cargo/src/doc/man/cargo-test.md +++ b/src/tools/cargo/src/doc/man/cargo-test.md @@ -59,12 +59,19 @@ on writing doc tests. ### Working directory of tests -The working directory of every test is set to the root directory of the package -the test belongs to. -Setting the working directory of tests to the package's root directory makes it +The working directory when running each unit and integration test is set to the +root directory of the package the test belongs to. +Setting the working directory of tests to the package's root directory makes it possible for tests to reliably access the package's files using relative paths, regardless from where `cargo test` was executed from. +For documentation tests, the working directory when invoking `rustdoc` is set to +the workspace root directory, and is also the directory `rustdoc` uses as the +compilation directory of each documentation test. +The working directory when running each documentation test is set to the root +directory of the package the test belongs to, and is controlled via `rustdoc`'s +`--test-run-directory` option. + ## OPTIONS ### Test Options diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-bench.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-bench.txt index 9108d6165..9610b72f0 100644 --- a/src/tools/cargo/src/doc/man/generated_txt/cargo-bench.txt +++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-bench.txt @@ -410,6 +410,7 @@ OPTIONS <https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. + If a string default is provided, it sets the value back to defaults. Should not be 0. --keep-going diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-build.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-build.txt index ff8bdb5ba..e396410d3 100644 --- a/src/tools/cargo/src/doc/man/generated_txt/cargo-build.txt +++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-build.txt @@ -341,6 +341,7 @@ OPTIONS <https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. + If a string default is provided, it sets the value back to defaults. Should not be 0. --keep-going diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-check.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-check.txt index bf8cb48f3..9814f445d 100644 --- a/src/tools/cargo/src/doc/man/generated_txt/cargo-check.txt +++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-check.txt @@ -326,6 +326,7 @@ OPTIONS <https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. + If a string default is provided, it sets the value back to defaults. Should not be 0. --keep-going diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-doc.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-doc.txt index 825032826..09f9b68c9 100644 --- a/src/tools/cargo/src/doc/man/generated_txt/cargo-doc.txt +++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-doc.txt @@ -297,6 +297,7 @@ OPTIONS <https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. + If a string default is provided, it sets the value back to defaults. Should not be 0. --keep-going diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-fix.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-fix.txt index 87d72ad38..b83c360eb 100644 --- a/src/tools/cargo/src/doc/man/generated_txt/cargo-fix.txt +++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-fix.txt @@ -399,6 +399,7 @@ OPTIONS <https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. + If a string default is provided, it sets the value back to defaults. Should not be 0. --keep-going diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-install.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-install.txt index 7aefd3492..a5b696111 100644 --- a/src/tools/cargo/src/doc/man/generated_txt/cargo-install.txt +++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-install.txt @@ -13,7 +13,8 @@ DESCRIPTION This command manages Cargo’s local set of installed binary crates. Only packages which have executable [[bin]] or [[example]] targets can be installed, and all executables are installed into the installation - root’s bin folder. + root’s bin folder. By default only binaries, not examples, are + installed. The installation root is determined, in order of precedence: @@ -137,7 +138,7 @@ OPTIONS Install only the specified binary. --bins - Install all binaries. + Install all binaries. This is the default behavior. --example name… Install only the specified example. @@ -273,6 +274,7 @@ OPTIONS <https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. + If a string default is provided, it sets the value back to defaults. Should not be 0. --keep-going diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-package.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-package.txt index 960e0248e..1201f3d07 100644 --- a/src/tools/cargo/src/doc/man/generated_txt/cargo-package.txt +++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-package.txt @@ -191,6 +191,7 @@ OPTIONS <https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. + If a string default is provided, it sets the value back to defaults. Should not be 0. --keep-going diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-publish.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-publish.txt index d35172ad7..e8850e3d4 100644 --- a/src/tools/cargo/src/doc/man/generated_txt/cargo-publish.txt +++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-publish.txt @@ -157,6 +157,7 @@ OPTIONS <https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. + If a string default is provided, it sets the value back to defaults. Should not be 0. --keep-going diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-run.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-run.txt index f6782be11..5fcfe66b4 100644 --- a/src/tools/cargo/src/doc/man/generated_txt/cargo-run.txt +++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-run.txt @@ -245,6 +245,7 @@ OPTIONS <https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. + If a string default is provided, it sets the value back to defaults. Should not be 0. --keep-going diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-rustc.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-rustc.txt index cc4241f93..7a70c5363 100644 --- a/src/tools/cargo/src/doc/man/generated_txt/cargo-rustc.txt +++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-rustc.txt @@ -343,6 +343,7 @@ OPTIONS <https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. + If a string default is provided, it sets the value back to defaults. Should not be 0. --keep-going diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-rustdoc.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-rustdoc.txt index 6a32a6b6e..4db66b3a8 100644 --- a/src/tools/cargo/src/doc/man/generated_txt/cargo-rustdoc.txt +++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-rustdoc.txt @@ -313,6 +313,7 @@ OPTIONS <https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. + If a string default is provided, it sets the value back to defaults. Should not be 0. --keep-going diff --git a/src/tools/cargo/src/doc/man/generated_txt/cargo-test.txt b/src/tools/cargo/src/doc/man/generated_txt/cargo-test.txt index 9e26f8aae..7955b0e3d 100644 --- a/src/tools/cargo/src/doc/man/generated_txt/cargo-test.txt +++ b/src/tools/cargo/src/doc/man/generated_txt/cargo-test.txt @@ -53,11 +53,18 @@ DESCRIPTION information on writing doc tests. Working directory of tests - The working directory of every test is set to the root directory of the - package the test belongs to. Setting the working directory of tests to - the package’s root directory makes it possible for tests to reliably - access the package’s files using relative paths, regardless from where - cargo test was executed from. + The working directory when running each unit and integration test is set + to the root directory of the package the test belongs to. Setting the + working directory of tests to the package’s root directory makes it + possible for tests to reliably access the package’s files using + relative paths, regardless from where cargo test was executed from. + + For documentation tests, the working directory when invoking rustdoc is + set to the workspace root directory, and is also the directory rustdoc + uses as the compilation directory of each documentation test. The + working directory when running each documentation test is set to the + root directory of the package the test belongs to, and is controlled via + rustdoc’s --test-run-directory option. OPTIONS Test Options @@ -432,6 +439,7 @@ OPTIONS <https://doc.rust-lang.org/cargo/reference/config.html>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of parallel jobs to the number of logical CPUs plus provided value. + If a string default is provided, it sets the value back to defaults. Should not be 0. --keep-going diff --git a/src/tools/cargo/src/doc/man/includes/options-jobs.md b/src/tools/cargo/src/doc/man/includes/options-jobs.md index 274263866..0030e18d8 100644 --- a/src/tools/cargo/src/doc/man/includes/options-jobs.md +++ b/src/tools/cargo/src/doc/man/includes/options-jobs.md @@ -2,6 +2,7 @@ Number of parallel jobs to run. May also be specified with the `build.jobs` [config value](../reference/config.html). Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string `default` is provided, it sets the value back to defaults. Should not be 0. {{/option}} diff --git a/src/tools/cargo/src/doc/src/commands/cargo-bench.md b/src/tools/cargo/src/doc/src/commands/cargo-bench.md index df0101be5..70ae187a7 100644 --- a/src/tools/cargo/src/doc/src/commands/cargo-bench.md +++ b/src/tools/cargo/src/doc/src/commands/cargo-bench.md @@ -476,7 +476,8 @@ Rust test harness runs benchmarks serially in a single thread. <dd class="option-desc">Number of parallel jobs to run. May also be specified with the <code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string <code>default</code> is provided, it sets the value back to defaults. Should not be 0.</dd> diff --git a/src/tools/cargo/src/doc/src/commands/cargo-build.md b/src/tools/cargo/src/doc/src/commands/cargo-build.md index 6e3cf157a..525ab14e5 100644 --- a/src/tools/cargo/src/doc/src/commands/cargo-build.md +++ b/src/tools/cargo/src/doc/src/commands/cargo-build.md @@ -402,7 +402,8 @@ requires the <code>-Z unstable-options</code> flag to enable (see <dd class="option-desc">Number of parallel jobs to run. May also be specified with the <code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string <code>default</code> is provided, it sets the value back to defaults. Should not be 0.</dd> diff --git a/src/tools/cargo/src/doc/src/commands/cargo-check.md b/src/tools/cargo/src/doc/src/commands/cargo-check.md index 2070293ac..4dbc97ad8 100644 --- a/src/tools/cargo/src/doc/src/commands/cargo-check.md +++ b/src/tools/cargo/src/doc/src/commands/cargo-check.md @@ -383,7 +383,8 @@ requires the <code>-Z unstable-options</code> flag to enable (see <dd class="option-desc">Number of parallel jobs to run. May also be specified with the <code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string <code>default</code> is provided, it sets the value back to defaults. Should not be 0.</dd> diff --git a/src/tools/cargo/src/doc/src/commands/cargo-doc.md b/src/tools/cargo/src/doc/src/commands/cargo-doc.md index e0e5c8ed2..d68bb84e7 100644 --- a/src/tools/cargo/src/doc/src/commands/cargo-doc.md +++ b/src/tools/cargo/src/doc/src/commands/cargo-doc.md @@ -357,7 +357,8 @@ requires the <code>-Z unstable-options</code> flag to enable (see <dd class="option-desc">Number of parallel jobs to run. May also be specified with the <code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string <code>default</code> is provided, it sets the value back to defaults. Should not be 0.</dd> diff --git a/src/tools/cargo/src/doc/src/commands/cargo-fix.md b/src/tools/cargo/src/doc/src/commands/cargo-fix.md index 1b9ec6a85..2bf83fc2e 100644 --- a/src/tools/cargo/src/doc/src/commands/cargo-fix.md +++ b/src/tools/cargo/src/doc/src/commands/cargo-fix.md @@ -463,7 +463,8 @@ requires the <code>-Z unstable-options</code> flag to enable (see <dd class="option-desc">Number of parallel jobs to run. May also be specified with the <code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string <code>default</code> is provided, it sets the value back to defaults. Should not be 0.</dd> diff --git a/src/tools/cargo/src/doc/src/commands/cargo-install.md b/src/tools/cargo/src/doc/src/commands/cargo-install.md index 5d413010c..af8efcae8 100644 --- a/src/tools/cargo/src/doc/src/commands/cargo-install.md +++ b/src/tools/cargo/src/doc/src/commands/cargo-install.md @@ -18,7 +18,7 @@ cargo-install --- Build and install a Rust binary This command manages Cargo's local set of installed binary crates. Only packages which have executable `[[bin]]` or `[[example]]` targets can be installed, and all executables are installed into the installation root's -`bin` folder. +`bin` folder. By default only binaries, not examples, are installed. The installation root is determined, in order of precedence: @@ -150,7 +150,7 @@ same time.</dd> <dt class="option-term" id="option-cargo-install---bins"><a class="option-anchor" href="#option-cargo-install---bins"></a><code>--bins</code></dt> -<dd class="option-desc">Install all binaries.</dd> +<dd class="option-desc">Install all binaries. This is the default behavior.</dd> <dt class="option-term" id="option-cargo-install---example"><a class="option-anchor" href="#option-cargo-install---example"></a><code>--example</code> <em>name</em>…</dt> @@ -312,7 +312,8 @@ offline.</p> <dd class="option-desc">Number of parallel jobs to run. May also be specified with the <code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string <code>default</code> is provided, it sets the value back to defaults. Should not be 0.</dd> diff --git a/src/tools/cargo/src/doc/src/commands/cargo-package.md b/src/tools/cargo/src/doc/src/commands/cargo-package.md index 776b150cf..f9c7c552d 100644 --- a/src/tools/cargo/src/doc/src/commands/cargo-package.md +++ b/src/tools/cargo/src/doc/src/commands/cargo-package.md @@ -227,7 +227,8 @@ offline.</p> <dd class="option-desc">Number of parallel jobs to run. May also be specified with the <code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string <code>default</code> is provided, it sets the value back to defaults. Should not be 0.</dd> diff --git a/src/tools/cargo/src/doc/src/commands/cargo-publish.md b/src/tools/cargo/src/doc/src/commands/cargo-publish.md index 1f4fbebb8..fe37d9f97 100644 --- a/src/tools/cargo/src/doc/src/commands/cargo-publish.md +++ b/src/tools/cargo/src/doc/src/commands/cargo-publish.md @@ -193,7 +193,8 @@ offline.</p> <dd class="option-desc">Number of parallel jobs to run. May also be specified with the <code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string <code>default</code> is provided, it sets the value back to defaults. Should not be 0.</dd> diff --git a/src/tools/cargo/src/doc/src/commands/cargo-run.md b/src/tools/cargo/src/doc/src/commands/cargo-run.md index f6f5ec2a3..bd3e4724a 100644 --- a/src/tools/cargo/src/doc/src/commands/cargo-run.md +++ b/src/tools/cargo/src/doc/src/commands/cargo-run.md @@ -299,7 +299,8 @@ requires the <code>-Z unstable-options</code> flag to enable (see <dd class="option-desc">Number of parallel jobs to run. May also be specified with the <code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string <code>default</code> is provided, it sets the value back to defaults. Should not be 0.</dd> diff --git a/src/tools/cargo/src/doc/src/commands/cargo-rustc.md b/src/tools/cargo/src/doc/src/commands/cargo-rustc.md index 946298af9..2147d617c 100644 --- a/src/tools/cargo/src/doc/src/commands/cargo-rustc.md +++ b/src/tools/cargo/src/doc/src/commands/cargo-rustc.md @@ -396,7 +396,8 @@ requires the <code>-Z unstable-options</code> flag to enable (see <dd class="option-desc">Number of parallel jobs to run. May also be specified with the <code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string <code>default</code> is provided, it sets the value back to defaults. Should not be 0.</dd> diff --git a/src/tools/cargo/src/doc/src/commands/cargo-rustdoc.md b/src/tools/cargo/src/doc/src/commands/cargo-rustdoc.md index 8467da2a3..22c1c8322 100644 --- a/src/tools/cargo/src/doc/src/commands/cargo-rustdoc.md +++ b/src/tools/cargo/src/doc/src/commands/cargo-rustdoc.md @@ -376,7 +376,8 @@ requires the <code>-Z unstable-options</code> flag to enable (see <dd class="option-desc">Number of parallel jobs to run. May also be specified with the <code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string <code>default</code> is provided, it sets the value back to defaults. Should not be 0.</dd> diff --git a/src/tools/cargo/src/doc/src/commands/cargo-test.md b/src/tools/cargo/src/doc/src/commands/cargo-test.md index 24fcc70ff..e38e9929e 100644 --- a/src/tools/cargo/src/doc/src/commands/cargo-test.md +++ b/src/tools/cargo/src/doc/src/commands/cargo-test.md @@ -59,12 +59,19 @@ on writing doc tests. ### Working directory of tests -The working directory of every test is set to the root directory of the package -the test belongs to. -Setting the working directory of tests to the package's root directory makes it +The working directory when running each unit and integration test is set to the +root directory of the package the test belongs to. +Setting the working directory of tests to the package's root directory makes it possible for tests to reliably access the package's files using relative paths, regardless from where `cargo test` was executed from. +For documentation tests, the working directory when invoking `rustdoc` is set to +the workspace root directory, and is also the directory `rustdoc` uses as the +compilation directory of each documentation test. +The working directory when running each documentation test is set to the root +directory of the package the test belongs to, and is controlled via `rustdoc`'s +`--test-run-directory` option. + ## OPTIONS ### Test Options @@ -503,7 +510,8 @@ includes an option to control the number of threads used: <dd class="option-desc">Number of parallel jobs to run. May also be specified with the <code>build.jobs</code> <a href="../reference/config.html">config value</a>. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string <code>default</code> is provided, it sets the value back to defaults. Should not be 0.</dd> diff --git a/src/tools/cargo/src/doc/src/faq.md b/src/tools/cargo/src/doc/src/faq.md index abe90c000..e4a753413 100644 --- a/src/tools/cargo/src/doc/src/faq.md +++ b/src/tools/cargo/src/doc/src/faq.md @@ -259,3 +259,49 @@ Some issues we've seen historically which can cause crates to get rebuilt are: If after trying to debug your issue, however, you're still running into problems then feel free to [open an issue](https://github.com/rust-lang/cargo/issues/new)! + +### What does "version conflict" mean and how to resolve it? + +> failed to select a version for `x` which could resolve this conflict + +Have you seen the error message above? + +This is one of the most annoying error message for Cargo users. There are several +situations may lead us to a version conflict. Below we'll walk through possible +causes and provide diagnostic techniques to help you out there: + +- The project and its dependencies use [links] to repeatedly link the local + library. Cargo forbids linking two packages with the same native library, so + even with multiple layers of dependencies it is not allowed. In this case, the + error message will prompt: `Only one package in the dependency graph may specify + the same links value`, you may need to manually check and delete duplicate link + values. The community also have [conventions in place] to alleviate this. + +- When depending on different crates in the project, if these crates use the same + dependent library, but the version used is restricted, making it impossible to + determine the correct version, it will also cause conflicts. The error message + will prompt: `all possible versions conflict with previously selected packages`. + You may need to modify the version requirements to make them consistent. + +- If there are multiple versions of dependencies in the project, when using + [`direct-minimal-versions`], the minimum version requirements cannot be met, + which will cause conflicts. You may need to modify version requirements of your + direct dependencies to meet the minimum SemVer version accordingly. + +- If the dependent crate does not have the features you choose, it will also + cause conflicts. At this time, you need to check the dependent version and its + features. + +- Conflicts may occur when merging branches or PRs, if there are non-trivial + conflicts, you can reset all "yours" changes, fix all other conflicts in the + branch, and then run some cargo command (like `cargo tree` or `cargo check`), + which should re-update the lockfile with your own local changes. If you previously + ran some `cargo update` commands in your branch, you can re-run them that this + time. The community has been looking to resolve merge conflicts with `Cargo.lock` + and `Cargo.toml` using a [custom merge tool]. + + +[links]: https://doc.rust-lang.org/cargo/reference/resolver.html#links +[conventions in place]: https://doc.rust-lang.org/cargo/reference/build-scripts.html#-sys-packages +[`direct-minimal-versions`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#direct-minimal-versions +[custom merge tool]: https://github.com/rust-lang/cargo/issues/1818 diff --git a/src/tools/cargo/src/doc/src/reference/config.md b/src/tools/cargo/src/doc/src/reference/config.md index c57a45f67..30053bb18 100644 --- a/src/tools/cargo/src/doc/src/reference/config.md +++ b/src/tools/cargo/src/doc/src/reference/config.md @@ -370,13 +370,14 @@ recursive_example = "rr --example recursions" The `[build]` table controls build-time operations and compiler settings. ##### `build.jobs` -* Type: integer +* Type: integer or string * Default: number of logical CPUs * Environment: `CARGO_BUILD_JOBS` Sets the maximum number of compiler processes to run in parallel. If negative, it sets the maximum number of compiler processes to the number of logical CPUs -plus provided value. Should not be 0. +plus provided value. Should not be 0. If a string `default` is provided, it sets +the value back to defaults. Can be overridden with the `--jobs` CLI option. 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 95c4c87fb..353742877 100644 --- a/src/tools/cargo/src/doc/src/reference/environment-variables.md +++ b/src/tools/cargo/src/doc/src/reference/environment-variables.md @@ -274,6 +274,7 @@ on the platform: * Windows: `PATH` * macOS: `DYLD_FALLBACK_LIBRARY_PATH` * Unix: `LD_LIBRARY_PATH` +* AIX: `LIBPATH` The value is extended from the existing value when Cargo starts. macOS has special consideration where if `DYLD_FALLBACK_LIBRARY_PATH` is not already diff --git a/src/tools/cargo/src/doc/src/reference/registry-index.md b/src/tools/cargo/src/doc/src/reference/registry-index.md index 9e5be3e5b..3eed6ef39 100644 --- a/src/tools/cargo/src/doc/src/reference/registry-index.md +++ b/src/tools/cargo/src/doc/src/reference/registry-index.md @@ -80,6 +80,8 @@ harder to support older versions of Cargo that lack `{prefix}`/`{lowerprefix}`. For example, nginx rewrite rules can easily construct `{prefix}` but can't perform case-conversion to construct `{lowerprefix}`. +### Name restrictions + Registries should consider enforcing limitations on package names added to their index. Cargo itself allows names with any [alphanumeric], `-`, or `_` characters. [crates.io] imposes its own limitations, including the following: @@ -98,6 +100,14 @@ attacks](https://en.wikipedia.org/wiki/IDN_homograph_attack) and other concerns in [UTR36](https://www.unicode.org/reports/tr36/) and [UTS39](https://www.unicode.org/reports/tr39/). +### Version uniqueness + +Indexes *must* ensure that each version only appears once for each package. +This includes ignoring SemVer build metadata. +For example, the index must *not* contain two entries with a version `1.0.7` and `1.0.7+extra`. + +### JSON schema + Each line in a package file contains a JSON object that describes a published version of the package. The following is a pretty-printed example with comments explaining the format of the entry. diff --git a/src/tools/cargo/src/doc/src/reference/resolver.md b/src/tools/cargo/src/doc/src/reference/resolver.md index af02d6cf2..151648f43 100644 --- a/src/tools/cargo/src/doc/src/reference/resolver.md +++ b/src/tools/cargo/src/doc/src/reference/resolver.md @@ -50,7 +50,7 @@ Tilde | `~1.2` | <code>>=1.2.0, <1.3.0</code> | Minimum version, with restr Wildcard | `1.*` | <code>>=1.0.0, <2.0.0</code> | Any version in the `*` position. Equals | `=1.2.3` | <code>=1.2.3</code> | Exactly the specified version only. Comparison | `>1.1` | <code>>=1.2.0</code> | Naive numeric comparison of specified digits. -Compound | <code>>=1.2, <1.5</code> | <code>>1.2.0, <1.5.0</code> | Multiple requirements that must be simultaneously satisfied. +Compound | <code>>=1.2, <1.5</code> | <code>>=1.2.0, <1.5.0</code> | Multiple requirements that must be simultaneously satisfied. When multiple packages specify a dependency for a common package, the resolver attempts to ensure that they use the same version of that common package, as 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 8d9eac308..8941e67ad 100644 --- a/src/tools/cargo/src/doc/src/reference/specifying-dependencies.md +++ b/src/tools/cargo/src/doc/src/reference/specifying-dependencies.md @@ -107,6 +107,43 @@ Here are some examples of comparison requirements: As shown in the examples above, multiple version requirements can be separated with a comma, e.g., `>= 1.2, < 1.5`. +> **Recommendation:** When in doubt, use the default version requirement operator. +> +> In rare circumstances, a package with a "public dependency" +> (re-exports the dependency or interoperates with it in its public API) +> that is compatible with multiple semver-incompatible versions +> (e.g. only uses a simple type that hasn't changed between releases, like an `Id`) +> may support users choosing which version of the "public dependency" to use. +> In this case, a version requirement like `">=0.4, <2"` may be of interest. +> *However* users of the package will likely run into errors and need to to +> manually select a version of the "public dependency" via `cargo update` if +> they also depend on it as Cargo might pick different versions of the "public +> dependency" when [resolving dependency versions](resolver.md) (see +> [#10599]). +> +> Avoid constraining the upper bound of a version to be anything less than the +> next semver incompatible version +> (e.g. avoid `">=2.0, <2.4"`) as other packages in the dependency tree may +> require a newer version, leading to an unresolvable error (see #6584). +> Consider whether controlling the version in your [`Cargo.lock`] would be more +> appropriate. +> +> In some instances this won't matter or the benefits might outweigh the cost, including: +> - When no one else depends on your package e.g. it only has a `[[bin]]` +> - When depending on a pre-release package and wishing to avoid breaking +> changes then a fully specified `"=1.2.3-alpha.3"` might be warranted (see +> [#2222]) +> - When a library re-exports a proc-macro but the proc-macro generates code that +> calls into the re-exporting library then a fully specified `=1.2.3` might be +> warranted to ensure the proc-macro isn't newer than the re-exporting library +> and generating code that uses parts of the API that don't exist within the +> current version + +[`Cargo.lock`]: ../guide/cargo-toml-vs-cargo-lock.md +[#2222]: https://github.com/rust-lang/cargo/issues/2222 +[#6584]: https://github.com/rust-lang/cargo/issues/6584 +[#10599]: https://github.com/rust-lang/cargo/issues/10599 + ### Specifying dependencies from other registries To specify a dependency from a registry other than [crates.io], first the @@ -140,7 +177,8 @@ Cargo will fetch the `git` repository at this location then look for a of a workspace and setting `git` to the repository containing the workspace). Since we haven’t specified any other information, Cargo assumes that -we intend to use the latest commit on the main branch to build our package. +we intend to use the latest commit on the default branch branch +to build our package, which may not necessarily be the main branch. You can combine the `git` key with the `rev`, `tag`, or `branch` keys to specify something else. Here's an example of specifying that you want to use the latest commit on a branch named `next`: diff --git a/src/tools/cargo/src/doc/src/reference/unstable.md b/src/tools/cargo/src/doc/src/reference/unstable.md index 7484db9b5..b59319196 100644 --- a/src/tools/cargo/src/doc/src/reference/unstable.md +++ b/src/tools/cargo/src/doc/src/reference/unstable.md @@ -52,10 +52,12 @@ how the feature works: ``` Each new feature described below should explain how to use it. +For the latest nightly, see the [nightly version] of this page. [config file]: config.md [nightly channel]: ../../book/appendix-07-nightly-rust.html [stabilized]: https://doc.crates.io/contrib/process/unstable.html#stabilization +[nightly version]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#script ### List of unstable features @@ -85,7 +87,6 @@ Each new feature described below should explain how to use it. * [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. * rustdoc - * [`doctest-in-workspace`](#doctest-in-workspace) --- Fixes workspace-relative paths when running doctests. * [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. * `Cargo.toml` extensions @@ -107,6 +108,7 @@ Each new feature described below should explain how to use it. * [registry-auth](#registry-auth) --- Adds support for authenticated registries, and generate registry authentication tokens using asymmetric cryptography. * Other * [gitoxide](#gitoxide) --- Use `gitoxide` instead of `git2` for a set of operations. + * [script](#script) --- Enable support for single-file `.rs` packages. ### allow-features @@ -450,27 +452,30 @@ cargo check --keep-going -Z unstable-options ### config-include * Tracking Issue: [#7723](https://github.com/rust-lang/cargo/issues/7723) +This feature requires the `-Zconfig-include` command-line option. + The `include` key in a config file can be used to load another config file. It -takes a string for a path to another file relative to the config file, or a -list of strings. It requires the `-Zconfig-include` command-line option. +takes a string for a path to another file relative to the config file, or an +array of config file paths. Only path ending with `.toml` is accepted. ```toml -# .cargo/config -include = '../../some-common-config.toml' -``` - -The config values are first loaded from the include path, and then the config -file's own values are merged on top of it. +# a path ending with `.toml` +include = "path/to/mordor.toml" -This can be paired with [config-cli](#config-cli) to specify a file to load -from the command-line. Pass a path to a config file as the argument to -`--config`: - -```console -cargo +nightly -Zunstable-options -Zconfig-include --config somefile.toml build +# or an array of paths +include = ["frodo.toml", "samwise.toml"] ``` -CLI paths are relative to the current working directory. +Unlike other config values, the merge behavior of the `include` key is +different. When a config file contains an `include` key: + +1. The config values are first loaded from the `include` path. + * If the value of the `include` key is an array of paths, the config values + are loaded and merged from left to right for each path. + * Recurse this step if the config values from the `include` path also + contain an `include` key. +2. Then, the config file's own values are merged on top of the config + from the `include` path. ### target-applies-to-host * Original Pull Request: [#9322](https://github.com/rust-lang/cargo/pull/9322) @@ -1185,24 +1190,6 @@ cargo +nightly -Zunstable-options config get build.rustflags If no config value is included, it will display all config values. See the `--help` output for more options available. -### `doctest-in-workspace` - -* Tracking Issue: [#9427](https://github.com/rust-lang/cargo/issues/9427) - -The `-Z doctest-in-workspace` flag changes the behavior of the current working -directory used when running doctests. Historically, Cargo has run `rustdoc ---test` relative to the root of the package, with paths relative from that -root. However, this is inconsistent with how `rustc` and `rustdoc` are -normally run in a workspace, where they are run relative to the workspace -root. This inconsistency causes problems in various ways, such as when passing -RUSTDOCFLAGS with relative paths, or dealing with diagnostic output. - -The `-Z doctest-in-workspace` flag causes cargo to switch to running `rustdoc` -from the root of the workspace. It also passes the `--test-run-directory` to -`rustdoc` so that when *running* the tests, they are run from the root of the -package. This preserves backwards compatibility and is consistent with how -normal unittests are run. - ### rustc `--print` * Tracking Issue: [#9357](https://github.com/rust-lang/cargo/issues/9357) @@ -1392,6 +1379,113 @@ Valid operations are the following: * When the unstable feature is on, fetching/cloning a git repository is always a shallow fetch. This roughly equals to `git fetch --depth 1` everywhere. * Even with the presence of `Cargo.lock` or specifying a commit `{ rev = "…" }`, gitoxide is still smart enough to shallow fetch without unshallowing the existing repository. +### script + +* Tracking Issue: [#12207](https://github.com/rust-lang/cargo/issues/12207) + +Cargo can directly run `.rs` files as: +```console +$ cargo +nightly -Zscript file.rs +``` +where `file.rs` can be as simple as: +```rust +fn main() {} +``` + +A user may optionally specify a manifest in a `cargo` code fence in a module-level comment, like: +```rust +#!/usr/bin/env -S cargo +nightly -Zscript + +//! ```cargo +//! [dependencies] +//! clap = { version = "4.2", features = ["derive"] } +//! ``` + +use clap::Parser; + +#[derive(Parser, Debug)] +#[clap(version)] +struct Args { + #[clap(short, long, help = "Path to config")] + config: Option<std::path::PathBuf>, +} + +fn main() { + let args = Args::parse(); + println!("{:?}", args); +} +``` + +#### Single-file packages + +In addition to today's multi-file packages (`Cargo.toml` file with other `.rs` +files), we are adding the concept of single-file packages which may contain an +embedded manifest. There is no required distinguishment for a single-file +`.rs` package from any other `.rs` file. + +Single-file packages may be selected via `--manifest-path`, like +`cargo test --manifest-path foo.rs`. Unlike `Cargo.toml`, these files cannot be auto-discovered. + +A single-file package may contain an embedded manifest. An embedded manifest +is stored using `TOML` in a markdown code-fence with `cargo` at the start of the +infostring inside a target-level doc-comment. It is an error to have multiple +`cargo` code fences in the target-level doc-comment. We can relax this later, +either merging the code fences or ignoring later code fences. + +Supported forms of embedded manifest are: +``````rust +//! ```cargo +//! ``` +`````` +``````rust +/*! + * ```cargo + * ``` + */ +`````` + +Inferred / defaulted manifest fields: +- `package.name = <slugified file stem>` +- `package.version = "0.0.0"` to [call attention to this crate being used in unexpected places](https://matklad.github.io/2021/08/22/large-rust-workspaces.html#Smaller-Tips) +- `package.publish = false` to avoid accidental publishes, particularly if we + later add support for including them in a workspace. +- `package.edition = <current>` to avoid always having to add an embedded + manifest at the cost of potentially breaking scripts on rust upgrades + - Warn when `edition` is unspecified to raise awareness of this + +Disallowed manifest fields: +- `[workspace]`, `[lib]`, `[[bin]]`, `[[example]]`, `[[test]]`, `[[bench]]` +- `package.workspace`, `package.build`, `package.links`, `package.autobins`, `package.autoexamples`, `package.autotests`, `package.autobenches` + +The default `CARGO_TARGET_DIR` for single-file packages is at `$CARGO_HOME/target/<hash>`: +- Avoid conflicts from multiple single-file packages being in the same directory +- Avoid problems with the single-file package's parent directory being read-only +- Avoid cluttering the user's directory + +The lockfile for single-file packages will be placed in `CARGO_TARGET_DIR`. In +the future, when workspaces are supported, that will allow a user to have a +persistent lockfile. + +#### Manifest-commands + +You may pass a manifest directly to the `cargo` command, without a subcommand, +like `foo/Cargo.toml` or a single-file package like `foo.rs`. This is mostly +intended for being put in `#!` lines. + +The precedence for how to interpret `cargo <subcommand>` is +1. Built-in xor single-file packages +2. Aliases +3. External subcommands + +A parameter is identified as a manifest-command if it has one of: +- Path separators +- A `.rs` extension +- The file name is `Cargo.toml` + +Differences between `cargo run --manifest-path <path>` and `cargo <path>` +- `cargo <path>` runs with the config for `<path>` and not the current dir, more like `cargo install --path <path>` +- `cargo <path>` is at a verbosity level below the normal default. Pass `-v` to get normal output. + ### `[lints]` * Tracking Issue: [#12115](https://github.com/rust-lang/cargo/issues/12115) @@ -1693,3 +1787,11 @@ See [Registry Protocols](registries.md#registry-protocols) for more information. The [`cargo logout`] command has been stabilized in the 1.70 release. [target triple]: ../appendix/glossary.md#target '"target" (glossary)' + + +### `doctest-in-workspace` + +The `-Z doctest-in-workspace` option for `cargo test` has been stabilized and +enabled by default in the 1.72 release. See the +[`cargo test` documentation](../commands/cargo-test.md#working-directory-of-tests) +for more information about the working directory for compiling and running tests. diff --git a/src/tools/cargo/src/etc/man/cargo-bench.1 b/src/tools/cargo/src/etc/man/cargo-bench.1 index 44ff593fd..993dd3415 100644 --- a/src/tools/cargo/src/etc/man/cargo-bench.1 +++ b/src/tools/cargo/src/etc/man/cargo-bench.1 @@ -497,7 +497,8 @@ Rust test harness runs benchmarks serially in a single thread. Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp diff --git a/src/tools/cargo/src/etc/man/cargo-build.1 b/src/tools/cargo/src/etc/man/cargo-build.1 index 80ae4ac90..4ee6a0d76 100644 --- a/src/tools/cargo/src/etc/man/cargo-build.1 +++ b/src/tools/cargo/src/etc/man/cargo-build.1 @@ -412,7 +412,8 @@ Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp diff --git a/src/tools/cargo/src/etc/man/cargo-check.1 b/src/tools/cargo/src/etc/man/cargo-check.1 index cf7a66d89..1aada2a21 100644 --- a/src/tools/cargo/src/etc/man/cargo-check.1 +++ b/src/tools/cargo/src/etc/man/cargo-check.1 @@ -393,7 +393,8 @@ Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp diff --git a/src/tools/cargo/src/etc/man/cargo-doc.1 b/src/tools/cargo/src/etc/man/cargo-doc.1 index 63ce2a050..24621e9f6 100644 --- a/src/tools/cargo/src/etc/man/cargo-doc.1 +++ b/src/tools/cargo/src/etc/man/cargo-doc.1 @@ -360,7 +360,8 @@ Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp diff --git a/src/tools/cargo/src/etc/man/cargo-fix.1 b/src/tools/cargo/src/etc/man/cargo-fix.1 index 51b1e3fd6..7f2a34cda 100644 --- a/src/tools/cargo/src/etc/man/cargo-fix.1 +++ b/src/tools/cargo/src/etc/man/cargo-fix.1 @@ -488,7 +488,8 @@ Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp diff --git a/src/tools/cargo/src/etc/man/cargo-install.1 b/src/tools/cargo/src/etc/man/cargo-install.1 index a9eb6266b..917c0d0e1 100644 --- a/src/tools/cargo/src/etc/man/cargo-install.1 +++ b/src/tools/cargo/src/etc/man/cargo-install.1 @@ -17,7 +17,7 @@ cargo\-install \[em] Build and install a Rust binary This command manages Cargo\[cq]s local set of installed binary crates. Only packages which have executable \fB[[bin]]\fR or \fB[[example]]\fR targets can be installed, and all executables are installed into the installation root\[cq]s -\fBbin\fR folder. +\fBbin\fR folder. By default only binaries, not examples, are installed. .sp The installation root is determined, in order of precedence: .sp @@ -177,7 +177,7 @@ Install only the specified binary. .sp \fB\-\-bins\fR .RS 4 -Install all binaries. +Install all binaries. This is the default behavior. .RE .sp \fB\-\-example\fR \fIname\fR\[u2026] @@ -338,7 +338,8 @@ May also be specified with the \fBnet.offline\fR \fIconfig value\fR <https://doc Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp diff --git a/src/tools/cargo/src/etc/man/cargo-package.1 b/src/tools/cargo/src/etc/man/cargo-package.1 index 9f4847d7d..8a7b1c191 100644 --- a/src/tools/cargo/src/etc/man/cargo-package.1 +++ b/src/tools/cargo/src/etc/man/cargo-package.1 @@ -234,7 +234,8 @@ May also be specified with the \fBnet.offline\fR \fIconfig value\fR <https://doc Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp diff --git a/src/tools/cargo/src/etc/man/cargo-publish.1 b/src/tools/cargo/src/etc/man/cargo-publish.1 index a54a7bcda..d18f9e690 100644 --- a/src/tools/cargo/src/etc/man/cargo-publish.1 +++ b/src/tools/cargo/src/etc/man/cargo-publish.1 @@ -184,7 +184,8 @@ May also be specified with the \fBnet.offline\fR \fIconfig value\fR <https://doc Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp diff --git a/src/tools/cargo/src/etc/man/cargo-run.1 b/src/tools/cargo/src/etc/man/cargo-run.1 index 7a85298cc..1c182ad1a 100644 --- a/src/tools/cargo/src/etc/man/cargo-run.1 +++ b/src/tools/cargo/src/etc/man/cargo-run.1 @@ -297,7 +297,8 @@ Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp diff --git a/src/tools/cargo/src/etc/man/cargo-rustc.1 b/src/tools/cargo/src/etc/man/cargo-rustc.1 index 6e901d9ec..50df99656 100644 --- a/src/tools/cargo/src/etc/man/cargo-rustc.1 +++ b/src/tools/cargo/src/etc/man/cargo-rustc.1 @@ -411,7 +411,8 @@ Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp diff --git a/src/tools/cargo/src/etc/man/cargo-rustdoc.1 b/src/tools/cargo/src/etc/man/cargo-rustdoc.1 index 0c9a0e74a..1792c6e2f 100644 --- a/src/tools/cargo/src/etc/man/cargo-rustdoc.1 +++ b/src/tools/cargo/src/etc/man/cargo-rustdoc.1 @@ -379,7 +379,8 @@ Unstable (nightly\-only) flags to Cargo. Run \fBcargo \-Z help\fR for details. Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp diff --git a/src/tools/cargo/src/etc/man/cargo-test.1 b/src/tools/cargo/src/etc/man/cargo-test.1 index 1ee2f7672..802169815 100644 --- a/src/tools/cargo/src/etc/man/cargo-test.1 +++ b/src/tools/cargo/src/etc/man/cargo-test.1 @@ -54,11 +54,18 @@ and may change in the future; beware of depending on it. See the \fIrustdoc book\fR <https://doc.rust\-lang.org/rustdoc/> for more information on writing doc tests. .SS "Working directory of tests" -The working directory of every test is set to the root directory of the package -the test belongs to. -Setting the working directory of tests to the package\[cq]s root directory makes it +The working directory when running each unit and integration test is set to the +root directory of the package the test belongs to. +Setting the working directory of tests to the package\[cq]s root directory makes it possible for tests to reliably access the package\[cq]s files using relative paths, regardless from where \fBcargo test\fR was executed from. +.sp +For documentation tests, the working directory when invoking \fBrustdoc\fR is set to +the workspace root directory, and is also the directory \fBrustdoc\fR uses as the +compilation directory of each documentation test. +The working directory when running each documentation test is set to the root +directory of the package the test belongs to, and is controlled via \fBrustdoc\fR\[cq]s +\fB\-\-test\-run\-directory\fR option. .SH "OPTIONS" .SS "Test Options" .sp @@ -523,7 +530,8 @@ cargo test \-j 2 \-\- \-\-test\-threads=2 Number of parallel jobs to run. May also be specified with the \fBbuild.jobs\fR \fIconfig value\fR <https://doc.rust\-lang.org/cargo/reference/config.html>\&. Defaults to the number of logical CPUs. If negative, it sets the maximum number of -parallel jobs to the number of logical CPUs plus provided value. +parallel jobs to the number of logical CPUs plus provided value. If +a string \fBdefault\fR is provided, it sets the value back to defaults. Should not be 0. .RE .sp diff --git a/src/tools/cargo/tests/internal.rs b/src/tools/cargo/tests/internal.rs deleted file mode 100644 index c42cfa8f0..000000000 --- a/src/tools/cargo/tests/internal.rs +++ /dev/null @@ -1,107 +0,0 @@ -//! Tests for internal code checks. - -#![allow(clippy::all)] - -use std::fs; - -#[test] -fn check_forbidden_code() { - // Do not use certain macros, functions, etc. - if !cargo_util::is_ci() { - // Only check these on CI, otherwise it could be annoying. - use std::io::Write; - writeln!( - std::io::stderr(), - "\nSkipping check_forbidden_code test, set CI=1 to enable" - ) - .unwrap(); - return; - } - let root_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src"); - for entry in walkdir::WalkDir::new(&root_path) - .into_iter() - .filter_entry(|e| e.path() != root_path.join("doc")) - .filter_map(|e| e.ok()) - { - let path = entry.path(); - if !entry - .file_name() - .to_str() - .map(|s| s.ends_with(".rs")) - .unwrap_or(false) - { - continue; - } - eprintln!("checking {}", path.display()); - let c = fs::read_to_string(path).unwrap(); - for (line_index, line) in c.lines().enumerate() { - if line.trim().starts_with("//") { - continue; - } - if line_has_print(line) { - if entry.file_name().to_str().unwrap() == "cargo_new.rs" && line.contains("Hello") { - // An exception. - continue; - } - panic!( - "found print macro in {}:{}\n\n{}\n\n\ - print! macros should not be used in Cargo because they can panic.\n\ - Use one of the drop_print macros instead.\n\ - ", - path.display(), - line_index, - line - ); - } - if line_has_macro(line, "dbg") { - panic!( - "found dbg! macro in {}:{}\n\n{}\n\n\ - dbg! should not be used outside of debugging.", - path.display(), - line_index, - line - ); - } - } - } -} - -fn line_has_print(line: &str) -> bool { - line_has_macro(line, "print") - || line_has_macro(line, "eprint") - || line_has_macro(line, "println") - || line_has_macro(line, "eprintln") -} - -#[test] -fn line_has_print_works() { - assert!(line_has_print("print!")); - assert!(line_has_print("println!")); - assert!(line_has_print("eprint!")); - assert!(line_has_print("eprintln!")); - assert!(line_has_print("(print!(\"hi!\"))")); - assert!(!line_has_print("print")); - assert!(!line_has_print("i like to print things")); - assert!(!line_has_print("drop_print!")); - assert!(!line_has_print("drop_println!")); - assert!(!line_has_print("drop_eprint!")); - assert!(!line_has_print("drop_eprintln!")); -} - -fn line_has_macro(line: &str, mac: &str) -> bool { - for (i, _) in line.match_indices(mac) { - if line.get(i + mac.len()..i + mac.len() + 1) != Some("!") { - continue; - } - if i == 0 { - return true; - } - // Check for identifier boundary start. - let prev1 = line.get(i - 1..i).unwrap().chars().next().unwrap(); - if prev1.is_alphanumeric() || prev1 == '_' { - continue; - } - return true; - } - false -} diff --git a/src/tools/cargo/tests/testsuite/bench.rs b/src/tools/cargo/tests/testsuite/bench.rs index 60ad2b60d..581acbe15 100644 --- a/src/tools/cargo/tests/testsuite/bench.rs +++ b/src/tools/cargo/tests/testsuite/bench.rs @@ -313,9 +313,8 @@ fn cargo_bench_failing_test() { [FINISHED] bench [optimized] target(s) in [..] [RUNNING] [..] (target/release/deps/foo-[..][EXE])", ) - .with_stdout_contains( - "[..]thread '[..]' panicked at 'assertion failed: `(left == right)`[..]", - ) + .with_stdout_contains("[..]thread '[..]' panicked at[..]") + .with_stdout_contains("[..]assertion failed[..]") .with_stdout_contains("[..]left: `\"hello\"`[..]") .with_stdout_contains("[..]right: `\"nope\"`[..]") .with_stdout_contains("[..]src/main.rs:15[..]") diff --git a/src/tools/cargo/tests/testsuite/build.rs b/src/tools/cargo/tests/testsuite/build.rs index 7b555a71b..8cb064a6f 100644 --- a/src/tools/cargo/tests/testsuite/build.rs +++ b/src/tools/cargo/tests/testsuite/build.rs @@ -1334,13 +1334,13 @@ fn cargo_default_env_metadata_env_var() { [COMPILING] bar v0.0.1 ([CWD]/bar) [RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--crate-type dylib \ --emit=[..]link \ - -C prefer-dynamic[..]-C debuginfo=2 \ + -C prefer-dynamic[..]-C debuginfo=2 [..]\ -C metadata=[..] \ --out-dir [..] \ -L dependency=[CWD]/target/debug/deps` [COMPILING] foo v0.0.1 ([CWD]) [RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \ - --emit=[..]link[..]-C debuginfo=2 \ + --emit=[..]link[..]-C debuginfo=2 [..]\ -C metadata=[..] \ -C extra-filename=[..] \ --out-dir [..] \ @@ -1362,13 +1362,13 @@ fn cargo_default_env_metadata_env_var() { [COMPILING] bar v0.0.1 ([CWD]/bar) [RUNNING] `rustc --crate-name bar bar/src/lib.rs [..]--crate-type dylib \ --emit=[..]link \ - -C prefer-dynamic[..]-C debuginfo=2 \ + -C prefer-dynamic[..]-C debuginfo=2 [..]\ -C metadata=[..] \ --out-dir [..] \ -L dependency=[CWD]/target/debug/deps` [COMPILING] foo v0.0.1 ([CWD]) [RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \ - --emit=[..]link[..]-C debuginfo=2 \ + --emit=[..]link[..]-C debuginfo=2 [..]\ -C metadata=[..] \ -C extra-filename=[..] \ --out-dir [..] \ @@ -2053,7 +2053,7 @@ fn verbose_build() { "\ [COMPILING] foo v0.0.1 ([CWD]) [RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \ - --emit=[..]link[..]-C debuginfo=2 \ + --emit=[..]link[..]-C debuginfo=2 [..]\ -C metadata=[..] \ --out-dir [..] \ -L dependency=[CWD]/target/debug/deps` @@ -4074,7 +4074,7 @@ fn message_format_json_forward_stderr() { }, "profile":{ "debug_assertions":false, - "debuginfo":null, + "debuginfo":0, "opt_level":"3", "overflow_checks": false, "test":false @@ -5432,7 +5432,7 @@ fn targets_selected_all() { // Unit tests. .with_stderr_contains( "[RUNNING] `rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]\ - -C debuginfo=2 --test [..]", + -C debuginfo=2 [..]--test [..]", ) .run(); } @@ -5449,7 +5449,7 @@ fn all_targets_no_lib() { // Unit tests. .with_stderr_contains( "[RUNNING] `rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]\ - -C debuginfo=2 --test [..]", + -C debuginfo=2 [..]--test [..]", ) .run(); } @@ -5566,6 +5566,8 @@ fn good_jobs() { p.cargo("build --jobs 1").run(); p.cargo("build --jobs -1").run(); + + p.cargo("build --jobs default").run(); } #[cargo_test] @@ -5599,8 +5601,8 @@ fn invalid_jobs() { .run(); p.cargo("build --jobs over9000") - .with_status(1) - .with_stderr("error: Invalid value: could not parse `over9000` as a number") + .with_status(101) + .with_stderr("error: could not parse `over9000`. Number of parallel jobs should be `default` or a number.") .run(); } @@ -5920,7 +5922,7 @@ fn build_lib_only() { "\ [COMPILING] foo v0.0.1 ([CWD]) [RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \ - --emit=[..]link[..]-C debuginfo=2 \ + --emit=[..]link[..]-C debuginfo=2 [..]\ -C metadata=[..] \ --out-dir [..] \ -L dependency=[CWD]/target/debug/deps` diff --git a/src/tools/cargo/tests/testsuite/build_script.rs b/src/tools/cargo/tests/testsuite/build_script.rs index 80a24960e..4840356c6 100644 --- a/src/tools/cargo/tests/testsuite/build_script.rs +++ b/src/tools/cargo/tests/testsuite/build_script.rs @@ -1755,7 +1755,7 @@ fn build_cmd_with_a_build_cmd() { --extern a=[..]liba[..].rlib` [RUNNING] `[..]/foo-[..]/build-script-build` [RUNNING] `rustc --crate-name foo [..]lib.rs [..]--crate-type lib \ - --emit=[..]link[..]-C debuginfo=2 \ + --emit=[..]link[..]-C debuginfo=2 [..]\ -C metadata=[..] \ --out-dir [..] \ -L [..]target/debug/deps` @@ -1926,7 +1926,6 @@ fn output_separate_lines_new() { .run(); } -#[cfg(not(windows))] // FIXME(#867) #[cargo_test] fn code_generation() { let p = project() @@ -1976,7 +1975,7 @@ fn code_generation() { "\ [COMPILING] foo v0.5.0 ([CWD]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] -[RUNNING] `target/debug/foo`", +[RUNNING] `target/debug/foo[EXE]`", ) .with_stdout("Hello, World!") .run(); @@ -5108,7 +5107,7 @@ fn duplicate_script_with_extra_env() { p.cargo("test --workspace -Z doctest-xcompile --doc --target") .arg(&target) .masquerade_as_nightly_cargo(&["doctest-xcompile"]) - .with_stdout_contains("test src/lib.rs - (line 2) ... ok") + .with_stdout_contains("test foo/src/lib.rs - (line 2) ... ok") .run(); } } diff --git a/src/tools/cargo/tests/testsuite/cargo_add/add_basic/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/add_basic/mod.rs index 33889dffa..67cd948ae 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/add_basic/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/add_basic/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/add_multiple/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/add_multiple/mod.rs index a9cc20575..ce5e9b71d 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/add_multiple/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/add_multiple/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/add_normalized_name_external/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/add_normalized_name_external/mod.rs index 63605d8dc..ced39b99c 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/add_normalized_name_external/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/add_normalized_name_external/mod.rs @@ -2,12 +2,28 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("linked-hash-map", "0.5.4") + .feature("clippy", &[]) + .feature("heapsize", &[]) + .feature("heapsize_impl", &[]) + .feature("nightly", &[]) + .feature("serde", &[]) + .feature("serde_impl", &[]) + .feature("serde_test", &[]) + .publish(); + cargo_test_support::registry::Package::new("inflector", "0.11.4") + .feature("default", &["heavyweight", "lazy_static", "regex"]) + .feature("heavyweight", &[]) + .feature("lazy_static", &[]) + .feature("regex", &[]) + .feature("unstable", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/in b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/in index 6c6a27fcf..6c6a27fcf 120000 --- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/in +++ b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/in diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/mod.rs index 705182f20..905b8c8a2 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/mod.rs @@ -2,25 +2,20 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; - let git_url = url::Url::from_directory_path(cwd.join("does-not-exist")) - .unwrap() - .to_string(); snapbox::cmd::Command::cargo_ui() .arg("add") - .args(["fake-git", "--git", &git_url]) + .arg_line("+nightly") .current_dir(cwd) .assert() - .code(101) + .failure() .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/invalid_git_external/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/out/Cargo.toml index 3ecdb6681..3ecdb6681 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/out/Cargo.toml +++ b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/out/Cargo.toml diff --git a/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/stderr.log new file mode 100644 index 000000000..0593685ad --- /dev/null +++ b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/stderr.log @@ -0,0 +1,2 @@ +error: invalid character `+` in dependency name: `+nightly` + Use `cargo +nightly add` if you meant to use the `nightly` toolchain. diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/stdout.log b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/stdout.log index e69de29bb..e69de29bb 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/stdout.log +++ b/src/tools/cargo/tests/testsuite/cargo_add/add_toolchain/stdout.log diff --git a/src/tools/cargo/tests/testsuite/cargo_add/build/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/build/mod.rs index 130ecfbb0..001dd3a8f 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/build/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/build/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + for name in ["my-build-package1", "my-build-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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/build_prefer_existing_version/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/build_prefer_existing_version/mod.rs index b0bb2e03b..285ec658a 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/build_prefer_existing_version/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/build_prefer_existing_version/mod.rs @@ -2,11 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_alt_registry; - #[cargo_test] fn case() { - init_alt_registry(); + cargo_test_support::registry::alt_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("cargo-list-test-fixture-dependency", ver) + .alternative(true) + .publish(); + } + let project = Project::from_template("tests/testsuite/cargo_add/build_prefer_existing_version/in"); let project_root = project.root(); diff --git a/src/tools/cargo/tests/testsuite/cargo_add/change_rename_target/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/change_rename_target/mod.rs index 94309b3ab..d1540c8ad 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/change_rename_target/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/change_rename_target/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/cyclic_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/cyclic_features/mod.rs index 5dffac323..8b4c6123a 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/cyclic_features/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/cyclic_features/mod.rs @@ -2,12 +2,17 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("test_cyclic_features", "0.1.1") + .feature("default", &["feature-one", "feature-two"]) + .feature("feature-one", &["feature-two"]) + .feature("feature-two", &["feature-one"]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/default_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/default_features/mod.rs index 88bdd8065..9d313b9d9 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/default_features/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/default_features/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/deprecated_default_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/deprecated_default_features/mod.rs index 10d4e4e98..c4fdd4f3e 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/deprecated_default_features/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/deprecated_default_features/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/deprecated_section/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/deprecated_section/mod.rs index 10d4e4e98..c4fdd4f3e 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/deprecated_section/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/deprecated_section/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit/mod.rs index 065fb4f93..2b9550ce0 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_features/mod.rs index 11ab2b1bf..8067166dc 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_features/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_features/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_optional/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_optional/mod.rs index 7557b520d..423f1a26d 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_optional/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/detect_workspace_inherit_optional/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/dev/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/dev/mod.rs index 112e92285..c838f1eac 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/dev/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/dev/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + for name in ["my-dev-package1", "my-dev-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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/dev_build_conflict/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/dev_build_conflict/mod.rs index 3f57c6b76..70432b529 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/dev_build_conflict/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/dev_build_conflict/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/dev_prefer_existing_version/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/dev_prefer_existing_version/mod.rs index 1785ac820..2d3698c14 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/dev_prefer_existing_version/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/dev_prefer_existing_version/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_alt_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_alt_registry(); + cargo_test_support::registry::alt_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("cargo-list-test-fixture-dependency", ver) + .alternative(true) + .publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/dry_run/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/dry_run/mod.rs index 209d20873..438ec7b73 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/dry_run/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/dry_run/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_table/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_table/mod.rs index f6c507188..d612a07f7 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_table/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/empty_dep_table/mod.rs @@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/features/mod.rs index 5e4115390..aebae9b3b 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/features/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/features/mod.rs @@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/features_empty/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/features_empty/mod.rs index 81dffc1ee..d58c6a292 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/features_empty/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/features_empty/mod.rs @@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/features_multiple_occurrences/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/features_multiple_occurrences/mod.rs index db47f860d..4189be7ea 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/features_multiple_occurrences/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/features_multiple_occurrences/mod.rs @@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/features_preserve/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/features_preserve/mod.rs index ed99a3111..708fb4ddf 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/features_preserve/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/features_preserve/mod.rs @@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/features_spaced_values/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/features_spaced_values/mod.rs index 2ef212e59..4ee483110 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/features_spaced_values/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/features_spaced_values/mod.rs @@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/features_unknown/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/features_unknown/mod.rs index 7fd8d9529..552319402 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/features_unknown/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/features_unknown/mod.rs @@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/features_unknown_no_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/features_unknown_no_features/mod.rs index 9f59a0353..dff50a61f 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/features_unknown_no_features/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/features_unknown_no_features/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git/mod.rs index bd82b3015..507fb417d 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/git/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/git/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_branch/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_branch/mod.rs index 051564566..8235a7c3c 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/git_branch/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/git_branch/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_conflicts_namever/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_conflicts_namever/mod.rs index f123298ae..86b268b59 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/git_conflicts_namever/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/git_conflicts_namever/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_dev/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_dev/mod.rs index 9e14a4007..96be4f46e 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/git_dev/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/git_dev/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_inferred_name/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_inferred_name/mod.rs index 52183adf4..4a3ded8c2 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/git_inferred_name/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/git_inferred_name/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_inferred_name_multiple/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_inferred_name_multiple/mod.rs index a708a8ae7..a915f3472 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/git_inferred_name_multiple/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/git_inferred_name_multiple/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_multiple_names/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_multiple_names/mod.rs index 39eb6e626..3ffe10714 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/git_multiple_names/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/git_multiple_names/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_normalized_name/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_normalized_name/mod.rs index 03d861856..dbad20105 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/git_normalized_name/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/git_normalized_name/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_registry/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_registry/mod.rs index 6bf6f8933..e44d71470 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/git_registry/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/git_registry/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_alt_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_alt_registry(); + cargo_test_support::registry::alt_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("versioned-package", ver) + .alternative(true) + .publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_rev/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_rev/mod.rs index 612607203..b8a669c7a 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/git_rev/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/git_rev/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/git_tag/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/git_tag/mod.rs index b355b1706..706fc9ab7 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/git_tag/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/git_tag/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/infer_prerelease/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/infer_prerelease/mod.rs index 94533f979..f2b49376a 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/infer_prerelease/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/infer_prerelease/mod.rs @@ -2,12 +2,13 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("prerelease_only", "0.2.0-alpha.1").publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_arg/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/invalid_arg/mod.rs index 265a571bc..a4e4d268a 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_arg/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/invalid_arg/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/stderr.log deleted file mode 100644 index 18656300b..000000000 --- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_external/stderr.log +++ /dev/null @@ -1,12 +0,0 @@ - Updating git repository `[ROOTURL]/case/does-not-exist/` -... -error: failed to load source for dependency `fake-git` - -Caused by: - Unable to update [ROOTURL]/case/does-not-exist/ - -Caused by: - failed to clone into: [ROOT]/home/.cargo/git/db/does-not-exist-[..] - -Caused by: -... diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_name/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_name/mod.rs index 0aff8c090..2f407105d 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_name/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/invalid_git_name/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_manifest/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/invalid_manifest/mod.rs index e385cfc3f..4a2d01765 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_manifest/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/invalid_manifest/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_name_external/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/invalid_name_external/mod.rs index 16e041738..8cb9b5129 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_name_external/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/invalid_name_external/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_path/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/invalid_path/mod.rs index 0d26b552d..d8e6a34a6 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_path/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/invalid_path/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_path_name/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/invalid_path_name/mod.rs index 10d841475..aec088020 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_path_name/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/invalid_path_name/mod.rs @@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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("cargo-list-test-fixture-dependency", ver) + .publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_path_self/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/invalid_path_self/mod.rs index a64190f44..4bb32531b 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_path_self/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/invalid_path_self/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_target_empty/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/invalid_target_empty/mod.rs index da93c4eb8..43b3a4a79 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_target_empty/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/invalid_target_empty/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/invalid_vers/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/invalid_vers/mod.rs index c3b4d1f97..2ad903d1f 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/invalid_vers/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/invalid_vers/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/list_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/list_features/mod.rs index e1e1b212f..0dbf824d0 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/list_features/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/list_features/mod.rs @@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/list_features_path/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/list_features_path/mod.rs index 22733b883..e58de1c1d 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/list_features_path/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/list_features_path/mod.rs @@ -2,12 +2,29 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package", ver).publish(); + } + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); diff --git a/src/tools/cargo/tests/testsuite/cargo_add/list_features_path_no_default/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/list_features_path_no_default/mod.rs index f520b2aca..0aa20873e 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/list_features_path_no_default/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/list_features_path_no_default/mod.rs @@ -2,12 +2,29 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package", ver).publish(); + } + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); diff --git a/src/tools/cargo/tests/testsuite/cargo_add/locked_changed/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/locked_changed/mod.rs index 9e3e57fe5..5de047f57 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/locked_changed/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/locked_changed/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/locked_unchanged/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/locked_unchanged/mod.rs index aba9918f7..613d8c863 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/locked_unchanged/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/locked_unchanged/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/lockfile_updated/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/lockfile_updated/mod.rs index 33889dffa..4e99a18ef 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/lockfile_updated/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/lockfile_updated/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + for name in ["my-package", "unrelateed-crate"] { + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/manifest_path_package/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/manifest_path_package/mod.rs index 008c2d33d..768cdb3a3 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/manifest_path_package/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/manifest_path_package/mod.rs @@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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("cargo-list-test-fixture-dependency", ver) + .publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/mod.rs index cd7e94e09..be7a1546b 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/mod.rs @@ -1,6 +1,7 @@ mod add_basic; mod add_multiple; mod add_normalized_name_external; +mod add_toolchain; mod build; mod build_prefer_existing_version; mod change_rename_target; @@ -36,7 +37,6 @@ mod git_rev; mod git_tag; mod infer_prerelease; mod invalid_arg; -mod invalid_git_external; mod invalid_git_name; mod invalid_key_inherit_dependency; mod invalid_key_overwrite_inherit_dependency; @@ -96,6 +96,7 @@ mod path_dev; mod path_inferred_name; mod path_inferred_name_conflicts_full_feature; mod path_normalized_name; +mod preserve_features_table; mod preserve_sorted; mod preserve_unsorted; mod quiet; @@ -114,95 +115,3 @@ mod vers; mod workspace_name; mod workspace_path; mod workspace_path_dev; - -fn init_registry() { - cargo_test_support::registry::init(); - add_registry_packages(false); -} - -fn init_alt_registry() { - cargo_test_support::registry::alt_init(); - add_registry_packages(true); -} - -fn add_registry_packages(alt: bool) { - for name in [ - "my-package", - "my-package1", - "my-package2", - "my-dev-package1", - "my-dev-package2", - "my-build-package1", - "my-build-package2", - "toml", - "versioned-package", - "cargo-list-test-fixture-dependency", - "unrelateed-crate", - ] { - cargo_test_support::registry::Package::new(name, "0.1.1+my-package") - .alternative(alt) - .publish(); - cargo_test_support::registry::Package::new(name, "0.2.0+my-package") - .alternative(alt) - .publish(); - cargo_test_support::registry::Package::new(name, "0.2.3+my-package") - .alternative(alt) - .publish(); - cargo_test_support::registry::Package::new(name, "0.4.1+my-package") - .alternative(alt) - .publish(); - cargo_test_support::registry::Package::new(name, "20.0.0+my-package") - .alternative(alt) - .publish(); - cargo_test_support::registry::Package::new(name, "99999.0.0+my-package") - .alternative(alt) - .publish(); - cargo_test_support::registry::Package::new(name, "99999.0.0-alpha.1+my-package") - .alternative(alt) - .publish(); - } - - cargo_test_support::registry::Package::new("prerelease_only", "0.2.0-alpha.1") - .alternative(alt) - .publish(); - cargo_test_support::registry::Package::new("test_breaking", "0.2.0") - .alternative(alt) - .publish(); - cargo_test_support::registry::Package::new("test_nonbreaking", "0.1.1") - .alternative(alt) - .publish(); - cargo_test_support::registry::Package::new("test_cyclic_features", "0.1.1") - .alternative(alt) - .feature("default", &["feature-one", "feature-two"]) - .feature("feature-one", &["feature-two"]) - .feature("feature-two", &["feature-one"]) - .publish(); - - // Normalization - cargo_test_support::registry::Package::new("linked-hash-map", "0.5.4") - .alternative(alt) - .feature("clippy", &[]) - .feature("heapsize", &[]) - .feature("heapsize_impl", &[]) - .feature("nightly", &[]) - .feature("serde", &[]) - .feature("serde_impl", &[]) - .feature("serde_test", &[]) - .publish(); - cargo_test_support::registry::Package::new("inflector", "0.11.4") - .alternative(alt) - .feature("default", &["heavyweight", "lazy_static", "regex"]) - .feature("heavyweight", &[]) - .feature("lazy_static", &[]) - .feature("regex", &[]) - .feature("unstable", &[]) - .publish(); - - cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") - .alternative(alt) - .feature("nose", &[]) - .feature("mouth", &[]) - .feature("eyes", &[]) - .feature("ears", &[]) - .publish(); -} diff --git a/src/tools/cargo/tests/testsuite/cargo_add/multiple_conflicts_with_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/multiple_conflicts_with_features/mod.rs index 10f824484..3cdad0e72 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/multiple_conflicts_with_features/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/multiple_conflicts_with_features/mod.rs @@ -2,12 +2,29 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package1", ver).publish(); + } + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/multiple_conflicts_with_rename/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/multiple_conflicts_with_rename/mod.rs index 293ed3eea..9a94dcffa 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/multiple_conflicts_with_rename/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/multiple_conflicts_with_rename/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/namever/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/namever/mod.rs index 90fda1a9f..063072c79 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/namever/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/namever/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + for name in ["my-package", "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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/no_args/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/no_args/mod.rs index 7eca17b56..aca1e5422 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/no_args/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/no_args/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/no_default_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/no_default_features/mod.rs index e72ca3be2..8a5d41c8d 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/no_default_features/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/no_default_features/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; 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 fdb983b21..9145528bf 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 @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/offline_empty_cache/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/offline_empty_cache/mod.rs index ae7485979..055b6a8a2 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/offline_empty_cache/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/offline_empty_cache/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; 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 94d1cbf34..408a46ed3 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/optional/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/optional/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_default_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_default_features/mod.rs index 88bdd8065..9d313b9d9 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_default_features/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_default_features/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/mod.rs index e72ca3be2..8a5d41c8d 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_default_features_with_no_default_features/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_features/mod.rs index 0b2ab18b8..da55fced2 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_features/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_features/mod.rs @@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_git_with_path/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_git_with_path/mod.rs index ab89e3a6d..fd63cc709 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_git_with_path/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_git_with_path/mod.rs @@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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("cargo-list-test-fixture-dependency", ver) + .publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_features_noop/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_features_noop/mod.rs index 161783282..2b9550ce0 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_features_noop/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_features_noop/mod.rs @@ -6,6 +6,8 @@ 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; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_noop/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_noop/mod.rs index 065fb4f93..2b9550ce0 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_noop/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_noop/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/mod.rs index 065fb4f93..2b9550ce0 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inherit_optional_noop/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inline_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inline_features/mod.rs index 356b4d788..52ad6968a 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inline_features/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_inline_features/mod.rs @@ -2,12 +2,29 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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("unrelateed-crate", ver).publish(); + } + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_dev_noop/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_dev_noop/mod.rs index b418c7809..ea37368d5 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_dev_noop/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_dev_noop/mod.rs @@ -2,12 +2,19 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_alt_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_alt_registry(); + cargo_test_support::registry::alt_init(); + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .alternative(true) + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_noop/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_noop/mod.rs index 193c5880b..120be4429 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_noop/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_name_noop/mod.rs @@ -2,12 +2,19 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_alt_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_alt_registry(); + cargo_test_support::registry::alt_init(); + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .alternative(true) + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_default_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_default_features/mod.rs index e72ca3be2..8a5d41c8d 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_default_features/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_default_features/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/mod.rs index 88bdd8065..9d313b9d9 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_no_default_features_with_default_features/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; 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 fdb983b21..9145528bf 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 @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; 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 94d1cbf34..408a46ed3 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 @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; 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 94d1cbf34..408a46ed3 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 @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; 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 c34c293f9..3090a7527 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 @@ -2,12 +2,29 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_noop/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_noop/mod.rs index f04405a34..fc057d852 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_noop/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_noop/mod.rs @@ -2,12 +2,19 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_alt_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_alt_registry(); + cargo_test_support::registry::alt_init(); + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .alternative(true) + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_with_version/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_with_version/mod.rs index 32674e23d..bb93cfa15 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_with_version/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_path_with_version/mod.rs @@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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("cargo-list-test-fixture-dependency", ver) + .publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_preserves_inline_table/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_preserves_inline_table/mod.rs index 0b2ab18b8..da55fced2 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_preserves_inline_table/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_preserves_inline_table/mod.rs @@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/mod.rs index a006c95fd..ea49a07c1 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_no_rename/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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("versioned-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename/mod.rs index e14282bc1..af96caef5 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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("versioned-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/mod.rs index c0ca2e552..734d0b99a 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_rename_with_rename_noop/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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("versioned-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_git/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_git/mod.rs index ce7a0acb0..fcf0aa5e6 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_git/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_git/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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("versioned-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_path/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_path/mod.rs index ab89e3a6d..fd63cc709 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_path/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_version_with_path/mod.rs @@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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("cargo-list-test-fixture-dependency", ver) + .publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_with_rename/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_with_rename/mod.rs index 05cc2d109..49a0ae0eb 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_with_rename/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_with_rename/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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("versioned-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_workspace_dep/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_workspace_dep/mod.rs index 87ed58f7f..15cfa571c 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_workspace_dep/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_workspace_dep/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_workspace_dep_features/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_workspace_dep_features/mod.rs index 87ed58f7f..15cfa571c 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/overwrite_workspace_dep_features/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/overwrite_workspace_dep_features/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/path/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/path/mod.rs index ab89e3a6d..fd63cc709 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/path/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/path/mod.rs @@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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("cargo-list-test-fixture-dependency", ver) + .publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); diff --git a/src/tools/cargo/tests/testsuite/cargo_add/path_dev/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/path_dev/mod.rs index 4ae04c70a..478e602a2 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/path_dev/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/path_dev/mod.rs @@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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("cargo-list-test-fixture-dependency", ver) + .publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); diff --git a/src/tools/cargo/tests/testsuite/cargo_add/path_inferred_name/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/path_inferred_name/mod.rs index ab89e3a6d..fd63cc709 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/path_inferred_name/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/path_inferred_name/mod.rs @@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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("cargo-list-test-fixture-dependency", ver) + .publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); diff --git a/src/tools/cargo/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/mod.rs index eadd096aa..17c328ea6 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/path_inferred_name_conflicts_full_feature/mod.rs @@ -2,12 +2,12 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); diff --git a/src/tools/cargo/tests/testsuite/cargo_add/path_normalized_name/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/path_normalized_name/mod.rs index 754f2783f..8ae8a4e7d 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/path_normalized_name/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/path_normalized_name/mod.rs @@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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("cargo-list-test-fixture-dependency", ver) + .publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/in/Cargo.toml new file mode 100644 index 000000000..d32f5eb82 --- /dev/null +++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/in/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "xxx" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +your-face = { version = "99999.0.0", optional = true } + +[features] +default = [ + "a", + "b", + "c", +] +a = [ + "your-face?/nose", # but not the mouth and nose +] +b = [] +c = [] diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/in/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/in/src/lib.rs new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/in/src/lib.rs diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/mod.rs new file mode 100644 index 000000000..1998fa742 --- /dev/null +++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/mod.rs @@ -0,0 +1,31 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use cargo_test_support::curr_dir; + +#[cargo_test] +fn case() { + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("your-face --no-optional") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/out/Cargo.toml new file mode 100644 index 000000000..39acd1016 --- /dev/null +++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/out/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "xxx" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +your-face = { version = "99999.0.0" } + +[features] +default = [ + "a", + "b", + "c", +] +a = [ + "your-face/nose", # but not the mouth and nose +] +b = [] +c = [] diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/stderr.log b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/stderr.log new file mode 100644 index 000000000..796b9601b --- /dev/null +++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/stderr.log @@ -0,0 +1,7 @@ + Updating `dummy-registry` index + Adding your-face v99999.0.0 to dependencies. + Features: + - ears + - eyes + - mouth + - nose diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/stdout.log b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/stdout.log new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_features_table/stdout.log diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_sorted/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/preserve_sorted/mod.rs index 4dfb06ed1..aef0d3d2f 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/preserve_sorted/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_sorted/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + for name in ["my-package", "versioned-package", "toml"] { + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/preserve_unsorted/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/preserve_unsorted/mod.rs index 4dfb06ed1..aef0d3d2f 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/preserve_unsorted/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/preserve_unsorted/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + for name in ["my-package", "versioned-package", "toml"] { + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/quiet/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/quiet/mod.rs index 357843901..1a02b0802 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/quiet/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/quiet/mod.rs @@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/registry/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/registry/mod.rs index d5ba9ef28..d0d937815 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/registry/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/registry/mod.rs @@ -2,12 +2,27 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_alt_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_alt_registry(); + cargo_test_support::registry::alt_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) + .alternative(true) + .publish(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/rename/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/rename/mod.rs index 3fefcccf3..28c92d770 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/rename/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/rename/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/require_weak/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/require_weak/mod.rs index d99e4482a..1998fa742 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/require_weak/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/require_weak/mod.rs @@ -2,12 +2,18 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("your-face", "99999.0.0+my-package") + .feature("nose", &[]) + .feature("mouth", &[]) + .feature("eyes", &[]) + .feature("ears", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; 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 9aa11eaac..a382d95f1 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 @@ -2,13 +2,11 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); - + cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("rust-version-user", "0.1.0") .rust_version("1.66") .publish(); diff --git a/src/tools/cargo/tests/testsuite/cargo_add/rust_version_incompatible/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/rust_version_incompatible/mod.rs index 6baad0e05..31fb57786 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/rust_version_incompatible/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/rust_version_incompatible/mod.rs @@ -2,13 +2,11 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); - + cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("rust-version-user", "0.1.0") .rust_version("1.66") .publish(); diff --git a/src/tools/cargo/tests/testsuite/cargo_add/rust_version_latest/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/rust_version_latest/mod.rs index 60e38960f..7a42c566e 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/rust_version_latest/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/rust_version_latest/mod.rs @@ -2,13 +2,11 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); - + cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("rust-version-user", "0.1.0") .rust_version("1.66") .publish(); diff --git a/src/tools/cargo/tests/testsuite/cargo_add/rust_version_older/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/rust_version_older/mod.rs index 60e38960f..7a42c566e 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/rust_version_older/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/rust_version_older/mod.rs @@ -2,13 +2,11 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); - + cargo_test_support::registry::init(); cargo_test_support::registry::Package::new("rust-version-user", "0.1.0") .rust_version("1.66") .publish(); diff --git a/src/tools/cargo/tests/testsuite/cargo_add/sorted_table_with_dotted_item/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/sorted_table_with_dotted_item/mod.rs index 55e4c2281..46e8708a1 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/sorted_table_with_dotted_item/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/sorted_table_with_dotted_item/mod.rs @@ -2,12 +2,30 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + for name in [ + "unrelateed-crate", + "versioned-package", + "toml", + "my-build-package1", + ] { + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/target/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/target/mod.rs index e263bad36..b47874949 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/target/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/target/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/target_cfg/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/target_cfg/mod.rs index 43efe8e8d..0478ece76 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/target_cfg/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/target_cfg/mod.rs @@ -2,12 +2,25 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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(); + } + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/vers/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/vers/mod.rs index fb78739e9..ee7dab2d1 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/vers/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/vers/mod.rs @@ -2,12 +2,23 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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-package", ver).publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_add/workspace_name/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/workspace_name/mod.rs index ccaf850f9..2ab0807de 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/workspace_name/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/workspace_name/mod.rs @@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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("cargo-list-test-fixture-dependency", ver) + .publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); diff --git a/src/tools/cargo/tests/testsuite/cargo_add/workspace_path/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/workspace_path/mod.rs index ab89e3a6d..fd63cc709 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/workspace_path/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/workspace_path/mod.rs @@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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("cargo-list-test-fixture-dependency", ver) + .publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); diff --git a/src/tools/cargo/tests/testsuite/cargo_add/workspace_path_dev/mod.rs b/src/tools/cargo/tests/testsuite/cargo_add/workspace_path_dev/mod.rs index 4ae04c70a..478e602a2 100644 --- a/src/tools/cargo/tests/testsuite/cargo_add/workspace_path_dev/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_add/workspace_path_dev/mod.rs @@ -2,12 +2,24 @@ use cargo_test_support::compare::assert_ui; use cargo_test_support::prelude::*; use cargo_test_support::Project; -use crate::cargo_add::init_registry; use cargo_test_support::curr_dir; #[cargo_test] fn case() { - init_registry(); + 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("cargo-list-test-fixture-dependency", ver) + .publish(); + } + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = project_root.join("primary"); diff --git a/src/tools/cargo/tests/testsuite/cargo_features.rs b/src/tools/cargo/tests/testsuite/cargo_features.rs index 6e5531431..ed5f53a1e 100644 --- a/src/tools/cargo/tests/testsuite/cargo_features.rs +++ b/src/tools/cargo/tests/testsuite/cargo_features.rs @@ -295,6 +295,7 @@ fn allow_features_to_rustc() { .file( "src/lib.rs", r#" + #![allow(internal_features)] #![feature(test_2018_feature)] "#, ) diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/in/Cargo.toml new file mode 100644 index 000000000..55ee0423c --- /dev/null +++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/in/Cargo.toml @@ -0,0 +1,6 @@ +[workspace] +resolver = "2" +members = ["crates/*"] + +[workspace.lints.rust] +unsafe_code = "forbid" diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/in/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/in/src/lib.rs new file mode 100644 index 000000000..7d12d9af8 --- /dev/null +++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/in/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/mod.rs new file mode 100644 index 000000000..0b7697d20 --- /dev/null +++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/mod.rs @@ -0,0 +1,24 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::curr_dir; +use cargo_test_support::CargoCommand; +use cargo_test_support::ChannelChanger; +use cargo_test_support::Project; + +#[cargo_test] +fn case() { + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("new") + .args(["crates/foo", "-Zlints"]) + .current_dir(cwd) + .masquerade_as_nightly_cargo(&["lints"]) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/Cargo.toml new file mode 100644 index 000000000..55ee0423c --- /dev/null +++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/Cargo.toml @@ -0,0 +1,6 @@ +[workspace] +resolver = "2" +members = ["crates/*"] + +[workspace.lints.rust] +unsafe_code = "forbid" diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/crates/foo/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/crates/foo/Cargo.toml new file mode 100644 index 000000000..0f3fe5d94 --- /dev/null +++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/crates/foo/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "foo" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[lints] +workspace = true diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/crates/foo/src/main.rs b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/crates/foo/src/main.rs new file mode 100644 index 000000000..e7a11a969 --- /dev/null +++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/crates/foo/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/src/lib.rs b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/src/lib.rs new file mode 100644 index 000000000..7d12d9af8 --- /dev/null +++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/out/src/lib.rs @@ -0,0 +1,14 @@ +pub fn add(left: usize, right: usize) -> usize { + left + right +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_works() { + let result = add(2, 2); + assert_eq!(result, 4); + } +} diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/stderr.log b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/stderr.log new file mode 100644 index 000000000..90150cdf5 --- /dev/null +++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/stderr.log @@ -0,0 +1 @@ + Created binary (application) `crates/foo` package diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/stdout.log b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/stdout.log new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_lints/stdout.log diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table.in/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table.in/Cargo.toml index b7a2e9036..cef4dd48b 100644 --- a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table.in/Cargo.toml +++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table.in/Cargo.toml @@ -1,4 +1,5 @@ [workspace] +resolver = "2" members = [ "crates/*", ] diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table/out/Cargo.toml index b7a2e9036..cef4dd48b 100644 --- a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table/out/Cargo.toml +++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table/out/Cargo.toml @@ -1,4 +1,5 @@ [workspace] +resolver = "2" members = [ "crates/*", ] diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table_with_edition/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table_with_edition/out/Cargo.toml index b7a2e9036..cef4dd48b 100644 --- a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table_with_edition/out/Cargo.toml +++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table_with_edition/out/Cargo.toml @@ -1,4 +1,5 @@ [workspace] +resolver = "2" members = [ "crates/*", ] diff --git a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table_with_registry/out/Cargo.toml b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table_with_registry/out/Cargo.toml index b7a2e9036..cef4dd48b 100644 --- a/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table_with_registry/out/Cargo.toml +++ b/src/tools/cargo/tests/testsuite/cargo_new/inherit_workspace_package_table_with_registry/out/Cargo.toml @@ -1,4 +1,5 @@ [workspace] +resolver = "2" members = [ "crates/*", ] diff --git a/src/tools/cargo/tests/testsuite/cargo_new/mod.rs b/src/tools/cargo/tests/testsuite/cargo_new/mod.rs index 762a70b34..e895cf883 100644 --- a/src/tools/cargo/tests/testsuite/cargo_new/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_new/mod.rs @@ -1,3 +1,4 @@ +mod inherit_workspace_lints; mod inherit_workspace_package_table; mod inherit_workspace_package_table_with_edition; mod inherit_workspace_package_table_with_registry; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/avoid_empty_tables/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/avoid_empty_tables/mod.rs index 59a2333d6..17bc3f949 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/avoid_empty_tables/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/avoid_empty_tables/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/build/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/build/mod.rs index f4c9dcb94..72ce478b9 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/build/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/build/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/dev/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/dev/mod.rs index 7d61fa954..d9cbdf1a7 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/dev/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/dev/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/dry_run/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/dry_run/mod.rs index dca189315..2e097135d 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/dry_run/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/dry_run/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/mod.rs index 2c1d592fb..ec521a5bb 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_patch/mod.rs @@ -5,11 +5,9 @@ use cargo_test_support::git; use cargo_test_support::project; use cargo_test_support::CargoCommand; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); let git_project1 = git::new("bar1", |project| { project diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_profile/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/gc_profile/mod.rs index 7047c92e2..98b99bec3 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/gc_profile/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_profile/mod.rs @@ -3,11 +3,22 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.2.3+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/gc_replace/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/gc_replace/mod.rs index 717adef3e..cbcf5bc07 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/gc_replace/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/gc_replace/mod.rs @@ -3,11 +3,22 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.2.3+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_arg/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_arg/mod.rs index eac3c8b46..97d5c8625 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_arg/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_arg/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/mod.rs index c4dbeae91..fb32a075f 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_dep/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_package/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_package/mod.rs index bff09882e..d7f84c035 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_package/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_package/mod.rs @@ -3,11 +3,23 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("ncurses", "20.0.0+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_package_multiple/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_package_multiple/mod.rs index 5093d5d2d..d14179e0c 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_package_multiple/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_package_multiple/mod.rs @@ -3,11 +3,23 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("ncurses", "20.0.0+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/mod.rs index 80d42be1d..94d475059 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/mod.rs index 7be8fd628..c20cd94d2 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_section_dep/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/mod.rs index 34deb6cb8..aba040a2b 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target/mod.rs @@ -3,11 +3,23 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("ncurses", "20.0.0+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/mod.rs index e04418fa8..f187e609c 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/invalid_target_dep/mod.rs @@ -3,11 +3,23 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("ncurses", "20.0.0+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs index fd8b4a233..feb08cea4 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/mod.rs @@ -28,61 +28,3 @@ mod update_lock_file; mod workspace; mod workspace_non_virtual; mod workspace_preserved; - -fn init_registry() { - cargo_test_support::registry::init(); - add_registry_packages(false); -} - -fn add_registry_packages(alt: bool) { - for name in [ - "clippy", - "dbus", - "docopt", - "ncurses", - "pad", - "regex", - "rustc-serialize", - "toml", - ] { - cargo_test_support::registry::Package::new(name, "0.1.1+my-package") - .alternative(alt) - .publish(); - cargo_test_support::registry::Package::new(name, "0.2.0+my-package") - .alternative(alt) - .publish(); - cargo_test_support::registry::Package::new(name, "0.2.3+my-package") - .alternative(alt) - .publish(); - cargo_test_support::registry::Package::new(name, "0.4.1+my-package") - .alternative(alt) - .publish(); - cargo_test_support::registry::Package::new(name, "0.6.2+my-package") - .alternative(alt) - .publish(); - cargo_test_support::registry::Package::new(name, "0.9.9+my-package") - .alternative(alt) - .publish(); - cargo_test_support::registry::Package::new(name, "1.0.90+my-package") - .alternative(alt) - .publish(); - cargo_test_support::registry::Package::new(name, "20.0.0+my-package") - .alternative(alt) - .publish(); - } - - for name in ["semver", "serde"] { - cargo_test_support::registry::Package::new(name, "0.1.1") - .alternative(alt) - .feature("std", &[]) - .publish(); - cargo_test_support::registry::Package::new(name, "0.9.0") - .alternative(alt) - .feature("std", &[]) - .publish(); - cargo_test_support::registry::Package::new(name, "1.0.90") - .alternative(alt) - .feature("std", &[]) - .publish(); - } -} diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/multiple_deps/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/multiple_deps/mod.rs index 35922b738..d0af30a30 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/multiple_deps/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/multiple_deps/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/mod.rs index 5eac7e2f8..7ec2bd0b7 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/multiple_dev/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/no_arg/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/no_arg/mod.rs index d0c66f9b0..f9e0e5548 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/no_arg/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/no_arg/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/offline/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/offline/mod.rs index d03463927..e227eb095 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/offline/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/offline/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/mod.rs index cae736b34..ce8fcf712 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_dep_feature/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/optional_feature/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/optional_feature/mod.rs index af54226bb..9ab3c4c24 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/optional_feature/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/optional_feature/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/package/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/package/mod.rs index 2714f3197..73d01b89d 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/package/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/package/mod.rs @@ -3,11 +3,23 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("ncurses", "20.0.0+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/remove_basic/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/remove_basic/mod.rs index 53381e6bc..7c92026a5 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/remove_basic/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/remove_basic/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/target/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/target/mod.rs index 1447c753d..6f6fc5c61 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/target/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/target/mod.rs @@ -3,11 +3,23 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("ncurses", "20.0.0+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/target_build/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/target_build/mod.rs index 11afbbf8f..7adffa229 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/target_build/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/target_build/mod.rs @@ -3,11 +3,23 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("ncurses", "20.0.0+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/target_dev/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/target_dev/mod.rs index d303c2b85..005eb7871 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/target_dev/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/target_dev/mod.rs @@ -3,11 +3,23 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("dbus", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("ncurses", "20.0.0+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/mod.rs index be5bc87f5..00e7964e4 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/update_lock_file/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.1+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/workspace/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/workspace/mod.rs index 225fbec00..5274b07d7 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/workspace/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/workspace/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/workspace_non_virtual/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/workspace_non_virtual/mod.rs index 225fbec00..5274b07d7 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/workspace_non_virtual/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/workspace_non_virtual/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/cargo_remove/workspace_preserved/mod.rs b/src/tools/cargo/tests/testsuite/cargo_remove/workspace_preserved/mod.rs index 225fbec00..5274b07d7 100644 --- a/src/tools/cargo/tests/testsuite/cargo_remove/workspace_preserved/mod.rs +++ b/src/tools/cargo/tests/testsuite/cargo_remove/workspace_preserved/mod.rs @@ -3,11 +3,21 @@ use cargo_test_support::curr_dir; use cargo_test_support::CargoCommand; use cargo_test_support::Project; -use crate::cargo_remove::init_registry; - #[cargo_test] fn case() { - init_registry(); + cargo_test_support::registry::init(); + cargo_test_support::registry::Package::new("clippy", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("docopt", "0.6.2+my-package").publish(); + cargo_test_support::registry::Package::new("regex", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("rustc-serialize", "0.4.0+my-package").publish(); + cargo_test_support::registry::Package::new("toml", "0.1.1+my-package").publish(); + cargo_test_support::registry::Package::new("semver", "0.1.1") + .feature("std", &[]) + .publish(); + cargo_test_support::registry::Package::new("serde", "1.0.90") + .feature("std", &[]) + .publish(); + let project = Project::from_template(curr_dir!().join("in")); let project_root = project.root(); let cwd = &project_root; diff --git a/src/tools/cargo/tests/testsuite/config.rs b/src/tools/cargo/tests/testsuite/config.rs index b0f9d167b..5d4163818 100644 --- a/src/tools/cargo/tests/testsuite/config.rs +++ b/src/tools/cargo/tests/testsuite/config.rs @@ -1,7 +1,7 @@ //! Tests for config settings. use cargo::core::{PackageIdSpec, Shell}; -use cargo::util::config::{self, Config, Definition, SslVersionConfig, StringList}; +use cargo::util::config::{self, Config, Definition, JobsConfig, SslVersionConfig, StringList}; use cargo::util::interning::InternedString; use cargo::util::toml::{self as cargo_toml, TomlDebugInfo, VecStringOrBool as VSOB}; use cargo::CargoResult; @@ -1651,3 +1651,63 @@ fn debuginfo_parsing() { .ends_with("could not load config key `profile.dev.debug`")); } } + +#[cargo_test] +fn build_jobs_missing() { + write_config( + "\ +[build] +", + ); + + let config = new_config(); + + assert!(config + .get::<Option<JobsConfig>>("build.jobs") + .unwrap() + .is_none()); +} + +#[cargo_test] +fn build_jobs_default() { + write_config( + "\ +[build] +jobs = \"default\" +", + ); + + let config = new_config(); + + let a = config + .get::<Option<JobsConfig>>("build.jobs") + .unwrap() + .unwrap(); + + match a { + JobsConfig::String(v) => assert_eq!(&v, "default"), + JobsConfig::Integer(_) => panic!("Did not except an integer."), + } +} + +#[cargo_test] +fn build_jobs_integer() { + write_config( + "\ +[build] +jobs = 2 +", + ); + + let config = new_config(); + + let a = config + .get::<Option<JobsConfig>>("build.jobs") + .unwrap() + .unwrap(); + + match a { + JobsConfig::String(_) => panic!("Did not except an integer."), + JobsConfig::Integer(v) => assert_eq!(v, 2), + } +} diff --git a/src/tools/cargo/tests/testsuite/config_include.rs b/src/tools/cargo/tests/testsuite/config_include.rs index ae568065a..057c583ae 100644 --- a/src/tools/cargo/tests/testsuite/config_include.rs +++ b/src/tools/cargo/tests/testsuite/config_include.rs @@ -6,9 +6,9 @@ use cargo_test_support::{no_such_file_err_msg, project}; #[cargo_test] fn gated() { // Requires -Z flag. - write_config("include='other'"); + write_config("include='other.toml'"); write_config_at( - ".cargo/other", + ".cargo/other.toml", " othervalue = 1 ", @@ -25,13 +25,13 @@ fn simple() { write_config_at( ".cargo/config", " - include = 'other' + include = 'other.toml' key1 = 1 key2 = 2 ", ); write_config_at( - ".cargo/other", + ".cargo/other.toml", " key2 = 3 key3 = 4 @@ -84,39 +84,63 @@ fn works_with_cli() { } #[cargo_test] -fn left_to_right() { - // How it merges multiple includes. +fn left_to_right_bottom_to_top() { + // How it merges multiple nested includes. write_config_at( ".cargo/config", " - include = ['one', 'two'] - primary = 1 + include = ['left-middle.toml', 'right-middle.toml'] + top = 1 + ", + ); + write_config_at( + ".cargo/right-middle.toml", + " + include = 'right-bottom.toml' + top = 0 + right-middle = 0 ", ); write_config_at( - ".cargo/one", + ".cargo/right-bottom.toml", " - one = 1 - primary = 2 + top = -1 + right-middle = -1 + right-bottom = -1 ", ); write_config_at( - ".cargo/two", + ".cargo/left-middle.toml", " - two = 2 - primary = 3 + include = 'left-bottom.toml' + top = -2 + right-middle = -2 + right-bottom = -2 + left-middle = -2 + ", + ); + write_config_at( + ".cargo/left-bottom.toml", + " + top = -3 + right-middle = -3 + right-bottom = -3 + left-middle = -3 + left-bottom = -3 ", ); let config = ConfigBuilder::new().unstable_flag("config-include").build(); - assert_eq!(config.get::<i32>("primary").unwrap(), 1); - assert_eq!(config.get::<i32>("one").unwrap(), 1); - assert_eq!(config.get::<i32>("two").unwrap(), 2); + assert_eq!(config.get::<i32>("top").unwrap(), 1); + assert_eq!(config.get::<i32>("right-middle").unwrap(), 0); + assert_eq!(config.get::<i32>("right-bottom").unwrap(), -1); + assert_eq!(config.get::<i32>("left-middle").unwrap(), -2); + assert_eq!(config.get::<i32>("left-bottom").unwrap(), -3); } #[cargo_test] fn missing_file() { // Error when there's a missing file. - write_config("include='missing'"); + write_config("include='missing.toml'"); let config = ConfigBuilder::new() .unstable_flag("config-include") .build_err(); @@ -127,10 +151,10 @@ fn missing_file() { could not load Cargo configuration Caused by: - failed to load config include `missing` from `[..]/.cargo/config` + failed to load config include `missing.toml` from `[..]/.cargo/config` Caused by: - failed to read configuration file `[..]/.cargo/missing` + failed to read configuration file `[..]/.cargo/missing.toml` Caused by: {}", @@ -140,11 +164,29 @@ Caused by: } #[cargo_test] +fn wrong_file_extension() { + // Error when it doesn't end with `.toml`. + write_config("include='config.png'"); + let config = ConfigBuilder::new() + .unstable_flag("config-include") + .build_err(); + assert_error( + config.unwrap_err(), + "\ +could not load Cargo configuration + +Caused by: + expected a config include path ending with `.toml`, but found `config.png` from `[..]/.cargo/config` +", + ); +} + +#[cargo_test] fn cycle() { // Detects a cycle. - write_config_at(".cargo/config", "include='one'"); - write_config_at(".cargo/one", "include='two'"); - write_config_at(".cargo/two", "include='config'"); + write_config_at(".cargo/config.toml", "include='one.toml'"); + write_config_at(".cargo/one.toml", "include='two.toml'"); + write_config_at(".cargo/two.toml", "include='config.toml'"); let config = ConfigBuilder::new() .unstable_flag("config-include") .build_err(); @@ -154,16 +196,16 @@ fn cycle() { could not load Cargo configuration Caused by: - failed to load config include `one` from `[..]/.cargo/config` + failed to load config include `one.toml` from `[..]/.cargo/config.toml` Caused by: - failed to load config include `two` from `[..]/.cargo/one` + failed to load config include `two.toml` from `[..]/.cargo/one.toml` Caused by: - failed to load config include `config` from `[..]/.cargo/two` + failed to load config include `config.toml` from `[..]/.cargo/two.toml` Caused by: - config `include` cycle detected with path `[..]/.cargo/config`", + config `include` cycle detected with path `[..]/.cargo/config.toml`", ); } @@ -178,10 +220,10 @@ fn cli_include() { bar = 2 ", ); - write_config_at(".cargo/config-foo", "foo = 2"); + write_config_at(".cargo/config-foo.toml", "foo = 2"); let config = ConfigBuilder::new() .unstable_flag("config-include") - .config_arg("include='.cargo/config-foo'") + .config_arg("include='.cargo/config-foo.toml'") .build(); assert_eq!(config.get::<i32>("foo").unwrap(), 2); assert_eq!(config.get::<i32>("bar").unwrap(), 2); @@ -209,7 +251,7 @@ fn cli_include_failed() { // Error message when CLI include fails to load. let config = ConfigBuilder::new() .unstable_flag("config-include") - .config_arg("include='foobar'") + .config_arg("include='foobar.toml'") .build_err(); assert_error( config.unwrap_err(), @@ -218,10 +260,10 @@ fn cli_include_failed() { failed to load --config include Caused by: - failed to load config include `foobar` from `--config cli option` + failed to load config include `foobar.toml` from `--config cli option` Caused by: - failed to read configuration file `[..]/foobar` + failed to read configuration file `[..]/foobar.toml` Caused by: {}", @@ -235,14 +277,14 @@ fn cli_merge_failed() { // Error message when CLI include merge fails. write_config("foo = ['a']"); write_config_at( - ".cargo/other", + ".cargo/other.toml", " foo = 'b' ", ); let config = ConfigBuilder::new() .unstable_flag("config-include") - .config_arg("include='.cargo/other'") + .config_arg("include='.cargo/other.toml'") .build_err(); // Maybe this error message should mention it was from an include file? assert_error( @@ -251,7 +293,7 @@ fn cli_merge_failed() { failed to merge --config key `foo` into `[..]/.cargo/config` Caused by: - failed to merge config value from `[..]/.cargo/other` into `[..]/.cargo/config`: \ + failed to merge config value from `[..]/.cargo/other.toml` into `[..]/.cargo/config`: \ expected array, but found string", ); } diff --git a/src/tools/cargo/tests/testsuite/cross_compile.rs b/src/tools/cargo/tests/testsuite/cross_compile.rs index cc9644550..1bc0c277d 100644 --- a/src/tools/cargo/tests/testsuite/cross_compile.rs +++ b/src/tools/cargo/tests/testsuite/cross_compile.rs @@ -398,7 +398,7 @@ fn linker() { "\ [COMPILING] foo v0.5.0 ([CWD]) [RUNNING] `rustc --crate-name foo src/foo.rs [..]--crate-type bin \ - --emit=[..]link[..]-C debuginfo=2 \ + --emit=[..]link[..]-C debuginfo=2 [..]\ -C metadata=[..] \ --out-dir [CWD]/target/{target}/debug/deps \ --target {target} \ diff --git a/src/tools/cargo/tests/testsuite/custom_target.rs b/src/tools/cargo/tests/testsuite/custom_target.rs index b7ad4d835..491d3233c 100644 --- a/src/tools/cargo/tests/testsuite/custom_target.rs +++ b/src/tools/cargo/tests/testsuite/custom_target.rs @@ -4,6 +4,7 @@ use cargo_test_support::{basic_manifest, project}; use std::fs; const MINIMAL_LIB: &str = r#" +#![allow(internal_features)] #![feature(no_core)] #![feature(lang_items)] #![no_core] @@ -80,6 +81,7 @@ fn custom_target_dependency() { .file( "src/lib.rs", r#" + #![allow(internal_features)] #![feature(no_core)] #![feature(lang_items)] #![feature(auto_traits)] diff --git a/src/tools/cargo/tests/testsuite/directory.rs b/src/tools/cargo/tests/testsuite/directory.rs index 0e28de039..e72f1f07d 100644 --- a/src/tools/cargo/tests/testsuite/directory.rs +++ b/src/tools/cargo/tests/testsuite/directory.rs @@ -188,7 +188,9 @@ fn simple_install_fail() { .with_status(101) .with_stderr( " Installing bar v0.1.0 -error: failed to compile `bar v0.1.0`, intermediate artifacts can be found at `[..]` +error: failed to compile `bar v0.1.0`, intermediate artifacts can be found at `[..]`. +To reuse those artifacts with a future compilation, set the environment variable \ +`CARGO_TARGET_DIR` to that path. Caused by: no matching package found @@ -759,7 +761,9 @@ fn version_missing() { .with_stderr( "\ [INSTALLING] bar v0.1.0 -error: failed to compile [..] +error: failed to compile [..], intermediate artifacts can be found at `[..]`. +To reuse those artifacts with a future compilation, set the environment variable \ +`CARGO_TARGET_DIR` to that path. Caused by: failed to select a version for the requirement `foo = \"^2\"` diff --git a/src/tools/cargo/tests/testsuite/doc.rs b/src/tools/cargo/tests/testsuite/doc.rs index 739bcf376..481df8590 100644 --- a/src/tools/cargo/tests/testsuite/doc.rs +++ b/src/tools/cargo/tests/testsuite/doc.rs @@ -756,6 +756,7 @@ fn doc_target() { .file( "src/lib.rs", r#" + #![allow(internal_features)] #![feature(no_core, lang_items)] #![no_core] @@ -2041,7 +2042,7 @@ fn crate_versions_flag_is_overridden() { asserts(output_documentation()); } -#[cargo_test(nightly, reason = "-Zdoctest-in-workspace is unstable")] +#[cargo_test] fn doc_test_in_workspace() { let p = project() .file( @@ -2087,8 +2088,7 @@ fn doc_test_in_workspace() { ", ) .build(); - p.cargo("test -Zdoctest-in-workspace --doc -vv") - .masquerade_as_nightly_cargo(&["doctest-in-workspace"]) + p.cargo("test --doc -vv") .with_stderr_contains("[DOCTEST] crate-a") .with_stdout_contains( " @@ -2096,7 +2096,6 @@ running 1 test test crate-a/src/lib.rs - (line 1) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..] - ", ) .with_stderr_contains("[DOCTEST] crate-b") @@ -2106,7 +2105,98 @@ running 1 test test crate-b/src/lib.rs - (line 1) ... ok test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..] +", + ) + .run(); +} + +/// This is a test for <https://github.com/rust-lang/rust/issues/46372>. +/// The `file!()` macro inside of an `include!()` should output +/// workspace-relative paths, just like it does in other cases. +#[cargo_test] +fn doc_test_include_file() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = [ + "child", + ] + [package] + name = "root" + version = "0.1.0" + "#, + ) + .file( + "src/lib.rs", + r#" + /// ``` + /// assert_eq!("src/lib.rs", file!().replace("\\", "/")) + /// ``` + pub mod included { + include!(concat!("../", file!(), ".included.rs")); + } + "#, + ) + .file( + "src/lib.rs.included.rs", + r#" + /// ``` + /// assert_eq!(1, 1) + /// ``` + pub fn foo() {} + "#, + ) + .file( + "child/Cargo.toml", + r#" + [package] + name = "child" + version = "0.1.0" + "#, + ) + .file( + "child/src/lib.rs", + r#" + /// ``` + /// assert_eq!("child/src/lib.rs", file!().replace("\\", "/")) + /// ``` + pub mod included { + include!(concat!("../../", file!(), ".included.rs")); + } + "#, + ) + .file( + "child/src/lib.rs.included.rs", + r#" + /// ``` + /// assert_eq!(1, 1) + /// ``` + pub fn foo() {} + "#, + ) + .build(); + + p.cargo("test --workspace --doc -vv -- --test-threads=1") + .with_stderr_contains("[DOCTEST] child") + .with_stdout_contains( + " +running 2 tests +test child/src/../../child/src/lib.rs.included.rs - included::foo (line 2) ... ok +test child/src/lib.rs - included (line 2) ... ok + +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..] +", + ) + .with_stderr_contains("[DOCTEST] root") + .with_stdout_contains( + " +running 2 tests +test src/../src/lib.rs.included.rs - included::foo (line 2) ... ok +test src/lib.rs - included (line 2) ... ok +test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out[..] ", ) .run(); diff --git a/src/tools/cargo/tests/testsuite/features.rs b/src/tools/cargo/tests/testsuite/features.rs index 848e05677..557fab14a 100644 --- a/src/tools/cargo/tests/testsuite/features.rs +++ b/src/tools/cargo/tests/testsuite/features.rs @@ -1937,8 +1937,8 @@ fn nonexistent_required_features() { } #[cargo_test] -fn invalid_feature_names_warning() { - // Warnings for more restricted feature syntax. +fn invalid_feature_names_error() { + // Errors for more restricted feature syntax. let p = project() .file( "Cargo.toml", @@ -1948,72 +1948,57 @@ fn invalid_feature_names_warning() { version = "0.1.0" [features] - # Some valid, but unusual names, shouldn't warn. - "c++17" = [] - "128bit" = [] - "_foo" = [] - "feat-name" = [] - "feat_name" = [] - "foo.bar" = [] - - # Invalid names. + # Invalid start character. "+foo" = [] - "-foo" = [] - ".foo" = [] - "foo:bar" = [] - "foo?" = [] - "?foo" = [] - "ⒶⒷⒸ" = [] - "a¼" = [] "#, ) .file("src/lib.rs", "") .build(); - // Unfortunately the warnings are duplicated due to the Summary being - // loaded twice (once in the Workspace, and once in PackageRegistry) and - // Cargo does not have a de-duplication system. This should probably be - // OK, since I'm not expecting this to affect anyone. p.cargo("check") - .with_stderr("\ -[WARNING] invalid character `+` in feature `+foo` in package foo v0.1.0 ([ROOT]/foo), the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`) -This was previously accepted but is being phased out; it will become a hard error in a future release. -For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project. -[WARNING] invalid character `-` in feature `-foo` in package foo v0.1.0 ([ROOT]/foo), the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`) -This was previously accepted but is being phased out; it will become a hard error in a future release. -For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project. -[WARNING] invalid character `.` in feature `.foo` in package foo v0.1.0 ([ROOT]/foo), the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`) -This was previously accepted but is being phased out; it will become a hard error in a future release. -For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project. -[WARNING] invalid character `?` in feature `?foo` in package foo v0.1.0 ([ROOT]/foo), the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`) -This was previously accepted but is being phased out; it will become a hard error in a future release. -For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project. -[WARNING] invalid character `¼` in feature `a¼` in package foo v0.1.0 ([ROOT]/foo), characters must be Unicode XID characters, `+`, or `.` (numbers, `+`, `-`, `_`, `.`, or most letters) -This was previously accepted but is being phased out; it will become a hard error in a future release. -For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project. -[WARNING] invalid character `:` in feature `foo:bar` in package foo v0.1.0 ([ROOT]/foo), characters must be Unicode XID characters, `+`, or `.` (numbers, `+`, `-`, `_`, `.`, or most letters) -This was previously accepted but is being phased out; it will become a hard error in a future release. -For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project. -[WARNING] invalid character `?` in feature `foo?` in package foo v0.1.0 ([ROOT]/foo), characters must be Unicode XID characters, `+`, or `.` (numbers, `+`, `-`, `_`, `.`, or most letters) -This was previously accepted but is being phased out; it will become a hard error in a future release. -For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project. -[WARNING] invalid character `Ⓐ` in feature `ⒶⒷⒸ` in package foo v0.1.0 ([ROOT]/foo), the first character must be a Unicode XID start character or digit (most letters or `_` or `0` to `9`) -This was previously accepted but is being phased out; it will become a hard error in a future release. -For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project. -[WARNING] invalid character `Ⓑ` in feature `ⒶⒷⒸ` in package foo v0.1.0 ([ROOT]/foo), characters must be Unicode XID characters, `+`, or `.` (numbers, `+`, `-`, `_`, `.`, or most letters) -This was previously accepted but is being phased out; it will become a hard error in a future release. -For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project. -[WARNING] invalid character `Ⓒ` in feature `ⒶⒷⒸ` in package foo v0.1.0 ([ROOT]/foo), characters must be Unicode XID characters, `+`, or `.` (numbers, `+`, `-`, `_`, `.`, or most letters) -This was previously accepted but is being phased out; it will become a hard error in a future release. -For more information, see issue #8813 <https://github.com/rust-lang/cargo/issues/8813>, and please leave a comment if this will be a problem for your project. -[CHECKING] foo v0.1.0 [..] -[FINISHED] [..] -") + .with_status(101) + .with_stderr( + "\ +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), \ + the first character must be a Unicode XID start character or digit \ + (most letters or `_` or `0` to `9`) +", + ) + .run(); + + p.change_file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [features] + # Invalid continue character. + "a&b" = [] + "#, + ); + + p.cargo("check") + .with_status(101) + .with_stderr( + "\ +error: failed to parse manifest at `[ROOT]/foo/Cargo.toml` + +Caused by: + invalid character `&` in feature `a&b` in package foo v0.1.0 ([ROOT]/foo), \ + characters must be Unicode XID characters, `+`, or `.` \ + (numbers, `+`, `-`, `_`, `.`, or most letters) +", + ) .run(); } #[cargo_test] -fn invalid_feature_names_error() { +fn invalid_feature_name_slash_error() { // Errors for more restricted feature syntax. let p = project() .file( diff --git a/src/tools/cargo/tests/testsuite/features2.rs b/src/tools/cargo/tests/testsuite/features2.rs index 494c83f1e..68fecb863 100644 --- a/src/tools/cargo/tests/testsuite/features2.rs +++ b/src/tools/cargo/tests/testsuite/features2.rs @@ -1407,6 +1407,41 @@ workspace: [..]/foo/Cargo.toml } #[cargo_test] +fn edition_2021_workspace_member() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["a"] + "#, + ) + .file( + "a/Cargo.toml", + r#" + [package] + name = "a" + version = "0.1.0" + edition = "2021" + "#, + ) + .file("a/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +warning: some crates are on edition 2021 which defaults to `resolver = \"2\"`, but virtual workspaces default to `resolver = \"1\"` +note: to keep the current resolver, specify `workspace.resolver = \"1\"` in the workspace root's manifest +note: to use the edition 2021 resolver, specify `workspace.resolver = \"2\"` in the workspace root's manifest +[CHECKING] a v0.1.0 [..] +[FINISHED] [..] +", + ) + .run(); +} + +#[cargo_test] fn resolver_ws_root_and_member() { // Check when specified in both ws root and member. let p = project() diff --git a/src/tools/cargo/tests/testsuite/future_incompat_report.rs b/src/tools/cargo/tests/testsuite/future_incompat_report.rs index 9f451a64c..4d2c66d17 100644 --- a/src/tools/cargo/tests/testsuite/future_incompat_report.rs +++ b/src/tools/cargo/tests/testsuite/future_incompat_report.rs @@ -164,7 +164,7 @@ fn test_multi_crate() { second-dep = "*" "#, ) - .file("src/main.rs", "fn main() {}") + .file("src/lib.rs", "") .build(); for command in &["build", "check", "rustc", "test"] { diff --git a/src/tools/cargo/tests/testsuite/git.rs b/src/tools/cargo/tests/testsuite/git.rs index 7c717e967..f60ee978a 100644 --- a/src/tools/cargo/tests/testsuite/git.rs +++ b/src/tools/cargo/tests/testsuite/git.rs @@ -3619,3 +3619,71 @@ fn cleans_temp_pack_files() { p.cargo("generate-lockfile").run(); assert!(!tmp_path.exists()); } + +#[cargo_test] +fn different_user_relative_submodules() { + let user1_git_project = git::new("user1/dep1", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("dep1")) + .file("src/lib.rs", "") + }); + + let user2_git_project = git::new("user2/dep1", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("dep1")) + .file("src/lib.rs", "") + }); + let user2_git_project2 = git::new("user2/dep2", |project| { + project + .file("Cargo.toml", &basic_lib_manifest("dep1")) + .file("src/lib.rs", "") + }); + + let user2_repo = git2::Repository::open(&user2_git_project.root()).unwrap(); + let url = "../dep2"; + git::add_submodule(&user2_repo, url, Path::new("dep2")); + git::commit(&user2_repo); + + let user1_repo = git2::Repository::open(&user1_git_project.root()).unwrap(); + let url = user2_git_project.url(); + git::add_submodule(&user1_repo, url.as_str(), Path::new("user2/dep1")); + git::commit(&user1_repo); + + let project = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.5.0" + + [dependencies.dep1] + git = '{}' + "#, + user1_git_project.url() + ), + ) + .file("src/main.rs", &main_file(r#""hello""#, &[])) + .build(); + + project + .cargo("build") + .with_stderr(&format!( + "\ +[UPDATING] git repository `{}` +[UPDATING] git submodule `{}` +[UPDATING] git submodule `{}` +[COMPILING] dep1 v0.5.0 ({}#[..]) +[COMPILING] foo v0.5.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + path2url(&user1_git_project.root()), + path2url(&user2_git_project.root()), + path2url(&user2_git_project2.root()), + path2url(&user1_git_project.root()), + )) + .run(); + + assert!(project.bin("foo").is_file()); +} diff --git a/src/tools/cargo/tests/testsuite/install.rs b/src/tools/cargo/tests/testsuite/install.rs index 9b881dfdc..0c7fc5037 100644 --- a/src/tools/cargo/tests/testsuite/install.rs +++ b/src/tools/cargo/tests/testsuite/install.rs @@ -58,6 +58,20 @@ fn simple() { } #[cargo_test] +fn toolchain() { + pkg("foo", "0.0.1"); + + cargo_process("install +nightly") + .with_status(101) + .with_stderr( + "\ +[ERROR] invalid character `+` in package name: `+nightly` + Use `cargo +nightly install` if you meant to use the `nightly` toolchain.", + ) + .run(); +} + +#[cargo_test] fn simple_with_message_format() { pkg("foo", "0.0.1"); @@ -964,7 +978,8 @@ fn compile_failure() { "\ [ERROR] could not compile `foo` (bin \"foo\") due to previous error [ERROR] failed to compile `foo v0.0.1 ([..])`, intermediate artifacts can be \ - found at `[..]target` + found at `[..]target`.\nTo reuse those artifacts with a future compilation, \ + set the environment variable `CARGO_TARGET_DIR` to that path. ", ) .run(); @@ -1269,7 +1284,8 @@ fn reports_unsuccessful_subcommand_result() { .run(); cargo_process("fail") .with_status(101) - .with_stderr_contains("thread '[..]' panicked at 'explicit panic', [..]") + .with_stderr_contains("thread '[..]' panicked at [..]src/main.rs:1:[..]") + .with_stderr_contains("[..]explicit panic[..]") .run(); } @@ -2250,7 +2266,9 @@ fn failed_install_retains_temp_directory() { ) .unwrap(); compare::match_contains( - "error: failed to compile `foo v0.0.1`, intermediate artifacts can be found at `[..]`", + "error: failed to compile `foo v0.0.1`, intermediate artifacts can be found at \ + `[..]`.\nTo reuse those artifacts with a future compilation, set the environment \ + variable `CARGO_TARGET_DIR` to that path.", &stderr, None, ) @@ -2258,7 +2276,7 @@ fn failed_install_retains_temp_directory() { // Find the path in the output. let start = stderr.find("found at `").unwrap() + 10; - let end = stderr[start..].find('\n').unwrap() - 1; + let end = stderr[start..].find('.').unwrap() - 1; let path = Path::new(&stderr[start..(end + start)]); assert!(path.exists()); assert!(path.join("release/deps").exists()); diff --git a/src/tools/cargo/tests/testsuite/lockfile_compat.rs b/src/tools/cargo/tests/testsuite/lockfile_compat.rs index aad8723c3..63148cc07 100644 --- a/src/tools/cargo/tests/testsuite/lockfile_compat.rs +++ b/src/tools/cargo/tests/testsuite/lockfile_compat.rs @@ -888,3 +888,81 @@ perhaps a crate was updated and forgotten to be re-vendored? ) .run(); } + +#[cargo_test] +fn v4_is_unstable() { + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + "#, + ), + ) + .file("src/lib.rs", "") + .file("Cargo.lock", "version = 4") + .build(); + + p.cargo("fetch") + .with_status(101) + .with_stderr( + "\ +error: failed to parse lock file at: [CWD]/Cargo.lock + +Caused by: + lock file version `4` was found, but this version of Cargo does not \ + understand this lock file, perhaps Cargo needs to be updated? +", + ) + .run(); + + // On nightly, let the user know about the `-Z` flag. + p.cargo("fetch") + .masquerade_as_nightly_cargo(&["-Znext-lockfile-bump"]) + .with_status(101) + .with_stderr( + "\ +error: failed to parse lock file at: [CWD]/Cargo.lock + +Caused by: + lock file version 4 requires `-Znext-lockfile-bump` +", + ) + .run(); +} + +#[cargo_test] +fn v4_cannot_be_created_from_scratch() { + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + version = "0.0.1" + "#, + ), + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("fetch -Znext-lockfile-bump") + .masquerade_as_nightly_cargo(&["-Znext-lockfile-bump"]) + .run(); + + let lockfile = r#"# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "foo" +version = "0.0.1" +"#; + + let lock = p.read_lockfile(); + assert_match_exact(lockfile, &lock); +} diff --git a/src/tools/cargo/tests/testsuite/main.rs b/src/tools/cargo/tests/testsuite/main.rs index 170a22667..2c282c0a3 100644 --- a/src/tools/cargo/tests/testsuite/main.rs +++ b/src/tools/cargo/tests/testsuite/main.rs @@ -123,6 +123,7 @@ mod rustdoc_extern_html; mod rustdocflags; mod rustflags; mod rustup; +mod script; mod search; mod shell_quoting; mod source_replacement; diff --git a/src/tools/cargo/tests/testsuite/profile_config.rs b/src/tools/cargo/tests/testsuite/profile_config.rs index cf9807964..26104e7e7 100644 --- a/src/tools/cargo/tests/testsuite/profile_config.rs +++ b/src/tools/cargo/tests/testsuite/profile_config.rs @@ -267,7 +267,7 @@ fn profile_config_all_options() { -C panic=abort \ -C lto[..]\ -C codegen-units=2 \ - -C debuginfo=2 \ + -C debuginfo=2 [..]\ -C debug-assertions=on \ -C overflow-checks=off [..]\ -C rpath [..]\ @@ -437,7 +437,7 @@ fn named_config_profile() { assert_eq!(p.name, "foo"); assert_eq!(p.codegen_units, Some(2)); // "foo" from config assert_eq!(p.opt_level, "1"); // "middle" from manifest - assert_eq!(p.debuginfo.to_option(), Some(TomlDebugInfo::Limited)); // "bar" from config + assert_eq!(p.debuginfo.into_inner(), TomlDebugInfo::Limited); // "bar" from config assert_eq!(p.debug_assertions, true); // "dev" built-in (ignore build-override) assert_eq!(p.overflow_checks, true); // "dev" built-in (ignore package override) @@ -446,7 +446,7 @@ fn named_config_profile() { assert_eq!(bo.name, "foo"); assert_eq!(bo.codegen_units, Some(6)); // "foo" build override from config assert_eq!(bo.opt_level, "0"); // default to zero - assert_eq!(bo.debuginfo.to_option(), Some(TomlDebugInfo::Limited)); // SAME as normal + assert_eq!(bo.debuginfo.into_inner(), TomlDebugInfo::Limited); // SAME as normal assert_eq!(bo.debug_assertions, false); // "foo" build override from manifest assert_eq!(bo.overflow_checks, true); // SAME as normal @@ -455,7 +455,7 @@ fn named_config_profile() { assert_eq!(po.name, "foo"); assert_eq!(po.codegen_units, Some(7)); // "foo" package override from config assert_eq!(po.opt_level, "1"); // SAME as normal - assert_eq!(po.debuginfo.to_option(), Some(TomlDebugInfo::Limited)); // SAME as normal + assert_eq!(po.debuginfo.into_inner(), TomlDebugInfo::Limited); // SAME as normal assert_eq!(po.debug_assertions, true); // SAME as normal assert_eq!(po.overflow_checks, false); // "middle" package override from manifest } @@ -509,12 +509,13 @@ fn test_with_dev_profile() { [DOWNLOADING] [..] [DOWNLOADED] [..] [COMPILING] somedep v1.0.0 -[RUNNING] `rustc --crate-name somedep [..]-C debuginfo=0[..] +[RUNNING] `rustc --crate-name somedep [..] [COMPILING] foo v0.1.0 [..] -[RUNNING] `rustc --crate-name foo [..]-C debuginfo=0[..] +[RUNNING] `rustc --crate-name foo [..] [FINISHED] [..] [EXECUTABLE] `[..]/target/debug/deps/foo-[..][EXE]` ", ) + .with_stdout_does_not_contain("[..] -C debuginfo=0[..]") .run(); } diff --git a/src/tools/cargo/tests/testsuite/profile_targets.rs b/src/tools/cargo/tests/testsuite/profile_targets.rs index 0449e8ab3..a88ca34fd 100644 --- a/src/tools/cargo/tests/testsuite/profile_targets.rs +++ b/src/tools/cargo/tests/testsuite/profile_targets.rs @@ -88,11 +88,11 @@ fn profile_selection_build() { .with_stderr_unordered("\ [COMPILING] bar [..] [RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..] -[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 -C debuginfo=0[..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..] [COMPILING] bdep [..] -[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 -C debuginfo=0[..] +[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..] [COMPILING] foo [..] -[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 -C debuginfo=0[..] +[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 [..] [RUNNING] `[..]/target/debug/build/foo-[..]/build-script-build` [foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 [RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..] @@ -100,6 +100,7 @@ fn profile_selection_build() { [FINISHED] dev [unoptimized + debuginfo] [..] " ) + .with_stderr_does_not_contain("[..] -C debuginfo=0[..]") .run(); p.cargo("build -vv") .with_stderr_unordered( @@ -154,8 +155,9 @@ fn profile_selection_build_all_targets() { // `build.rs` is a plugin. // - Benchmark dependencies are compiled in `dev` mode, which may be // surprising. See issue rust-lang/cargo#4929. - // - We make sure that the build dependencies bar, bdep, and build.rs - // are built with debuginfo=0. + // - We make sure that the build dependencies bar, bdep, and build.rs are built with + // debuginfo=0; but since we don't pass `-C debuginfo` when it's set to 0, we have to test + // explicitly that there's no `-C debuginfo` flag. // // - Dependency profiles: // Pkg Target Profile Reason @@ -181,24 +183,25 @@ fn profile_selection_build_all_targets() { [COMPILING] bar [..] [RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..] [RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..] -[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 -C debuginfo=0[..] +[RUNNING] `[..] rustc --crate-name bar bar/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..] [COMPILING] bdep [..] -[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 -C debuginfo=0[..] +[RUNNING] `[..] rustc --crate-name bdep bdep/src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=5 [..] [COMPILING] foo [..] -[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 -C debuginfo=0[..] +[RUNNING] `[..] rustc --crate-name build_script_build build.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=5 [..] [RUNNING] `[..]/target/debug/build/foo-[..]/build-script-build` [foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 [RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]` -[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 --test [..]` +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]` [RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..]` -[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 --test [..]` -[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 --test [..]` -[RUNNING] `[..] rustc --crate-name bench1 benches/bench1.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 --test [..]` +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]` +[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]` +[RUNNING] `[..] rustc --crate-name bench1 benches/bench1.rs [..]--emit=[..]link[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..]` [RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--crate-type bin --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]` [RUNNING] `[..] rustc --crate-name ex1 examples/ex1.rs [..]--crate-type bin --emit=[..]link -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..]` [FINISHED] dev [unoptimized + debuginfo] [..] " ) + .with_stderr_does_not_contain("[..] -C debuginfo=0[..]") .run(); p.cargo("build -vv") .with_stderr_unordered( @@ -315,10 +318,10 @@ fn profile_selection_test() { [foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 [RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link -C panic=abort[..]-C codegen-units=3 -C debuginfo=2 [..] [RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 [..] -[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 --test [..] -[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 --test [..] +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..] +[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..] [RUNNING] `[..] rustc --crate-name ex1 examples/ex1.rs [..]--crate-type bin --emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 [..] -[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 --test [..] +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..] [RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--crate-type bin --emit=[..]link -C panic=abort[..]-C codegen-units=3 -C debuginfo=2 [..] [FINISHED] test [unoptimized + debuginfo] [..] [RUNNING] `[..]/deps/foo-[..]` @@ -513,10 +516,10 @@ fn profile_selection_check_all_targets() { [foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 [RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]metadata -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..] [RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 [..] -[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 --test [..] -[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 --test [..] -[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 --test [..] -[RUNNING] `[..] rustc --crate-name bench1 benches/bench1.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 --test [..] +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..] +[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..] +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..] +[RUNNING] `[..] rustc --crate-name bench1 benches/bench1.rs [..]--emit=[..]metadata[..]-C codegen-units=1 -C debuginfo=2 [..]--test [..] [RUNNING] `[..] rustc --crate-name ex1 examples/ex1.rs [..]--crate-type bin --emit=[..]metadata -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..] [RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--crate-type bin --emit=[..]metadata -C panic=abort[..]-C codegen-units=1 -C debuginfo=2 [..] [FINISHED] dev [unoptimized + debuginfo] [..] @@ -615,11 +618,11 @@ fn profile_selection_check_all_targets_test() { [RUNNING] `[..]target/debug/build/foo-[..]/build-script-build` [foo 0.0.1] foo custom build PROFILE=debug DEBUG=true OPT_LEVEL=0 [RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..] -[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 --test [..] -[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 --test [..] -[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 --test [..] -[RUNNING] `[..] rustc --crate-name bench1 benches/bench1.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 --test [..] -[RUNNING] `[..] rustc --crate-name ex1 examples/ex1.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 --test [..] +[RUNNING] `[..] rustc --crate-name foo src/lib.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..] +[RUNNING] `[..] rustc --crate-name test1 tests/test1.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..] +[RUNNING] `[..] rustc --crate-name foo src/main.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..] +[RUNNING] `[..] rustc --crate-name bench1 benches/bench1.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..] +[RUNNING] `[..] rustc --crate-name ex1 examples/ex1.rs [..]--emit=[..]metadata[..]-C codegen-units=3 -C debuginfo=2 [..]--test [..] [FINISHED] test [unoptimized + debuginfo] [..] ").run(); diff --git a/src/tools/cargo/tests/testsuite/profiles.rs b/src/tools/cargo/tests/testsuite/profiles.rs index 2d2646fe3..465ab3b99 100644 --- a/src/tools/cargo/tests/testsuite/profiles.rs +++ b/src/tools/cargo/tests/testsuite/profiles.rs @@ -66,7 +66,7 @@ fn opt_level_override_0() { [COMPILING] test v0.0.0 ([CWD]) [RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \ --emit=[..]link[..]\ - -C debuginfo=2 \ + -C debuginfo=2 [..]\ -C metadata=[..] \ --out-dir [..] \ -L dependency=[CWD]/target/debug/deps` @@ -99,7 +99,7 @@ fn debug_override_1() { [COMPILING] test v0.0.0 ([CWD]) [RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \ --emit=[..]link[..]\ - -C debuginfo=1 \ + -C debuginfo=1 [..]\ -C metadata=[..] \ --out-dir [..] \ -L dependency=[CWD]/target/debug/deps` @@ -136,7 +136,7 @@ fn check_opt_level_override(profile_level: &str, rustc_level: &str) { [RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \ --emit=[..]link \ -C opt-level={level}[..]\ - -C debuginfo=2 \ + -C debuginfo=2 [..]\ -C debug-assertions=on \ -C metadata=[..] \ --out-dir [..] \ @@ -211,7 +211,7 @@ fn top_level_overrides_deps() { --emit=[..]link \ -C prefer-dynamic \ -C opt-level=1[..]\ - -C debuginfo=2 \ + -C debuginfo=2 [..]\ -C metadata=[..] \ --out-dir [CWD]/target/release/deps \ -L dependency=[CWD]/target/release/deps` @@ -219,7 +219,7 @@ fn top_level_overrides_deps() { [RUNNING] `rustc --crate-name test src/lib.rs [..]--crate-type lib \ --emit=[..]link \ -C opt-level=1[..]\ - -C debuginfo=2 \ + -C debuginfo=2 [..]\ -C metadata=[..] \ --out-dir [..] \ -L dependency=[CWD]/target/release/deps \ @@ -467,10 +467,11 @@ fn debug_0_report() { .with_stderr( "\ [COMPILING] foo v0.1.0 [..] -[RUNNING] `rustc --crate-name foo src/lib.rs [..]-C debuginfo=0 [..] +[RUNNING] `rustc --crate-name foo src/lib.rs [..] [FINISHED] dev [unoptimized] target(s) in [..] ", ) + .with_stderr_does_not_contain("-C debuginfo") .run(); } @@ -742,3 +743,42 @@ Caused by: ) .run(); } + +#[cargo_test(nightly, reason = "debug options stabilized in 1.70")] +fn debug_options_valid() { + let build = |option| { + let p = project() + .file( + "Cargo.toml", + &format!( + r#" + [package] + name = "foo" + authors = [] + version = "0.0.0" + + [profile.dev] + debug = "{option}" + "# + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build -v") + }; + + for (option, cli) in [ + ("line-directives-only", "line-directives-only"), + ("line-tables-only", "line-tables-only"), + ("limited", "1"), + ("full", "2"), + ] { + build(option) + .with_stderr_contains(&format!("[RUNNING] `rustc [..]-C debuginfo={cli} [..]")) + .run(); + } + build("none") + .with_stderr_does_not_contain("[..]-C debuginfo[..]") + .run(); +} diff --git a/src/tools/cargo/tests/testsuite/required_features.rs b/src/tools/cargo/tests/testsuite/required_features.rs index ac6c9d233..88be89fd4 100644 --- a/src/tools/cargo/tests/testsuite/required_features.rs +++ b/src/tools/cargo/tests/testsuite/required_features.rs @@ -658,7 +658,9 @@ Consider enabling some of the needed features by passing, e.g., `--features=\"a\ "\ [INSTALLING] foo v0.0.1 ([..]) [ERROR] failed to compile `foo v0.0.1 ([..])`, intermediate artifacts can be found at \ - `[..]target` + `[..]target`. +To reuse those artifacts with a future compilation, set the environment \ +variable `CARGO_TARGET_DIR` to that path. Caused by: target `foo` in package `foo` requires the features: `a` @@ -678,7 +680,8 @@ Caused by: "\ [INSTALLING] foo v0.0.1 ([..]) [ERROR] failed to compile `foo v0.0.1 ([..])`, intermediate artifacts can be found at \ - `[..]target` + `[..]target`.\nTo reuse those artifacts with a future compilation, set the environment \ + variable `CARGO_TARGET_DIR` to that path. Caused by: target `foo` in package `foo` requires the features: `a` diff --git a/src/tools/cargo/tests/testsuite/run.rs b/src/tools/cargo/tests/testsuite/run.rs index aa210d6ae..586502288 100644 --- a/src/tools/cargo/tests/testsuite/run.rs +++ b/src/tools/cargo/tests/testsuite/run.rs @@ -777,14 +777,14 @@ fast2", [COMPILING] bar v0.5.0 ([CWD]/bar) [RUNNING] `rustc --crate-name bar bar/src/bar.rs [..]--crate-type lib \ --emit=[..]link[..]\ - -C debuginfo=2 \ + -C debuginfo=2 [..]\ -C metadata=[..] \ --out-dir [CWD]/target/debug/deps \ -L dependency=[CWD]/target/debug/deps` [COMPILING] foo v0.0.1 ([CWD]) [RUNNING] `rustc --crate-name a examples/a.rs [..]--crate-type bin \ --emit=[..]link[..]\ - -C debuginfo=2 \ + -C debuginfo=2 [..]\ -C metadata=[..] \ --out-dir [CWD]/target/debug/examples \ -L dependency=[CWD]/target/debug/deps \ diff --git a/src/tools/cargo/tests/testsuite/rustc.rs b/src/tools/cargo/tests/testsuite/rustc.rs index 65e0740f8..6e3d69cf7 100644 --- a/src/tools/cargo/tests/testsuite/rustc.rs +++ b/src/tools/cargo/tests/testsuite/rustc.rs @@ -18,7 +18,7 @@ fn build_lib_for_foo() { "\ [COMPILING] foo v0.0.1 ([CWD]) [RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \ - --emit=[..]link[..]-C debuginfo=2 \ + --emit=[..]link[..]-C debuginfo=2 [..]\ -C metadata=[..] \ --out-dir [..] \ -L dependency=[CWD]/target/debug/deps` @@ -40,7 +40,7 @@ fn lib() { "\ [COMPILING] foo v0.0.1 ([CWD]) [RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib \ - --emit=[..]link[..]-C debuginfo=2 \ + --emit=[..]link[..]-C debuginfo=2 [..]\ -C debug-assertions=off \ -C metadata=[..] \ --out-dir [..] \ @@ -63,12 +63,12 @@ fn build_main_and_allow_unstable_options() { "\ [COMPILING] {name} v{version} ([CWD]) [RUNNING] `rustc --crate-name {name} src/lib.rs [..]--crate-type lib \ - --emit=[..]link[..]-C debuginfo=2 \ + --emit=[..]link[..]-C debuginfo=2 [..]\ -C metadata=[..] \ --out-dir [..] \ -L dependency=[CWD]/target/debug/deps` [RUNNING] `rustc --crate-name {name} src/main.rs [..]--crate-type bin \ - --emit=[..]link[..]-C debuginfo=2 \ + --emit=[..]link[..]-C debuginfo=2 [..]\ -C debug-assertions \ -C metadata=[..] \ --out-dir [..] \ @@ -109,10 +109,10 @@ fn build_with_args_to_one_of_multiple_binaries() { "\ [COMPILING] foo v0.0.1 ([CWD]) [RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link[..]\ - -C debuginfo=2 -C metadata=[..] \ + -C debuginfo=2 [..]-C metadata=[..] \ --out-dir [..]` [RUNNING] `rustc --crate-name bar src/bin/bar.rs [..]--crate-type bin --emit=[..]link[..]\ - -C debuginfo=2 -C debug-assertions [..]` + -C debuginfo=2 [..]-C debug-assertions [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ) @@ -381,9 +381,9 @@ fn build_with_args_to_one_of_multiple_tests() { "\ [COMPILING] foo v0.0.1 ([CWD]) [RUNNING] `rustc --crate-name foo src/lib.rs [..]--crate-type lib --emit=[..]link[..]\ - -C debuginfo=2 -C metadata=[..] \ + -C debuginfo=2 [..]-C metadata=[..] \ --out-dir [..]` -[RUNNING] `rustc --crate-name bar tests/bar.rs [..]--emit=[..]link[..]-C debuginfo=2 \ +[RUNNING] `rustc --crate-name bar tests/bar.rs [..]--emit=[..]link[..]-C debuginfo=2 [..]\ -C debug-assertions [..]--test[..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", @@ -420,7 +420,7 @@ fn build_foo_with_bar_dependency() { [COMPILING] bar v0.1.0 ([..]) [RUNNING] `[..] -C debuginfo=2 [..]` [COMPILING] foo v0.0.1 ([CWD]) -[RUNNING] `[..] -C debuginfo=2 -C debug-assertions [..]` +[RUNNING] `[..] -C debuginfo=2 [..]-C debug-assertions [..]` [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] ", ) @@ -478,7 +478,7 @@ fn targets_selected_default() { // unit test .with_stderr_does_not_contain( "[RUNNING] `rustc --crate-name foo src/main.rs [..]--emit=[..]link \ - -C debuginfo=2 --test [..]", + -C debuginfo=2 [..]--test [..]", ) .run(); } @@ -495,7 +495,7 @@ fn targets_selected_all() { // unit test .with_stderr_contains( "[RUNNING] `rustc --crate-name foo src/main.rs [..]--emit=[..]link[..]\ - -C debuginfo=2 --test [..]", + -C debuginfo=2 [..]--test [..]", ) .run(); } diff --git a/src/tools/cargo/tests/testsuite/script.rs b/src/tools/cargo/tests/testsuite/script.rs new file mode 100644 index 000000000..fcf58de69 --- /dev/null +++ b/src/tools/cargo/tests/testsuite/script.rs @@ -0,0 +1,1184 @@ +use cargo_test_support::basic_manifest; +use cargo_test_support::registry::Package; + +const ECHO_SCRIPT: &str = r#"#!/usr/bin/env cargo + +fn main() { + let mut args = std::env::args_os(); + let bin = args.next().unwrap().to_str().unwrap().to_owned(); + let args = args.collect::<Vec<_>>(); + println!("bin: {bin}"); + println!("args: {args:?}"); +} + +#[test] +fn test () {} +"#; + +#[cfg(unix)] +fn path() -> Vec<std::path::PathBuf> { + std::env::split_paths(&std::env::var_os("PATH").unwrap_or_default()).collect() +} + +#[cargo_test] +fn basic_rs() { + let p = cargo_test_support::project() + .file("echo.rs", ECHO_SCRIPT) + .build(); + + p.cargo("-Zscript -v echo.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"bin: [..]/debug/echo[EXE] +args: [] +"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] echo v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[..]/debug/echo[EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn basic_path() { + let p = cargo_test_support::project() + .file("echo", ECHO_SCRIPT) + .build(); + + p.cargo("-Zscript -v ./echo") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"bin: [..]/debug/echo[EXE] +args: [] +"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] echo v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[..]/debug/echo[EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn basic_cargo_toml() { + let p = cargo_test_support::project() + .file("src/main.rs", ECHO_SCRIPT) + .build(); + + p.cargo("-Zscript -v Cargo.toml") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"bin: target/debug/foo[EXE] +args: [] +"#, + ) + .with_stderr( + "\ +[COMPILING] foo v0.0.1 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `target/debug/foo[EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn path_required() { + let p = cargo_test_support::project() + .file("echo", ECHO_SCRIPT) + .build(); + + p.cargo("-Zscript -v echo") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stdout("") + .with_stderr( + "\ +error: no such command: `echo` + +<tab>Did you mean `bench`? + +<tab>View all installed commands with `cargo --list` +", + ) + .run(); +} + +#[cargo_test] +#[cfg(unix)] +fn manifest_precedence_over_plugins() { + let p = cargo_test_support::project() + .file("echo.rs", ECHO_SCRIPT) + .executable(std::path::Path::new("path-test").join("cargo-echo.rs"), "") + .build(); + + // With path - fmt is there with known description + let mut path = path(); + path.push(p.root().join("path-test")); + let path = std::env::join_paths(path.iter()).unwrap(); + + p.cargo("-Zscript -v echo.rs") + .env("PATH", &path) + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"bin: [..]/debug/echo[EXE] +args: [] +"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] echo v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[..]/debug/echo[EXE]` +", + ) + .run(); +} + +#[cargo_test] +#[cfg(unix)] +fn warn_when_plugin_masks_manifest_on_stable() { + let p = cargo_test_support::project() + .file("echo.rs", ECHO_SCRIPT) + .executable(std::path::Path::new("path-test").join("cargo-echo.rs"), "") + .build(); + + let mut path = path(); + path.push(p.root().join("path-test")); + let path = std::env::join_paths(path.iter()).unwrap(); + + p.cargo("-v echo.rs") + .env("PATH", &path) + .with_stdout("") + .with_stderr( + "\ +warning: external subcommand `echo.rs` has the appearance of a manfiest-command +This was previously accepted but will be phased out when `-Zscript` is stabilized. +For more information, see issue #12207 <https://github.com/rust-lang/cargo/issues/12207>. +", + ) + .run(); +} + +#[cargo_test] +fn requires_nightly() { + let p = cargo_test_support::project() + .file("echo.rs", ECHO_SCRIPT) + .build(); + + p.cargo("-v echo.rs") + .with_status(101) + .with_stdout("") + .with_stderr( + "\ +error: running `echo.rs` requires `-Zscript` +", + ) + .run(); +} + +#[cargo_test] +fn requires_z_flag() { + let p = cargo_test_support::project() + .file("echo.rs", ECHO_SCRIPT) + .build(); + + p.cargo("-v echo.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stdout("") + .with_stderr( + "\ +error: running `echo.rs` requires `-Zscript` +", + ) + .run(); +} + +#[cargo_test] +fn clean_output_with_edition() { + let script = r#"#!/usr/bin/env cargo + +//! ```cargo +//! [package] +//! edition = "2018" +//! ``` + +fn main() { + println!("Hello world!"); +}"#; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"Hello world! +"#, + ) + .with_stderr( + "\ +[COMPILING] script v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[..]/debug/script[EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn warning_without_edition() { + let script = r#"#!/usr/bin/env cargo + +//! ```cargo +//! [package] +//! ``` + +fn main() { + println!("Hello world!"); +}"#; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"Hello world! +"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] script v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[..]/debug/script[EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn rebuild() { + let script = r#"#!/usr/bin/env cargo-eval + +fn main() { + let msg = option_env!("_MESSAGE").unwrap_or("undefined"); + println!("msg = {}", msg); +}"#; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"msg = undefined +"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] script v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[..]/debug/script[EXE]` +", + ) + .run(); + + // Verify we don't rebuild + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"msg = undefined +"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[..]/debug/script[EXE]` +", + ) + .run(); + + // Verify we do rebuild + p.cargo("-Zscript -v script.rs") + .env("_MESSAGE", "hello") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"msg = hello +"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] script v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[..]/debug/script[EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn use_script_config() { + let script = ECHO_SCRIPT; + let _ = cargo_test_support::project() + .at("script") + .file("script.rs", script) + .build(); + + let p = cargo_test_support::project() + .file( + ".cargo/config", + r#" +[build] +rustc = "non-existent-rustc" +"#, + ) + .file("script.rs", script) + .build(); + + // Verify the config is bad + p.cargo("-Zscript script.rs -NotAnArg") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stderr_contains( + "\ +[ERROR] could not execute process `non-existent-rustc -vV` (never executed) +", + ) + .run(); + + // Verify that the config isn't used + p.cargo("-Zscript ../script/script.rs -NotAnArg") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"bin: [..]/debug/script[EXE] +args: ["-NotAnArg"] +"#, + ) + .run(); +} + +#[cargo_test] +fn default_programmatic_verbosity() { + let script = ECHO_SCRIPT; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("-Zscript script.rs -NotAnArg") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"bin: [..]/debug/script[EXE] +args: ["-NotAnArg"] +"#, + ) + .with_stderr( + "\ +", + ) + .run(); +} + +#[cargo_test] +fn quiet() { + let script = ECHO_SCRIPT; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("-Zscript -q script.rs -NotAnArg") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"bin: [..]/debug/script[EXE] +args: ["-NotAnArg"] +"#, + ) + .with_stderr( + "\ +", + ) + .run(); +} + +#[cargo_test] +fn test_line_numbering_preserved() { + let script = r#"#!/usr/bin/env cargo + +fn main() { + println!("line: {}", line!()); +} +"#; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"line: 4 +"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] script v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[..]/debug/script[EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn test_escaped_hyphen_arg() { + let script = ECHO_SCRIPT; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("-Zscript -v -- script.rs -NotAnArg") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"bin: [..]/debug/script[EXE] +args: ["-NotAnArg"] +"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] script v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[..]/debug/script[EXE] -NotAnArg` +", + ) + .run(); +} + +#[cargo_test] +fn test_unescaped_hyphen_arg() { + let script = ECHO_SCRIPT; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("-Zscript -v script.rs -NotAnArg") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"bin: [..]/debug/script[EXE] +args: ["-NotAnArg"] +"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] script v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[..]/debug/script[EXE] -NotAnArg` +", + ) + .run(); +} + +#[cargo_test] +fn test_same_flags() { + let script = ECHO_SCRIPT; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("-Zscript -v script.rs --help") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"bin: [..]/debug/script[EXE] +args: ["--help"] +"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] script v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[..]/debug/script[EXE] --help` +", + ) + .run(); +} + +#[cargo_test] +fn test_name_has_weird_chars() { + let script = ECHO_SCRIPT; + let p = cargo_test_support::project() + .file("s-h.w§c!.rs", script) + .build(); + + p.cargo("-Zscript -v s-h.w§c!.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"bin: [..]/debug/s-h-w-c-[EXE] +args: [] +"#, + ) + .with_stderr( + r#"[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] s-h-w-c- v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[..]/debug/s-h-w-c-[EXE]` +"#, + ) + .run(); +} + +#[cargo_test] +fn script_like_dir() { + let p = cargo_test_support::project() + .file("script.rs/foo", "something") + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stderr( + "\ +error: manifest path `script.rs` is a directory but expected a file +", + ) + .run(); +} + +#[cargo_test] +fn missing_script_rs() { + let p = cargo_test_support::project().build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stderr( + "\ +[ERROR] manifest path `script.rs` does not exist +", + ) + .run(); +} + +#[cargo_test] +fn test_name_same_as_dependency() { + Package::new("script", "1.0.0").publish(); + let script = r#"#!/usr/bin/env cargo + +//! ```cargo +//! [dependencies] +//! script = "1.0.0" +//! ``` + +fn main() { + println!("Hello world!"); +}"#; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("-Zscript -v script.rs --help") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"Hello world! +"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[UPDATING] `dummy-registry` index +[DOWNLOADING] crates ... +[DOWNLOADED] script v1.0.0 (registry `dummy-registry`) +[COMPILING] script v1.0.0 +[COMPILING] script v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[..]/debug/script[EXE] --help` +", + ) + .run(); +} + +#[cargo_test] +fn test_path_dep() { + let script = r#"#!/usr/bin/env cargo + +//! ```cargo +//! [dependencies] +//! bar.path = "./bar" +//! ``` + +fn main() { + println!("Hello world!"); +}"#; + let p = cargo_test_support::project() + .file("script.rs", script) + .file("src/lib.rs", "pub fn foo() {}") + .file("bar/Cargo.toml", &basic_manifest("bar", "0.0.1")) + .file("bar/src/lib.rs", "pub fn bar() {}") + .build(); + + p.cargo("-Zscript -v script.rs --help") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"Hello world! +"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] bar v0.0.1 ([ROOT]/foo/bar) +[COMPILING] script v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[..]/debug/script[EXE] --help` +", + ) + .run(); +} + +#[cargo_test] +fn test_no_build_rs() { + let script = r#"#!/usr/bin/env cargo + +fn main() { + println!("Hello world!"); +}"#; + let p = cargo_test_support::project() + .file("script.rs", script) + .file("build.rs", "broken") + .build(); + + p.cargo("-Zscript -v script.rs --help") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"Hello world! +"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] script v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[..]/debug/script[EXE] --help` +", + ) + .run(); +} + +#[cargo_test] +fn test_no_autobins() { + let script = r#"#!/usr/bin/env cargo + +fn main() { + println!("Hello world!"); +}"#; + let p = cargo_test_support::project() + .file("script.rs", script) + .file("src/bin/not-script/main.rs", "fn main() {}") + .build(); + + p.cargo("-Zscript -v script.rs --help") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"Hello world! +"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] script v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[..]/debug/script[EXE] --help` +", + ) + .run(); +} + +#[cargo_test] +fn implicit_target_dir() { + let script = ECHO_SCRIPT; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"bin: [ROOT]/home/.cargo/target/[..]/debug/script[EXE] +args: [] +"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] script v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[ROOT]/home/.cargo/target/[..]/debug/script[EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn no_local_lockfile() { + let script = ECHO_SCRIPT; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + let local_lockfile_path = p.root().join("Cargo.lock"); + + assert!(!local_lockfile_path.exists()); + + p.cargo("-Zscript -v script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"bin: [ROOT]/home/.cargo/target/[..]/debug/script[EXE] +args: [] +"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] script v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[ROOT]/home/.cargo/target/[..]/debug/script[EXE]` +", + ) + .run(); + + assert!(!local_lockfile_path.exists()); +} + +#[cargo_test] +fn cmd_check_requires_nightly() { + let script = ECHO_SCRIPT; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("check --manifest-path script.rs") + .with_status(101) + .with_stdout("") + .with_stderr( + "\ +error: embedded manifest `[ROOT]/foo/script.rs` requires `-Zscript` +", + ) + .run(); +} + +#[cargo_test] +fn cmd_check_requires_z_flag() { + let script = ECHO_SCRIPT; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("check --manifest-path script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stdout("") + .with_stderr( + "\ +error: embedded manifest `[ROOT]/foo/script.rs` requires `-Zscript` +", + ) + .run(); +} + +#[cargo_test] +fn cmd_check_with_embedded() { + let script = ECHO_SCRIPT; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("-Zscript check --manifest-path script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + "\ +", + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[CHECKING] script v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); +} + +#[cargo_test] +fn cmd_check_with_missing_script_rs() { + let p = cargo_test_support::project().build(); + + p.cargo("-Zscript check --manifest-path script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stdout( + "\ +", + ) + .with_stderr( + "\ +[ERROR] manifest path `script.rs` does not exist +", + ) + .run(); +} + +#[cargo_test] +fn cmd_check_with_missing_script() { + let p = cargo_test_support::project().build(); + + p.cargo("-Zscript check --manifest-path script") + .masquerade_as_nightly_cargo(&["script"]) + .with_status(101) + .with_stdout( + "\ +", + ) + .with_stderr( + "\ +[ERROR] the manifest-path must be a path to a Cargo.toml file +", + ) + .run(); +} + +#[cargo_test] +fn cmd_build_with_embedded() { + let script = ECHO_SCRIPT; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("-Zscript build --manifest-path script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + "\ +", + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] script v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +", + ) + .run(); +} + +#[cargo_test] +fn cmd_test_with_embedded() { + let script = ECHO_SCRIPT; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("-Zscript test --manifest-path script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + " +running 1 test +test test ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in [..]s + +", + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] script v0.0.0 ([ROOT]/foo) +[FINISHED] test [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] unittests script.rs ([..]) +", + ) + .run(); +} + +#[cargo_test] +fn cmd_clean_with_embedded() { + let script = ECHO_SCRIPT; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + // Ensure there is something to clean + p.cargo("-Zscript script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .run(); + + p.cargo("-Zscript clean --manifest-path script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + "\ +", + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +", + ) + .run(); +} + +#[cargo_test] +fn cmd_generate_lockfile_with_embedded() { + let script = ECHO_SCRIPT; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("-Zscript generate-lockfile --manifest-path script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + "\ +", + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +", + ) + .run(); +} + +#[cargo_test] +fn cmd_metadata_with_embedded() { + let script = ECHO_SCRIPT; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("-Zscript metadata --manifest-path script.rs --format-version=1") + .masquerade_as_nightly_cargo(&["script"]) + .with_json( + r#" + { + "packages": [ + { + "authors": [ + ], + "categories": [], + "default_run": null, + "name": "script", + "version": "0.0.0", + "id": "script[..]", + "keywords": [], + "source": null, + "dependencies": [], + "edition": "[..]", + "license": null, + "license_file": null, + "links": null, + "description": null, + "readme": null, + "repository": null, + "rust_version": null, + "homepage": null, + "documentation": null, + "homepage": null, + "documentation": null, + "targets": [ + { + "kind": [ + "bin" + ], + "crate_types": [ + "bin" + ], + "doc": true, + "doctest": false, + "test": true, + "edition": "[..]", + "name": "script", + "src_path": "[..]/script.rs" + } + ], + "features": {}, + "manifest_path": "[..]script.rs", + "metadata": null, + "publish": [] + } + ], + "workspace_members": ["script 0.0.0 (path+file:[..]foo)"], + "workspace_default_members": ["script 0.0.0 (path+file:[..]foo)"], + "resolve": { + "nodes": [ + { + "dependencies": [], + "deps": [], + "features": [], + "id": "script 0.0.0 (path+file:[..]foo)" + } + ], + "root": "script 0.0.0 (path+file:[..]foo)" + }, + "target_directory": "[ROOT]/home/.cargo/target/[..]", + "version": 1, + "workspace_root": "[..]/foo", + "metadata": null + }"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +", + ) + .run(); +} + +#[cargo_test] +fn cmd_read_manifest_with_embedded() { + let script = ECHO_SCRIPT; + let p = cargo_test_support::project() + .file("script.rs", script) + .build(); + + p.cargo("-Zscript read-manifest --manifest-path script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_json( + r#" +{ + "authors": [ + ], + "categories": [], + "default_run": null, + "name":"script", + "readme": null, + "homepage": null, + "documentation": null, + "repository": null, + "rust_version": null, + "version":"0.0.0", + "id":"script[..]0.0.0[..](path+file://[..]/foo)", + "keywords": [], + "license": null, + "license_file": null, + "links": null, + "description": null, + "edition": "[..]", + "source":null, + "dependencies":[], + "targets":[{ + "kind":["bin"], + "crate_types":["bin"], + "doc": true, + "doctest": false, + "test": true, + "edition": "[..]", + "name":"script", + "src_path":"[..]/script.rs" + }], + "features":{}, + "manifest_path":"[..]script.rs", + "metadata": null, + "publish": [] +}"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +", + ) + .run(); +} + +#[cargo_test] +fn cmd_run_with_embedded() { + let p = cargo_test_support::project() + .file("script.rs", ECHO_SCRIPT) + .build(); + + p.cargo("-Zscript run --manifest-path script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + r#"bin: [..]/debug/script[EXE] +args: [] +"#, + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +[COMPILING] script v0.0.0 ([ROOT]/foo) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[RUNNING] `[..]/debug/script[EXE]` +", + ) + .run(); +} + +#[cargo_test] +fn cmd_tree_with_embedded() { + let p = cargo_test_support::project() + .file("script.rs", ECHO_SCRIPT) + .build(); + + p.cargo("-Zscript tree --manifest-path script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + "\ +script v0.0.0 ([ROOT]/foo) +", + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +", + ) + .run(); +} + +#[cargo_test] +fn cmd_update_with_embedded() { + let p = cargo_test_support::project() + .file("script.rs", ECHO_SCRIPT) + .build(); + + p.cargo("-Zscript update --manifest-path script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_stdout( + "\ +", + ) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +", + ) + .run(); +} + +#[cargo_test] +fn cmd_verify_project_with_embedded() { + let p = cargo_test_support::project() + .file("script.rs", ECHO_SCRIPT) + .build(); + + p.cargo("-Zscript verify-project --manifest-path script.rs") + .masquerade_as_nightly_cargo(&["script"]) + .with_json(r#"{"success":"true"}"#) + .with_stderr( + "\ +[WARNING] `package.edition` is unspecifiead, defaulting to `2021` +", + ) + .run(); +} diff --git a/src/tools/cargo/tests/testsuite/test.rs b/src/tools/cargo/tests/testsuite/test.rs index add0a991f..6a062cfb6 100644 --- a/src/tools/cargo/tests/testsuite/test.rs +++ b/src/tools/cargo/tests/testsuite/test.rs @@ -387,8 +387,9 @@ test test_hello ... FAILED failures: ---- test_hello stdout ---- -[..]thread '[..]' panicked at 'assertion failed:[..]", +[..]thread '[..]' panicked at [..]", ) + .with_stdout_contains("[..]assertion failed[..]") .with_stdout_contains("[..]`(left == right)`[..]") .with_stdout_contains("[..]left: `\"hello\"`,[..]") .with_stdout_contains("[..]right: `\"nope\"`[..]") @@ -437,10 +438,10 @@ test test_hello ... FAILED failures: ---- test_hello stdout ---- -[..]thread '[..]' panicked at 'assertion failed: false', \ - tests/footest.rs:1[..] +[..]thread '[..]' panicked at [..]tests/footest.rs:1:[..] ", ) + .with_stdout_contains("[..]assertion failed[..]") .with_stdout_contains( "\ failures: @@ -473,10 +474,10 @@ test test_hello ... FAILED failures: ---- test_hello stdout ---- -[..]thread '[..]' panicked at 'assertion failed: false', \ - src/lib.rs:1[..] +[..]thread '[..]' panicked at [..]src/lib.rs:1:[..] ", ) + .with_stdout_contains("[..]assertion failed[..]") .with_stdout_contains( "\ failures: diff --git a/src/tools/cargo/triagebot.toml b/src/tools/cargo/triagebot.toml index 00a98e008..6d07f438f 100644 --- a/src/tools/cargo/triagebot.toml +++ b/src/tools/cargo/triagebot.toml @@ -137,7 +137,7 @@ trigger_files = [ ] [autolabel."A-interacts-with-crates.io"] -trigger_files = ["crates/crates-io/", "src/cargo/ops/registry.rs"] +trigger_files = ["crates/crates-io/", "src/cargo/ops/registry/"] [autolabel."A-layout"] trigger_files = [ @@ -180,7 +180,7 @@ trigger_files = ["src/cargo/core/compiler/fingerprint/"] trigger_files = ["src/cargo/sources/registry/", "src/cargo/core/registry.rs"] [autolabel."A-registry-authentication"] -trigger_files = ["src/cargo/util/auth.rs", "credential/"] +trigger_files = ["src/cargo/util/auth/", "credential/"] [autolabel."A-semver"] trigger_files = [ @@ -266,10 +266,10 @@ trigger_files = ["src/bin/cargo/commands/install.rs", "src/cargo/ops/cargo_insta trigger_files = ["src/bin/cargo/commands/locate_project.rs"] [autolabel."Command-login"] -trigger_files = ["src/bin/cargo/commands/login.rs"] +trigger_files = ["src/bin/cargo/commands/login.rs", "src/cargo/ops/registry/login.rs"] [autolabel."Command-logout"] -trigger_files = ["src/bin/cargo/commands/logout.rs"] +trigger_files = ["src/bin/cargo/commands/logout.rs", "src/cargo/ops/registry/logout.rs"] [autolabel."Command-metadata"] trigger_files = ["src/bin/cargo/commands/metadata.rs", "src/cargo/ops/cargo_output_metadata.rs"] @@ -278,7 +278,7 @@ trigger_files = ["src/bin/cargo/commands/metadata.rs", "src/cargo/ops/cargo_outp trigger_files = ["src/bin/cargo/commands/new.rs", "src/cargo/ops/cargo_new.rs"] [autolabel."Command-owner"] -trigger_files = ["src/bin/cargo/commands/owner.rs"] +trigger_files = ["src/bin/cargo/commands/owner.rs", "src/cargo/ops/registry/owner.rs"] [autolabel."Command-package"] trigger_files = ["src/bin/cargo/commands/package.rs", "src/cargo/ops/cargo_package.rs"] @@ -287,7 +287,7 @@ trigger_files = ["src/bin/cargo/commands/package.rs", "src/cargo/ops/cargo_packa trigger_files = ["src/bin/cargo/commands/pkgid.rs", "src/cargo/ops/cargo_pkgid.rs"] [autolabel."Command-publish"] -trigger_files = ["src/bin/cargo/commands/publish.rs"] +trigger_files = ["src/bin/cargo/commands/publish.rs", "src/cargo/ops/registry/publish.rs"] [autolabel."Command-read-manifest"] trigger_files = ["src/bin/cargo/commands/read_manifest.rs", "src/cargo/ops/cargo_read_manifest.rs"] @@ -308,7 +308,7 @@ trigger_files = ["src/bin/cargo/commands/rustc.rs"] trigger_files = ["src/bin/cargo/commands/rustdoc.rs"] [autolabel."Command-search"] -trigger_files = ["src/bin/cargo/commands/search.rs"] +trigger_files = ["src/bin/cargo/commands/search.rs", "src/cargo/ops/registry/search.rs"] [autolabel."Command-test"] trigger_files = ["src/bin/cargo/commands/test.rs", "src/cargo/ops/cargo_test.rs"] @@ -332,4 +332,4 @@ trigger_files = ["src/bin/cargo/commands/verify_project.rs"] trigger_files = ["src/bin/cargo/commands/version.rs"] [autolabel."Command-yank"] -trigger_files = ["src/bin/cargo/commands/yank.rs"] +trigger_files = ["src/bin/cargo/commands/yank.rs", "src/cargo/ops/registry/yank.rs"] |