Skip to content

Commit cabee8c

Browse files
committed
rustup_mode: add toolchain install --allow-downgrade option
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.
1 parent 2200a24 commit cabee8c

File tree

7 files changed

+104
-27
lines changed

7 files changed

+104
-27
lines changed

src/cli/rustup_mode.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,12 @@ pub fn cli() -> App<'static, 'static> {
319319
.help("Force an update, even if some components are missing")
320320
.long("force")
321321
.takes_value(false),
322+
)
323+
.arg(
324+
Arg::with_name("allow-downgrade")
325+
.help("Allow rustup to downgrade the toolchain to satisfy your component choice")
326+
.long("allow-downgrade")
327+
.takes_value(false),
322328
),
323329
)
324330
.subcommand(
@@ -822,7 +828,12 @@ fn update(cfg: &mut Cfg, m: &ArgMatches<'_>) -> Result<()> {
822828
.values_of("targets")
823829
.map(|v| v.collect())
824830
.unwrap_or_else(Vec::new);
825-
Some(toolchain.install_from_dist(m.is_present("force"), &components, &targets)?)
831+
Some(toolchain.install_from_dist(
832+
m.is_present("force"),
833+
m.is_present("allow-downgrade"),
834+
&components,
835+
&targets,
836+
)?)
826837
} else if !toolchain.exists() {
827838
return Err(ErrorKind::InvalidToolchainName(toolchain.name().to_string()).into());
828839
} else {

src/cli/self_update.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -782,7 +782,7 @@ fn maybe_install_rust(
782782
println!();
783783
} else if cfg.find_default()?.is_none() {
784784
let toolchain = cfg.get_toolchain(toolchain_str, false)?;
785-
let status = toolchain.install_from_dist(true, components, targets)?;
785+
let status = toolchain.install_from_dist(true, false, components, targets)?;
786786
cfg.set_default(toolchain_str)?;
787787
println!();
788788
common::show_channel_update(&cfg, toolchain_str, Ok(status))?;

src/config.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,7 @@ impl Cfg {
438438
ErrorKind::OverrideToolchainNotInstalled(name.to_string())
439439
})
440440
} else {
441-
toolchain.install_from_dist(true, &[], &[])?;
441+
toolchain.install_from_dist(true, false, &[], &[])?;
442442
Ok(Some((toolchain, reason)))
443443
}
444444
}
@@ -547,7 +547,7 @@ impl Cfg {
547547
// Update toolchains and collect the results
548548
let channels = channels.map(|(n, t)| {
549549
let t = t.and_then(|t| {
550-
let t = t.install_from_dist(force_update, &[], &[]);
550+
let t = t.install_from_dist(force_update, false, &[], &[]);
551551
if let Err(ref e) = t {
552552
(self.notify_handler)(Notification::NonFatalError(e));
553553
}
@@ -599,7 +599,7 @@ impl Cfg {
599599
) -> Result<Command> {
600600
let toolchain = self.get_toolchain(toolchain, false)?;
601601
if install_if_missing && !toolchain.exists() {
602-
toolchain.install_from_dist(true, &[], &[])?;
602+
toolchain.install_from_dist(true, false, &[], &[])?;
603603
}
604604

605605
if let Some(cmd) = self.maybe_do_cargo_fallback(&toolchain, binary)? {

src/dist/dist.rs

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -580,6 +580,7 @@ pub fn update_from_dist<'a>(
580580
profile: Option<Profile>,
581581
prefix: &InstallPrefix,
582582
force_update: bool,
583+
allow_downgrade: bool,
583584
old_date: Option<&str>,
584585
components: &[&str],
585586
targets: &[&str],
@@ -601,6 +602,7 @@ pub fn update_from_dist<'a>(
601602
profile,
602603
prefix,
603604
force_update,
605+
allow_downgrade,
604606
old_date,
605607
components,
606608
targets,
@@ -622,6 +624,7 @@ fn update_from_dist_<'a>(
622624
profile: Option<Profile>,
623625
prefix: &InstallPrefix,
624626
force_update: bool,
627+
allow_downgrade: bool,
625628
old_date: Option<&str>,
626629
components: &[&str],
627630
targets: &[&str],
@@ -644,21 +647,24 @@ fn update_from_dist_<'a>(
644647
Some(if provided < 1 { 1 } else { provided })
645648
};
646649

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

663669
loop {
664670
match try_update_from_dist_(
@@ -713,22 +719,12 @@ fn update_from_dist_<'a>(
713719
// the components that the user currently has installed. Let's try the previous
714720
// nightlies in reverse chronological order until we find a nightly that does,
715721
// starting at one date earlier than the current manifest's date.
716-
let try_next = Utc
717-
.from_utc_date(
718-
&NaiveDate::parse_from_str(
719-
toolchain.date.as_ref().unwrap_or(&fetched),
720-
"%Y-%m-%d",
721-
)
722-
.unwrap_or_else(|_| {
723-
panic!(
724-
"Malformed manifest date: {:?}",
725-
toolchain.date.as_ref().unwrap_or(&fetched)
726-
)
727-
}),
728-
)
722+
let toolchain_date = toolchain.date.as_ref().unwrap_or(&fetched);
723+
let try_next = utc_from_manifest_date(toolchain_date)
724+
.unwrap_or_else(|| panic!("Malformed manifest date: {:?}", toolchain_date))
729725
.pred();
730726

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

927923
Ok(urls)
928924
}
925+
926+
fn utc_from_manifest_date(date_str: &str) -> Option<Date<Utc>> {
927+
NaiveDate::parse_from_str(date_str, "%Y-%m-%d")
928+
.ok()
929+
.map(|date| Utc.from_utc_date(&date))
930+
}

src/install.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ pub enum InstallMethod<'a> {
2424
DownloadCfg<'a>,
2525
// --force
2626
bool,
27+
// --allow-downgrade
28+
bool,
2729
// toolchain already exists
2830
bool,
2931
// currently installed date
@@ -66,6 +68,7 @@ impl<'a> InstallMethod<'a> {
6668
update_hash,
6769
dl_cfg,
6870
force_update,
71+
allow_downgrade,
6972
exists,
7073
old_date,
7174
components,
@@ -79,6 +82,7 @@ impl<'a> InstallMethod<'a> {
7982
if exists { None } else { Some(profile) },
8083
prefix,
8184
force_update,
85+
allow_downgrade,
8286
old_date,
8387
components,
8488
targets,

src/toolchain.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ impl<'a> Toolchain<'a> {
166166
pub fn install_from_dist(
167167
&self,
168168
force_update: bool,
169+
allow_downgrade: bool,
169170
components: &[&str],
170171
targets: &[&str],
171172
) -> Result<UpdateStatus> {
@@ -177,6 +178,7 @@ impl<'a> Toolchain<'a> {
177178
update_hash.as_ref().map(|p| &**p),
178179
self.download_cfg(),
179180
force_update,
181+
allow_downgrade,
180182
self.exists(),
181183
old_date.as_ref().map(|s| &**s),
182184
components,
@@ -193,6 +195,7 @@ impl<'a> Toolchain<'a> {
193195
self.download_cfg(),
194196
false,
195197
false,
198+
false,
196199
None,
197200
&[],
198201
&[],

tests/cli-v2.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,3 +1392,60 @@ fn check_pgp_keys() {
13921392
);
13931393
})
13941394
}
1395+
1396+
#[test]
1397+
fn install_allow_downgrade() {
1398+
clitools::setup(Scenario::MissingComponent, &|config| {
1399+
let trip = TargetTriple::from_build();
1400+
1401+
// this dist has no rls and there is no newer one
1402+
set_current_dist_date(config, "2019-09-14");
1403+
expect_ok(
1404+
config,
1405+
&[
1406+
"rustup",
1407+
"toolchain",
1408+
"install",
1409+
"nightly",
1410+
"--no-self-update",
1411+
],
1412+
);
1413+
expect_stdout_ok(config, &["rustc", "--version"], "hash-nightly-3");
1414+
expect_component_not_executable(config, "rls");
1415+
1416+
expect_err(
1417+
config,
1418+
&[
1419+
"rustup",
1420+
"toolchain",
1421+
"install",
1422+
"nightly",
1423+
"--no-self-update",
1424+
"-c",
1425+
"rls",
1426+
],
1427+
&format!(
1428+
"component 'rls' for target '{}' is unavailable for download for channel nightly",
1429+
trip,
1430+
),
1431+
);
1432+
expect_stdout_ok(config, &["rustc", "--version"], "hash-nightly-3");
1433+
expect_component_not_executable(config, "rls");
1434+
1435+
expect_ok(
1436+
config,
1437+
&[
1438+
"rustup",
1439+
"toolchain",
1440+
"install",
1441+
"nightly",
1442+
"--no-self-update",
1443+
"-c",
1444+
"rls",
1445+
"--allow-downgrade",
1446+
],
1447+
);
1448+
expect_stdout_ok(config, &["rustc", "--version"], "hash-nightly-2");
1449+
expect_component_executable(config, "rls");
1450+
});
1451+
}

0 commit comments

Comments
 (0)