diff --git a/src/cargo/ops/cargo_install.rs b/src/cargo/ops/cargo_install.rs index 957ab43e6e6..16027233edb 100644 --- a/src/cargo/ops/cargo_install.rs +++ b/src/cargo/ops/cargo_install.rs @@ -68,6 +68,7 @@ impl<'cfg> InstallablePackage<'cfg> { force: bool, no_track: bool, needs_update_if_source_is_index: bool, + current_rust_version: Option<&semver::Version>, ) -> CargoResult> { if let Some(name) = krate { if name == "." { @@ -105,6 +106,7 @@ impl<'cfg> InstallablePackage<'cfg> { dep, |git: &mut GitSource<'_>| git.read_packages(), config, + current_rust_version, )? } else if source_id.is_path() { let mut src = path_source(source_id, config)?; @@ -142,6 +144,7 @@ impl<'cfg> InstallablePackage<'cfg> { dep, |path: &mut PathSource<'_>| path.read_packages(), config, + current_rust_version, )? } else if let Some(dep) = dep { let mut source = map.load(source_id, &HashSet::new())?; @@ -161,7 +164,13 @@ impl<'cfg> InstallablePackage<'cfg> { config.shell().status("Ignored", &msg)?; 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 { bail!( "must specify a crate to install from \ @@ -616,6 +625,21 @@ pub fn install( let dst = root.join("bin").into_path_unlocked(); 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 (krate, vers) = krates .iter() @@ -623,7 +647,18 @@ pub fn install( .map(|(k, v)| (Some(k.as_str()), v.as_ref())) .unwrap_or((None, None)); 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; if let Some(installable_pkg) = installable_pkg { @@ -654,6 +689,7 @@ pub fn install( force, no_track, !did_update, + current_rust_version.as_ref(), ) { Ok(Some(installable_pkg)) => { did_update = true; @@ -773,7 +809,7 @@ where // 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 // 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) = 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) { diff --git a/src/cargo/ops/cargo_uninstall.rs b/src/cargo/ops/cargo_uninstall.rs index 3551544183b..1f22e191e80 100644 --- a/src/cargo/ops/cargo_uninstall.rs +++ b/src/cargo/ops/cargo_uninstall.rs @@ -90,6 +90,7 @@ fn uninstall_cwd(root: &Filesystem, bins: &[String], config: &Config) -> CargoRe None, |path: &mut PathSource<'_>| path.read_packages(), config, + None, )?; let pkgid = pkg.package_id(); uninstall_pkgid(root, tracker, pkgid, bins, config) diff --git a/src/cargo/ops/common_for_install_and_uninstall.rs b/src/cargo/ops/common_for_install_and_uninstall.rs index 45c74ab3c11..7b3ca0534f1 100644 --- a/src/cargo/ops/common_for_install_and_uninstall.rs +++ b/src/cargo/ops/common_for_install_and_uninstall.rs @@ -532,6 +532,7 @@ pub fn select_dep_pkg( dep: Dependency, config: &Config, needs_update: bool, + current_rust_version: Option<&semver::Version>, ) -> CargoResult where T: Source, @@ -551,9 +552,19 @@ where Poll::Pending => source.block_until_ready()?, } }; - match deps.iter().map(|p| p.package_id()).max() { - Some(pkgid) => { - let pkg = Box::new(source).download_now(pkgid, config)?; + match deps.iter().max_by_key(|p| p.package_id()) { + Some(summary) => { + 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) } None => { @@ -599,6 +610,7 @@ pub fn select_pkg( dep: Option, mut list_all: F, config: &Config, + current_rust_version: Option<&semver::Version>, ) -> CargoResult where T: Source, @@ -612,7 +624,7 @@ where source.invalidate_cache(); return if let Some(dep) = dep { - select_dep_pkg(source, dep, config, false) + select_dep_pkg(source, dep, config, false, current_rust_version) } else { let candidates = list_all(source)?; let binaries = candidates diff --git a/tests/testsuite/install.rs b/tests/testsuite/install.rs index 5312354d291..cb1539eb8db 100644 --- a/tests/testsuite/install.rs +++ b/tests/testsuite/install.rs @@ -2478,15 +2478,7 @@ fn install_incompat_msrv() { cargo_process("install foo") .with_stderr("\ [UPDATING] `dummy-registry` index -[DOWNLOADING] crates ... -[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` +[ERROR] cannot install package `foo 0.2.0`, it requires rustc 1.9876.0 or newer, while the currently active rustc version is [..] ") .with_status(101).run(); }