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 12, 2019
1 parent 2ad91d8 commit db96bf9
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 29 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("Installs the most recent toolchain which supports all required components, even than this is older than the currently installed toolchain")
.long("allow-downgrade")
.takes_value(false),
),
)
.subcommand(
Expand Down Expand Up @@ -819,7 +825,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
45 changes: 21 additions & 24 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,21 @@ 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 = if allow_downgrade || old_date == None {
first_manifest
} else {
utc_from_manifest_date(old_date.unwrap())
};

loop {
match try_update_from_dist_(
Expand Down Expand Up @@ -713,22 +716,10 @@ 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)
)
}),
)
.pred();

if try_next < first_manifest {
let toolchain_date = toolchain.date.as_ref().unwrap_or(&fetched);
let try_next = utc_from_manifest_date(toolchain_date).pred();

if try_next < old_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 +917,9 @@ fn dl_v1_manifest<'a>(download: DownloadCfg<'a>, toolchain: &ToolchainDesc) -> R

Ok(urls)
}

fn utc_from_manifest_date(date_str: &str) -> Date<Utc> {
let msg = format!("Malformed manifest date: {:?}", date_str);
let date = NaiveDate::parse_from_str(date_str, "%Y-%m-%d").expect(&msg);
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
68 changes: 68 additions & 0 deletions tests/cli-v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1374,3 +1374,71 @@ fn check_pgp_keys() {
);
})
}

#[test]
fn install_fails_on_missing_component() {
clitools::setup(Scenario::MissingComponent, &|config| {
let trip = TargetTriple::from_build();
let expected = format!(
"component 'rls' for target '{}' is unavailable for download for channel nightly",
trip,
);

// 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_err(
config,
&[
"rustup",
"toolchain",
"install",
"nightly",
"--no-self-update",
"-c",
"rls",
],
&expected,
);
});
}

#[test]
fn install_allow_downgrade() {
clitools::setup(Scenario::MissingComponent, &|config| {
// this dist has no rls, but rls is available on 2019-09-13
set_current_dist_date(config, "2019-09-14");
expect_ok(
config,
&[
"rustup",
"toolchain",
"install",
"nightly",
"--no-self-update",
],
);
expect_ok(
config,
&[
"rustup",
"toolchain",
"install",
"nightly",
"--no-self-update",
"-c",
"rls",
"--allow-downgrade",
],
);
});
}

0 comments on commit db96bf9

Please sign in to comment.