diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 21acb72ef86d..cd08bebddd53 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -80,6 +80,13 @@ jobs: # Deny warnings on CI to avoid warnings getting into the codebase. - run: cargo test --features 'deny-warnings' + # The testsuite generates a huge amount of data, and fetch-smoke-test was + # running out of disk space. + - name: Clear test output + run: | + df -h + rm -rf target/tmp + df -h - name: Check operability of rustc invocation with argfile env: __CARGO_TEST_FORCE_ARGFILE: 1 @@ -111,7 +118,7 @@ jobs: cargo check --manifest-path benches/capture/Cargo.toml # The testsuite generates a huge amount of data, and fetch-smoke-test was # running out of disk space. - - name: Clear test output + - name: Clear benchmark output run: | df -h rm -rf target/tmp diff --git a/CHANGELOG.md b/CHANGELOG.md index 311dcfe44e53..a3e3ebf1c98e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,137 @@ # Changelog +## Cargo 1.67 (2023-01-26) +[7e484fc1...HEAD](https://github.com/rust-lang/cargo/compare/7e484fc1...HEAD) + +### Added + +- `cargo remove` now cleans up the root workspace manifest after a + successful removal of an inherited dependency from a workspace member. + [#11242](https://github.com/rust-lang/cargo/pull/11242) +- `cargo package` and `cargo publish` now report total and compressed crate size + after packaging. + [#11270](https://github.com/rust-lang/cargo/pull/11270) +- Suggests `cargo fix` when some compilation warnings/errors can be auto-fixed. + [#10989](https://github.com/rust-lang/cargo/pull/10989) + +### Changed + +- ❗ Cargo now reuses the value of `$CARGO` if it's already set in the environment, + and forwards the value when executing external subcommands and build scripts. + [#11285](https://github.com/rust-lang/cargo/pull/11285) +- Updated the internal HTTP library libcurl with various fixes and updates. + [#11307](https://github.com/rust-lang/cargo/pull/11307) + [#11326](https://github.com/rust-lang/cargo/pull/11326) + +### Fixed + +- Fixed `cargo clean` for removing fingerprints and build script + artifacts of only the requested package + [#10621](https://github.com/rust-lang/cargo/pull/10621) + +### Nightly only + ## Cargo 1.66 (2022-12-15) -[08250398...HEAD](https://github.com/rust-lang/cargo/compare/08250398...HEAD) +[08250398...rust-1.66.0](https://github.com/rust-lang/cargo/compare/08250398...rust-1.66.0) ### Added +- 🎉 Added `cargo remove` command for removing dependencies from `Cargo.toml`. + [docs](https://doc.rust-lang.org/nightly/cargo/commands/cargo-remove.html) + [#11059](https://github.com/rust-lang/cargo/pull/11059) + [#11099](https://github.com/rust-lang/cargo/pull/11099) + [#11193](https://github.com/rust-lang/cargo/pull/11193) + [#11204](https://github.com/rust-lang/cargo/pull/11204) + [#11227](https://github.com/rust-lang/cargo/pull/11227) +- Added support for git dependencies having git submodules with relative paths. + [#11106](https://github.com/rust-lang/cargo/pull/11106) +- Cargo now sends requests with a `Accept-Encoding` header to registries. + [#11292](https://github.com/rust-lang/cargo/pull/11292) +- Cargo now forwards non-UTF8 arguments to external subcommands. + [#11118](https://github.com/rust-lang/cargo/pull/11118) + ### Changed +- ❗ Disambiguate source replacements from various angles. + [RFC-3289](https://github.com/rust-lang/rfcs/blob/master/text/3289-source_replacement_ambiguity.md) + [#10907](https://github.com/rust-lang/cargo/pull/10907) + - When the crates-io source is replaced, the user is required to specify which registry to use with `--registry ` when performing an API operation. + - Publishing to source-replaced crates.io is no longer permitted using the crates.io token (`registry.token`). + - In source replacement, the `replace-with` key can reference the name of an alternative registry in the `[registries]` table. +- ❗ `cargo publish` now blocks until it sees the published package in the index. + [#11062](https://github.com/rust-lang/cargo/pull/11062) + [#11210](https://github.com/rust-lang/cargo/pull/11210) + [#11216](https://github.com/rust-lang/cargo/pull/11216) + [#11255](https://github.com/rust-lang/cargo/pull/11255) +- Cargo now uses the clap v4 library for command-line argument parsing. + [#11116](https://github.com/rust-lang/cargo/pull/11116) + [#11119](https://github.com/rust-lang/cargo/pull/11119) + [#11159](https://github.com/rust-lang/cargo/pull/11159) + [#11190](https://github.com/rust-lang/cargo/pull/11190) + [#11239](https://github.com/rust-lang/cargo/pull/11239) + [#11280](https://github.com/rust-lang/cargo/pull/11280) +- Cargo now only warns on a user-defined alias shadowing an external command. + [#11170](https://github.com/rust-lang/cargo/pull/11170) +- Several documentation improvements. + [#10770](https://github.com/rust-lang/cargo/pull/10770) + [#10938](https://github.com/rust-lang/cargo/pull/10938) + [#11082](https://github.com/rust-lang/cargo/pull/11082) + [#11093](https://github.com/rust-lang/cargo/pull/11093) + [#11157](https://github.com/rust-lang/cargo/pull/11157) + [#11185](https://github.com/rust-lang/cargo/pull/11185) + [#11207](https://github.com/rust-lang/cargo/pull/11207) + [#11219](https://github.com/rust-lang/cargo/pull/11219) + [#11240](https://github.com/rust-lang/cargo/pull/11240) + [#11241](https://github.com/rust-lang/cargo/pull/11241) + [#11282](https://github.com/rust-lang/cargo/pull/11282) + ### Fixed +- ❗ Config file loaded via `cargo --config ` now takes priority over + environment variables. This is a documented behaviour but the old + implementation accidentally got it wrong. + [#11077](https://github.com/rust-lang/cargo/pull/11077) +- ❗ Cargo collects rustflags in `target.cfg(…).rustflags` more correctly + and warns if that's not enough for convergence. + [#11114](https://github.com/rust-lang/cargo/pull/11114) +- Final artifacts not removed by linker should be removed before a compilation gets started. + [#11122](https://github.com/rust-lang/cargo/pull/11122) +- `cargo add` now reports unknown features in a more discoverable manner. + [#11098](https://github.com/rust-lang/cargo/pull/11098) +- Cargo now reports command aliasing failure with more error contexts. + [#11087](https://github.com/rust-lang/cargo/pull/11087) +- A better error message when `cargo login` prompt receives empty input. + [#11145](https://github.com/rust-lang/cargo/pull/11145) +- A better error message for fields with wrong types + where workspace inheritance is supported. + [#11113](https://github.com/rust-lang/cargo/pull/11113) +- A better error message when mixing feature syntax `dep:` with `/`. + [#11172](https://github.com/rust-lang/cargo/pull/11172) +- A better error message when publishing but `package.publish` is `false` + in the manifest. + [#11280](https://github.com/rust-lang/cargo/pull/11280) + ### Nightly only +- Added new config option `publish.timeout` behind `-Zpublish-timeout`. + [docs](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#publish-timeout) + [#11230](https://github.com/rust-lang/cargo/pull/11230) +- Added retry support to sparse registries. + [#11069](https://github.com/rust-lang/cargo/pull/11069) +- Fixed sparse registry lockfile urls containing `registry+sparse+`. + [#11177](https://github.com/rust-lang/cargo/pull/11177) +- Add new config option `registries.crates-io.protocol` + for controlling crates.io protocol. + [#11215](https://github.com/rust-lang/cargo/pull/11215) +- Removed `sparse+` prefix for index.crates.io. + [#11247](https://github.com/rust-lang/cargo/pull/11247) +- Fixed publishing with a dependency on a sparse registry. + [#11268](https://github.com/rust-lang/cargo/pull/11268) +- Fixed confusing error messages when using `-Zsparse-registry`. + [#11283](https://github.com/rust-lang/cargo/pull/11283) +- Fixed 410 gone response handling for sparse registries. + [#11286](https://github.com/rust-lang/cargo/pull/11286) + ## Cargo 1.65 (2022-11-03) [4fd148c4...rust-1.65.0](https://github.com/rust-lang/cargo/compare/4fd148c4...rust-1.65.0) @@ -75,6 +196,8 @@ - Scanning the package directory now ignores errors from broken but excluded symlink files. [#11008](https://github.com/rust-lang/cargo/pull/11008) +- Fixed deadlock when build scripts are waiting for input on stdin. + [#11257](https://github.com/rust-lang/cargo/pull/11257) ### Nightly diff --git a/Cargo.toml b/Cargo.toml index 0b48283156d1..e38f19dee621 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cargo" -version = "0.67.0" +version = "0.68.0" edition = "2021" license = "MIT OR Apache-2.0" homepage = "https://crates.io" @@ -21,11 +21,11 @@ bytesize = "1.0" cargo-platform = { path = "crates/cargo-platform", version = "0.1.2" } cargo-util = { path = "crates/cargo-util", version = "0.2.1" } crates-io = { path = "crates/crates-io", version = "0.34.0" } -curl = { version = "0.4.43", features = ["http2"] } -curl-sys = "0.4.55" +curl = { version = "0.4.44", features = ["http2"] } +curl-sys = "0.4.59" env_logger = "0.9.0" pretty_env_logger = { version = "0.4", optional = true } -anyhow = "1.0" +anyhow = "1.0.47" filetime = "0.2.9" flate2 = { version = "1.0.3", default-features = false, features = ["zlib"] } git2 = "0.15.0" @@ -101,7 +101,7 @@ features = [ cargo-test-macro = { path = "crates/cargo-test-macro" } cargo-test-support = { path = "crates/cargo-test-support" } same-file = "1.0.6" -snapbox = { version = "0.3.0", features = ["diff", "path"] } +snapbox = { version = "0.4.0", features = ["diff", "path"] } [build-dependencies] flate2 = { version = "1.0.3", default-features = false, features = ["zlib"] } diff --git a/crates/cargo-test-support/Cargo.toml b/crates/cargo-test-support/Cargo.toml index 658bdf6dfa82..20a12986399e 100644 --- a/crates/cargo-test-support/Cargo.toml +++ b/crates/cargo-test-support/Cargo.toml @@ -12,14 +12,13 @@ anyhow = "1.0.34" cargo-test-macro = { path = "../cargo-test-macro" } cargo-util = { path = "../cargo-util" } crates-io = { path = "../crates-io" } -snapbox = { version = "0.3.0", features = ["diff", "path"] } +snapbox = { version = "0.4.0", features = ["diff", "path"] } filetime = "0.2" flate2 = { version = "1.0", default-features = false, features = ["zlib"] } git2 = "0.15.0" glob = "0.3" itertools = "0.10.0" lazy_static = "1.0" -remove_dir_all = "0.5" serde_json = "1.0" tar = { version = "0.4.38", default-features = false } termcolor = "1.1.2" diff --git a/crates/cargo-test-support/src/compare.rs b/crates/cargo-test-support/src/compare.rs index 9d33904d4304..a9feb5f6feac 100644 --- a/crates/cargo-test-support/src/compare.rs +++ b/crates/cargo-test-support/src/compare.rs @@ -174,6 +174,7 @@ fn substitute_macros(input: &str) -> String { ("[REMOVING]", " Removing"), ("[DOCTEST]", " Doc-tests"), ("[PACKAGING]", " Packaging"), + ("[PACKAGED]", " Packaged"), ("[DOWNLOADING]", " Downloading"), ("[DOWNLOADED]", " Downloaded"), ("[UPLOADING]", " Uploading"), diff --git a/crates/cargo-test-support/src/lib.rs b/crates/cargo-test-support/src/lib.rs index 37976e0f50ec..4ce0162bfbd2 100644 --- a/crates/cargo-test-support/src/lib.rs +++ b/crates/cargo-test-support/src/lib.rs @@ -410,8 +410,10 @@ impl Project { /// Example: /// p.cargo("build --bin foo").run(); pub fn cargo(&self, cmd: &str) -> Execs { - let mut execs = self.process(&cargo_exe()); + let cargo = cargo_exe(); + let mut execs = self.process(&cargo); if let Some(ref mut p) = execs.process_builder { + p.env("CARGO", cargo); p.arg_line(cmd); } execs @@ -1328,7 +1330,9 @@ impl ArgLine for snapbox::cmd::Command { } pub fn cargo_process(s: &str) -> Execs { - let mut p = process(&cargo_exe()); + let cargo = cargo_exe(); + let mut p = process(&cargo); + p.env("CARGO", cargo); p.arg_line(s); execs().with_process_builder(p) } diff --git a/crates/cargo-test-support/src/paths.rs b/crates/cargo-test-support/src/paths.rs index d3a7ca8c6da0..6925597a1dae 100644 --- a/crates/cargo-test-support/src/paths.rs +++ b/crates/cargo-test-support/src/paths.rs @@ -141,7 +141,7 @@ impl CargoPathExt for Path { // actually performing the removal, but we don't care all that much // for our tests. if meta.is_dir() { - if let Err(e) = remove_dir_all::remove_dir_all(self) { + if let Err(e) = fs::remove_dir_all(self) { panic!("failed to remove {:?}: {:?}", self, e) } } else if let Err(e) = fs::remove_file(self) { diff --git a/crates/cargo-test-support/src/registry.rs b/crates/cargo-test-support/src/registry.rs index 17a90e8f6be2..1f30ea011346 100644 --- a/crates/cargo-test-support/src/registry.rs +++ b/crates/cargo-test-support/src/registry.rs @@ -469,6 +469,7 @@ impl Drop for HttpServerHandle { } /// Request to the test http server +#[derive(Clone)] pub struct Request { pub url: Url, pub method: String, diff --git a/crates/cargo-util/Cargo.toml b/crates/cargo-util/Cargo.toml index 2ab4a60c26ec..2d63f6488433 100644 --- a/crates/cargo-util/Cargo.toml +++ b/crates/cargo-util/Cargo.toml @@ -24,5 +24,5 @@ walkdir = "2.3.1" core-foundation = { version = "0.9.0", features = ["mac_os_10_7_support"] } [target.'cfg(windows)'.dependencies] -miow = "0.3.6" +miow = "0.4.0" winapi = { version = "0.3.9", features = ["consoleapi", "minwindef"] } diff --git a/crates/cargo-util/src/paths.rs b/crates/cargo-util/src/paths.rs index 9795415a4ead..dc0141312e5f 100644 --- a/crates/cargo-util/src/paths.rs +++ b/crates/cargo-util/src/paths.rs @@ -638,7 +638,7 @@ pub fn create_dir_all_excluded_from_backups_atomic(p: impl AsRef) -> Resul // it from backups, then rename it to the desired name. If we created the // directory directly where it should be and then excluded it from backups // we would risk a situation where cargo is interrupted right after the directory - // creation but before the exclusion the the directory would remain non-excluded from + // creation but before the exclusion the directory would remain non-excluded from // backups because we only perform exclusion right after we created the directory // ourselves. // @@ -653,7 +653,7 @@ pub fn create_dir_all_excluded_from_backups_atomic(p: impl AsRef) -> Resul // the directory being created concurrently by another thread or process as success, // hence the check below to follow the existing behavior. If we get an error at // rename() and suddently the directory (which didn't exist a moment earlier) exists - // we can infer from it it's another cargo process doing work. + // we can infer from it's another cargo process doing work. if let Err(e) = fs::rename(tempdir.path(), path) { if !path.exists() { return Err(anyhow::Error::from(e)); diff --git a/crates/cargo-util/src/process_builder.rs b/crates/cargo-util/src/process_builder.rs index 424c9089d1d7..be8a5fca3b38 100644 --- a/crates/cargo-util/src/process_builder.rs +++ b/crates/cargo-util/src/process_builder.rs @@ -203,8 +203,8 @@ impl ProcessBuilder { /// /// Ref: /// - /// - https://doc.rust-lang.org/rustdoc/command-line-arguments.html#path-load-command-line-flags-from-a-path - /// - https://doc.rust-lang.org/rustc/command-line-arguments.html#path-load-command-line-flags-from-a-path> + /// - + /// - pub fn retry_with_argfile(&mut self, enabled: bool) -> &mut Self { self.retry_with_argfile = enabled; self diff --git a/src/bin/cargo/cli.rs b/src/bin/cargo/cli.rs index c6b57910b031..3053854d4c7b 100644 --- a/src/bin/cargo/cli.rs +++ b/src/bin/cargo/cli.rs @@ -149,7 +149,7 @@ Run with 'cargo -Z [FLAG] [COMMAND]'", } }; config_configure(config, &expanded_args, subcommand_args, global_args)?; - super::init_git_transports(config); + super::init_git(config); execute_subcommand(config, cmd, subcommand_args) } diff --git a/src/bin/cargo/commands/remove.rs b/src/bin/cargo/commands/remove.rs index 62a3e223ddd9..c80889280d83 100644 --- a/src/bin/cargo/commands/remove.rs +++ b/src/bin/cargo/commands/remove.rs @@ -1,9 +1,12 @@ use cargo::core::dependency::DepKind; +use cargo::core::Workspace; use cargo::ops::cargo_remove::remove; use cargo::ops::cargo_remove::RemoveOptions; use cargo::ops::resolve_ws; use cargo::util::command_prelude::*; use cargo::util::toml_mut::manifest::DepTable; +use cargo::util::toml_mut::manifest::LocalManifest; +use cargo::CargoResult; pub fn cli() -> clap::Command { clap::Command::new("remove") @@ -85,6 +88,9 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { remove(&options)?; if !dry_run { + // Clean up workspace dependencies + gc_workspace(&workspace, &options.dependencies)?; + // Reload the workspace since we've changed dependencies let ws = args.workspace(config)?; resolve_ws(&ws)?; @@ -114,3 +120,50 @@ fn parse_section(args: &ArgMatches) -> DepTable { table } + +/// Clean up workspace dependencies which no longer have a reference to them. +fn gc_workspace(workspace: &Workspace<'_>, dependencies: &[String]) -> CargoResult<()> { + let mut manifest: toml_edit::Document = + cargo_util::paths::read(workspace.root_manifest())?.parse()?; + + let members = workspace + .members() + .map(|p| LocalManifest::try_new(p.manifest_path())) + .collect::>>()?; + + for dep in dependencies { + if !dep_in_workspace(dep, &members) { + remove_workspace_dep(dep, &mut manifest); + } + } + + cargo_util::paths::write(workspace.root_manifest(), manifest.to_string().as_bytes())?; + + Ok(()) +} + +/// Get whether or not a dependency is depended upon in a workspace. +fn dep_in_workspace(dep: &str, members: &[LocalManifest]) -> bool { + members.iter().any(|manifest| { + manifest.get_sections().iter().any(|(_, table)| { + table + .as_table_like() + .unwrap() + .get(dep) + .and_then(|t| t.get("workspace")) + .and_then(|v| v.as_bool()) + .unwrap_or(false) + }) + }) +} + +/// Remove a dependency from a workspace manifest. +fn remove_workspace_dep(dep: &str, ws_manifest: &mut toml_edit::Document) { + if let Some(toml_edit::Item::Table(table)) = ws_manifest + .get_mut("workspace") + .and_then(|t| t.get_mut("dependencies")) + { + table.set_implicit(true); + table.remove(dep); + } +} diff --git a/src/bin/cargo/main.rs b/src/bin/cargo/main.rs index 70adebb9431c..aaac0d126101 100644 --- a/src/bin/cargo/main.rs +++ b/src/bin/cargo/main.rs @@ -246,6 +246,38 @@ fn search_directories(config: &Config) -> Vec { path_dirs } +/// Initialize libgit2. +fn init_git(config: &Config) { + // Disabling the owner validation in git can, in theory, lead to code execution + // vulnerabilities. However, libgit2 does not launch executables, which is the foundation of + // the original security issue. Meanwhile, issues with refusing to load git repos in + // `CARGO_HOME` for example will likely be very frustrating for users. So, we disable the + // validation. + // + // For further discussion of Cargo's current interactions with git, see + // + // https://github.com/rust-lang/rfcs/pull/3279 + // + // and in particular the subsection on "Git support". + // + // Note that we only disable this when Cargo is run as a binary. If Cargo is used as a library, + // this code won't be invoked. Instead, developers will need to explicitly disable the + // validation in their code. This is inconvenient, but won't accidentally open consuming + // applications up to security issues if they use git2 to open repositories elsewhere in their + // code. + unsafe { + git2::opts::set_verify_owner_validation(false) + .expect("set_verify_owner_validation should never fail"); + } + + init_git_transports(config); +} + +/// Configure libgit2 to use libcurl if necessary. +/// +/// If the user has a non-default network configuration, then libgit2 will be +/// 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) { // Only use a custom transport if any HTTP options are specified, // such as proxies or custom certificate authorities. The custom @@ -274,27 +306,4 @@ fn init_git_transports(config: &Config) { unsafe { git2_curl::register(handle); } - - // Disabling the owner validation in git can, in theory, lead to code execution - // vulnerabilities. However, libgit2 does not launch executables, which is the foundation of - // the original security issue. Meanwhile, issues with refusing to load git repos in - // `CARGO_HOME` for example will likely be very frustrating for users. So, we disable the - // validation. - // - // For further discussion of Cargo's current interactions with git, see - // - // https://github.com/rust-lang/rfcs/pull/3279 - // - // and in particular the subsection on "Git support". - // - // Note that we only disable this when Cargo is run as a binary. If Cargo is used as a library, - // this code won't be invoked. Instead, developers will need to explicitly disable the - // validation in their code. This is inconvenient, but won't accidentally open consuming - // applications up to security issues if they use git2 to open repositories elsewhere in their - // code. - unsafe { - if git2::opts::set_verify_owner_validation(false).is_err() { - return; - } - } } diff --git a/src/cargo/core/compiler/fingerprint.rs b/src/cargo/core/compiler/fingerprint.rs index 52adfadc7cb3..e147ac03d86d 100644 --- a/src/cargo/core/compiler/fingerprint.rs +++ b/src/cargo/core/compiler/fingerprint.rs @@ -1254,20 +1254,24 @@ fn calculate(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult, unit: &Unit) -> CargoResult { - // Recursively calculate the fingerprint for all of our dependencies. - // - // Skip fingerprints of binaries because they don't actually induce a - // recompile, they're just dependencies in the sense that they need to be - // built. - // - // Create Vec since mutable cx is needed in closure. - let deps = Vec::from(cx.unit_deps(unit)); - let mut deps = deps - .into_iter() - .filter(|dep| !dep.unit.target.is_bin()) - .map(|dep| DepFingerprint::new(cx, unit, &dep)) - .collect::>>()?; - deps.sort_by(|a, b| a.pkg_id.cmp(&b.pkg_id)); + let deps = { + // Recursively calculate the fingerprint for all of our dependencies. + // + // Skip fingerprints of binaries because they don't actually induce a + // recompile, they're just dependencies in the sense that they need to be + // built. The only exception here are artifact dependencies, + // which is an actual dependency that needs a recompile. + // + // Create Vec since mutable cx is needed in closure. + let deps = Vec::from(cx.unit_deps(unit)); + let mut deps = deps + .into_iter() + .filter(|dep| !dep.unit.target.is_bin() || dep.unit.artifact.is_true()) + .map(|dep| DepFingerprint::new(cx, unit, &dep)) + .collect::>>()?; + deps.sort_by(|a, b| a.pkg_id.cmp(&b.pkg_id)); + deps + }; // Afterwards calculate our own fingerprint information. let target_root = target_root(cx); @@ -1493,7 +1497,7 @@ fn build_script_local_fingerprints( // figure out a better scheme where a package fingerprint // may be a string (like for a registry) or a list of files // (like for a path dependency). Those list of files would - // be stored here rather than the the mtime of them. + // be stored here rather than the mtime of them. Some(f) => { let s = f()?; debug!( diff --git a/src/cargo/core/compiler/job_queue.rs b/src/cargo/core/compiler/job_queue.rs index b568a5ee2fca..bd3a859f04de 100644 --- a/src/cargo/core/compiler/job_queue.rs +++ b/src/cargo/core/compiler/job_queue.rs @@ -129,12 +129,8 @@ struct DrainState<'cfg> { messages: Arc>, /// Diagnostic deduplication support. diag_dedupe: DiagDedupe<'cfg>, - /// Count of warnings, used to print a summary after the job succeeds. - /// - /// First value is the total number of warnings, and the second value is - /// the number that were suppressed because they were duplicates of a - /// previous warning. - warning_count: HashMap, + /// Count of warnings, used to print a summary after the job succeeds + warning_count: HashMap, active: HashMap, compiled: HashSet, documented: HashSet, @@ -170,6 +166,50 @@ struct DrainState<'cfg> { per_package_future_incompat_reports: Vec, } +/// Count of warnings, used to print a summary after the job succeeds +#[derive(Default)] +pub struct WarningCount { + /// total number of warnings + pub total: usize, + /// number of warnings that were suppressed because they + /// were duplicates of a previous warning + pub duplicates: usize, + /// number of fixable warnings set to `NotAllowed` + /// if any errors have been seen ofr the current + /// target + pub fixable: FixableWarnings, +} + +impl WarningCount { + /// If an error is seen this should be called + /// to set `fixable` to `NotAllowed` + fn disallow_fixable(&mut self) { + self.fixable = FixableWarnings::NotAllowed; + } + + /// Checks fixable if warnings are allowed + /// fixable warnings are allowed if no + /// errors have been seen for the current + /// target. If an error was seen `fixable` + /// will be `NotAllowed`. + fn fixable_allowed(&self) -> bool { + match &self.fixable { + FixableWarnings::NotAllowed => false, + _ => true, + } + } +} + +/// Used to keep track of how many fixable warnings there are +/// and if fixable warnings are allowed +#[derive(Default)] +pub enum FixableWarnings { + NotAllowed, + #[default] + Zero, + Positive(usize), +} + pub struct ErrorsDuringDrain { pub count: usize, } @@ -311,10 +351,12 @@ enum Message { id: JobId, level: String, diag: String, + fixable: bool, }, WarningCount { id: JobId, emitted: bool, + fixable: bool, }, FixDiagnostic(diagnostic_server::Message), Token(io::Result), @@ -363,13 +405,14 @@ impl<'a, 'cfg> JobState<'a, 'cfg> { Ok(()) } - pub fn emit_diag(&self, level: String, diag: String) -> CargoResult<()> { + pub fn emit_diag(&self, level: String, diag: String, fixable: bool) -> CargoResult<()> { if let Some(dedupe) = self.output { let emitted = dedupe.emit_diag(&diag)?; if level == "warning" { self.messages.push(Message::WarningCount { id: self.id, emitted, + fixable, }); } } else { @@ -377,6 +420,7 @@ impl<'a, 'cfg> JobState<'a, 'cfg> { id: self.id, level, diag, + fixable, }); } Ok(()) @@ -679,14 +723,28 @@ impl<'cfg> DrainState<'cfg> { shell.print_ansi_stderr(err.as_bytes())?; shell.err().write_all(b"\n")?; } - Message::Diagnostic { id, level, diag } => { + Message::Diagnostic { + id, + level, + diag, + fixable, + } => { let emitted = self.diag_dedupe.emit_diag(&diag)?; if level == "warning" { - self.bump_warning_count(id, emitted); + self.bump_warning_count(id, emitted, fixable); + } + if level == "error" { + let cnts = self.warning_count.entry(id).or_default(); + // If there is an error, the `cargo fix` message should not show + cnts.disallow_fixable(); } } - Message::WarningCount { id, emitted } => { - self.bump_warning_count(id, emitted); + Message::WarningCount { + id, + emitted, + fixable, + } => { + self.bump_warning_count(id, emitted, fixable); } Message::FixDiagnostic(msg) => { self.print.print(&msg)?; @@ -1127,19 +1185,34 @@ impl<'cfg> DrainState<'cfg> { Ok(()) } - fn bump_warning_count(&mut self, id: JobId, emitted: bool) { + fn bump_warning_count(&mut self, id: JobId, emitted: bool, fixable: bool) { let cnts = self.warning_count.entry(id).or_default(); - cnts.0 += 1; + cnts.total += 1; if !emitted { - cnts.1 += 1; + cnts.duplicates += 1; + // Don't add to fixable if it's already been emitted + } else if fixable { + // Do not add anything to the fixable warning count if + // is `NotAllowed` since that indicates there was an + // error while building this `Unit` + if cnts.fixable_allowed() { + cnts.fixable = match cnts.fixable { + FixableWarnings::NotAllowed => FixableWarnings::NotAllowed, + FixableWarnings::Zero => FixableWarnings::Positive(1), + FixableWarnings::Positive(fixable) => FixableWarnings::Positive(fixable + 1), + }; + } } } /// Displays a final report of the warnings emitted by a particular job. fn report_warning_count(&mut self, config: &Config, id: JobId) { let count = match self.warning_count.remove(&id) { - Some(count) => count, - None => return, + // An error could add an entry for a `Unit` + // with 0 warnings but having fixable + // warnings be disallowed + Some(count) if count.total > 0 => count, + None | Some(_) => return, }; let unit = &self.active[&id]; let mut message = format!("`{}` ({}", unit.pkg.name(), unit.target.description_named()); @@ -1151,15 +1224,47 @@ impl<'cfg> DrainState<'cfg> { message.push_str(" doc"); } message.push_str(") generated "); - match count.0 { + match count.total { 1 => message.push_str("1 warning"), n => drop(write!(message, "{} warnings", n)), }; - match count.1 { + match count.duplicates { 0 => {} 1 => message.push_str(" (1 duplicate)"), n => drop(write!(message, " ({} duplicates)", n)), } + // Only show the `cargo fix` message if its a local `Unit` + if unit.is_local() && config.nightly_features_allowed { + // Do not show this if there are any errors or no fixable warnings + if let FixableWarnings::Positive(fixable) = count.fixable { + // `cargo fix` doesnt have an option for custom builds + if !unit.target.is_custom_build() { + let mut command = { + let named = unit.target.description_named(); + // if its a lib we need to add the package to fix + if unit.target.is_lib() { + format!("{} -p {}", named, unit.pkg.name()) + } else { + named + } + }; + if unit.mode.is_rustc_test() + && !(unit.target.is_test() || unit.target.is_bench()) + { + command.push_str(" --tests"); + } + let mut suggestions = format!("{} suggestion", fixable); + if fixable > 1 { + suggestions.push_str("s") + } + drop(write!( + message, + " (run `cargo fix --{}` to apply {})", + command, suggestions + )) + } + } + } // Errors are ignored here because it is tricky to handle them // correctly, and they aren't important. drop(config.shell().warn(message)); diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index affd415e3b42..9fe859feee69 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -61,6 +61,7 @@ use crate::util::interning::InternedString; use crate::util::machine_message::{self, Message}; use crate::util::{add_path_args, internal, iter_join_onto, profile}; use cargo_util::{paths, ProcessBuilder, ProcessError}; +use rustfix::diagnostics::{Applicability, Diagnostic}; const RUSTDOC_CRATE_VERSION_FLAG: &str = "--crate-version"; @@ -1420,7 +1421,10 @@ fn on_stderr_line_inner( rendered: String, message: String, level: String, + // `children: Vec` if we need to check them recursively + children: Vec, } + if let Ok(mut msg) = serde_json::from_str::(compiler_message.get()) { if msg.message.starts_with("aborting due to") || msg.message.ends_with("warning emitted") @@ -1436,15 +1440,26 @@ fn on_stderr_line_inner( let rendered = if options.color { msg.rendered } else { - // Strip only fails if the the Writer fails, which is Cursor + // Strip only fails if the Writer fails, which is Cursor // on a Vec, which should never fail. strip_ansi_escapes::strip(&msg.rendered) .map(|v| String::from_utf8(v).expect("utf8")) .expect("strip should never fail") }; if options.show_diagnostics { + let machine_applicable: bool = msg + .children + .iter() + .map(|child| { + child + .spans + .iter() + .filter_map(|span| span.suggestion_applicability) + .any(|app| app == Applicability::MachineApplicable) + }) + .any(|b| b); count_diagnostic(&msg.level, options); - state.emit_diag(msg.level, rendered)?; + state.emit_diag(msg.level, rendered, machine_applicable)?; } return Ok(true); } diff --git a/src/cargo/core/compiler/timings.rs b/src/cargo/core/compiler/timings.rs index ecb660fa1694..575f9a3d2a60 100644 --- a/src/cargo/core/compiler/timings.rs +++ b/src/cargo/core/compiler/timings.rs @@ -272,7 +272,7 @@ impl<'cfg> Timings<'cfg> { Some(state) => state, None => return, }; - // Don't take samples too too frequently, even if requested. + // Don't take samples too frequently, even if requested. let now = Instant::now(); if self.last_cpu_recording.elapsed() < Duration::from_millis(100) { return; diff --git a/src/cargo/core/resolver/features.rs b/src/cargo/core/resolver/features.rs index 65ce1810226d..a2dba2869cff 100644 --- a/src/cargo/core/resolver/features.rs +++ b/src/cargo/core/resolver/features.rs @@ -67,7 +67,7 @@ pub struct ResolvedFeatures { /// Options for how the feature resolver works. #[derive(Default)] pub struct FeatureOpts { - /// Build deps and proc-macros will not share share features with other dep kinds, + /// Build deps and proc-macros will not share features with other dep kinds, /// and so won't artifact targets. /// In other terms, if true, features associated with certain kinds of dependencies /// will only be unified together. diff --git a/src/cargo/core/resolver/mod.rs b/src/cargo/core/resolver/mod.rs index 000ded24652c..b0551891da14 100644 --- a/src/cargo/core/resolver/mod.rs +++ b/src/cargo/core/resolver/mod.rs @@ -416,7 +416,7 @@ fn activate_deps_loop( // global cache which lists sets of packages where, when // activated, the dependency is unresolvable. // - // If any our our frame's dependencies fit in that bucket, + // If any our frame's dependencies fit in that bucket, // aka known unresolvable, then we extend our own set of // conflicting activations with theirs. We can do this // because the set of conflicts we found implies the diff --git a/src/cargo/core/source/source_id.rs b/src/cargo/core/source/source_id.rs index 81203d56f2e3..bb43843b5c03 100644 --- a/src/cargo/core/source/source_id.rs +++ b/src/cargo/core/source/source_id.rs @@ -428,7 +428,10 @@ impl SourceId { let url = self.inner.url.as_str(); url == CRATES_IO_INDEX || url == CRATES_IO_HTTP_INDEX - || std::env::var("__CARGO_TEST_CRATES_IO_URL_DO_NOT_USE_THIS").as_deref() == Ok(url) + || std::env::var("__CARGO_TEST_CRATES_IO_URL_DO_NOT_USE_THIS") + .as_deref() + .map(|u| u.trim_start_matches("sparse+")) + == Ok(url) } /// Hashes `self`. diff --git a/src/cargo/ops/cargo_clean.rs b/src/cargo/ops/cargo_clean.rs index 08fb3ac181b9..507c2d89b1ce 100644 --- a/src/cargo/ops/cargo_clean.rs +++ b/src/cargo/ops/cargo_clean.rs @@ -141,7 +141,12 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> { // Clean fingerprints. for (_, layout) in &layouts_with_host { let dir = escape_glob_path(layout.fingerprint())?; - rm_rf_glob(&Path::new(&dir).join(&pkg_dir), config, &mut progress)?; + rm_rf_package_glob_containing_hash( + &pkg.name(), + &Path::new(&dir).join(&pkg_dir), + config, + &mut progress, + )?; } for target in pkg.targets() { @@ -149,7 +154,12 @@ pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> { // Get both the build_script_build and the output directory. for (_, layout) in &layouts_with_host { let dir = escape_glob_path(layout.build())?; - rm_rf_glob(&Path::new(&dir).join(&pkg_dir), config, &mut progress)?; + rm_rf_package_glob_containing_hash( + &pkg.name(), + &Path::new(&dir).join(&pkg_dir), + config, + &mut progress, + )?; } continue; } @@ -222,6 +232,40 @@ fn escape_glob_path(pattern: &Path) -> CargoResult { Ok(glob::Pattern::escape(pattern)) } +/// Glob remove artifacts for the provided `package` +/// +/// Make sure the artifact is for `package` and not another crate that is prefixed by +/// `package` by getting the original name stripped of the trailing hash and possible +/// extension +fn rm_rf_package_glob_containing_hash( + package: &str, + pattern: &Path, + config: &Config, + progress: &mut dyn CleaningProgressBar, +) -> CargoResult<()> { + // TODO: Display utf8 warning to user? Or switch to globset? + let pattern = pattern + .to_str() + .ok_or_else(|| anyhow::anyhow!("expected utf-8 path"))?; + for path in glob::glob(pattern)? { + let path = path?; + + let pkg_name = path + .file_name() + .and_then(std::ffi::OsStr::to_str) + .and_then(|artifact| artifact.rsplit_once('-')) + .ok_or_else(|| anyhow::anyhow!("expected utf-8 path"))? + .0; + + if pkg_name != package { + continue; + } + + rm_rf(&path, config, progress)?; + } + Ok(()) +} + fn rm_rf_glob( pattern: &Path, config: &Config, diff --git a/src/cargo/ops/cargo_new.rs b/src/cargo/ops/cargo_new.rs index 961631f2d949..b7ec89cba0d3 100644 --- a/src/cargo/ops/cargo_new.rs +++ b/src/cargo/ops/cargo_new.rs @@ -2,7 +2,7 @@ use crate::core::{Edition, Shell, Workspace}; use crate::util::errors::CargoResult; use crate::util::{existing_vcs_repo, FossilRepo, GitRepo, HgRepo, PijulRepo}; use crate::util::{restricted_names, Config}; -use anyhow::Context as _; +use anyhow::{anyhow, Context as _}; use cargo_util::paths; use serde::de; use serde::Deserialize; @@ -613,9 +613,22 @@ impl IgnoreList { /// already exists. It reads the contents of the given `BufRead` and /// checks if the contents of the ignore list are already existing in the /// file. - fn format_existing(&self, existing: T, vcs: VersionControl) -> String { - // TODO: is unwrap safe? - let existing_items = existing.lines().collect::, _>>().unwrap(); + fn format_existing(&self, existing: T, vcs: VersionControl) -> CargoResult { + let mut existing_items = Vec::new(); + for (i, item) in existing.lines().enumerate() { + match item { + Ok(s) => existing_items.push(s), + Err(err) => match err.kind() { + ErrorKind::InvalidData => { + return Err(anyhow!( + "Character at line {} is invalid. Cargo only supports UTF-8.", + i + )) + } + _ => return Err(anyhow!(err)), + }, + } + } let ignore_items = match vcs { VersionControl::Hg => &self.hg_ignore, @@ -649,7 +662,7 @@ impl IgnoreList { out.push('\n'); } - out + Ok(out) } } @@ -678,7 +691,7 @@ fn write_ignore_file(base_path: &Path, list: &IgnoreList, vcs: VersionControl) - Some(io_err) if io_err.kind() == ErrorKind::NotFound => list.format_new(vcs), _ => return Err(err), }, - Ok(file) => list.format_existing(BufReader::new(file), vcs), + Ok(file) => list.format_existing(BufReader::new(file), vcs)?, }; paths::append(&fp_ignore, ignore.as_bytes())?; diff --git a/src/cargo/ops/cargo_package.rs b/src/cargo/ops/cargo_package.rs index d3fd85af3773..ae5c7558fc4c 100644 --- a/src/cargo/ops/cargo_package.rs +++ b/src/cargo/ops/cargo_package.rs @@ -14,7 +14,7 @@ use crate::core::{Package, PackageId, PackageSet, Resolve, SourceId}; use crate::sources::PathSource; use crate::util::errors::CargoResult; use crate::util::toml::TomlManifest; -use crate::util::{self, restricted_names, Config, FileLock}; +use crate::util::{self, human_readable_bytes, restricted_names, Config, FileLock}; use crate::{drop_println, ops}; use anyhow::Context as _; use cargo_util::paths; @@ -110,6 +110,8 @@ pub fn package_one( let ar_files = build_ar_list(ws, pkg, src_files, vcs_info)?; + let filecount = ar_files.len(); + if opts.list { for ar_file in ar_files { drop_println!(config, "{}", ar_file.rel_str); @@ -138,7 +140,7 @@ pub fn package_one( .shell() .status("Packaging", pkg.package_id().to_string())?; dst.file().set_len(0)?; - tar(ws, pkg, ar_files, dst.file(), &filename) + let uncompressed_size = tar(ws, pkg, ar_files, dst.file(), &filename) .with_context(|| "failed to prepare local package for uploading")?; if opts.verify { dst.seek(SeekFrom::Start(0))?; @@ -151,6 +153,22 @@ pub fn package_one( fs::rename(&src_path, &dst_path) .with_context(|| "failed to move temporary tarball into final location")?; + let dst_metadata = dst + .file() + .metadata() + .with_context(|| format!("could not learn metadata for: `{}`", dst_path.display()))?; + let compressed_size = dst_metadata.len(); + + let uncompressed = human_readable_bytes(uncompressed_size); + let compressed = human_readable_bytes(compressed_size); + + let message = format!( + "{} files, {:.1}{} ({:.1}{} compressed)", + filecount, uncompressed.0, uncompressed.1, compressed.0, compressed.1, + ); + // It doesn't really matter if this fails. + drop(config.shell().status("Packaged", message)); + return Ok(Some(dst)); } @@ -567,13 +585,16 @@ fn check_repo_state( } } +/// Compresses and packages a list of [`ArchiveFile`]s and writes into the given file. +/// +/// Returns the uncompressed size of the contents of the new archive file. fn tar( ws: &Workspace<'_>, pkg: &Package, ar_files: Vec, dst: &File, filename: &str, -) -> CargoResult<()> { +) -> CargoResult { // Prepare the encoder and its header. let filename = Path::new(filename); let encoder = GzBuilder::new() @@ -586,6 +607,8 @@ fn tar( let base_name = format!("{}-{}", pkg.name(), pkg.version()); let base_path = Path::new(&base_name); + + let mut uncompressed_size = 0; for ar_file in ar_files { let ArchiveFile { rel_path, @@ -611,6 +634,7 @@ fn tar( .with_context(|| { format!("could not archive source file `{}`", disk_path.display()) })?; + uncompressed_size += metadata.len() as u64; } FileContents::Generated(generated_kind) => { let contents = match generated_kind { @@ -626,13 +650,14 @@ fn tar( header.set_cksum(); ar.append_data(&mut header, &ar_path, contents.as_bytes()) .with_context(|| format!("could not archive source file `{}`", rel_str))?; + uncompressed_size += contents.len() as u64; } } } let encoder = ar.into_inner()?; encoder.finish()?; - Ok(()) + Ok(uncompressed_size) } /// Generate warnings when packaging Cargo.lock, and the resolve have changed. diff --git a/src/cargo/ops/registry.rs b/src/cargo/ops/registry.rs index 80772e03998f..30d7546cc84f 100644 --- a/src/cargo/ops/registry.rs +++ b/src/cargo/ops/registry.rs @@ -192,7 +192,7 @@ pub fn publish(ws: &Workspace<'_>, opts: &PublishOpts<'_>) -> CargoResult<()> { opts.dry_run, )?; if !opts.dry_run { - const DEFAULT_TIMEOUT: u64 = 0; + const DEFAULT_TIMEOUT: u64 = 60; let timeout = if opts.config.cli_unstable().publish_timeout { let timeout: Option = opts.config.get("publish.timeout")?; timeout.unwrap_or(DEFAULT_TIMEOUT) @@ -643,6 +643,9 @@ pub fn configure_http_handle(config: &Config, handle: &mut Easy) -> CargoResult< handle.useragent(&format!("cargo {}", version()))?; } + // Empty string accept encoding expands to the encodings supported by the current libcurl. + handle.accept_encoding("")?; + fn to_ssl_version(s: &str) -> CargoResult { let version = match s { "default" => SslVersion::Default, @@ -675,6 +678,24 @@ pub fn configure_http_handle(config: &Config, handle: &mut Easy) -> CargoResult< 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 { diff --git a/src/cargo/ops/registry/auth.rs b/src/cargo/ops/registry/auth.rs index 648e051e6dc2..85b51a50ab0e 100644 --- a/src/cargo/ops/registry/auth.rs +++ b/src/cargo/ops/registry/auth.rs @@ -123,7 +123,7 @@ fn run_command( let mut cmd = Command::new(&exe); cmd.args(args) - .env("CARGO", config.cargo_exe()?) + .env(crate::CARGO_ENV, config.cargo_exe()?) .env("CARGO_REGISTRY_NAME", name) .env("CARGO_REGISTRY_API_URL", api_url); match action { diff --git a/src/cargo/sources/git/utils.rs b/src/cargo/sources/git/utils.rs index 4629f91748f7..623caceb803a 100644 --- a/src/cargo/sources/git/utils.rs +++ b/src/cargo/sources/git/utils.rs @@ -3,7 +3,7 @@ use crate::core::GitReference; use crate::util::errors::CargoResult; -use crate::util::{network, Config, IntoUrl, MetricsCounter, Progress}; +use crate::util::{human_readable_bytes, network, Config, IntoUrl, MetricsCounter, Progress}; use anyhow::{anyhow, Context as _}; use cargo_util::{paths, ProcessBuilder}; use curl::easy::List; @@ -755,13 +755,8 @@ pub fn with_fetch_options( counter.add(stats.received_bytes(), now); last_update = now; } - fn format_bytes(bytes: f32) -> (&'static str, f32) { - static UNITS: [&str; 5] = ["", "Ki", "Mi", "Gi", "Ti"]; - let i = (bytes.log2() / 10.0).min(4.0) as usize; - (UNITS[i], bytes / 1024_f32.powi(i as i32)) - } - let (unit, rate) = format_bytes(counter.rate()); - format!(", {:.2}{}B/s", rate, unit) + let (rate, unit) = human_readable_bytes(counter.rate() as u64); + format!(", {:.2}{}/s", rate, unit) }; progress .tick(stats.indexed_objects(), stats.total_objects(), &msg) @@ -808,10 +803,12 @@ 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 too expensive compared to the network + // In theory this shouldn't be too expensive compared to the network // request we're about to issue. maybe_gc_repo(repo)?; + clean_repo_temp_files(repo); + // Translate the reference desired here into an actual list of refspecs // which need to get fetched. Additionally record if we're fetching tags. let mut refspecs = Vec::new(); @@ -1001,6 +998,43 @@ fn maybe_gc_repo(repo: &mut git2::Repository) -> CargoResult<()> { reinitialize(repo) } +/// Removes temporary files left from previous activity. +/// +/// If libgit2 is interrupted while indexing pack files, it will leave behind +/// some temporary files that it doesn't clean up. These can be quite large in +/// size, so this tries to clean things up. +/// +/// This intentionally ignores errors. This is only an opportunistic cleaning, +/// and we don't really care if there are issues (there's unlikely anything +/// that can be done). +/// +/// The git CLI has similar behavior (its temp files look like +/// `objects/pack/tmp_pack_9kUSA8`). Those files are normally deleted via `git +/// prune` which is run by `git gc`. However, it doesn't know about libgit2's +/// filenames, so they never get cleaned up. +fn clean_repo_temp_files(repo: &git2::Repository) { + let path = repo.path().join("objects/pack/pack_git2_*"); + let pattern = match path.to_str() { + Some(p) => p, + None => { + log::warn!("cannot convert {path:?} to a string"); + return; + } + }; + let paths = match glob::glob(pattern) { + Ok(paths) => paths, + Err(_) => return, + }; + for path in paths { + if let Ok(path) = path { + match paths::remove_file(&path) { + Ok(_) => log::debug!("removed stale temp git file {path:?}"), + Err(e) => log::warn!("failed to remove {path:?} while cleaning temp files: {e}"), + } + } + } +} + 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 diff --git a/src/cargo/sources/registry/http_remote.rs b/src/cargo/sources/registry/http_remote.rs index e659cb3ccfe1..805942274c01 100644 --- a/src/cargo/sources/registry/http_remote.rs +++ b/src/cargo/sources/registry/http_remote.rs @@ -549,6 +549,7 @@ impl<'cfg> RegistryData for HttpRegistry<'cfg> { // All it does is ensure that a subsequent load will double-check files with the // server rather than rely on a locally cached copy of the index files. debug!("invalidated index cache"); + self.fresh.clear(); self.requested_update = true; } diff --git a/src/cargo/sources/replaced.rs b/src/cargo/sources/replaced.rs index 2d8a2903d763..a2147703388f 100644 --- a/src/cargo/sources/replaced.rs +++ b/src/cargo/sources/replaced.rs @@ -100,11 +100,17 @@ impl<'cfg> Source for ReplacedSource<'cfg> { } fn describe(&self) -> String { - format!( - "{} (which is replacing {})", - self.inner.describe(), - self.to_replace - ) + if self.replace_with.is_crates_io() && self.to_replace.is_crates_io() { + // Built-in source replacement of crates.io for sparse registry or tests + // doesn't need duplicate description (crates.io replacing crates.io). + self.inner.describe() + } else { + format!( + "{} (which is replacing {})", + self.inner.describe(), + self.to_replace + ) + } } fn is_replaced(&self) -> bool { diff --git a/src/cargo/util/command_prelude.rs b/src/cargo/util/command_prelude.rs index 54466f6a0e54..15d9e61ede4c 100644 --- a/src/cargo/util/command_prelude.rs +++ b/src/cargo/util/command_prelude.rs @@ -469,25 +469,26 @@ pub trait ArgMatchesExt { ansi: false, render_diagnostics: false, }; + let two_kinds_of_msg_format_err = "cannot specify two kinds of `message-format` arguments"; for fmt in self._values_of("message-format") { for fmt in fmt.split(',') { let fmt = fmt.to_ascii_lowercase(); match fmt.as_str() { "json" => { if message_format.is_some() { - bail!("cannot specify two kinds of `message-format` arguments"); + bail!(two_kinds_of_msg_format_err); } message_format = Some(default_json); } "human" => { if message_format.is_some() { - bail!("cannot specify two kinds of `message-format` arguments"); + bail!(two_kinds_of_msg_format_err); } message_format = Some(MessageFormat::Human); } "short" => { if message_format.is_some() { - bail!("cannot specify two kinds of `message-format` arguments"); + bail!(two_kinds_of_msg_format_err); } message_format = Some(MessageFormat::Short); } @@ -499,7 +500,7 @@ pub trait ArgMatchesExt { Some(MessageFormat::Json { render_diagnostics, .. }) => *render_diagnostics = true, - _ => bail!("cannot specify two kinds of `message-format` arguments"), + _ => bail!(two_kinds_of_msg_format_err), } } "json-diagnostic-short" => { @@ -508,7 +509,7 @@ pub trait ArgMatchesExt { } match &mut message_format { Some(MessageFormat::Json { short, .. }) => *short = true, - _ => bail!("cannot specify two kinds of `message-format` arguments"), + _ => bail!(two_kinds_of_msg_format_err), } } "json-diagnostic-rendered-ansi" => { @@ -517,7 +518,7 @@ pub trait ArgMatchesExt { } match &mut message_format { Some(MessageFormat::Json { ansi, .. }) => *ansi = true, - _ => bail!("cannot specify two kinds of `message-format` arguments"), + _ => bail!(two_kinds_of_msg_format_err), } } s => bail!("invalid message format specifier: `{}`", s), diff --git a/src/cargo/util/config/mod.rs b/src/cargo/util/config/mod.rs index 765c93db7042..d2fa0b5caf97 100644 --- a/src/cargo/util/config/mod.rs +++ b/src/cargo/util/config/mod.rs @@ -406,6 +406,18 @@ impl Config { pub fn cargo_exe(&self) -> CargoResult<&Path> { self.cargo_exe .try_borrow_with(|| { + fn from_env() -> CargoResult { + // Try re-using the `cargo` set in the environment already. This allows + // commands that use Cargo as a library to inherit (via `cargo `) + // or set (by setting `$CARGO`) a correct path to `cargo` when the current exe + // is not actually cargo (e.g., `cargo-*` binaries, Valgrind, `ld.so`, etc.). + let exe = env::var_os(crate::CARGO_ENV) + .map(PathBuf::from) + .ok_or_else(|| anyhow!("$CARGO not set"))? + .canonicalize()?; + Ok(exe) + } + fn from_current_exe() -> CargoResult { // Try fetching the path to `cargo` using `env::current_exe()`. // The method varies per operating system and might fail; in particular, @@ -431,7 +443,8 @@ impl Config { paths::resolve_executable(&argv0) } - let exe = from_current_exe() + let exe = from_env() + .or_else(|_| from_current_exe()) .or_else(|_| from_argv()) .with_context(|| "couldn't get the path to cargo executable")?; Ok(exe) diff --git a/src/cargo/util/mod.rs b/src/cargo/util/mod.rs index 9881332ecafc..8fb243688563 100644 --- a/src/cargo/util/mod.rs +++ b/src/cargo/util/mod.rs @@ -73,6 +73,15 @@ pub fn elapsed(duration: Duration) -> String { } } +/// Formats a number of bytes into a human readable SI-prefixed size. +/// Returns a tuple of `(quantity, units)`. +pub fn human_readable_bytes(bytes: u64) -> (f32, &'static str) { + static UNITS: [&str; 7] = ["B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"]; + let bytes = bytes as f32; + let i = ((bytes.log2() / 10.0) as usize).min(UNITS.len() - 1); + (bytes / 1024_f32.powi(i as i32), UNITS[i]) +} + pub fn iter_join_onto(mut w: W, iter: I, delim: &str) -> fmt::Result where W: fmt::Write, @@ -122,3 +131,37 @@ pub fn truncate_with_ellipsis(s: &str, max_width: usize) -> String { } prefix } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_human_readable_bytes() { + assert_eq!(human_readable_bytes(0), (0., "B")); + assert_eq!(human_readable_bytes(8), (8., "B")); + assert_eq!(human_readable_bytes(1000), (1000., "B")); + assert_eq!(human_readable_bytes(1024), (1., "KiB")); + assert_eq!(human_readable_bytes(1024 * 420 + 512), (420.5, "KiB")); + assert_eq!(human_readable_bytes(1024 * 1024), (1., "MiB")); + assert_eq!( + human_readable_bytes(1024 * 1024 + 1024 * 256), + (1.25, "MiB") + ); + assert_eq!(human_readable_bytes(1024 * 1024 * 1024), (1., "GiB")); + assert_eq!( + human_readable_bytes((1024. * 1024. * 1024. * 3.1415) as u64), + (3.1415, "GiB") + ); + assert_eq!(human_readable_bytes(1024 * 1024 * 1024 * 1024), (1., "TiB")); + assert_eq!( + human_readable_bytes(1024 * 1024 * 1024 * 1024 * 1024), + (1., "PiB") + ); + assert_eq!( + human_readable_bytes(1024 * 1024 * 1024 * 1024 * 1024 * 1024), + (1., "EiB") + ); + assert_eq!(human_readable_bytes(u64::MAX), (16., "EiB")); + } +} diff --git a/src/cargo/util/toml_mut/manifest.rs b/src/cargo/util/toml_mut/manifest.rs index 9d98d581b636..8c88333608ad 100644 --- a/src/cargo/util/toml_mut/manifest.rs +++ b/src/cargo/util/toml_mut/manifest.rs @@ -246,7 +246,7 @@ impl std::fmt::Display for Manifest { } /// An editable Cargo manifest that is available locally. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct LocalManifest { /// Path to the manifest. pub path: PathBuf, diff --git a/src/doc/contrib/src/architecture/codebase.md b/src/doc/contrib/src/architecture/codebase.md index 12c0c1710d8a..77b0d14c6238 100644 --- a/src/doc/contrib/src/architecture/codebase.md +++ b/src/doc/contrib/src/architecture/codebase.md @@ -12,7 +12,7 @@ This is a very high-level overview of the Cargo codebase. — Every major operation is implemented here. This is where the binary CLI usually calls into to perform the appropriate action. - * [`src/cargo/ops/cargo_compile.rs`](https://github.com/rust-lang/cargo/blob/master/src/cargo/ops/cargo_compile.rs) + * [`src/cargo/ops/cargo_compile/mod.rs`](https://github.com/rust-lang/cargo/blob/master/src/cargo/ops/cargo_compile/mod.rs) — This is the entry point for all the compilation commands. This is a good place to start if you want to follow how compilation starts and flows to completion. diff --git a/src/doc/contrib/src/process/index.md b/src/doc/contrib/src/process/index.md index 7fac605920c9..af0f030b70c8 100644 --- a/src/doc/contrib/src/process/index.md +++ b/src/doc/contrib/src/process/index.md @@ -95,18 +95,19 @@ implemented. The Cargo project uses several bots: * [GitHub Actions] are used to automatically run all tests for each PR. -* [rust-highfive] automatically assigns reviewers for PRs. +* [triagebot] automatically assigns reviewers for PRs, see [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 assignment](#issue-assignment). * [rfcbot] is used for making asynchronous decisions by team members. -[rust-highfive]: https://github.com/rust-highfive [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 [rfcbot]: https://github.com/rust-lang/rfcbot-rs +[Assignment]: https://github.com/rust-lang/triagebot/wiki/Assignment ## Issue assignment diff --git a/src/doc/contrib/src/process/working-on-cargo.md b/src/doc/contrib/src/process/working-on-cargo.md index deb628663ff0..e90bb8588f6c 100644 --- a/src/doc/contrib/src/process/working-on-cargo.md +++ b/src/doc/contrib/src/process/working-on-cargo.md @@ -110,7 +110,7 @@ open a Pull Request #1234 to the PR. When the PR is merged, GitHub will automatically close the issue. -The [rust-highfive] bot will automatically assign a reviewer for the PR. It +[`@rustbot`] will automatically assign a reviewer for the PR. It may take at least a few days for someone to respond. If you don't get a response in over a week, feel free to ping the assigned reviewer. @@ -162,7 +162,6 @@ more information on how Cargo releases are made. [how-to-clone]: https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/cloning-a-repository [Testing chapter]: ../tests/index.md [GitHub's keywords]: https://docs.github.com/en/github/managing-your-work-on-github/linking-a-pull-request-to-an-issue -[rust-highfive]: https://github.com/rust-highfive [bors]: https://buildbot2.rust-lang.org/homu/ [`@bors`]: https://github.com/bors [homu-cargo]: https://buildbot2.rust-lang.org/homu/queue/cargo diff --git a/src/doc/src/guide/tests.md b/src/doc/src/guide/tests.md index d1809a39ca77..402e8e35c953 100644 --- a/src/doc/src/guide/tests.md +++ b/src/doc/src/guide/tests.md @@ -33,7 +33,7 @@ This will run any test with `foo` in its name. `cargo test` runs additional checks as well. It will compile any examples you’ve included to ensure they still compile. It also runs documentation -tests to ensure your code samples from documentation comments compiles. +tests to ensure your code samples from documentation comments compile. Please see the [testing guide][testing] in the Rust documentation for a general view of writing and organizing tests. See [Cargo Targets: Tests] to learn more about different styles of tests in Cargo. diff --git a/src/doc/src/reference/config.md b/src/doc/src/reference/config.md index f60c185e9165..1b4243ffa344 100644 --- a/src/doc/src/reference/config.md +++ b/src/doc/src/reference/config.md @@ -240,7 +240,7 @@ precedence rules as other options specified directly with `--config`. ### Config-relative paths Paths in config files may be absolute, relative, or a bare name without any path separators. -Paths for executables without a path separator will use the `PATH` environment variable to search for the executable. +Paths for executables without a path separator will use the `PATH` environment variable to search for the executable. Paths for non-executables will be relative to where the config value is defined. In particular, rules are: @@ -258,7 +258,7 @@ In particular, rules are: > > To avoid unexpected results, the rule of thumb is putting your extra config files > at the same level of discovered `.cargo/config.toml` in your porject. -> For instance, given a project `/my/project`, +> For instance, given a project `/my/project`, > it is recommended to put config files under `/my/project/.cargo` > or a new directory at the same level, such as `/my/project/.config`. @@ -348,6 +348,7 @@ c = "check" d = "doc" t = "test" r = "run" +rm = "remove" ``` Aliases are not allowed to redefine existing built-in commands. diff --git a/src/doc/src/reference/environment-variables.md b/src/doc/src/reference/environment-variables.md index 42417b8a05b7..d49922afbfde 100644 --- a/src/doc/src/reference/environment-variables.md +++ b/src/doc/src/reference/environment-variables.md @@ -23,6 +23,12 @@ system: * `CARGO_TARGET_DIR` — Location of where to place all generated artifacts, relative to the current working directory. See [`build.target-dir`] to set via config. +* `CARGO` - If set, Cargo will forward this value instead of setting it + to its own auto-detected path when it builds crates and when it + executes build scripts and external subcommands. This value is not + directly executed by Cargo, and should always point at a command that + behaves exactly like `cargo`, as that's what users of the variable + will be expecting. * `RUSTC` — Instead of running `rustc`, Cargo will execute this specified compiler instead. See [`build.rustc`] to set via config. * `RUSTC_WRAPPER` — Instead of simply running `rustc`, Cargo will execute this diff --git a/src/doc/src/reference/semver.md b/src/doc/src/reference/semver.md index ee6962eafa58..37dccf68aba2 100644 --- a/src/doc/src/reference/semver.md +++ b/src/doc/src/reference/semver.md @@ -18,7 +18,7 @@ See also [rust-semverver], which is an experimental tool that attempts to programmatically check compatibility rules. [Change categories]: #change-categories -[rust-semverver]: https://github.com/rust-dev-tools/rust-semverver +[rust-semverver]: https://github.com/rust-lang/rust-semverver [SemVer compatibility]: resolver.md#semver-compatibility ## Change categories @@ -391,7 +391,7 @@ pub enum E { fn main() { use updated_crate::E; let x = E::Variant1; - match x { // Error: `Variant2` not covered + match x { // Error: `E::Variant2` not covered E::Variant1 => {} } } diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index 9f75a971f49d..13cb61cff47d 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -787,14 +787,14 @@ the command line) target. * Tracking Issue: [#9096](https://github.com/rust-lang/cargo/pull/9096) * Original Pull Request: [#9992](https://github.com/rust-lang/cargo/pull/9992) -Allow Cargo packages to depend on `bin`, `cdylib`, and `staticlib` crates, +Allow Cargo packages to depend on `bin`, `cdylib`, and `staticlib` crates, and use the artifacts built by those crates at compile time. Run `cargo` with `-Z bindeps` to enable this functionality. **Example:** use _cdylib_ artifact in build script -The `Cargo.toml` in the consuming package, building the `bar` library as `cdylib` +The `Cargo.toml` in the consuming package, building the `bar` library as `cdylib` for a specific build target… ```toml @@ -842,6 +842,13 @@ crates, which can save significant time and bandwidth. The format of the sparse index is identical to a checkout of a git-based index. +The `registries.crates-io.protocol` config option can be used to set the default protocol +for crates.io. This option requires `-Z sparse-registry` to be enabled. + +* `sparse` — Use sparse index. +* `git` — Use git index. +* If the option is unset, it will be sparse index if `-Z sparse-registry` is enabled, otherwise it will be git index. + ### publish-timeout * Tracking Issue: [11222](https://github.com/rust-lang/cargo/issues/11222) @@ -1376,7 +1383,7 @@ for more information. ### Workspace Inheritance Workspace Inheritance has been stabilized in the 1.64 release. -See [workspace.package](workspaces.md#the-package-table), -[workspace.dependencies](workspaces.md#the-dependencies-table), +See [workspace.package](workspaces.md#the-package-table), +[workspace.dependencies](workspaces.md#the-dependencies-table), and [inheriting-a-dependency-from-a-workspace](specifying-dependencies.md#inheriting-a-dependency-from-a-workspace) for more information. diff --git a/tests/testsuite/artifact_dep.rs b/tests/testsuite/artifact_dep.rs index 2a089395f2c0..0665df649fd5 100644 --- a/tests/testsuite/artifact_dep.rs +++ b/tests/testsuite/artifact_dep.rs @@ -1872,7 +1872,9 @@ fn env_vars_and_build_products_for_various_build_targets() { #[cargo_test] fn publish_artifact_dep() { + // HACK below allows us to use a local registry let registry = registry::init(); + Package::new("bar", "1.0.0").publish(); Package::new("baz", "1.0.0").publish(); @@ -1901,6 +1903,15 @@ fn publish_artifact_dep() { .file("src/lib.rs", "") .build(); + // HACK: Inject `foo` directly into the index so `publish` won't block for it to be in + // the index. + // + // This is to ensure we can verify the Summary we post to the registry as doing so precludes + // the registry from processing the publish. + Package::new("foo", "0.1.0") + .file("src/lib.rs", "") + .publish(); + p.cargo("publish -Z bindeps --no-verify") .replace_crates_io(registry.index_url()) .masquerade_as_nightly_cargo(&["bindeps"]) @@ -1908,7 +1919,9 @@ fn publish_artifact_dep() { "\ [UPDATING] [..] [PACKAGING] foo v0.1.0 [..] +[PACKAGED] [..] [UPLOADING] foo v0.1.0 [..] +[UPDATING] [..] ", ) .run(); @@ -2263,3 +2276,69 @@ fn build_script_features_for_shared_dependency() { .masquerade_as_nightly_cargo(&["bindeps"]) .run(); } + +#[cargo_test] +fn calc_bin_artifact_fingerprint() { + // See rust-lang/cargo#10527 + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + resolver = "2" + + [dependencies] + bar = { path = "bar/", artifact = "bin" } + "#, + ) + .file( + "src/main.rs", + r#" + fn main() { + let _b = include_bytes!(env!("CARGO_BIN_FILE_BAR")); + } + "#, + ) + .file("bar/Cargo.toml", &basic_bin_manifest("bar")) + .file("bar/src/main.rs", r#"fn main() { println!("foo") }"#) + .build(); + p.cargo("check -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[COMPILING] bar v0.5.0 ([CWD]/bar) +[CHECKING] foo v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + p.change_file("bar/src/main.rs", r#"fn main() { println!("bar") }"#); + // Change in artifact bin dep `bar` propagates to `foo`, triggering recompile. + p.cargo("check -v -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[COMPILING] bar v0.5.0 ([CWD]/bar) +[RUNNING] `rustc --crate-name bar [..]` +[CHECKING] foo v0.1.0 ([CWD]) +[RUNNING] `rustc --crate-name foo [..]` +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); + + // All units are fresh. No recompile. + p.cargo("check -v -Z bindeps") + .masquerade_as_nightly_cargo(&["bindeps"]) + .with_stderr( + "\ +[FRESH] bar v0.5.0 ([CWD]/bar) +[FRESH] foo v0.1.0 ([CWD]) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + ) + .run(); +} diff --git a/tests/testsuite/build_script.rs b/tests/testsuite/build_script.rs index 8157b9e73aa7..e81f6ef1ece7 100644 --- a/tests/testsuite/build_script.rs +++ b/tests/testsuite/build_script.rs @@ -4,9 +4,11 @@ use cargo_test_support::compare::assert_match_exact; use cargo_test_support::paths::CargoPathExt; use cargo_test_support::registry::Package; use cargo_test_support::tools; -use cargo_test_support::{basic_manifest, cross_compile, is_coarse_mtime, project, project_in}; +use cargo_test_support::{ + basic_manifest, cargo_exe, cross_compile, is_coarse_mtime, project, project_in, +}; use cargo_test_support::{rustc_host, sleep_ms, slow_cpu_multiplier, symlink_supported}; -use cargo_util::paths::remove_dir_all; +use cargo_util::paths::{self, remove_dir_all}; use std::env; use std::fs; use std::io; @@ -80,8 +82,15 @@ fn custom_build_env_vars() { ) .file("bar/src/lib.rs", "pub fn hello() {}"); + let cargo = cargo_exe().canonicalize().unwrap(); + let cargo = cargo.to_str().unwrap(); + let rustc = paths::resolve_executable("rustc".as_ref()) + .unwrap() + .canonicalize() + .unwrap(); + let rustc = rustc.to_str().unwrap(); let file_content = format!( - r#" + r##" use std::env; use std::path::Path; @@ -107,7 +116,12 @@ fn custom_build_env_vars() { let _feat = env::var("CARGO_FEATURE_FOO").unwrap(); - let _cargo = env::var("CARGO").unwrap(); + let cargo = env::var("CARGO").unwrap(); + if env::var_os("CHECK_CARGO_IS_RUSTC").is_some() {{ + assert_eq!(cargo, r#"{rustc}"#); + }} else {{ + assert_eq!(cargo, r#"{cargo}"#); + }} let rustc = env::var("RUSTC").unwrap(); assert_eq!(rustc, "rustc"); @@ -124,7 +138,7 @@ fn custom_build_env_vars() { let rustflags = env::var("CARGO_ENCODED_RUSTFLAGS").unwrap(); assert_eq!(rustflags, ""); }} - "#, + "##, p.root() .join("target") .join("debug") @@ -135,6 +149,11 @@ fn custom_build_env_vars() { let p = p.file("bar/build.rs", &file_content).build(); p.cargo("build --features bar_feat").run(); + p.cargo("build --features bar_feat") + // we use rustc since $CARGO is only used if it points to a path that exists + .env("CHECK_CARGO_IS_RUSTC", "1") + .env(cargo::CARGO_ENV, rustc) + .run(); } #[cargo_test] diff --git a/tests/testsuite/cargo_command.rs b/tests/testsuite/cargo_command.rs index ad969d1ba6eb..e96f7ae2184d 100644 --- a/tests/testsuite/cargo_command.rs +++ b/tests/testsuite/cargo_command.rs @@ -335,13 +335,26 @@ fn cargo_subcommand_env() { let cargo = cargo_exe().canonicalize().unwrap(); let mut path = path(); - path.push(target_dir); + path.push(target_dir.clone()); let path = env::join_paths(path.iter()).unwrap(); cargo_process("envtest") .env("PATH", &path) .with_stdout(cargo.to_str().unwrap()) .run(); + + // Check that subcommands inherit an overriden $CARGO + let envtest_bin = target_dir + .join("cargo-envtest") + .with_extension(std::env::consts::EXE_EXTENSION) + .canonicalize() + .unwrap(); + let envtest_bin = envtest_bin.to_str().unwrap(); + cargo_process("envtest") + .env("PATH", &path) + .env(cargo::CARGO_ENV, &envtest_bin) + .with_stdout(envtest_bin) + .run(); } #[cargo_test] diff --git a/tests/testsuite/cargo_remove/mod.rs b/tests/testsuite/cargo_remove/mod.rs index 75178531ee33..4aa2dbe585b6 100644 --- a/tests/testsuite/cargo_remove/mod.rs +++ b/tests/testsuite/cargo_remove/mod.rs @@ -22,6 +22,9 @@ mod target; mod target_build; mod target_dev; mod update_lock_file; +mod workspace; +mod workspace_non_virtual; +mod workspace_preserved; fn init_registry() { cargo_test_support::registry::init(); diff --git a/tests/testsuite/cargo_remove/workspace/in/Cargo.toml b/tests/testsuite/cargo_remove/workspace/in/Cargo.toml new file mode 100644 index 000000000000..fd5e80a8ba1f --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ "my-package" ] + +[workspace.dependencies] +semver = "0.1.0" diff --git a/tests/testsuite/cargo_remove/workspace/in/my-package/Cargo.toml b/tests/testsuite/cargo_remove/workspace/in/my-package/Cargo.toml new file mode 100644 index 000000000000..6690d593ba19 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace/in/my-package/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "my-package" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = { workspace = true } + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/workspace/in/my-package/src/main.rs b/tests/testsuite/cargo_remove/workspace/in/my-package/src/main.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace/in/my-package/src/main.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/workspace/mod.rs b/tests/testsuite/cargo_remove/workspace/mod.rs new file mode 100644 index 000000000000..225fbec00e88 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +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(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["--package", "my-package", "--build", "semver"]) + .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/tests/testsuite/cargo_remove/workspace/out/Cargo.toml b/tests/testsuite/cargo_remove/workspace/out/Cargo.toml new file mode 100644 index 000000000000..83a6a04d0716 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace/out/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = [ "my-package" ] diff --git a/tests/testsuite/cargo_remove/workspace/out/my-package/Cargo.toml b/tests/testsuite/cargo_remove/workspace/out/my-package/Cargo.toml new file mode 100644 index 000000000000..402780535e2a --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace/out/my-package/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "my-package" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/workspace/out/my-package/src/main.rs b/tests/testsuite/cargo_remove/workspace/out/my-package/src/main.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace/out/my-package/src/main.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/workspace/stderr.log b/tests/testsuite/cargo_remove/workspace/stderr.log new file mode 100644 index 000000000000..f037ebe28cec --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace/stderr.log @@ -0,0 +1,2 @@ + Removing semver from build-dependencies + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/workspace/stdout.log b/tests/testsuite/cargo_remove/workspace/stdout.log new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/testsuite/cargo_remove/workspace_non_virtual/in/Cargo.toml b/tests/testsuite/cargo_remove/workspace_non_virtual/in/Cargo.toml new file mode 100644 index 000000000000..dbac8ab442ea --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_non_virtual/in/Cargo.toml @@ -0,0 +1,30 @@ +[workspace] +members = [ "my-member" ] + +[workspace.dependencies] +semver = "0.1.0" + +[package] +name = "my-package" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = { workspace = true } + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/workspace_non_virtual/in/my-member/Cargo.toml b/tests/testsuite/cargo_remove/workspace_non_virtual/in/my-member/Cargo.toml new file mode 100644 index 000000000000..bb78904ffe6a --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_non_virtual/in/my-member/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "my-member" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/tests/testsuite/cargo_remove/workspace_non_virtual/in/my-member/src/main.rs b/tests/testsuite/cargo_remove/workspace_non_virtual/in/my-member/src/main.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/testsuite/cargo_remove/workspace_non_virtual/mod.rs b/tests/testsuite/cargo_remove/workspace_non_virtual/mod.rs new file mode 100644 index 000000000000..225fbec00e88 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_non_virtual/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +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(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["--package", "my-package", "--build", "semver"]) + .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/tests/testsuite/cargo_remove/workspace_non_virtual/out/Cargo.toml b/tests/testsuite/cargo_remove/workspace_non_virtual/out/Cargo.toml new file mode 100644 index 000000000000..9a3261484d12 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_non_virtual/out/Cargo.toml @@ -0,0 +1,24 @@ +[workspace] +members = [ "my-member" ] + +[package] +name = "my-package" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/workspace_non_virtual/out/my-member/Cargo.toml b/tests/testsuite/cargo_remove/workspace_non_virtual/out/my-member/Cargo.toml new file mode 100644 index 000000000000..bb78904ffe6a --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_non_virtual/out/my-member/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "my-member" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/tests/testsuite/cargo_remove/workspace_non_virtual/out/my-member/src/main.rs b/tests/testsuite/cargo_remove/workspace_non_virtual/out/my-member/src/main.rs new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/testsuite/cargo_remove/workspace_non_virtual/stderr.log b/tests/testsuite/cargo_remove/workspace_non_virtual/stderr.log new file mode 100644 index 000000000000..f037ebe28cec --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_non_virtual/stderr.log @@ -0,0 +1,2 @@ + Removing semver from build-dependencies + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/workspace_non_virtual/stdout.log b/tests/testsuite/cargo_remove/workspace_non_virtual/stdout.log new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/testsuite/cargo_remove/workspace_preserved/in/Cargo.toml b/tests/testsuite/cargo_remove/workspace_preserved/in/Cargo.toml new file mode 100644 index 000000000000..f1992ac886c9 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/in/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ "my-package", "my-other-package" ] + +[workspace.dependencies] +semver = "0.1.0" diff --git a/tests/testsuite/cargo_remove/workspace_preserved/in/my-other-package/Cargo.toml b/tests/testsuite/cargo_remove/workspace_preserved/in/my-other-package/Cargo.toml new file mode 100644 index 000000000000..d65972868cc6 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/in/my-other-package/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "my-other-package" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +semver = { workspace = true } +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/workspace_preserved/in/my-other-package/src/main.rs b/tests/testsuite/cargo_remove/workspace_preserved/in/my-other-package/src/main.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/in/my-other-package/src/main.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/workspace_preserved/in/my-package/Cargo.toml b/tests/testsuite/cargo_remove/workspace_preserved/in/my-package/Cargo.toml new file mode 100644 index 000000000000..6690d593ba19 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/in/my-package/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "my-package" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[build-dependencies] +semver = { workspace = true } + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/workspace_preserved/in/my-package/src/main.rs b/tests/testsuite/cargo_remove/workspace_preserved/in/my-package/src/main.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/in/my-package/src/main.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/workspace_preserved/mod.rs b/tests/testsuite/cargo_remove/workspace_preserved/mod.rs new file mode 100644 index 000000000000..225fbec00e88 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +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(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("remove") + .args(["--package", "my-package", "--build", "semver"]) + .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/tests/testsuite/cargo_remove/workspace_preserved/out/Cargo.toml b/tests/testsuite/cargo_remove/workspace_preserved/out/Cargo.toml new file mode 100644 index 000000000000..f1992ac886c9 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/out/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ "my-package", "my-other-package" ] + +[workspace.dependencies] +semver = "0.1.0" diff --git a/tests/testsuite/cargo_remove/workspace_preserved/out/my-other-package/Cargo.toml b/tests/testsuite/cargo_remove/workspace_preserved/out/my-other-package/Cargo.toml new file mode 100644 index 000000000000..d65972868cc6 --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/out/my-other-package/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "my-other-package" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +semver = { workspace = true } +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/workspace_preserved/out/my-other-package/src/main.rs b/tests/testsuite/cargo_remove/workspace_preserved/out/my-other-package/src/main.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/out/my-other-package/src/main.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/workspace_preserved/out/my-package/Cargo.toml b/tests/testsuite/cargo_remove/workspace_preserved/out/my-package/Cargo.toml new file mode 100644 index 000000000000..402780535e2a --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/out/my-package/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "my-package" +version = "0.1.0" + +[[bin]] +name = "main" +path = "src/main.rs" + +[dependencies] +docopt = "0.6" +rustc-serialize = "0.4" +semver = "0.1" +toml = "0.1" +clippy = "0.4" + +[dev-dependencies] +regex = "0.1.1" +serde = "1.0.90" + +[features] +std = ["serde/std", "semver/std"] diff --git a/tests/testsuite/cargo_remove/workspace_preserved/out/my-package/src/main.rs b/tests/testsuite/cargo_remove/workspace_preserved/out/my-package/src/main.rs new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/out/my-package/src/main.rs @@ -0,0 +1 @@ + diff --git a/tests/testsuite/cargo_remove/workspace_preserved/stderr.log b/tests/testsuite/cargo_remove/workspace_preserved/stderr.log new file mode 100644 index 000000000000..f037ebe28cec --- /dev/null +++ b/tests/testsuite/cargo_remove/workspace_preserved/stderr.log @@ -0,0 +1,2 @@ + Removing semver from build-dependencies + Updating `dummy-registry` index diff --git a/tests/testsuite/cargo_remove/workspace_preserved/stdout.log b/tests/testsuite/cargo_remove/workspace_preserved/stdout.log new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/testsuite/check.rs b/tests/testsuite/check.rs index cea327150d09..2cfb797cd4ea 100644 --- a/tests/testsuite/check.rs +++ b/tests/testsuite/check.rs @@ -2,11 +2,12 @@ use std::fmt::{self, Write}; +use crate::messages::raw_rustc_output; use cargo_test_support::install::exe; use cargo_test_support::paths::CargoPathExt; use cargo_test_support::registry::Package; use cargo_test_support::tools; -use cargo_test_support::{basic_manifest, git, project}; +use cargo_test_support::{basic_bin_manifest, basic_manifest, git, project}; #[cargo_test] fn check_success() { @@ -1176,3 +1177,273 @@ fn git_manifest_with_project() { ) .run(); } + +#[cargo_test] +fn check_fixable_warning() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file("src/lib.rs", "use std::io;") + .build(); + + foo.cargo("check") + .masquerade_as_nightly_cargo(&["auto-fix note"]) + .with_stderr_contains("[..] (run `cargo fix --lib -p foo` to apply 1 suggestion)") + .run(); +} + +#[cargo_test] +fn check_fixable_not_nightly() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file("src/lib.rs", "use std::io;") + .build(); + + let rustc_message = raw_rustc_output(&foo, "src/lib.rs", &[]); + let expected_output = format!( + "\ +[CHECKING] foo v0.0.1 ([..]) +{}\ +[WARNING] `foo` (lib) generated 1 warning +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +", + rustc_message + ); + foo.cargo("check").with_stderr(expected_output).run(); +} + +#[cargo_test] +fn check_fixable_test_warning() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file( + "src/lib.rs", + "\ +mod tests { + #[test] + fn t1() { + use std::io; + } +} + ", + ) + .build(); + + foo.cargo("check --all-targets") + .masquerade_as_nightly_cargo(&["auto-fix note"]) + .with_stderr_contains("[..] (run `cargo fix --lib -p foo --tests` to apply 1 suggestion)") + .run(); + foo.cargo("fix --lib -p foo --tests --allow-no-vcs").run(); + assert!(!foo.read_file("src/lib.rs").contains("use std::io;")); +} + +#[cargo_test] +fn check_fixable_error_no_fix() { + let foo = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file( + "src/lib.rs", + "use std::io;\n#[derive(Debug(x))]\nstruct Foo;", + ) + .build(); + + let rustc_message = raw_rustc_output(&foo, "src/lib.rs", &[]); + let expected_output = format!( + "\ +[CHECKING] foo v0.0.1 ([..]) +{}\ +[WARNING] `foo` (lib) generated 1 warning +[ERROR] could not compile `foo` due to previous error; 1 warning emitted +", + rustc_message + ); + foo.cargo("check") + .masquerade_as_nightly_cargo(&["auto-fix note"]) + .with_status(101) + .with_stderr(expected_output) + .run(); +} + +#[cargo_test] +fn check_fixable_warning_workspace() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar"] + "#, + ) + .file( + "foo/Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + "#, + ) + .file("foo/src/lib.rs", "use std::io;") + .file( + "bar/Cargo.toml", + r#" + [package] + name = "bar" + version = "0.0.1" + + [dependencies] + foo = { path = "../foo" } + "#, + ) + .file("bar/src/lib.rs", "use std::io;") + .build(); + + p.cargo("check") + .masquerade_as_nightly_cargo(&["auto-fix note"]) + .with_stderr_contains("[..] (run `cargo fix --lib -p foo` to apply 1 suggestion)") + .with_stderr_contains("[..] (run `cargo fix --lib -p bar` to apply 1 suggestion)") + .run(); +} + +#[cargo_test] +fn check_fixable_example() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + fn hello() -> &'static str { + "hello" + } + + pub fn main() { + println!("{}", hello()) + } + "#, + ) + .file("examples/ex1.rs", "use std::fmt; fn main() {}") + .build(); + p.cargo("check --all-targets") + .masquerade_as_nightly_cargo(&["auto-fix note"]) + .with_stderr_contains("[..] (run `cargo fix --example \"ex1\"` to apply 1 suggestion)") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn check_fixable_bench() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + #![feature(test)] + #[cfg(test)] + extern crate test; + + fn hello() -> &'static str { + "hello" + } + + pub fn main() { + println!("{}", hello()) + } + + #[bench] + fn bench_hello(_b: &mut test::Bencher) { + use std::io; + assert_eq!(hello(), "hello") + } + "#, + ) + .file( + "benches/bench.rs", + " + #![feature(test)] + extern crate test; + + #[bench] + fn bench(_b: &mut test::Bencher) { use std::fmt; } + ", + ) + .build(); + p.cargo("check --all-targets") + .masquerade_as_nightly_cargo(&["auto-fix note"]) + .with_stderr_contains("[..] (run `cargo fix --bench \"bench\"` to apply 1 suggestion)") + .run(); +} + +#[cargo_test(nightly, reason = "bench")] +fn check_fixable_mixed() { + let p = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file( + "src/main.rs", + r#" + #![feature(test)] + #[cfg(test)] + extern crate test; + + fn hello() -> &'static str { + "hello" + } + + pub fn main() { + println!("{}", hello()) + } + + #[bench] + fn bench_hello(_b: &mut test::Bencher) { + use std::io; + assert_eq!(hello(), "hello") + } + #[test] + fn t1() { + use std::fmt; + } + "#, + ) + .file("examples/ex1.rs", "use std::fmt; fn main() {}") + .file( + "benches/bench.rs", + " + #![feature(test)] + extern crate test; + + #[bench] + fn bench(_b: &mut test::Bencher) { use std::fmt; } + ", + ) + .build(); + p.cargo("check --all-targets") + .masquerade_as_nightly_cargo(&["auto-fix note"]) + .with_stderr_contains("[..] (run `cargo fix --bin \"foo\" --tests` to apply 2 suggestions)") + .with_stderr_contains("[..] (run `cargo fix --example \"ex1\"` to apply 1 suggestion)") + .with_stderr_contains("[..] (run `cargo fix --bench \"bench\"` to apply 1 suggestion)") + .run(); +} diff --git a/tests/testsuite/clean.rs b/tests/testsuite/clean.rs index 98c1e54ddedb..8aa633381d44 100644 --- a/tests/testsuite/clean.rs +++ b/tests/testsuite/clean.rs @@ -96,26 +96,26 @@ fn clean_multiple_packages_in_glob_char_path() { .build(); let foo_path = &p.build_dir().join("debug").join("deps"); + #[cfg(not(target_env = "msvc"))] + let file_glob = "foo-*"; + + #[cfg(target_env = "msvc")] + let file_glob = "foo.pdb"; + // Assert that build artifacts are produced p.cargo("build").run(); - assert_ne!(get_build_artifacts(foo_path).len(), 0); + assert_ne!(get_build_artifacts(foo_path, file_glob).len(), 0); // Assert that build artifacts are destroyed p.cargo("clean -p foo").run(); - assert_eq!(get_build_artifacts(foo_path).len(), 0); + assert_eq!(get_build_artifacts(foo_path, file_glob).len(), 0); } -fn get_build_artifacts(path: &PathBuf) -> Vec> { +fn get_build_artifacts(path: &PathBuf, file_glob: &str) -> Vec> { let pattern = path.to_str().expect("expected utf-8 path"); let pattern = glob::Pattern::escape(pattern); - #[cfg(not(target_env = "msvc"))] - const FILE: &str = "foo-*"; - - #[cfg(target_env = "msvc")] - const FILE: &str = "foo.pdb"; - - let path = PathBuf::from(pattern).join(FILE); + let path = PathBuf::from(pattern).join(file_glob); let path = path.to_str().expect("expected utf-8 path"); glob::glob(path) .expect("expected glob to run") @@ -123,6 +123,86 @@ fn get_build_artifacts(path: &PathBuf) -> Vec> { .collect::>>() } +#[cargo_test] +fn clean_p_only_cleans_specified_package() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = [ + "foo", + "foo_core", + "foo-base", + ] + "#, + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", "//! foo") + .file("foo_core/Cargo.toml", &basic_manifest("foo_core", "0.1.0")) + .file("foo_core/src/lib.rs", "//! foo_core") + .file("foo-base/Cargo.toml", &basic_manifest("foo-base", "0.1.0")) + .file("foo-base/src/lib.rs", "//! foo-base") + .build(); + + let fingerprint_path = &p.build_dir().join("debug").join(".fingerprint"); + + p.cargo("build -p foo -p foo_core -p foo-base").run(); + + let mut fingerprint_names = get_fingerprints_without_hashes(fingerprint_path); + + // Artifacts present for all after building + assert!(fingerprint_names.iter().any(|e| e == "foo")); + let num_foo_core_artifacts = fingerprint_names + .iter() + .filter(|&e| e == "foo_core") + .count(); + assert_ne!(num_foo_core_artifacts, 0); + let num_foo_base_artifacts = fingerprint_names + .iter() + .filter(|&e| e == "foo-base") + .count(); + assert_ne!(num_foo_base_artifacts, 0); + + p.cargo("clean -p foo").run(); + + fingerprint_names = get_fingerprints_without_hashes(fingerprint_path); + + // Cleaning `foo` leaves artifacts for the others + assert!(!fingerprint_names.iter().any(|e| e == "foo")); + assert_eq!( + fingerprint_names + .iter() + .filter(|&e| e == "foo_core") + .count(), + num_foo_core_artifacts, + ); + assert_eq!( + fingerprint_names + .iter() + .filter(|&e| e == "foo-base") + .count(), + num_foo_core_artifacts, + ); +} + +fn get_fingerprints_without_hashes(fingerprint_path: &Path) -> Vec { + std::fs::read_dir(fingerprint_path) + .expect("Build dir should be readable") + .filter_map(|entry| entry.ok()) + .map(|entry| { + let name = entry.file_name(); + let name = name + .into_string() + .expect("fingerprint name should be UTF-8"); + name.rsplit_once('-') + .expect("Name should contain at least one hyphen") + .0 + .to_owned() + }) + .collect() +} + #[cargo_test] fn clean_release() { let p = project() diff --git a/tests/testsuite/credential_process.rs b/tests/testsuite/credential_process.rs index 38c8ca13e11a..0739e8e2bff8 100644 --- a/tests/testsuite/credential_process.rs +++ b/tests/testsuite/credential_process.rs @@ -1,6 +1,6 @@ //! Tests for credential-process. -use cargo_test_support::registry::TestRegistry; +use cargo_test_support::registry::{Package, TestRegistry}; use cargo_test_support::{basic_manifest, cargo_process, paths, project, registry, Project}; use std::fs; @@ -94,6 +94,16 @@ fn warn_both_token_and_process() { .file("src/lib.rs", "") .build(); + // HACK: Inject `foo` directly into the index so `publish` won't block for it to be in + // the index. + // + // This is to ensure we can verify the Summary we post to the registry as doing so precludes + // the registry from processing the publish. + Package::new("foo", "0.1.0") + .file("src/lib.rs", "") + .alternative(true) + .publish(); + p.cargo("publish --no-verify --registry alternative -Z credential-process") .masquerade_as_nightly_cargo(&["credential-process"]) .with_status(101) @@ -124,7 +134,9 @@ Only one of these values may be set, remove one or the other to proceed. "\ [UPDATING] [..] [PACKAGING] foo v0.1.0 [..] +[PACKAGED] [..] [UPLOADING] foo v0.1.0 [..] +[UPDATING] [..] ", ) .run(); @@ -139,7 +151,7 @@ Only one of these values may be set, remove one or the other to proceed. /// * Create a simple `foo` project to run the test against. /// * Configure the credential-process config. /// -/// Returns returns the simple `foo` project to test against and the API server handle. +/// Returns the simple `foo` project to test against and the API server handle. fn get_token_test() -> (Project, TestRegistry) { // API server that checks that the token is included correctly. let server = registry::RegistryBuilder::new() @@ -197,7 +209,9 @@ fn publish() { "\ [UPDATING] [..] [PACKAGING] foo v0.1.0 [..] +[PACKAGED] [..] [UPLOADING] foo v0.1.0 [..] +[UPDATING] [..] ", ) .run(); diff --git a/tests/testsuite/cross_publish.rs b/tests/testsuite/cross_publish.rs index aa02a8389f8f..5b1416ef0729 100644 --- a/tests/testsuite/cross_publish.rs +++ b/tests/testsuite/cross_publish.rs @@ -46,6 +46,7 @@ fn simple_cross_package() { [VERIFYING] foo v0.0.0 ([CWD]) [COMPILING] foo v0.0.0 ([CWD]/target/package/foo-0.0.0) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] 4 files, [..] ([..] compressed) ", ) .run(); @@ -66,7 +67,8 @@ fn publish_with_target() { return; } - let registry = registry::init(); + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); let p = project() .file( @@ -108,7 +110,9 @@ fn publish_with_target() { [VERIFYING] foo v0.0.0 ([CWD]) [COMPILING] foo v0.0.0 ([CWD]/target/package/foo-0.0.0) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] [..] [UPLOADING] foo v0.0.0 ([CWD]) +[UPDATING] crates.io index ", ) .run(); diff --git a/tests/testsuite/features2.rs b/tests/testsuite/features2.rs index 15035051a026..6103e4bd5d45 100644 --- a/tests/testsuite/features2.rs +++ b/tests/testsuite/features2.rs @@ -2472,3 +2472,74 @@ fn all_features_merges_with_features() { .with_stdout("it works") .run(); } + +#[cargo_test] +fn dep_with_optional_host_deps_activated() { + // To prevent regression like rust-lang/cargo#11330 + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + edition = "2021" + + [dependencies] + serde = { path = "serde", features = ["derive", "build"] } + "#, + ) + .file("src/lib.rs", "") + .file( + "serde/Cargo.toml", + r#" + [package] + name = "serde" + version = "0.1.0" + edition = "2021" + + [dependencies] + serde_derive = { path = "../serde_derive", optional = true } + + [build-dependencies] + serde_build = { path = "../serde_build", optional = true } + + [features] + derive = ["dep:serde_derive"] + build = ["dep:serde_build"] + "#, + ) + .file("serde/src/lib.rs", "") + .file("serde/build.rs", "fn main() {}") + .file( + "serde_derive/Cargo.toml", + r#" + [package] + name = "serde_derive" + version = "0.1.0" + edition = "2021" + + [lib] + proc-macro = true + "#, + ) + .file("serde_derive/src/lib.rs", "") + .file( + "serde_build/Cargo.toml", + &basic_manifest("serde_build", "0.1.0"), + ) + .file("serde_build/src/lib.rs", "") + .build(); + + p.cargo("check") + .with_stderr( + "\ +[COMPILING] serde_build v0.1.0 ([CWD]/serde_build) +[COMPILING] serde_derive v0.1.0 ([CWD]/serde_derive) +[COMPILING] serde v0.1.0 ([CWD]/serde) +[CHECKING] foo v0.1.0 ([CWD]) +[FINISHED] dev [..] +", + ) + .run(); +} diff --git a/tests/testsuite/features_namespaced.rs b/tests/testsuite/features_namespaced.rs index 8aefdbdc7b4a..72ad6c5cb209 100644 --- a/tests/testsuite/features_namespaced.rs +++ b/tests/testsuite/features_namespaced.rs @@ -858,7 +858,9 @@ bar v1.0.0 #[cargo_test] fn publish_no_implicit() { + // HACK below allows us to use a local registry let registry = registry::init(); + // Does not include implicit features or dep: syntax on publish. Package::new("opt-dep1", "1.0.0").publish(); Package::new("opt-dep2", "1.0.0").publish(); @@ -885,13 +887,24 @@ fn publish_no_implicit() { .file("src/lib.rs", "") .build(); + // HACK: Inject `foo` directly into the index so `publish` won't block for it to be in + // the index. + // + // This is to ensure we can verify the Summary we post to the registry as doing so precludes + // the registry from processing the publish. + Package::new("foo", "0.1.0") + .file("src/lib.rs", "") + .publish(); + p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_stderr( "\ [UPDATING] [..] [PACKAGING] foo v0.1.0 [..] +[PACKAGED] [..] [UPLOADING] foo v0.1.0 [..] +[UPDATING] [..] ", ) .run(); @@ -971,7 +984,9 @@ feat = ["opt-dep1"] #[cargo_test] fn publish() { + // HACK below allows us to use a local registry let registry = registry::init(); + // Publish behavior with explicit dep: syntax. Package::new("bar", "1.0.0").publish(); let p = project() @@ -997,6 +1012,15 @@ fn publish() { .file("src/lib.rs", "") .build(); + // HACK: Inject `foo` directly into the index so `publish` won't block for it to be in + // the index. + // + // This is to ensure we can verify the Summary we post to the registry as doing so precludes + // the registry from processing the publish. + Package::new("foo", "0.1.0") + .file("src/lib.rs", "") + .publish(); + p.cargo("publish") .replace_crates_io(registry.index_url()) .with_stderr( @@ -1006,7 +1030,9 @@ fn publish() { [VERIFYING] foo v0.1.0 [..] [COMPILING] foo v0.1.0 [..] [FINISHED] [..] +[PACKAGED] [..] [UPLOADING] foo v0.1.0 [..] +[UPDATING] [..] ", ) .run(); diff --git a/tests/testsuite/git.rs b/tests/testsuite/git.rs index 615787f19346..cdcbe95848d1 100644 --- a/tests/testsuite/git.rs +++ b/tests/testsuite/git.rs @@ -10,6 +10,7 @@ use std::sync::Arc; use std::thread; use cargo_test_support::paths::{self, CargoPathExt}; +use cargo_test_support::registry::Package; use cargo_test_support::{basic_lib_manifest, basic_manifest, git, main_file, path2url, project}; use cargo_test_support::{sleep_ms, t, Project}; @@ -3619,3 +3620,35 @@ fn _corrupted_checkout(with_cli: bool) { e.run(); assert!(ok.exists()); } + +#[cargo_test] +fn cleans_temp_pack_files() { + // Checks that cargo removes temp files left by libgit2 when it is + // interrupted (see clean_repo_temp_files). + Package::new("bar", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "1.0" + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("fetch").run(); + // Simulate what happens when libgit2 is interrupted while indexing a pack file. + let tmp_path = super::git_gc::find_index().join(".git/objects/pack/pack_git2_91ab40da04fdc2e7"); + fs::write(&tmp_path, "test").unwrap(); + let mut perms = fs::metadata(&tmp_path).unwrap().permissions(); + perms.set_readonly(true); + fs::set_permissions(&tmp_path, perms).unwrap(); + + // Trigger an index update. + p.cargo("generate-lockfile").run(); + assert!(!tmp_path.exists()); +} diff --git a/tests/testsuite/git_gc.rs b/tests/testsuite/git_gc.rs index ff65c448bb75..045bc78ad5e0 100644 --- a/tests/testsuite/git_gc.rs +++ b/tests/testsuite/git_gc.rs @@ -11,7 +11,7 @@ use cargo_test_support::registry::Package; use url::Url; -fn find_index() -> PathBuf { +pub fn find_index() -> PathBuf { let dir = paths::home().join(".cargo/registry/index"); dir.read_dir().unwrap().next().unwrap().unwrap().path() } diff --git a/tests/testsuite/install.rs b/tests/testsuite/install.rs index 70fff6aaaa78..0538a872f48f 100644 --- a/tests/testsuite/install.rs +++ b/tests/testsuite/install.rs @@ -2031,3 +2031,41 @@ fn install_semver_metadata() { ) .run(); } + +#[cargo_test] +fn no_auto_fix_note() { + Package::new("auto_fix", "0.0.1") + .file("src/lib.rs", "use std::io;") + .file( + "src/main.rs", + &format!("extern crate {}; use std::io; fn main() {{}}", "auto_fix"), + ) + .publish(); + + // This should not contain a suggestion to run `cargo fix` + // + // This is checked by matching the full output as `with_stderr_does_not_contain` + // can be brittle + cargo_process("install auto_fix") + .masquerade_as_nightly_cargo(&["auto-fix note"]) + .with_stderr( + "\ +[UPDATING] `[..]` index +[DOWNLOADING] crates ... +[DOWNLOADED] auto_fix v0.0.1 (registry [..]) +[INSTALLING] auto_fix v0.0.1 +[COMPILING] auto_fix v0.0.1 +[FINISHED] release [optimized] target(s) in [..] +[INSTALLING] [CWD]/home/.cargo/bin/auto_fix[EXE] +[INSTALLED] package `auto_fix v0.0.1` (executable `auto_fix[EXE]`) +[WARNING] be sure to add `[..]` to your PATH to be able to run the installed binaries +", + ) + .run(); + assert_has_installed_exe(cargo_home(), "auto_fix"); + + cargo_process("uninstall auto_fix") + .with_stderr("[REMOVING] [CWD]/home/.cargo/bin/auto_fix[EXE]") + .run(); + assert_has_not_installed_exe(cargo_home(), "auto_fix"); +} diff --git a/tests/testsuite/lto.rs b/tests/testsuite/lto.rs index ebbeba0b58f6..ba1041c10ab7 100644 --- a/tests/testsuite/lto.rs +++ b/tests/testsuite/lto.rs @@ -482,7 +482,7 @@ fn verify_lto(output: &Output, krate: &str, krate_info: &str, expected_lto: Lto) fn cdylib_and_rlib() { let p = project_with_dep("'cdylib', 'rlib'"); let output = p.cargo("build --release -v").exec_with_output().unwrap(); - // `registry` is ObjectAndBitcode because because it needs Object for the + // `registry` is ObjectAndBitcode because it needs Object for the // rlib, and Bitcode for the cdylib (which doesn't support LTO). verify_lto( &output, diff --git a/tests/testsuite/new.rs b/tests/testsuite/new.rs index 747670f89d9a..da2dc919f554 100644 --- a/tests/testsuite/new.rs +++ b/tests/testsuite/new.rs @@ -510,6 +510,26 @@ fn git_default_branch() { assert_eq!(head.symbolic_target().unwrap(), "refs/heads/hello"); } +#[cargo_test] +fn non_utf8_str_in_ignore_file() { + let gitignore = paths::home().join(".gitignore"); + File::create(gitignore).unwrap(); + + fs::write(paths::home().join(".gitignore"), &[0xFF, 0xFE]).unwrap(); + + cargo_process(&format!("init {} --vcs git", paths::home().display())) + .with_status(101) + .with_stderr( + "\ +error: Failed to create package `home` at `[..]` + +Caused by: + Character at line 0 is invalid. Cargo only supports UTF-8. +", + ) + .run(); +} + #[cfg(unix)] #[cargo_test] fn path_with_invalid_character() { diff --git a/tests/testsuite/package.rs b/tests/testsuite/package.rs index 5ebde46358e2..d2e4c9ab72e2 100644 --- a/tests/testsuite/package.rs +++ b/tests/testsuite/package.rs @@ -39,6 +39,7 @@ See [..] [VERIFYING] foo v0.0.1 ([CWD]) [COMPILING] foo v0.0.1 ([CWD][..]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] 4 files, [..] ([..] compressed) ", ) .run(); @@ -77,6 +78,7 @@ See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for [VERIFYING] foo v0.0.1 ([CWD]) [COMPILING] foo v0.0.1 ([CWD][..]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] [..] files, [..] ([..] compressed) ", ) .run(); @@ -103,6 +105,7 @@ See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for [VERIFYING] foo v0.0.1 ([CWD]) [COMPILING] foo v0.0.1 ([CWD][..]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] [..] files, [..] ([..] compressed) ", ) .run(); @@ -129,6 +132,7 @@ See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for [VERIFYING] foo v0.0.1 ([CWD]) [COMPILING] foo v0.0.1 ([CWD][..]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] [..] files, [..] ([..] compressed) ", ) .run(); @@ -158,6 +162,7 @@ See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for [ARCHIVING] Cargo.toml [ARCHIVING] Cargo.toml.orig [ARCHIVING] src/main.rs +[PACKAGED] 5 files, [..] ([..] compressed) ", ) .run(); @@ -198,6 +203,7 @@ See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for [ARCHIVING] Cargo.toml [ARCHIVING] Cargo.toml.orig [ARCHIVING] src/lib.rs +[PACKAGED] 4 files, [..] ([..] compressed) ", ) .run(); @@ -239,6 +245,7 @@ See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for [VERIFYING] foo v0.0.1 ([CWD]) [COMPILING] foo v0.0.1 ([CWD][..]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] [..] files, [..] ([..] compressed) ", ) .run(); @@ -488,6 +495,7 @@ See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for [ARCHIVING] some_dir/file_deep_4 [ARCHIVING] some_dir/file_deep_5 [ARCHIVING] src/main.rs +[PACKAGED] 15 files, [..] ([..] compressed) ", ) .run(); @@ -555,6 +563,7 @@ See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for [ARCHIVING] Cargo.toml.orig [ARCHIVING] foo.txt [ARCHIVING] src/main.rs +[PACKAGED] 7 files, [..] ([..] compressed) ", ) .run(); @@ -708,6 +717,7 @@ See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for [VERIFYING] foo v0.0.1 ([CWD]) [COMPILING] foo v0.0.1 ([CWD][..]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] 4 files, [..] ([..] compressed) ", ) .run(); @@ -775,6 +785,7 @@ See [..] [VERIFYING] foo v0.0.1 ([CWD]) [COMPILING] foo v0.0.1 ([CWD][..]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] 5 files, [..] ([..] compressed) ", ) .run(); @@ -1788,6 +1799,7 @@ fn invalid_license_file_path() { Please update the license-file setting in the manifest at `[..]/foo/Cargo.toml` This may become a hard error in the future. [PACKAGING] foo v1.0.0 ([..]/foo) +[PACKAGED] [..] files, [..] ([..] compressed) ", ) .run(); @@ -1835,6 +1847,7 @@ subdir/LICENSE [ARCHIVING] Cargo.toml.orig [ARCHIVING] src/lib.rs [ARCHIVING] subdir/LICENSE +[PACKAGED] 5 files, [..] ([..] compressed) ", ) .run(); @@ -1891,6 +1904,7 @@ src/lib.rs [VERIFYING] foo v1.0.0 [..] [COMPILING] foo v1.0.0 [..] [FINISHED] [..] +[PACKAGED] 4 files, [..] ([..] compressed) ", ) .run(); @@ -1949,6 +1963,7 @@ src/lib.rs [VERIFYING] foo v1.0.0 [..] [COMPILING] foo v1.0.0 [..] [FINISHED] [..] +[PACKAGED] 4 files, [..] ([..] compressed) ", ) .run(); @@ -1994,6 +2009,7 @@ fn package_restricted_windows() { [PACKAGING] foo [..] [VERIFYING] foo [..] [COMPILING] foo [..] +[PACKAGED] [..] files, [..] ([..] compressed) [FINISHED] [..] ", ) @@ -2367,12 +2383,14 @@ See [..] [VERIFYING] bar v0.0.1 ([CWD]/bar) [COMPILING] bar v0.0.1 ([CWD][..]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] [..] files, [..] ([..] compressed) [WARNING] manifest has no documentation, [..] See [..] [PACKAGING] foo v0.0.1 ([CWD]) [VERIFYING] foo v0.0.1 ([CWD]) [COMPILING] foo v0.0.1 ([CWD][..]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] [..] files, [..] ([..] compressed) ", ) .run(); @@ -2451,3 +2469,296 @@ version = "0.1.0" &[("Cargo.toml", &rewritten_toml)], ); } + +fn verify_packaged_status_line( + output: std::process::Output, + num_files: usize, + uncompressed_size: u64, + compressed_size: u64, +) { + use cargo::util::human_readable_bytes; + + let stderr = String::from_utf8(output.stderr).unwrap(); + let mut packaged_lines = stderr + .lines() + .filter(|line| line.trim().starts_with("Packaged")); + let packaged_line = packaged_lines + .next() + .expect("`Packaged` status line should appear in stderr"); + assert!( + packaged_lines.next().is_none(), + "Only one `Packaged` status line should appear in stderr" + ); + let size_info = packaged_line.trim().trim_start_matches("Packaged").trim(); + let uncompressed = human_readable_bytes(uncompressed_size); + let compressed = human_readable_bytes(compressed_size); + let expected = format!( + "{} files, {:.1}{} ({:.1}{} compressed)", + num_files, uncompressed.0, uncompressed.1, compressed.0, compressed.1 + ); + assert_eq!(size_info, expected); +} + +#[cargo_test] +fn basic_filesizes() { + let cargo_toml_orig_contents = r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + exclude = ["*.txt"] + license = "MIT" + description = "foo" + "#; + let main_rs_contents = r#"fn main() { println!("🦀"); }"#; + let cargo_toml_contents = format!( + r#"{} +[package] +name = "foo" +version = "0.0.1" +authors = [] +exclude = ["*.txt"] +description = "foo" +license = "MIT" +"#, + cargo::core::package::MANIFEST_PREAMBLE + ); + let cargo_lock_contents = 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 p = project() + .file("Cargo.toml", cargo_toml_orig_contents) + .file("src/main.rs", main_rs_contents) + .file("src/bar.txt", "Ignored text file contents") // should be ignored when packaging + .build(); + + let uncompressed_size = (cargo_toml_orig_contents.len() + + main_rs_contents.len() + + cargo_toml_contents.len() + + cargo_lock_contents.len()) as u64; + let output = p.cargo("package").exec_with_output().unwrap(); + + assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); + p.cargo("package -l") + .with_stdout( + "\ +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/main.rs +", + ) + .run(); + p.cargo("package").with_stdout("").run(); + + let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); + let compressed_size = f.metadata().unwrap().len(); + verify_packaged_status_line(output, 4, uncompressed_size, compressed_size); + validate_crate_contents( + f, + "foo-0.0.1.crate", + &["Cargo.lock", "Cargo.toml", "Cargo.toml.orig", "src/main.rs"], + &[ + ("Cargo.lock", cargo_lock_contents), + ("Cargo.toml", &cargo_toml_contents), + ("Cargo.toml.orig", cargo_toml_orig_contents), + ("src/main.rs", main_rs_contents), + ], + ); +} + +#[cargo_test] +fn larger_filesizes() { + let cargo_toml_orig_contents = r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#; + let lots_of_crabs = std::iter::repeat("🦀").take(1337).collect::(); + let main_rs_contents = format!(r#"fn main() {{ println!("{}"); }}"#, lots_of_crabs); + let bar_txt_contents = "This file is relatively uncompressible, to increase the compressed + package size beyond 1KiB. + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est + laborum."; + let cargo_toml_contents = format!( + r#"{} +[package] +name = "foo" +version = "0.0.1" +authors = [] +description = "foo" +license = "MIT" +"#, + cargo::core::package::MANIFEST_PREAMBLE + ); + let cargo_lock_contents = 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 p = project() + .file("Cargo.toml", cargo_toml_orig_contents) + .file("src/main.rs", &main_rs_contents) + .file("src/bar.txt", bar_txt_contents) + .build(); + + let uncompressed_size = (cargo_toml_orig_contents.len() + + main_rs_contents.len() + + cargo_toml_contents.len() + + cargo_lock_contents.len() + + bar_txt_contents.len()) as u64; + + let output = p.cargo("package").exec_with_output().unwrap(); + assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); + p.cargo("package -l") + .with_stdout( + "\ +Cargo.lock +Cargo.toml +Cargo.toml.orig +src/bar.txt +src/main.rs +", + ) + .run(); + p.cargo("package").with_stdout("").run(); + + let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); + let compressed_size = f.metadata().unwrap().len(); + verify_packaged_status_line(output, 5, uncompressed_size, compressed_size); + validate_crate_contents( + f, + "foo-0.0.1.crate", + &[ + "Cargo.lock", + "Cargo.toml", + "Cargo.toml.orig", + "src/bar.txt", + "src/main.rs", + ], + &[ + ("Cargo.lock", cargo_lock_contents), + ("Cargo.toml", &cargo_toml_contents), + ("Cargo.toml.orig", cargo_toml_orig_contents), + ("src/bar.txt", bar_txt_contents), + ("src/main.rs", &main_rs_contents), + ], + ); +} + +#[cargo_test] +fn symlink_filesizes() { + if !symlink_supported() { + return; + } + + let cargo_toml_orig_contents = r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + license = "MIT" + description = "foo" + "#; + let lots_of_crabs = std::iter::repeat("🦀").take(1337).collect::(); + let main_rs_contents = format!(r#"fn main() {{ println!("{}"); }}"#, lots_of_crabs); + let bar_txt_contents = "This file is relatively uncompressible, to increase the compressed + package size beyond 1KiB. + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt + ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in + reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur + sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est + laborum."; + let cargo_toml_contents = format!( + r#"{} +[package] +name = "foo" +version = "0.0.1" +authors = [] +description = "foo" +license = "MIT" +"#, + cargo::core::package::MANIFEST_PREAMBLE + ); + let cargo_lock_contents = 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 p = project() + .file("Cargo.toml", cargo_toml_orig_contents) + .file("src/main.rs", &main_rs_contents) + .file("bla/bar.txt", bar_txt_contents) + .symlink("src/main.rs", "src/main.rs.bak") + .symlink_dir("bla", "foo") + .build(); + + let uncompressed_size = (cargo_toml_orig_contents.len() + + main_rs_contents.len() * 2 + + cargo_toml_contents.len() + + cargo_lock_contents.len() + + bar_txt_contents.len() * 2) as u64; + + let output = p.cargo("package").exec_with_output().unwrap(); + assert!(p.root().join("target/package/foo-0.0.1.crate").is_file()); + p.cargo("package -l") + .with_stdout( + "\ +Cargo.lock +Cargo.toml +Cargo.toml.orig +bla/bar.txt +foo/bar.txt +src/main.rs +src/main.rs.bak +", + ) + .run(); + p.cargo("package").with_stdout("").run(); + + let f = File::open(&p.root().join("target/package/foo-0.0.1.crate")).unwrap(); + let compressed_size = f.metadata().unwrap().len(); + verify_packaged_status_line(output, 7, uncompressed_size, compressed_size); + validate_crate_contents( + f, + "foo-0.0.1.crate", + &[ + "Cargo.lock", + "Cargo.toml", + "Cargo.toml.orig", + "bla/bar.txt", + "foo/bar.txt", + "src/main.rs", + "src/main.rs.bak", + ], + &[ + ("Cargo.lock", cargo_lock_contents), + ("Cargo.toml", &cargo_toml_contents), + ("Cargo.toml.orig", cargo_toml_orig_contents), + ("bla/bar.txt", bar_txt_contents), + ("foo/bar.txt", bar_txt_contents), + ("src/main.rs", &main_rs_contents), + ("src/main.rs.bak", &main_rs_contents), + ], + ); +} diff --git a/tests/testsuite/publish.rs b/tests/testsuite/publish.rs index f121426ae072..aed1aa396a41 100644 --- a/tests/testsuite/publish.rs +++ b/tests/testsuite/publish.rs @@ -85,22 +85,9 @@ fn validate_upload_li() { ); } -fn validate_upload_foo_clean() { - publish::validate_upload( - CLEAN_FOO_JSON, - "foo-0.0.1.crate", - &[ - "Cargo.lock", - "Cargo.toml", - "Cargo.toml.orig", - "src/main.rs", - ".cargo_vcs_info.json", - ], - ); -} - #[cargo_test] fn simple() { + // HACK below allows us to use a local registry let registry = registry::init(); let p = project() @@ -118,6 +105,15 @@ fn simple() { .file("src/main.rs", "fn main() {}") .build(); + // HACK: Inject `foo` directly into the index so `publish` won't block for it to be in + // the index. + // + // This is to ensure we can verify the Summary we post to the registry as doing so precludes + // the registry from processing the publish. + Package::new("foo", "0.0.1") + .file("src/lib.rs", "") + .publish(); + p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_stderr( @@ -126,7 +122,9 @@ fn simple() { [WARNING] manifest has no documentation, [..] See [..] [PACKAGING] foo v0.0.1 ([CWD]) +[PACKAGED] [..] files, [..] ([..] compressed) [UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] [..] ", ) .run(); @@ -138,7 +136,8 @@ See [..] // `[registry]` table. #[cargo_test] fn old_token_location() { - let registry = registry::init(); + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); let p = project() .file( @@ -178,17 +177,21 @@ fn old_token_location() { [WARNING] manifest has no documentation, [..] See [..] [PACKAGING] foo v0.0.1 ([CWD]) +[PACKAGED] [..] files, [..] ([..] compressed) [UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] [..] ", ) .run(); - validate_upload_foo(); + // Skip `validate_upload_foo` as we just cared we got far enough for verify the token behavior. + // Other tests will verify the endpoint gets the right payload. } #[cargo_test] fn simple_with_index() { - let registry = registry::init(); + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); let p = project() .file( @@ -216,15 +219,20 @@ fn simple_with_index() { [..] [..] [..] -[UPLOADING] foo v0.0.1 ([CWD])", +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] [..] +", ) .run(); - validate_upload_foo(); + // Skip `validate_upload_foo` as we just cared we got far enough for verify the VCS behavior. + // Other tests will verify the endpoint gets the right payload. } #[cargo_test] fn git_deps() { + // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project() @@ -262,6 +270,7 @@ the `git` specification will be removed from the dependency declaration. #[cargo_test] fn path_dependency_no_version() { + // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project() @@ -301,6 +310,7 @@ the `path` specification will be removed from the dependency declaration. #[cargo_test] fn unpublishable_crate() { + // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project() @@ -333,6 +343,7 @@ fn unpublishable_crate() { #[cargo_test] fn dont_publish_dirty() { + // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project().file("bar", "").build(); @@ -374,7 +385,8 @@ to proceed despite this and include the uncommitted changes, pass the `--allow-d #[cargo_test] fn publish_clean() { - let registry = registry::init(); + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); let p = project().build(); @@ -405,16 +417,21 @@ fn publish_clean() { [VERIFYING] foo v0.0.1 ([CWD]) [..] [..] -[UPLOADING] foo v0.0.1 ([CWD])", +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] [..] +", ) .run(); - validate_upload_foo_clean(); + // Skip `validate_upload_foo_clean` as we just cared we got far enough for verify the VCS behavior. + // Other tests will verify the endpoint gets the right payload. } #[cargo_test] fn publish_in_sub_repo() { - let registry = registry::init(); + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); let p = project().no_manifest().file("baz", "").build(); @@ -446,16 +463,21 @@ fn publish_in_sub_repo() { [VERIFYING] foo v0.0.1 ([CWD]) [..] [..] -[UPLOADING] foo v0.0.1 ([CWD])", +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] [..] +", ) .run(); - validate_upload_foo_clean(); + // Skip `validate_upload_foo_clean` as we just cared we got far enough for verify the VCS behavior. + // Other tests will verify the endpoint gets the right payload. } #[cargo_test] fn publish_when_ignored() { - let registry = registry::init(); + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); let p = project().file("baz", "").build(); @@ -487,27 +509,21 @@ fn publish_when_ignored() { [VERIFYING] foo v0.0.1 ([CWD]) [..] [..] -[UPLOADING] foo v0.0.1 ([CWD])", +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] [..] +", ) .run(); - publish::validate_upload( - CLEAN_FOO_JSON, - "foo-0.0.1.crate", - &[ - "Cargo.lock", - "Cargo.toml", - "Cargo.toml.orig", - "src/main.rs", - ".gitignore", - ".cargo_vcs_info.json", - ], - ); + // Skip `validate_upload` as we just cared we got far enough for verify the VCS behavior. + // Other tests will verify the endpoint gets the right payload. } #[cargo_test] fn ignore_when_crate_ignored() { - let registry = registry::init(); + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); let p = project().no_manifest().file("bar/baz", "").build(); @@ -538,25 +554,20 @@ fn ignore_when_crate_ignored() { [VERIFYING] foo v0.0.1 ([CWD]) [..] [..] -[UPLOADING] foo v0.0.1 ([CWD])", +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] [..] +", ) .run(); - publish::validate_upload( - CLEAN_FOO_JSON, - "foo-0.0.1.crate", - &[ - "Cargo.lock", - "Cargo.toml", - "Cargo.toml.orig", - "src/main.rs", - "baz", - ], - ); + // Skip `validate_upload` as we just cared we got far enough for verify the VCS behavior. + // Other tests will verify the endpoint gets the right payload. } #[cargo_test] fn new_crate_rejected() { + // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project().file("baz", "").build(); @@ -589,6 +600,7 @@ fn new_crate_rejected() { #[cargo_test] fn dry_run() { + // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project() @@ -617,6 +629,7 @@ See [..] [VERIFYING] foo v0.0.1 ([CWD]) [COMPILING] foo v0.0.1 [..] [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] [..] files, [..] ([..] compressed) [UPLOADING] foo v0.0.1 ([CWD]) [WARNING] aborting upload due to dry run ", @@ -692,6 +705,7 @@ fn publish_empty_list() { #[cargo_test] fn publish_allowed_registry() { + // HACK below allows us to use a local registry registry::alt_init(); let p = project().build(); @@ -715,6 +729,16 @@ fn publish_allowed_registry() { .file("src/main.rs", "fn main() {}") .build(); + // HACK: Inject `foo` directly into the index so `publish` won't block for it to be in + // the index. + // + // This is to ensure we can verify the Summary we post to the registry as doing so precludes + // the registry from processing the publish. + Package::new("foo", "0.0.1") + .file("src/lib.rs", "") + .alternative(true) + .publish(); + p.cargo("publish --registry alternative") .with_stderr( "\ @@ -723,7 +747,10 @@ fn publish_allowed_registry() { [VERIFYING] foo v0.0.1 ([CWD]) [..] [..] -[UPLOADING] foo v0.0.1 ([CWD])", +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] `alternative` index +", ) .run(); @@ -742,6 +769,7 @@ fn publish_allowed_registry() { #[cargo_test] fn publish_implicitly_to_only_allowed_registry() { + // HACK below allows us to use a local registry registry::alt_init(); let p = project().build(); @@ -765,6 +793,16 @@ fn publish_implicitly_to_only_allowed_registry() { .file("src/main.rs", "fn main() {}") .build(); + // HACK: Inject `foo` directly into the index so `publish` won't block for it to be in + // the index. + // + // This is to ensure we can verify the Summary we post to the registry as doing so precludes + // the registry from processing the publish. + Package::new("foo", "0.0.1") + .file("src/lib.rs", "") + .alternative(true) + .publish(); + p.cargo("publish") .with_stderr( "\ @@ -774,7 +812,10 @@ fn publish_implicitly_to_only_allowed_registry() { [VERIFYING] foo v0.0.1 ([CWD]) [..] [..] -[UPLOADING] foo v0.0.1 ([CWD])", +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] `alternative` index +", ) .run(); @@ -857,7 +898,8 @@ fn block_publish_no_registry() { // Explicitly setting `crates-io` in the publish list. #[cargo_test] fn publish_with_crates_io_explicit() { - let registry = registry::init(); + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); let p = project() .file( @@ -896,14 +938,18 @@ The registry `alternative` is not listed in the `package.publish` value in Cargo [VERIFYING] foo v0.0.1 ([CWD]) [..] [..] -[UPLOADING] foo v0.0.1 ([CWD])", +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] crates.io index +", ) .run(); } #[cargo_test] fn publish_with_select_features() { - let registry = registry::init(); + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); let p = project() .file( @@ -940,7 +986,9 @@ fn publish_with_select_features() { [VERIFYING] foo v0.0.1 ([CWD]) [..] [..] +[..] [UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] crates.io index ", ) .run(); @@ -948,7 +996,8 @@ fn publish_with_select_features() { #[cargo_test] fn publish_with_all_features() { - let registry = registry::init(); + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); let p = project() .file( @@ -985,7 +1034,9 @@ fn publish_with_all_features() { [VERIFYING] foo v0.0.1 ([CWD]) [..] [..] +[..] [UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] crates.io index ", ) .run(); @@ -993,6 +1044,7 @@ fn publish_with_all_features() { #[cargo_test] fn publish_with_no_default_features() { + // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project() @@ -1028,6 +1080,7 @@ fn publish_with_no_default_features() { #[cargo_test] fn publish_with_patch() { + // HACK below allows us to use a local registry let registry = registry::init(); Package::new("bar", "1.0.0").publish(); @@ -1071,6 +1124,15 @@ fn publish_with_patch() { // Remove the usage of new functionality and try again. p.change_file("src/main.rs", "extern crate bar; pub fn main() {}"); + // HACK: Inject `foo` directly into the index so `publish` won't block for it to be in + // the index. + // + // This is to ensure we can verify the Summary we post to the registry as doing so precludes + // the registry from processing the publish. + Package::new("foo", "0.0.1") + .file("src/lib.rs", "") + .publish(); + p.cargo("publish") .replace_crates_io(registry.index_url()) .with_stderr( @@ -1083,7 +1145,10 @@ fn publish_with_patch() { [..] [..] [..] -[UPLOADING] foo v0.0.1 ([CWD])", +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] crates.io index +", ) .run(); @@ -1168,6 +1233,7 @@ fn publish_checks_for_token_before_verify() { [VERIFYING] foo v0.0.1 ([CWD]) [..] [..] +[..] [UPLOADING] foo v0.0.1 [..] [WARNING] aborting upload due to dry run ", @@ -1226,7 +1292,9 @@ include `--registry crates-io` to use crates.io // A dependency with both `git` and `version`. #[cargo_test] fn publish_git_with_version() { + // HACK below allows us to use a local registry let registry = registry::init(); + Package::new("dep1", "1.0.1") .file("src/lib.rs", "pub fn f() -> i32 {1}") .publish(); @@ -1267,6 +1335,16 @@ fn publish_git_with_version() { .build(); p.cargo("run").with_stdout("2").run(); + + // HACK: Inject `foo` directly into the index so `publish` won't block for it to be in + // the index. + // + // This is to ensure we can verify the Summary we post to the registry as doing so precludes + // the registry from processing the publish. + Package::new("foo", "0.1.0") + .file("src/lib.rs", "") + .publish(); + p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_stderr( @@ -1275,7 +1353,10 @@ fn publish_git_with_version() { [..] [..] [..] -[UPLOADING] foo v0.1.0 ([CWD])", +[..] +[UPLOADING] foo v0.1.0 ([CWD]) +[UPDATING] crates.io index +", ) .run(); @@ -1360,6 +1441,7 @@ fn publish_git_with_version() { #[cargo_test] fn publish_dev_dep_no_version() { + // HACK below allows us to use a local registry let registry = registry::init(); let p = project() @@ -1385,13 +1467,24 @@ fn publish_dev_dep_no_version() { .file("bar/src/lib.rs", "") .build(); + // HACK: Inject `foo` directly into the index so `publish` won't block for it to be in + // the index. + // + // This is to ensure we can verify the Summary we post to the registry as doing so precludes + // the registry from processing the publish. + Package::new("foo", "0.1.0") + .file("src/lib.rs", "") + .publish(); + p.cargo("publish --no-verify") .replace_crates_io(registry.index_url()) .with_stderr( "\ [UPDATING] [..] [PACKAGING] foo v0.1.0 [..] +[PACKAGED] [..] files, [..] ([..] compressed) [UPLOADING] foo v0.1.0 [..] +[UPDATING] crates.io index ", ) .run(); @@ -1444,7 +1537,8 @@ repository = "foo" #[cargo_test] fn credentials_ambiguous_filename() { - let registry = registry::init(); + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); let credentials_toml = paths::home().join(".cargo/credentials.toml"); fs::write(credentials_toml, r#"token = "api-token""#).unwrap(); @@ -1473,19 +1567,21 @@ fn credentials_ambiguous_filename() { [..] [..] [..] +[..] [UPLOADING] foo v0.0.1 [..] +[UPDATING] crates.io index ", ) .run(); - - validate_upload_foo(); } // --index will not load registry.token to avoid possibly leaking // crates.io token to another server. #[cargo_test] fn index_requires_token() { + // Use local registry for faster test times since no publish will occur let registry = registry::init(); + let credentials = paths::home().join(".cargo/credentials"); fs::remove_file(&credentials).unwrap(); @@ -1548,7 +1644,9 @@ include `--registry dummy-registry` or `--registry crates-io` #[cargo_test] fn publish_with_missing_readme() { + // Use local registry for faster test times since no publish will occur let registry = registry::init(); + let p = project() .file( "Cargo.toml", @@ -1573,6 +1671,7 @@ fn publish_with_missing_readme() { "\ [UPDATING] [..] [PACKAGING] foo v0.1.0 [..] +[PACKAGED] [..] files, [..] ([..] compressed) [UPLOADING] foo v0.1.0 [..] [ERROR] failed to read `readme` file for package `foo v0.1.0 ([ROOT]/foo)` @@ -1624,6 +1723,7 @@ fn api_error_json() { "\ [UPDATING] [..] [PACKAGING] foo v0.0.1 [..] +[PACKAGED] [..] files, [..] ([..] compressed) [UPLOADING] foo v0.0.1 [..] [ERROR] failed to publish to registry at http://127.0.0.1:[..]/ @@ -1671,6 +1771,7 @@ fn api_error_200() { "\ [UPDATING] [..] [PACKAGING] foo v0.0.1 [..] +[PACKAGED] [..] files, [..] ([..] compressed) [UPLOADING] foo v0.0.1 [..] [ERROR] failed to publish to registry at http://127.0.0.1:[..]/ @@ -1718,6 +1819,7 @@ fn api_error_code() { "\ [UPDATING] [..] [PACKAGING] foo v0.0.1 [..] +[PACKAGED] [..] files, [..] ([..] compressed) [UPLOADING] foo v0.0.1 [..] [ERROR] failed to publish to registry at http://127.0.0.1:[..]/ @@ -1773,6 +1875,7 @@ fn api_curl_error() { "\ [UPDATING] [..] [PACKAGING] foo v0.0.1 [..] +[PACKAGED] [..] files, [..] ([..] compressed) [UPLOADING] foo v0.0.1 [..] [ERROR] failed to publish to registry at http://127.0.0.1:[..]/ @@ -1820,6 +1923,7 @@ fn api_other_error() { "\ [UPDATING] [..] [PACKAGING] foo v0.0.1 [..] +[PACKAGED] [..] files, [..] ([..] compressed) [UPLOADING] foo v0.0.1 [..] [ERROR] failed to publish to registry at http://127.0.0.1:[..]/ @@ -1835,6 +1939,7 @@ Caused by: #[cargo_test] fn in_package_workspace() { + // HACK below allows us to use a local registry let registry = registry::init(); let p = project() @@ -1863,6 +1968,13 @@ fn in_package_workspace() { .file("li/src/main.rs", "fn main() {}") .build(); + // HACK: Inject `foo` directly into the index so `publish` won't block for it to be in + // the index. + // + // This is to ensure we can verify the Summary we post to the registry as doing so precludes + // the registry from processing the publish. + Package::new("li", "0.0.1").file("src/lib.rs", "").publish(); + p.cargo("publish -p li --no-verify") .replace_crates_io(registry.index_url()) .with_stderr( @@ -1871,7 +1983,9 @@ fn in_package_workspace() { [WARNING] manifest has no documentation, homepage or repository. See [..] [PACKAGING] li v0.0.1 ([CWD]/li) +[PACKAGED] [..] files, [..] ([..] compressed) [UPLOADING] li v0.0.1 ([CWD]/li) +[UPDATING] crates.io index ", ) .run(); @@ -1881,6 +1995,7 @@ See [..] #[cargo_test] fn with_duplicate_spec_in_members() { + // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project() @@ -1932,6 +2047,7 @@ fn with_duplicate_spec_in_members() { #[cargo_test] fn in_package_workspace_with_members_with_features_old() { + // HACK below allows us to use a local registry let registry = registry::init(); let p = project() @@ -1959,6 +2075,13 @@ fn in_package_workspace_with_members_with_features_old() { .file("li/src/main.rs", "fn main() {}") .build(); + // HACK: Inject `foo` directly into the index so `publish` won't block for it to be in + // the index. + // + // This is to ensure we can verify the Summary we post to the registry as doing so precludes + // the registry from processing the publish. + Package::new("li", "0.0.1").file("src/lib.rs", "").publish(); + p.cargo("publish -p li --no-verify") .replace_crates_io(registry.index_url()) .with_stderr( @@ -1967,7 +2090,9 @@ fn in_package_workspace_with_members_with_features_old() { [WARNING] manifest has no documentation, homepage or repository. See [..] [PACKAGING] li v0.0.1 ([CWD]/li) +[PACKAGED] [..] files, [..] ([..] compressed) [UPLOADING] li v0.0.1 ([CWD]/li) +[UPDATING] crates.io index ", ) .run(); @@ -1977,6 +2102,7 @@ See [..] #[cargo_test] fn in_virtual_workspace() { + // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project() @@ -2012,7 +2138,8 @@ fn in_virtual_workspace() { #[cargo_test] fn in_virtual_workspace_with_p() { - let registry = registry::init(); + // `publish` generally requires a remote registry + let registry = registry::RegistryBuilder::new().http_api().build(); let p = project() .file( @@ -2055,7 +2182,9 @@ fn in_virtual_workspace_with_p() { [WARNING] manifest has no documentation, homepage or repository. See [..] [PACKAGING] li v0.0.1 ([CWD]/li) +[PACKAGED] [..] files, [..] ([..] compressed) [UPLOADING] li v0.0.1 ([CWD]/li) +[UPDATING] crates.io index ", ) .run(); @@ -2063,6 +2192,7 @@ See [..] #[cargo_test] fn in_package_workspace_not_found() { + // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project() @@ -2107,6 +2237,7 @@ error: package ID specification `li` did not match any packages #[cargo_test] fn in_package_workspace_found_multiple() { + // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project() @@ -2164,6 +2295,7 @@ error: the `-p` argument must be specified to select a single package to publish #[cargo_test] // https://github.com/rust-lang/cargo/issues/10536 fn publish_path_dependency_without_workspace() { + // Use local registry for faster test times since no publish will occur let registry = registry::init(); let p = project() @@ -2237,7 +2369,10 @@ fn http_api_not_noop() { [VERIFYING] foo v0.0.1 ([CWD]) [..] [..] -[UPLOADING] foo v0.0.1 ([CWD])", +[..] +[UPLOADING] foo v0.0.1 ([CWD]) +[UPDATING] [..] +", ) .run(); @@ -2263,7 +2398,7 @@ fn http_api_not_noop() { } #[cargo_test] -fn wait_for_publish() { +fn wait_for_first_publish() { // Counter for number of tries before the package is "published" let arc: Arc> = Arc::new(Mutex::new(0)); let arc2 = arc.clone(); @@ -2284,12 +2419,6 @@ fn wait_for_publish() { }) .build(); - // The sparse-registry test server does not know how to publish on its own. - // So let us call publish for it. - Package::new("delay", "0.0.1") - .file("src/lib.rs", "") - .publish(); - let p = project() .file( "Cargo.toml", @@ -2304,28 +2433,22 @@ fn wait_for_publish() { "#, ) .file("src/lib.rs", "") - .file( - ".cargo/config", - " - [publish] - timeout = 60 - ", - ) .build(); - p.cargo("publish --no-verify -Z sparse-registry -Z publish-timeout") - .masquerade_as_nightly_cargo(&["sparse-registry", "publish-timeout"]) + p.cargo("publish --no-verify -Z sparse-registry") + .masquerade_as_nightly_cargo(&["sparse-registry"]) .replace_crates_io(registry.index_url()) .with_status(0) .with_stderr( "\ -[UPDATING] `crates-io` index +[UPDATING] crates.io index [WARNING] manifest has no documentation, [..] See [..] [PACKAGING] delay v0.0.1 ([CWD]) +[PACKAGED] [..] files, [..] ([..] compressed) [UPLOADING] delay v0.0.1 ([CWD]) -[UPDATING] `crates-io` index -[WAITING] on `delay` to propagate to `crates-io` index (which is replacing registry `crates-io`) (ctrl-c to wait asynchronously) +[UPDATING] crates.io index +[WAITING] on `delay` to propagate to crates.io index (ctrl-c to wait asynchronously) ", ) .run(); @@ -2360,7 +2483,7 @@ See [..] /// the responder twice per cargo invocation. If that ever gets changed /// this test will need to be changed accordingly. #[cargo_test] -fn wait_for_publish_underscore() { +fn wait_for_first_publish_underscore() { // Counter for number of tries before the package is "published" let arc: Arc> = Arc::new(Mutex::new(0)); let arc2 = arc.clone(); @@ -2381,12 +2504,6 @@ fn wait_for_publish_underscore() { }) .build(); - // The sparse-registry test server does not know how to publish on its own. - // So let us call publish for it. - Package::new("delay_with_underscore", "0.0.1") - .file("src/lib.rs", "") - .publish(); - let p = project() .file( "Cargo.toml", @@ -2401,28 +2518,22 @@ fn wait_for_publish_underscore() { "#, ) .file("src/lib.rs", "") - .file( - ".cargo/config", - " - [publish] - timeout = 60 - ", - ) .build(); - p.cargo("publish --no-verify -Z sparse-registry -Z publish-timeout") - .masquerade_as_nightly_cargo(&["sparse-registry", "publish-timeout"]) + p.cargo("publish --no-verify -Z sparse-registry") + .masquerade_as_nightly_cargo(&["sparse-registry"]) .replace_crates_io(registry.index_url()) .with_status(0) .with_stderr( "\ -[UPDATING] `crates-io` index +[UPDATING] crates.io index [WARNING] manifest has no documentation, [..] See [..] [PACKAGING] delay_with_underscore v0.0.1 ([CWD]) +[PACKAGED] [..] files, [..] ([..] compressed) [UPLOADING] delay_with_underscore v0.0.1 ([CWD]) -[UPDATING] `crates-io` index -[WAITING] on `delay_with_underscore` to propagate to `crates-io` index (which is replacing registry `crates-io`) (ctrl-c to wait asynchronously) +[UPDATING] crates.io index +[WAITING] on `delay_with_underscore` to propagate to crates.io index (ctrl-c to wait asynchronously) ", ) .run(); @@ -2454,6 +2565,98 @@ See [..] .run(); } +#[cargo_test] +fn wait_for_subsequent_publish() { + // Counter for number of tries before the package is "published" + let arc: Arc> = Arc::new(Mutex::new(0)); + let arc2 = arc.clone(); + let publish_req = Arc::new(Mutex::new(None)); + let publish_req2 = publish_req.clone(); + + let registry = registry::RegistryBuilder::new() + .http_index() + .http_api() + .add_responder("/api/v1/crates/new", move |req, server| { + // Capture the publish request, but defer publishing + *publish_req.lock().unwrap() = Some(req.clone()); + server.ok(req) + }) + .add_responder("/index/de/la/delay", move |req, server| { + let mut lock = arc.lock().unwrap(); + *lock += 1; + if *lock == 3 { + // Run the publish on the 3rd attempt + server.publish(&publish_req2.lock().unwrap().as_ref().unwrap()); + } + server.index(req) + }) + .build(); + + // Publish an earlier version + Package::new("delay", "0.0.1") + .file("src/lib.rs", "") + .publish(); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "delay" + version = "0.0.2" + authors = [] + license = "MIT" + description = "foo" + + "#, + ) + .file("src/lib.rs", "") + .build(); + + p.cargo("publish --no-verify -Z sparse-registry") + .masquerade_as_nightly_cargo(&["sparse-registry"]) + .replace_crates_io(registry.index_url()) + .with_status(0) + .with_stderr( + "\ +[UPDATING] crates.io index +[WARNING] manifest has no documentation, [..] +See [..] +[PACKAGING] delay v0.0.2 ([CWD]) +[PACKAGED] [..] files, [..] ([..] compressed) +[UPLOADING] delay v0.0.2 ([CWD]) +[UPDATING] crates.io index +[WAITING] on `delay` to propagate to crates.io index (ctrl-c to wait asynchronously) +", + ) + .run(); + + // Verify the responder has been pinged + let lock = arc2.lock().unwrap(); + assert_eq!(*lock, 3); + drop(lock); + + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.0.1" + authors = [] + [dependencies] + delay = "0.0.2" + "#, + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build -Z sparse-registry") + .masquerade_as_nightly_cargo(&["sparse-registry"]) + .with_status(0) + .run(); +} + #[cargo_test] fn skip_wait_for_publish() { // Intentionally using local registry so the crate never makes it to the index @@ -2490,6 +2693,7 @@ fn skip_wait_for_publish() { [WARNING] manifest has no documentation, [..] See [..] [PACKAGING] foo v0.0.1 ([CWD]) +[PACKAGED] [..] files, [..] ([..] compressed) [UPLOADING] foo v0.0.1 ([CWD]) ", ) diff --git a/tests/testsuite/publish_lockfile.rs b/tests/testsuite/publish_lockfile.rs index 56ece3e645fc..96d24d40f50d 100644 --- a/tests/testsuite/publish_lockfile.rs +++ b/tests/testsuite/publish_lockfile.rs @@ -77,6 +77,7 @@ fn package_lockfile() { [VERIFYING] foo v0.0.1 ([CWD]) [COMPILING] foo v0.0.1 ([CWD][..]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] [..] files, [..] ([..] compressed) ", ) .run(); @@ -135,6 +136,7 @@ src/main.rs [COMPILING] foo v0.0.1 ([..]) [RUNNING] `rustc --crate-name foo src/main.rs [..] [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] 5 files, [..] ([..] compressed) ", ) .run(); @@ -232,6 +234,7 @@ fn note_resolve_changes() { [UPDATING] `[..]` index [NOTE] package `multi v0.1.0` added to the packaged Cargo.lock file, was originally sourced from `[..]/foo/multi` [NOTE] package `patched v1.0.0` added to the packaged Cargo.lock file, was originally sourced from `[..]/foo/patched` +[PACKAGED] [..] files, [..] ([..] compressed) ", ) .run(); @@ -251,7 +254,12 @@ fn outdated_lock_version_change_does_not_warn() { p.change_file("Cargo.toml", &pl_manifest("foo", "0.2.0", "")); p.cargo("package --no-verify") - .with_stderr("[PACKAGING] foo v0.2.0 ([..])") + .with_stderr( + "\ +[PACKAGING] foo v0.2.0 ([..]) +[PACKAGED] [..] files, [..] ([..] compressed) +", + ) .run(); } @@ -301,6 +309,7 @@ fn no_warn_workspace_extras() { "\ [PACKAGING] a v0.1.0 ([..]) [UPDATING] `[..]` index +[PACKAGED] [..] files, [..] ([..] compressed) ", ) .run(); @@ -334,6 +343,7 @@ fn warn_package_with_yanked() { [UPDATING] `[..]` index [WARNING] package `bar v0.1.0` in Cargo.lock is yanked in registry \ `crates-io`, consider updating to a version that is not yanked +[PACKAGED] [..] files, [..] ([..] compressed) ", ) .run(); @@ -450,6 +460,7 @@ src/main.rs [COMPILING] foo v0.0.1 ([..]) [RUNNING] `rustc --crate-name foo src/main.rs [..] [FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[PACKAGED] 5 files, [..] ([..] compressed) ", ) .run(); @@ -476,6 +487,7 @@ fn ignore_lockfile_inner() { [ARCHIVING] Cargo.toml [ARCHIVING] Cargo.toml.orig [ARCHIVING] src/main.rs +[PACKAGED] 6 files, [..] ([..] compressed) ", ) .run(); diff --git a/tests/testsuite/registry.rs b/tests/testsuite/registry.rs index 60c52fd54625..5311c0b8cd70 100644 --- a/tests/testsuite/registry.rs +++ b/tests/testsuite/registry.rs @@ -500,6 +500,7 @@ Caused by: [COMPILING] notyet v0.0.1 [COMPILING] foo v0.0.1 ([CWD][..]) [FINISHED] dev [unoptimized + debuginfo] target(s) in [..]s +[PACKAGED] [..] ", ) .run(); diff --git a/tests/testsuite/source_replacement.rs b/tests/testsuite/source_replacement.rs index 8401cd403ea0..ef7cc555c049 100644 --- a/tests/testsuite/source_replacement.rs +++ b/tests/testsuite/source_replacement.rs @@ -206,7 +206,8 @@ fn publish_with_replacement() { p.cargo("publish --registry crates-io") .replace_crates_io(crates_io.index_url()) .with_stderr( - "[UPDATING] crates.io index + "\ +[UPDATING] crates.io index [WARNING] manifest has no documentation, homepage or repository. See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([..]) @@ -217,7 +218,10 @@ See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for [COMPILING] bar v1.0.0 [COMPILING] foo v0.0.1 ([..]foo-0.0.1) [FINISHED] dev [..] -[UPLOADING] foo v0.0.1 ([..])", +[PACKAGED] [..] +[UPLOADING] foo v0.0.1 ([..]) +[UPDATING] crates.io index +", ) .run(); } diff --git a/tests/testsuite/test.rs b/tests/testsuite/test.rs index b6e21775cc51..88f5f34c2ff7 100644 --- a/tests/testsuite/test.rs +++ b/tests/testsuite/test.rs @@ -3485,6 +3485,19 @@ fn cargo_test_env() { .with_stderr_contains(cargo.to_str().unwrap()) .with_stdout_contains("test env_test ... ok") .run(); + + // Check that `cargo test` propagates the environment's $CARGO + let rustc = cargo_util::paths::resolve_executable("rustc".as_ref()) + .unwrap() + .canonicalize() + .unwrap(); + let rustc = rustc.to_str().unwrap(); + p.cargo("test --lib -- --nocapture") + // we use rustc since $CARGO is only used if it points to a path that exists + .env(cargo::CARGO_ENV, rustc) + .with_stderr_contains(rustc) + .with_stdout_contains("test env_test ... ok") + .run(); } #[cargo_test] diff --git a/tests/testsuite/weak_dep_features.rs b/tests/testsuite/weak_dep_features.rs index 1ef12b371ef7..4e3cd8dcb639 100644 --- a/tests/testsuite/weak_dep_features.rs +++ b/tests/testsuite/weak_dep_features.rs @@ -523,7 +523,9 @@ bar v1.0.0 #[cargo_test] fn publish() { + // HACK below allows us to use a local registry let registry = registry::init(); + // Publish behavior with /? syntax. Package::new("bar", "1.0.0").feature("feat", &[]).publish(); let p = project() @@ -548,6 +550,15 @@ fn publish() { .file("src/lib.rs", "") .build(); + // HACK: Inject `foo` directly into the index so `publish` won't block for it to be in + // the index. + // + // This is to ensure we can verify the Summary we post to the registry as doing so precludes + // the registry from processing the publish. + Package::new("foo", "0.1.0") + .file("src/lib.rs", "") + .publish(); + p.cargo("publish") .replace_crates_io(registry.index_url()) .with_stderr( @@ -557,7 +568,9 @@ fn publish() { [VERIFYING] foo v0.1.0 [..] [COMPILING] foo v0.1.0 [..] [FINISHED] [..] +[PACKAGED] [..] [UPLOADING] foo v0.1.0 [..] +[UPDATING] [..] ", ) .run(); diff --git a/triagebot.toml b/triagebot.toml index a71ad23ac911..7a809e498fdc 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1,5 +1,3 @@ -[assign] - [ping.windows] message = """\ Hey Windows Group! This bug has been identified as a good "Windows candidate". @@ -13,3 +11,11 @@ label = "O-windows" [shortcut] +[autolabel."S-waiting-on-review"] +new_pr = true + +[assign] +contributing_url = "https://rust-lang.github.io/cargo/contrib/" + +[assign.owners] +"*" = ["@ehuss", "@epage", "@weihanglo"]