fix(install): Don't suggest --locked for MSRV when its root package

This will also report the error without having to download the `.crate`
first.

If installing multiple packages, this will also report it immediately,
rather than waiting for the other packages to be installed first.

This also offers us more flexibility in the error we report,
like suggesting more appropriate fixes.
This commit is contained in:
Ed Page 2023-10-09 16:31:30 -05:00
parent 699b30a5f4
commit 2976e2ac66
4 changed files with 57 additions and 16 deletions

View File

@ -68,6 +68,7 @@ impl<'cfg> InstallablePackage<'cfg> {
force: bool, force: bool,
no_track: bool, no_track: bool,
needs_update_if_source_is_index: bool, needs_update_if_source_is_index: bool,
current_rust_version: Option<&semver::Version>,
) -> CargoResult<Option<Self>> { ) -> CargoResult<Option<Self>> {
if let Some(name) = krate { if let Some(name) = krate {
if name == "." { if name == "." {
@ -105,6 +106,7 @@ impl<'cfg> InstallablePackage<'cfg> {
dep, dep,
|git: &mut GitSource<'_>| git.read_packages(), |git: &mut GitSource<'_>| git.read_packages(),
config, config,
current_rust_version,
)? )?
} else if source_id.is_path() { } else if source_id.is_path() {
let mut src = path_source(source_id, config)?; let mut src = path_source(source_id, config)?;
@ -142,6 +144,7 @@ impl<'cfg> InstallablePackage<'cfg> {
dep, dep,
|path: &mut PathSource<'_>| path.read_packages(), |path: &mut PathSource<'_>| path.read_packages(),
config, config,
current_rust_version,
)? )?
} else if let Some(dep) = dep { } else if let Some(dep) = dep {
let mut source = map.load(source_id, &HashSet::new())?; let mut source = map.load(source_id, &HashSet::new())?;
@ -161,7 +164,13 @@ impl<'cfg> InstallablePackage<'cfg> {
config.shell().status("Ignored", &msg)?; config.shell().status("Ignored", &msg)?;
return Ok(None); return Ok(None);
} }
select_dep_pkg(&mut source, dep, config, needs_update_if_source_is_index)? select_dep_pkg(
&mut source,
dep,
config,
needs_update_if_source_is_index,
current_rust_version,
)?
} else { } else {
bail!( bail!(
"must specify a crate to install from \ "must specify a crate to install from \
@ -616,6 +625,21 @@ pub fn install(
let dst = root.join("bin").into_path_unlocked(); let dst = root.join("bin").into_path_unlocked();
let map = SourceConfigMap::new(config)?; let map = SourceConfigMap::new(config)?;
let current_rust_version = if opts.honor_rust_version {
let rustc = config.load_global_rustc(None)?;
// Remove any pre-release identifiers for easier comparison
let current_version = &rustc.version;
let untagged_version = semver::Version::new(
current_version.major,
current_version.minor,
current_version.patch,
);
Some(untagged_version)
} else {
None
};
let (installed_anything, scheduled_error) = if krates.len() <= 1 { let (installed_anything, scheduled_error) = if krates.len() <= 1 {
let (krate, vers) = krates let (krate, vers) = krates
.iter() .iter()
@ -623,7 +647,18 @@ pub fn install(
.map(|(k, v)| (Some(k.as_str()), v.as_ref())) .map(|(k, v)| (Some(k.as_str()), v.as_ref()))
.unwrap_or((None, None)); .unwrap_or((None, None));
let installable_pkg = InstallablePackage::new( let installable_pkg = InstallablePackage::new(
config, root, map, krate, source_id, from_cwd, vers, opts, force, no_track, true, config,
root,
map,
krate,
source_id,
from_cwd,
vers,
opts,
force,
no_track,
true,
current_rust_version.as_ref(),
)?; )?;
let mut installed_anything = true; let mut installed_anything = true;
if let Some(installable_pkg) = installable_pkg { if let Some(installable_pkg) = installable_pkg {
@ -654,6 +689,7 @@ pub fn install(
force, force,
no_track, no_track,
!did_update, !did_update,
current_rust_version.as_ref(),
) { ) {
Ok(Some(installable_pkg)) => { Ok(Some(installable_pkg)) => {
did_update = true; did_update = true;
@ -773,7 +809,7 @@ where
// expensive network call in the case that the package is already installed. // expensive network call in the case that the package is already installed.
// If this fails, the caller will possibly do an index update and try again, this is just a // If this fails, the caller will possibly do an index update and try again, this is just a
// best-effort check to see if we can avoid hitting the network. // best-effort check to see if we can avoid hitting the network.
if let Ok(pkg) = select_dep_pkg(source, dep, config, false) { if let Ok(pkg) = select_dep_pkg(source, dep, config, false, None) {
let (_ws, rustc, target) = let (_ws, rustc, target) =
make_ws_rustc_target(config, opts, &source.source_id(), pkg.clone())?; make_ws_rustc_target(config, opts, &source.source_id(), pkg.clone())?;
if let Ok(true) = is_installed(&pkg, config, opts, &rustc, &target, root, dst, force) { if let Ok(true) = is_installed(&pkg, config, opts, &rustc, &target, root, dst, force) {

View File

@ -90,6 +90,7 @@ fn uninstall_cwd(root: &Filesystem, bins: &[String], config: &Config) -> CargoRe
None, None,
|path: &mut PathSource<'_>| path.read_packages(), |path: &mut PathSource<'_>| path.read_packages(),
config, config,
None,
)?; )?;
let pkgid = pkg.package_id(); let pkgid = pkg.package_id();
uninstall_pkgid(root, tracker, pkgid, bins, config) uninstall_pkgid(root, tracker, pkgid, bins, config)

View File

@ -532,6 +532,7 @@ pub fn select_dep_pkg<T>(
dep: Dependency, dep: Dependency,
config: &Config, config: &Config,
needs_update: bool, needs_update: bool,
current_rust_version: Option<&semver::Version>,
) -> CargoResult<Package> ) -> CargoResult<Package>
where where
T: Source, T: Source,
@ -551,9 +552,19 @@ where
Poll::Pending => source.block_until_ready()?, Poll::Pending => source.block_until_ready()?,
} }
}; };
match deps.iter().map(|p| p.package_id()).max() { match deps.iter().max_by_key(|p| p.package_id()) {
Some(pkgid) => { Some(summary) => {
let pkg = Box::new(source).download_now(pkgid, config)?; if let (Some(current), Some(msrv)) = (current_rust_version, summary.rust_version()) {
let msrv_req = msrv.caret_req();
if !msrv_req.matches(current) {
let name = summary.name();
let ver = summary.version();
bail!("\
cannot install package `{name} {ver}`, it requires rustc {msrv} or newer, while the currently active rustc version is {current}"
)
}
}
let pkg = Box::new(source).download_now(summary.package_id(), config)?;
Ok(pkg) Ok(pkg)
} }
None => { None => {
@ -599,6 +610,7 @@ pub fn select_pkg<T, F>(
dep: Option<Dependency>, dep: Option<Dependency>,
mut list_all: F, mut list_all: F,
config: &Config, config: &Config,
current_rust_version: Option<&semver::Version>,
) -> CargoResult<Package> ) -> CargoResult<Package>
where where
T: Source, T: Source,
@ -612,7 +624,7 @@ where
source.invalidate_cache(); source.invalidate_cache();
return if let Some(dep) = dep { return if let Some(dep) = dep {
select_dep_pkg(source, dep, config, false) select_dep_pkg(source, dep, config, false, current_rust_version)
} else { } else {
let candidates = list_all(source)?; let candidates = list_all(source)?;
let binaries = candidates let binaries = candidates

View File

@ -2478,15 +2478,7 @@ fn install_incompat_msrv() {
cargo_process("install foo") cargo_process("install foo")
.with_stderr("\ .with_stderr("\
[UPDATING] `dummy-registry` index [UPDATING] `dummy-registry` index
[DOWNLOADING] crates ... [ERROR] cannot install package `foo 0.2.0`, it requires rustc 1.9876.0 or newer, while the currently active rustc version is [..]
[DOWNLOADED] foo v0.2.0 (registry `[..]`)
[INSTALLING] foo v0.2.0
[ERROR] failed to compile `foo v0.2.0`, intermediate artifacts can be found at `[..]`.
To reuse those artifacts with a future compilation, set the environment variable `CARGO_TARGET_DIR` to that path.
Caused by:
package `foo v0.2.0` cannot be built because it requires rustc 1.9876.0 or newer, while the currently active rustc version is [..]
Try re-running cargo install with `--locked`
") ")
.with_status(101).run(); .with_status(101).run();
} }