Skip to content

Commit

Permalink
rustup_mode: add toolchain install --allow-downgrade option
Browse files Browse the repository at this point in the history
In case you have an already installed nightly toolchain
and you want to install a nightly toolchain with a component
which is not available in the installed toolchain,
rustup is searching for the most recent nightly supporting
all requested components and which is not older than the currently
installed nightly.

With this option set it will also search for toolchains older
than the currently installed one.
  • Loading branch information
payload committed Nov 26, 2019
1 parent 2200a24 commit cabee8c
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 27 deletions.
13 changes: 12 additions & 1 deletion src/cli/rustup_mode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,12 @@ pub fn cli() -> App<'static, 'static> {
.help("Force an update, even if some components are missing")
.long("force")
.takes_value(false),
)
.arg(
Arg::with_name("allow-downgrade")
.help("Allow rustup to downgrade the toolchain to satisfy your component choice")
.long("allow-downgrade")
.takes_value(false),
),
)
.subcommand(
Expand Down Expand Up @@ -822,7 +828,12 @@ fn update(cfg: &mut Cfg, m: &ArgMatches<'_>) -> Result<()> {
.values_of("targets")
.map(|v| v.collect())
.unwrap_or_else(Vec::new);
Some(toolchain.install_from_dist(m.is_present("force"), &components, &targets)?)
Some(toolchain.install_from_dist(
m.is_present("force"),
m.is_present("allow-downgrade"),
&components,
&targets,
)?)
} else if !toolchain.exists() {
return Err(ErrorKind::InvalidToolchainName(toolchain.name().to_string()).into());
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/cli/self_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,7 @@ fn maybe_install_rust(
println!();
} else if cfg.find_default()?.is_none() {
let toolchain = cfg.get_toolchain(toolchain_str, false)?;
let status = toolchain.install_from_dist(true, components, targets)?;
let status = toolchain.install_from_dist(true, false, components, targets)?;
cfg.set_default(toolchain_str)?;
println!();
common::show_channel_update(&cfg, toolchain_str, Ok(status))?;
Expand Down
6 changes: 3 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,7 @@ impl Cfg {
ErrorKind::OverrideToolchainNotInstalled(name.to_string())
})
} else {
toolchain.install_from_dist(true, &[], &[])?;
toolchain.install_from_dist(true, false, &[], &[])?;
Ok(Some((toolchain, reason)))
}
}
Expand Down Expand Up @@ -547,7 +547,7 @@ impl Cfg {
// Update toolchains and collect the results
let channels = channels.map(|(n, t)| {
let t = t.and_then(|t| {
let t = t.install_from_dist(force_update, &[], &[]);
let t = t.install_from_dist(force_update, false, &[], &[]);
if let Err(ref e) = t {
(self.notify_handler)(Notification::NonFatalError(e));
}
Expand Down Expand Up @@ -599,7 +599,7 @@ impl Cfg {
) -> Result<Command> {
let toolchain = self.get_toolchain(toolchain, false)?;
if install_if_missing && !toolchain.exists() {
toolchain.install_from_dist(true, &[], &[])?;
toolchain.install_from_dist(true, false, &[], &[])?;
}

if let Some(cmd) = self.maybe_do_cargo_fallback(&toolchain, binary)? {
Expand Down
46 changes: 24 additions & 22 deletions src/dist/dist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,7 @@ pub fn update_from_dist<'a>(
profile: Option<Profile>,
prefix: &InstallPrefix,
force_update: bool,
allow_downgrade: bool,
old_date: Option<&str>,
components: &[&str],
targets: &[&str],
Expand All @@ -601,6 +602,7 @@ pub fn update_from_dist<'a>(
profile,
prefix,
force_update,
allow_downgrade,
old_date,
components,
targets,
Expand All @@ -622,6 +624,7 @@ fn update_from_dist_<'a>(
profile: Option<Profile>,
prefix: &InstallPrefix,
force_update: bool,
allow_downgrade: bool,
old_date: Option<&str>,
components: &[&str],
targets: &[&str],
Expand All @@ -644,21 +647,24 @@ fn update_from_dist_<'a>(
Some(if provided < 1 { 1 } else { provided })
};

// We never want to backtrack further back than the nightly that's already installed.
// In case there is no allow-downgrade option set
// we never want to backtrack further back than the nightly that's already installed.
//
// If no nightly is installed, it makes no sense to backtrack beyond the first ever manifest,
// which is 2014-12-20 according to
// https://static.rust-lang.org/cargo-dist/index.html.
//
// We could arguably use the date of the first rustup release here, but that would break a
// bunch of the tests, which (inexplicably) use 2015-01-01 as their manifest dates.
let first_manifest = old_date
.map(|date| {
Utc.from_utc_date(
&NaiveDate::parse_from_str(date, "%Y-%m-%d").expect("Malformed manifest date"),
)
})
.unwrap_or_else(|| Utc.from_utc_date(&NaiveDate::from_ymd(2014, 12, 20)));
let first_manifest = Utc.from_utc_date(&NaiveDate::from_ymd(2014, 12, 20));
let old_manifest = old_date
.and_then(|date| utc_from_manifest_date(date))
.unwrap_or(first_manifest);
let last_manifest = if allow_downgrade {
first_manifest
} else {
old_manifest
};

loop {
match try_update_from_dist_(
Expand Down Expand Up @@ -713,22 +719,12 @@ fn update_from_dist_<'a>(
// the components that the user currently has installed. Let's try the previous
// nightlies in reverse chronological order until we find a nightly that does,
// starting at one date earlier than the current manifest's date.
let try_next = Utc
.from_utc_date(
&NaiveDate::parse_from_str(
toolchain.date.as_ref().unwrap_or(&fetched),
"%Y-%m-%d",
)
.unwrap_or_else(|_| {
panic!(
"Malformed manifest date: {:?}",
toolchain.date.as_ref().unwrap_or(&fetched)
)
}),
)
let toolchain_date = toolchain.date.as_ref().unwrap_or(&fetched);
let try_next = utc_from_manifest_date(toolchain_date)
.unwrap_or_else(|| panic!("Malformed manifest date: {:?}", toolchain_date))
.pred();

if try_next < first_manifest {
if try_next < last_manifest {
// Wouldn't be an update if we go further back than the user's current nightly.
if let Some(e) = first_err {
break Err(e);
Expand Down Expand Up @@ -926,3 +922,9 @@ fn dl_v1_manifest<'a>(download: DownloadCfg<'a>, toolchain: &ToolchainDesc) -> R

Ok(urls)
}

fn utc_from_manifest_date(date_str: &str) -> Option<Date<Utc>> {
NaiveDate::parse_from_str(date_str, "%Y-%m-%d")
.ok()
.map(|date| Utc.from_utc_date(&date))
}
4 changes: 4 additions & 0 deletions src/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ pub enum InstallMethod<'a> {
DownloadCfg<'a>,
// --force
bool,
// --allow-downgrade
bool,
// toolchain already exists
bool,
// currently installed date
Expand Down Expand Up @@ -66,6 +68,7 @@ impl<'a> InstallMethod<'a> {
update_hash,
dl_cfg,
force_update,
allow_downgrade,
exists,
old_date,
components,
Expand All @@ -79,6 +82,7 @@ impl<'a> InstallMethod<'a> {
if exists { None } else { Some(profile) },
prefix,
force_update,
allow_downgrade,
old_date,
components,
targets,
Expand Down
3 changes: 3 additions & 0 deletions src/toolchain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ impl<'a> Toolchain<'a> {
pub fn install_from_dist(
&self,
force_update: bool,
allow_downgrade: bool,
components: &[&str],
targets: &[&str],
) -> Result<UpdateStatus> {
Expand All @@ -177,6 +178,7 @@ impl<'a> Toolchain<'a> {
update_hash.as_ref().map(|p| &**p),
self.download_cfg(),
force_update,
allow_downgrade,
self.exists(),
old_date.as_ref().map(|s| &**s),
components,
Expand All @@ -193,6 +195,7 @@ impl<'a> Toolchain<'a> {
self.download_cfg(),
false,
false,
false,
None,
&[],
&[],
Expand Down
57 changes: 57 additions & 0 deletions tests/cli-v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1392,3 +1392,60 @@ fn check_pgp_keys() {
);
})
}

#[test]
fn install_allow_downgrade() {
clitools::setup(Scenario::MissingComponent, &|config| {
let trip = TargetTriple::from_build();

// this dist has no rls and there is no newer one
set_current_dist_date(config, "2019-09-14");
expect_ok(
config,
&[
"rustup",
"toolchain",
"install",
"nightly",
"--no-self-update",
],
);
expect_stdout_ok(config, &["rustc", "--version"], "hash-nightly-3");
expect_component_not_executable(config, "rls");

expect_err(
config,
&[
"rustup",
"toolchain",
"install",
"nightly",
"--no-self-update",
"-c",
"rls",
],
&format!(
"component 'rls' for target '{}' is unavailable for download for channel nightly",
trip,
),
);
expect_stdout_ok(config, &["rustc", "--version"], "hash-nightly-3");
expect_component_not_executable(config, "rls");

expect_ok(
config,
&[
"rustup",
"toolchain",
"install",
"nightly",
"--no-self-update",
"-c",
"rls",
"--allow-downgrade",
],
);
expect_stdout_ok(config, &["rustc", "--version"], "hash-nightly-2");
expect_component_executable(config, "rls");
});
}

0 comments on commit cabee8c

Please sign in to comment.