From 8fe3bde2b5d02a5552315572428bdf94d3add98e Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Sat, 25 Sep 2021 10:09:13 -0700 Subject: [PATCH] Allow `cargo update --precise` with metadata. --- src/cargo/ops/cargo_generate_lockfile.rs | 12 ++-- src/cargo/sources/registry/index.rs | 35 ++++++++--- tests/testsuite/update.rs | 78 ++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 12 deletions(-) diff --git a/src/cargo/ops/cargo_generate_lockfile.rs b/src/cargo/ops/cargo_generate_lockfile.rs index 181e25b12..04d4010f4 100644 --- a/src/cargo/ops/cargo_generate_lockfile.rs +++ b/src/cargo/ops/cargo_generate_lockfile.rs @@ -1,8 +1,3 @@ -use std::collections::{BTreeMap, HashSet}; - -use log::debug; -use termcolor::Color::{self, Cyan, Green, Red}; - use crate::core::registry::PackageRegistry; use crate::core::resolver::features::{CliFeatures, HasDevUnits}; use crate::core::{PackageId, PackageIdSpec}; @@ -10,6 +5,10 @@ use crate::core::{Resolve, SourceId, Workspace}; use crate::ops; use crate::util::config::Config; use crate::util::CargoResult; +use anyhow::Context; +use log::debug; +use std::collections::{BTreeMap, HashSet}; +use termcolor::Color::{self, Cyan, Green, Red}; pub struct UpdateOptions<'a> { pub config: &'a Config, @@ -95,6 +94,9 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes // seems like a pretty hokey reason to single out // the registry as well. let precise = if dep.source_id().is_registry() { + semver::Version::parse(precise).with_context(|| { + format!("invalid version format for precise version `{}`", precise) + })?; format!("{}={}->{}", dep.name(), dep.version(), precise) } else { precise.to_string() diff --git a/src/cargo/sources/registry/index.rs b/src/cargo/sources/registry/index.rs index d0be07347..3d873acd3 100644 --- a/src/cargo/sources/registry/index.rs +++ b/src/cargo/sources/registry/index.rs @@ -460,19 +460,40 @@ impl<'cfg> RegistryIndex<'cfg> { // this source, `` is the version installed and ` is the // version requested (argument to `--precise`). let name = dep.package_name().as_str(); - let summaries = summaries.filter(|s| match source_id.precise() { + let precise = match source_id.precise() { Some(p) if p.starts_with(name) && p[name.len()..].starts_with('=') => { let mut vers = p[name.len() + 1..].splitn(2, "->"); - if dep - .version_req() - .matches(&vers.next().unwrap().to_semver().unwrap()) - { - vers.next().unwrap() == s.version().to_string() + let current_vers = vers.next().unwrap().to_semver().unwrap(); + let requested_vers = vers.next().unwrap().to_semver().unwrap(); + Some((current_vers, requested_vers)) + } + _ => None, + }; + let summaries = summaries.filter(|s| match &precise { + Some((current, requested)) => { + if dep.version_req().matches(current) { + // Unfortunately crates.io allows versions to differ only + // by build metadata. This shouldn't be allowed, but since + // it is, this will honor it if requested. However, if not + // specified, then ignore it. + let s_vers = s.version(); + match (s_vers.build.is_empty(), requested.build.is_empty()) { + (true, true) => s_vers == requested, + (true, false) => false, + (false, true) => { + // Strip out the metadata. + s_vers.major == requested.major + && s_vers.minor == requested.minor + && s_vers.patch == requested.patch + && s_vers.pre == requested.pre + } + (false, false) => s_vers == requested, + } } else { true } } - _ => true, + None => true, }); let mut count = 0; diff --git a/tests/testsuite/update.rs b/tests/testsuite/update.rs index 33ca18c38..d63606693 100644 --- a/tests/testsuite/update.rs +++ b/tests/testsuite/update.rs @@ -678,3 +678,81 @@ fn workspace_only() { assert!(!lock1.contains("0.0.2")); assert!(!lock2.contains("0.0.1")); } + +#[cargo_test] +fn precise_with_build_metadata() { + // +foo syntax shouldn't be necessary with --precise + Package::new("bar", "0.1.0+extra-stuff.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + [package] + name = "foo" + version = "0.1.0" + + [dependencies] + bar = "0.1" + "#, + ) + .file("src/lib.rs", "") + .build(); + p.cargo("generate-lockfile").run(); + Package::new("bar", "0.1.1+extra-stuff.1").publish(); + Package::new("bar", "0.1.2+extra-stuff.2").publish(); + + p.cargo("update -p bar --precise 0.1") + .with_status(101) + .with_stderr( + "\ +error: invalid version format for precise version `0.1` + +Caused by: + unexpected end of input while parsing minor version number +", + ) + .run(); + + p.cargo("update -p bar --precise 0.1.1+does-not-match") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] index +error: no matching package named `bar` found +location searched: registry `crates-io` +required by package `foo v0.1.0 ([ROOT]/foo)` +", + ) + .run(); + + p.cargo("update -p bar --precise 0.1.1") + .with_stderr( + "\ +[UPDATING] [..] index +[UPDATING] bar v0.1.0+extra-stuff.0 -> v0.1.1+extra-stuff.1 +", + ) + .run(); + + Package::new("bar", "0.1.3").publish(); + p.cargo("update -p bar --precise 0.1.3+foo") + .with_status(101) + .with_stderr( + "\ +[UPDATING] [..] index +error: no matching package named `bar` found +location searched: registry `crates-io` +required by package `foo v0.1.0 ([ROOT]/foo)` +", + ) + .run(); + + p.cargo("update -p bar --precise 0.1.3") + .with_stderr( + "\ +[UPDATING] [..] index +[UPDATING] bar v0.1.1+extra-stuff.1 -> v0.1.3 +", + ) + .run(); +}