Skip to content

Commit a721e62

Browse files
committed
Try installing exact versions before updating
When an exact version is being installed, if we already have that version from the index, we don't need to update the index before installing it. Don't do this if it's not an exact version, because the update may find us a newer version. This is particularly useful for scripts which unconditionally run `cargo install some-crate --version=1.2.3`. Before install-update, I wrote a crate to do this (https://crates.io/crates/cargo-ensure-installed) which I'm trying to replace with just `cargo install`, but the extra latency of updating the index for a no-op is noticeable. This introduces an interesting edge-case around yanked crates; the yanked-ness of crates will no longer change on install for exact version matches which were already downloaded. This feels niche enough to hopefully not matter...
1 parent e8b0344 commit a721e62

File tree

4 files changed

+33
-10
lines changed

4 files changed

+33
-10
lines changed

src/cargo/ops/cargo_install.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ fn install_one(
151151
krate,
152152
vers,
153153
config,
154-
true,
154+
NeedsUpdate::True,
155155
&mut |git| git.read_packages(),
156156
)?
157157
} else if source_id.is_path() {
@@ -180,7 +180,7 @@ fn install_one(
180180
}
181181
}
182182
src.update()?;
183-
select_pkg(src, krate, vers, config, false, &mut |path| {
183+
select_pkg(src, krate, vers, config, NeedsUpdate::False, &mut |path| {
184184
path.read_packages()
185185
})?
186186
} else {
@@ -189,7 +189,11 @@ fn install_one(
189189
krate,
190190
vers,
191191
config,
192-
is_first_install,
192+
if is_first_install {
193+
NeedsUpdate::TryWithoutFirst
194+
} else {
195+
NeedsUpdate::False
196+
},
193197
&mut |_| {
194198
bail!(
195199
"must specify a crate to install from \

src/cargo/ops/cargo_uninstall.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ fn uninstall_cwd(root: &Filesystem, bins: &[String], config: &Config) -> CargoRe
8585
let tracker = InstallTracker::load(config, root)?;
8686
let source_id = SourceId::for_path(config.cwd())?;
8787
let src = path_source(source_id, config)?;
88-
let pkg = select_pkg(src, None, None, config, true, &mut |path| {
88+
let pkg = select_pkg(src, None, None, config, NeedsUpdate::True, &mut |path| {
8989
path.read_packages()
9090
})?;
9191
let pkgid = pkg.package_id();

src/cargo/ops/common_for_install_and_uninstall.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -525,13 +525,20 @@ pub fn path_source(source_id: SourceId, config: &Config) -> CargoResult<PathSour
525525
Ok(PathSource::new(&path, source_id, config))
526526
}
527527

528+
#[derive(Clone, Copy, PartialEq, Eq)]
529+
pub enum NeedsUpdate {
530+
True,
531+
False,
532+
TryWithoutFirst,
533+
}
534+
528535
/// Gets a Package based on command-line requirements.
529536
pub fn select_pkg<'a, T>(
530537
mut source: T,
531538
name: Option<&str>,
532539
vers: Option<&str>,
533540
config: &Config,
534-
needs_update: bool,
541+
needs_update: NeedsUpdate,
535542
list_all: &mut dyn FnMut(&mut T) -> CargoResult<Vec<Package>>,
536543
) -> CargoResult<Package>
537544
where
@@ -542,7 +549,7 @@ where
542549
// with other global Cargos
543550
let _lock = config.acquire_package_cache_lock()?;
544551

545-
if needs_update {
552+
if let NeedsUpdate::True = needs_update {
546553
source.update()?;
547554
}
548555

@@ -603,8 +610,21 @@ where
603610
vers
604611
};
605612
let dep = Dependency::parse_no_deprecated(name, vers_spec, source.source_id())?;
606-
let deps = source.query_vec(&dep)?;
607-
match deps.iter().map(|p| p.package_id()).max() {
613+
let deps = (|| {
614+
if let Some(vers_spec) = vers_spec {
615+
if semver::VersionReq::parse(vers_spec).unwrap().is_exact() {
616+
let deps = source.query_vec(&dep)?;
617+
if deps.len() == 1 {
618+
return Ok(deps);
619+
}
620+
}
621+
}
622+
if needs_update != NeedsUpdate::False {
623+
source.update()?;
624+
}
625+
source.query_vec(&dep)
626+
})();
627+
match deps?.iter().map(|p| p.package_id()).max() {
608628
Some(pkgid) => {
609629
let pkg = Box::new(&mut source).download_now(pkgid, config)?;
610630
Ok(pkg)

tests/testsuite/install_upgrade.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,7 @@ fn ambiguous_version_no_longer_allowed() {
225225
cargo_process("install foo --version=1.0")
226226
.with_stderr(
227227
"\
228-
[UPDATING] `[..]` index
229-
[ERROR] the `--vers` provided, `1.0`, is not a valid semver version: cannot parse '1.0' as a semver
228+
error: the `--vers` provided, `1.0`, is not a valid semver version: cannot parse '1.0' as a semver
230229
231230
if you want to specify semver range, add an explicit qualifier, like ^1.0
232231
",

0 commit comments

Comments
 (0)