Skip to content

Commit 1a2dd23

Browse files
authored
Merge pull request #1298 from kennytm/fix-1297-must-install-rustc-cargo
Prevent `rustup update` to a toolchain without `rustc` or `cargo`.
2 parents a1f0d0d + ed1791c commit 1a2dd23

File tree

6 files changed

+146
-61
lines changed

6 files changed

+146
-61
lines changed

src/rustup-dist/src/manifestation.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,22 @@ impl Manifestation {
107107
return Ok(UpdateStatus::Unchanged);
108108
}
109109

110+
// Make sure we don't accidentally uninstall the essential components! (see #1297)
111+
let missing_essential_components = ["rustc", "cargo"]
112+
.iter()
113+
.filter_map(|pkg| if final_component_list.iter().any(|c| &c.pkg == pkg) {
114+
None
115+
} else {
116+
Some(Component {
117+
pkg: pkg.to_string(),
118+
target: Some(self.target_triple.clone()),
119+
})
120+
})
121+
.collect::<Vec<_>>();
122+
if !missing_essential_components.is_empty() {
123+
return Err(ErrorKind::RequestedComponentsUnavailable(missing_essential_components).into());
124+
}
125+
110126
// Validate that the requested components are available
111127
let unavailable_components: Vec<Component> = components_to_install.iter().filter(|c| {
112128
use manifest::*;

src/rustup-dist/tests/dist.rs

Lines changed: 53 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,9 @@ pub fn create_mock_channel(channel: &str, date: &str,
5151
// Put the date in the files so they can be differentiated
5252
let contents = Arc::new(date.as_bytes().to_vec());
5353

54-
let rust_pkg = MockPackage {
54+
let mut packages = Vec::with_capacity(5);
55+
56+
packages.push(MockPackage {
5557
name: "rust",
5658
version: "1.0.0",
5759
targets: vec![
@@ -63,6 +65,10 @@ pub fn create_mock_channel(channel: &str, date: &str,
6365
name: "rustc".to_string(),
6466
target: "x86_64-apple-darwin".to_string(),
6567
},
68+
MockComponent {
69+
name: "cargo".to_string(),
70+
target: "x86_64-apple-darwin".to_string(),
71+
},
6672
MockComponent {
6773
name: "rust-std".to_string(),
6874
target: "x86_64-apple-darwin".to_string(),
@@ -90,6 +96,10 @@ pub fn create_mock_channel(channel: &str, date: &str,
9096
name: "rustc".to_string(),
9197
target: "i686-apple-darwin".to_string(),
9298
},
99+
MockComponent {
100+
name: "cargo".to_string(),
101+
target: "i686-apple-darwin".to_string(),
102+
},
93103
MockComponent {
94104
name: "rust-std".to_string(),
95105
target: "i686-apple-darwin".to_string(),
@@ -101,39 +111,42 @@ pub fn create_mock_channel(channel: &str, date: &str,
101111
}
102112
}
103113
]
104-
};
114+
});
105115

106-
let rustc_pkg = MockPackage {
107-
name: "rustc",
108-
version: "1.0.0",
109-
targets: vec![
110-
MockTargetedPackage {
111-
target: "x86_64-apple-darwin".to_string(),
112-
available: true,
113-
components: vec![],
114-
extensions: vec![],
115-
installer: MockInstallerBuilder {
116-
components: vec![MockComponentBuilder {
117-
name: "rustc".to_string(),
118-
files: vec![
119-
MockFile::new_arc("bin/rustc", contents.clone()),
120-
],
121-
}],
122-
}
123-
},
124-
MockTargetedPackage {
125-
target: "i686-apple-darwin".to_string(),
126-
available: true,
127-
components: vec![],
128-
extensions: vec![],
129-
installer: MockInstallerBuilder {
130-
components: vec![]
131-
}
132-
}
133-
]
134-
};
116+
for bin in &["bin/rustc", "bin/cargo"] {
117+
let pkg = &bin[4..];
118+
packages.push(MockPackage {
119+
name: pkg,
120+
version: "1.0.0",
121+
targets: vec![
122+
MockTargetedPackage {
123+
target: "x86_64-apple-darwin".to_string(),
124+
available: true,
125+
components: vec![],
126+
extensions: vec![],
127+
installer: MockInstallerBuilder {
128+
components: vec![MockComponentBuilder {
129+
name: pkg.to_string(),
130+
files: vec![
131+
MockFile::new_arc(*bin, contents.clone()),
132+
],
133+
}],
134+
}
135+
},
136+
MockTargetedPackage {
137+
target: "i686-apple-darwin".to_string(),
138+
available: true,
139+
components: vec![],
140+
extensions: vec![],
141+
installer: MockInstallerBuilder {
142+
components: vec![]
143+
}
144+
},
145+
],
146+
});
147+
}
135148

136-
let std_pkg = MockPackage {
149+
packages.push(MockPackage {
137150
name: "rust-std",
138151
version: "1.0.0",
139152
targets: vec![
@@ -180,26 +193,20 @@ pub fn create_mock_channel(channel: &str, date: &str,
180193
}
181194
},
182195
]
183-
};
196+
});
184197

185198
// An extra package that can be used as a component of the other packages
186199
// for various tests
187-
let bonus_pkg = bonus_component("bonus", contents.clone());
200+
packages.push(bonus_component("bonus", contents.clone()));
188201

189-
let mut rust_pkg = rust_pkg;
190202
if let Some(edit) = edit {
191-
edit(date, &mut rust_pkg);
203+
edit(date, &mut packages[0]);
192204
}
193205

194206
MockChannel {
195207
name: channel.to_string(),
196208
date: date.to_string(),
197-
packages: vec![
198-
rust_pkg,
199-
rustc_pkg,
200-
std_pkg,
201-
bonus_pkg,
202-
],
209+
packages,
203210
renames: HashMap::new(),
204211
}
205212
}
@@ -270,7 +277,7 @@ fn rename_component() {
270277

271278
let date_2 = "2016-02-02";
272279
let mut channel_2 = create_mock_channel("nightly", date_2, Some(edit_2));
273-
channel_2.packages[3] = bonus_component("bobo", Arc::new(date_2.as_bytes().to_vec()));
280+
channel_2.packages[4] = bonus_component("bobo", Arc::new(date_2.as_bytes().to_vec()));
274281
channel_2.renames.insert("bonus".to_owned(), "bobo".to_owned());
275282
let mock_dist_server = MockDistServer {
276283
path: dist_tempdir.path().to_owned(),
@@ -309,10 +316,10 @@ fn rename_component_ignore() {
309316

310317
let date_1 = "2016-02-01";
311318
let mut channel_1 = create_mock_channel("nightly", date_1, Some(edit));
312-
channel_1.packages[3] = bonus_component("bobo", Arc::new(date_1.as_bytes().to_vec()));
319+
channel_1.packages[4] = bonus_component("bobo", Arc::new(date_1.as_bytes().to_vec()));
313320
let date_2 = "2016-02-02";
314321
let mut channel_2 = create_mock_channel("nightly", date_2, Some(edit));
315-
channel_2.packages[3] = bonus_component("bobo", Arc::new(date_2.as_bytes().to_vec()));
322+
channel_2.packages[4] = bonus_component("bobo", Arc::new(date_2.as_bytes().to_vec()));
316323
channel_2.renames.insert("bonus".to_owned(), "bobo".to_owned());
317324
let mock_dist_server = MockDistServer {
318325
path: dist_tempdir.path().to_owned(),
@@ -351,7 +358,7 @@ fn rename_component_new() {
351358

352359
let date_2 = "2016-02-02";
353360
let mut channel_2 = create_mock_channel("nightly", date_2, Some(edit_2));
354-
channel_2.packages[3] = bonus_component("bobo", Arc::new(date_2.as_bytes().to_vec()));
361+
channel_2.packages[4] = bonus_component("bobo", Arc::new(date_2.as_bytes().to_vec()));
355362
channel_2.renames.insert("bonus".to_owned(), "bobo".to_owned());
356363
let mock_dist_server = MockDistServer {
357364
path: dist_tempdir.path().to_owned(),

src/rustup-dist/tests/install.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -337,21 +337,21 @@ fn unix_permissions() {
337337
let tx = pkg.install(&components, "mycomponent", None, tx).unwrap();
338338
tx.commit();
339339

340-
let m = fs::metadata(instdir.path().join("bin/foo")).unwrap().permissions().mode();
340+
let m = 0o777 & fs::metadata(instdir.path().join("bin/foo")).unwrap().permissions().mode();
341341
assert_eq!(m, 0o755);
342-
let m = fs::metadata(instdir.path().join("lib/bar")).unwrap().permissions().mode();
342+
let m = 0o777 & fs::metadata(instdir.path().join("lib/bar")).unwrap().permissions().mode();
343343
assert_eq!(m, 0o644);
344-
let m = fs::metadata(instdir.path().join("lib/foobar")).unwrap().permissions().mode();
344+
let m = 0o777 & fs::metadata(instdir.path().join("lib/foobar")).unwrap().permissions().mode();
345345
assert_eq!(m, 0o755);
346-
let m = fs::metadata(instdir.path().join("doc/stuff/")).unwrap().permissions().mode();
346+
let m = 0o777 & fs::metadata(instdir.path().join("doc/stuff/")).unwrap().permissions().mode();
347347
assert_eq!(m, 0o755);
348-
let m = fs::metadata(instdir.path().join("doc/stuff/doc1")).unwrap().permissions().mode();
348+
let m = 0o777 & fs::metadata(instdir.path().join("doc/stuff/doc1")).unwrap().permissions().mode();
349349
assert_eq!(m, 0o644);
350-
let m = fs::metadata(instdir.path().join("doc/stuff/morestuff")).unwrap().permissions().mode();
350+
let m = 0o777 & fs::metadata(instdir.path().join("doc/stuff/morestuff")).unwrap().permissions().mode();
351351
assert_eq!(m, 0o755);
352-
let m = fs::metadata(instdir.path().join("doc/stuff/morestuff/doc2")).unwrap().permissions().mode();
352+
let m = 0o777 & fs::metadata(instdir.path().join("doc/stuff/morestuff/doc2")).unwrap().permissions().mode();
353353
assert_eq!(m, 0o644);
354-
let m = fs::metadata(instdir.path().join("doc/stuff/morestuff/tool")).unwrap().permissions().mode();
354+
let m = 0o777 & fs::metadata(instdir.path().join("doc/stuff/morestuff/tool")).unwrap().permissions().mode();
355355
assert_eq!(m, 0o755);
356356
}
357357

src/rustup-mock/src/clitools.rs

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ pub enum Scenario {
5050
SimpleV2, // One date, v2 manifests
5151
SimpleV1, // One date, v1 manifests
5252
MultiHost, // One date, v2 manifests, MULTI_ARCH1 host
53+
Unavailable, // Two dates, v2 manifests, everything unavailable in second date.
5354
}
5455

5556
pub static CROSS_ARCH1: &'static str = "x86_64-unknown-linux-musl";
@@ -333,13 +334,23 @@ pub fn run(config: &Config, name: &str, args: &[&str], env: &[(&str, &str)]) ->
333334
// Creates a mock dist server populated with some test data
334335
fn create_mock_dist_server(path: &Path, s: Scenario) {
335336
let mut chans = Vec::new();
336-
if s == Scenario::Full || s == Scenario::ArchivesV1 || s == Scenario::ArchivesV2 {
337+
338+
let dates_count = match s {
339+
Scenario::SimpleV1 | Scenario::SimpleV2 | Scenario::MultiHost => 1,
340+
Scenario::Full | Scenario::ArchivesV1 | Scenario::ArchivesV2 | Scenario::Unavailable => 2,
341+
};
342+
343+
if dates_count > 1 {
337344
let c1 = build_mock_channel(s, "nightly", "2015-01-01", "1.2.0", "hash-n-1", false);
338345
let c2 = build_mock_channel(s, "beta", "2015-01-01", "1.1.0", "hash-b-1", false);
339346
let c3 = build_mock_channel(s, "stable", "2015-01-01", "1.0.0", "hash-s-1", false);
340347
chans.extend(vec![c1, c2, c3]);
341348
}
342-
let c4 = build_mock_channel(s, "nightly", "2015-01-02", "1.3.0", "hash-n-2", true);
349+
let c4 = if s == Scenario::Unavailable {
350+
build_mock_unavailable_channel("nightly", "2015-01-02", "1.3.0")
351+
} else {
352+
build_mock_channel(s, "nightly", "2015-01-02", "1.3.0", "hash-n-2", true)
353+
};
343354
let c5 = build_mock_channel(s, "beta", "2015-01-02", "1.2.0", "hash-b-2", false);
344355
let c6 = build_mock_channel(s, "stable", "2015-01-02", "1.1.0", "hash-s-2", false);
345356
chans.extend(vec![c4, c5, c6]);
@@ -348,7 +359,7 @@ fn create_mock_dist_server(path: &Path, s: Scenario) {
348359
Scenario::Full => vec![ManifestVersion::V1, ManifestVersion::V2],
349360
Scenario::SimpleV1 | Scenario::ArchivesV1 => vec![ManifestVersion::V1],
350361
Scenario::SimpleV2 | Scenario::ArchivesV2 |
351-
Scenario::MultiHost => vec![ManifestVersion::V2],
362+
Scenario::MultiHost | Scenario::Unavailable => vec![ManifestVersion::V2],
352363
};
353364

354365
MockDistServer {
@@ -357,7 +368,7 @@ fn create_mock_dist_server(path: &Path, s: Scenario) {
357368
}.write(vs, true);
358369

359370
// Also create the manifests for stable releases by version
360-
if s == Scenario::Full || s == Scenario::ArchivesV1 || s == Scenario::ArchivesV2 {
371+
if dates_count > 1 {
361372
let _ = hard_link(path.join("dist/2015-01-01/channel-rust-stable.toml"),
362373
path.join("dist/channel-rust-1.0.0.toml"));
363374
let _ = hard_link(path.join("dist/2015-01-01/channel-rust-stable.toml.sha256"),
@@ -370,7 +381,7 @@ fn create_mock_dist_server(path: &Path, s: Scenario) {
370381

371382
// Same for v1 manifests. These are just the installers.
372383
let host_triple = this_host_triple();
373-
if s == Scenario::Full || s == Scenario::ArchivesV1 || s == Scenario::ArchivesV2 {
384+
if dates_count > 1 {
374385
hard_link(path.join(format!("dist/2015-01-01/rust-stable-{}.tar.gz", host_triple)),
375386
path.join(format!("dist/rust-1.0.0-{}.tar.gz", host_triple))).unwrap();
376387
hard_link(path.join(format!("dist/2015-01-01/rust-stable-{}.tar.gz.sha256", host_triple)),
@@ -534,6 +545,40 @@ fn build_mock_channel(s: Scenario, channel: &str, date: &str,
534545
}
535546
}
536547

548+
fn build_mock_unavailable_channel(channel: &str, date: &str, version: &'static str) -> MockChannel {
549+
let ref host_triple = this_host_triple();
550+
551+
let packages = [
552+
"cargo",
553+
"rust",
554+
"rust-docs",
555+
"rust-std",
556+
"rustc",
557+
"rls-preview",
558+
"rust-analysis",
559+
];
560+
let packages = packages.iter().map(|name| MockPackage {
561+
name,
562+
version,
563+
targets: vec![MockTargetedPackage {
564+
target: host_triple.clone(),
565+
available: false,
566+
components: vec![],
567+
extensions: vec![],
568+
installer: MockInstallerBuilder {
569+
components: vec![],
570+
},
571+
}],
572+
}).collect();
573+
574+
MockChannel {
575+
name: channel.to_string(),
576+
date: date.to_string(),
577+
packages,
578+
renames: HashMap::new(),
579+
}
580+
}
581+
537582
pub fn this_host_triple() -> String {
538583
if let Some(triple) = option_env!("RUSTUP_OVERRIDE_BUILD_TRIPLE") {
539584
triple.to_owned()

tests/cli-misc.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,3 +567,20 @@ r"info: no toolchain installed for 'test'
567567
");
568568
});
569569
}
570+
571+
// issue #1297
572+
#[test]
573+
fn update_unavailable_rustc() {
574+
clitools::setup(Scenario::Unavailable, &|config| {
575+
set_current_dist_date(config, "2015-01-01");
576+
expect_ok(config, &["rustup", "default", "nightly"]);
577+
578+
expect_stdout_ok(config, &["rustc", "--version"], "hash-n-1");
579+
580+
set_current_dist_date(config, "2015-01-02");
581+
expect_err(config, &["rustup", "update", "nightly"],
582+
"some components unavailable for download: 'rustc', 'cargo'");
583+
584+
expect_stdout_ok(config, &["rustc", "--version"], "hash-n-1");
585+
});
586+
}

tests/cli-v2.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -683,8 +683,8 @@ fn make_component_unavailable(config: &Config, name: &str, target: &TargetTriple
683683
let ref manifest_str = rustup_utils::raw::read_file(manifest_path).unwrap();
684684
let mut manifest = Manifest::parse(manifest_str).unwrap();
685685
{
686-
let mut std_pkg = manifest.packages.get_mut(name).unwrap();
687-
let mut target_pkg = std_pkg.targets.get_mut(target).unwrap();
686+
let std_pkg = manifest.packages.get_mut(name).unwrap();
687+
let target_pkg = std_pkg.targets.get_mut(target).unwrap();
688688
target_pkg.bins = None;
689689
}
690690
let ref manifest_str = manifest.stringify();

0 commit comments

Comments
 (0)