Skip to content

Commit

Permalink
Merge pull request #1298 from kennytm/fix-1297-must-install-rustc-cargo
Browse files Browse the repository at this point in the history
Prevent `rustup update` to a toolchain without `rustc` or `cargo`.
  • Loading branch information
alexcrichton authored Dec 1, 2017
2 parents a1f0d0d + ed1791c commit 1a2dd23
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 61 deletions.
16 changes: 16 additions & 0 deletions src/rustup-dist/src/manifestation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,22 @@ impl Manifestation {
return Ok(UpdateStatus::Unchanged);
}

// Make sure we don't accidentally uninstall the essential components! (see #1297)
let missing_essential_components = ["rustc", "cargo"]
.iter()
.filter_map(|pkg| if final_component_list.iter().any(|c| &c.pkg == pkg) {
None
} else {
Some(Component {
pkg: pkg.to_string(),
target: Some(self.target_triple.clone()),
})
})
.collect::<Vec<_>>();
if !missing_essential_components.is_empty() {
return Err(ErrorKind::RequestedComponentsUnavailable(missing_essential_components).into());
}

// Validate that the requested components are available
let unavailable_components: Vec<Component> = components_to_install.iter().filter(|c| {
use manifest::*;
Expand Down
99 changes: 53 additions & 46 deletions src/rustup-dist/tests/dist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ pub fn create_mock_channel(channel: &str, date: &str,
// Put the date in the files so they can be differentiated
let contents = Arc::new(date.as_bytes().to_vec());

let rust_pkg = MockPackage {
let mut packages = Vec::with_capacity(5);

packages.push(MockPackage {
name: "rust",
version: "1.0.0",
targets: vec![
Expand All @@ -63,6 +65,10 @@ pub fn create_mock_channel(channel: &str, date: &str,
name: "rustc".to_string(),
target: "x86_64-apple-darwin".to_string(),
},
MockComponent {
name: "cargo".to_string(),
target: "x86_64-apple-darwin".to_string(),
},
MockComponent {
name: "rust-std".to_string(),
target: "x86_64-apple-darwin".to_string(),
Expand Down Expand Up @@ -90,6 +96,10 @@ pub fn create_mock_channel(channel: &str, date: &str,
name: "rustc".to_string(),
target: "i686-apple-darwin".to_string(),
},
MockComponent {
name: "cargo".to_string(),
target: "i686-apple-darwin".to_string(),
},
MockComponent {
name: "rust-std".to_string(),
target: "i686-apple-darwin".to_string(),
Expand All @@ -101,39 +111,42 @@ pub fn create_mock_channel(channel: &str, date: &str,
}
}
]
};
});

let rustc_pkg = MockPackage {
name: "rustc",
version: "1.0.0",
targets: vec![
MockTargetedPackage {
target: "x86_64-apple-darwin".to_string(),
available: true,
components: vec![],
extensions: vec![],
installer: MockInstallerBuilder {
components: vec![MockComponentBuilder {
name: "rustc".to_string(),
files: vec![
MockFile::new_arc("bin/rustc", contents.clone()),
],
}],
}
},
MockTargetedPackage {
target: "i686-apple-darwin".to_string(),
available: true,
components: vec![],
extensions: vec![],
installer: MockInstallerBuilder {
components: vec![]
}
}
]
};
for bin in &["bin/rustc", "bin/cargo"] {
let pkg = &bin[4..];
packages.push(MockPackage {
name: pkg,
version: "1.0.0",
targets: vec![
MockTargetedPackage {
target: "x86_64-apple-darwin".to_string(),
available: true,
components: vec![],
extensions: vec![],
installer: MockInstallerBuilder {
components: vec![MockComponentBuilder {
name: pkg.to_string(),
files: vec![
MockFile::new_arc(*bin, contents.clone()),
],
}],
}
},
MockTargetedPackage {
target: "i686-apple-darwin".to_string(),
available: true,
components: vec![],
extensions: vec![],
installer: MockInstallerBuilder {
components: vec![]
}
},
],
});
}

let std_pkg = MockPackage {
packages.push(MockPackage {
name: "rust-std",
version: "1.0.0",
targets: vec![
Expand Down Expand Up @@ -180,26 +193,20 @@ pub fn create_mock_channel(channel: &str, date: &str,
}
},
]
};
});

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

let mut rust_pkg = rust_pkg;
if let Some(edit) = edit {
edit(date, &mut rust_pkg);
edit(date, &mut packages[0]);
}

MockChannel {
name: channel.to_string(),
date: date.to_string(),
packages: vec![
rust_pkg,
rustc_pkg,
std_pkg,
bonus_pkg,
],
packages,
renames: HashMap::new(),
}
}
Expand Down Expand Up @@ -270,7 +277,7 @@ fn rename_component() {

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

let date_1 = "2016-02-01";
let mut channel_1 = create_mock_channel("nightly", date_1, Some(edit));
channel_1.packages[3] = bonus_component("bobo", Arc::new(date_1.as_bytes().to_vec()));
channel_1.packages[4] = bonus_component("bobo", Arc::new(date_1.as_bytes().to_vec()));
let date_2 = "2016-02-02";
let mut channel_2 = create_mock_channel("nightly", date_2, Some(edit));
channel_2.packages[3] = bonus_component("bobo", Arc::new(date_2.as_bytes().to_vec()));
channel_2.packages[4] = bonus_component("bobo", Arc::new(date_2.as_bytes().to_vec()));
channel_2.renames.insert("bonus".to_owned(), "bobo".to_owned());
let mock_dist_server = MockDistServer {
path: dist_tempdir.path().to_owned(),
Expand Down Expand Up @@ -351,7 +358,7 @@ fn rename_component_new() {

let date_2 = "2016-02-02";
let mut channel_2 = create_mock_channel("nightly", date_2, Some(edit_2));
channel_2.packages[3] = bonus_component("bobo", Arc::new(date_2.as_bytes().to_vec()));
channel_2.packages[4] = bonus_component("bobo", Arc::new(date_2.as_bytes().to_vec()));
channel_2.renames.insert("bonus".to_owned(), "bobo".to_owned());
let mock_dist_server = MockDistServer {
path: dist_tempdir.path().to_owned(),
Expand Down
16 changes: 8 additions & 8 deletions src/rustup-dist/tests/install.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,21 +337,21 @@ fn unix_permissions() {
let tx = pkg.install(&components, "mycomponent", None, tx).unwrap();
tx.commit();

let m = fs::metadata(instdir.path().join("bin/foo")).unwrap().permissions().mode();
let m = 0o777 & fs::metadata(instdir.path().join("bin/foo")).unwrap().permissions().mode();
assert_eq!(m, 0o755);
let m = fs::metadata(instdir.path().join("lib/bar")).unwrap().permissions().mode();
let m = 0o777 & fs::metadata(instdir.path().join("lib/bar")).unwrap().permissions().mode();
assert_eq!(m, 0o644);
let m = fs::metadata(instdir.path().join("lib/foobar")).unwrap().permissions().mode();
let m = 0o777 & fs::metadata(instdir.path().join("lib/foobar")).unwrap().permissions().mode();
assert_eq!(m, 0o755);
let m = fs::metadata(instdir.path().join("doc/stuff/")).unwrap().permissions().mode();
let m = 0o777 & fs::metadata(instdir.path().join("doc/stuff/")).unwrap().permissions().mode();
assert_eq!(m, 0o755);
let m = fs::metadata(instdir.path().join("doc/stuff/doc1")).unwrap().permissions().mode();
let m = 0o777 & fs::metadata(instdir.path().join("doc/stuff/doc1")).unwrap().permissions().mode();
assert_eq!(m, 0o644);
let m = fs::metadata(instdir.path().join("doc/stuff/morestuff")).unwrap().permissions().mode();
let m = 0o777 & fs::metadata(instdir.path().join("doc/stuff/morestuff")).unwrap().permissions().mode();
assert_eq!(m, 0o755);
let m = fs::metadata(instdir.path().join("doc/stuff/morestuff/doc2")).unwrap().permissions().mode();
let m = 0o777 & fs::metadata(instdir.path().join("doc/stuff/morestuff/doc2")).unwrap().permissions().mode();
assert_eq!(m, 0o644);
let m = fs::metadata(instdir.path().join("doc/stuff/morestuff/tool")).unwrap().permissions().mode();
let m = 0o777 & fs::metadata(instdir.path().join("doc/stuff/morestuff/tool")).unwrap().permissions().mode();
assert_eq!(m, 0o755);
}

Expand Down
55 changes: 50 additions & 5 deletions src/rustup-mock/src/clitools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub enum Scenario {
SimpleV2, // One date, v2 manifests
SimpleV1, // One date, v1 manifests
MultiHost, // One date, v2 manifests, MULTI_ARCH1 host
Unavailable, // Two dates, v2 manifests, everything unavailable in second date.
}

pub static CROSS_ARCH1: &'static str = "x86_64-unknown-linux-musl";
Expand Down Expand Up @@ -333,13 +334,23 @@ pub fn run(config: &Config, name: &str, args: &[&str], env: &[(&str, &str)]) ->
// Creates a mock dist server populated with some test data
fn create_mock_dist_server(path: &Path, s: Scenario) {
let mut chans = Vec::new();
if s == Scenario::Full || s == Scenario::ArchivesV1 || s == Scenario::ArchivesV2 {

let dates_count = match s {
Scenario::SimpleV1 | Scenario::SimpleV2 | Scenario::MultiHost => 1,
Scenario::Full | Scenario::ArchivesV1 | Scenario::ArchivesV2 | Scenario::Unavailable => 2,
};

if dates_count > 1 {
let c1 = build_mock_channel(s, "nightly", "2015-01-01", "1.2.0", "hash-n-1", false);
let c2 = build_mock_channel(s, "beta", "2015-01-01", "1.1.0", "hash-b-1", false);
let c3 = build_mock_channel(s, "stable", "2015-01-01", "1.0.0", "hash-s-1", false);
chans.extend(vec![c1, c2, c3]);
}
let c4 = build_mock_channel(s, "nightly", "2015-01-02", "1.3.0", "hash-n-2", true);
let c4 = if s == Scenario::Unavailable {
build_mock_unavailable_channel("nightly", "2015-01-02", "1.3.0")
} else {
build_mock_channel(s, "nightly", "2015-01-02", "1.3.0", "hash-n-2", true)
};
let c5 = build_mock_channel(s, "beta", "2015-01-02", "1.2.0", "hash-b-2", false);
let c6 = build_mock_channel(s, "stable", "2015-01-02", "1.1.0", "hash-s-2", false);
chans.extend(vec![c4, c5, c6]);
Expand All @@ -348,7 +359,7 @@ fn create_mock_dist_server(path: &Path, s: Scenario) {
Scenario::Full => vec![ManifestVersion::V1, ManifestVersion::V2],
Scenario::SimpleV1 | Scenario::ArchivesV1 => vec![ManifestVersion::V1],
Scenario::SimpleV2 | Scenario::ArchivesV2 |
Scenario::MultiHost => vec![ManifestVersion::V2],
Scenario::MultiHost | Scenario::Unavailable => vec![ManifestVersion::V2],
};

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

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

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

fn build_mock_unavailable_channel(channel: &str, date: &str, version: &'static str) -> MockChannel {
let ref host_triple = this_host_triple();

let packages = [
"cargo",
"rust",
"rust-docs",
"rust-std",
"rustc",
"rls-preview",
"rust-analysis",
];
let packages = packages.iter().map(|name| MockPackage {
name,
version,
targets: vec![MockTargetedPackage {
target: host_triple.clone(),
available: false,
components: vec![],
extensions: vec![],
installer: MockInstallerBuilder {
components: vec![],
},
}],
}).collect();

MockChannel {
name: channel.to_string(),
date: date.to_string(),
packages,
renames: HashMap::new(),
}
}

pub fn this_host_triple() -> String {
if let Some(triple) = option_env!("RUSTUP_OVERRIDE_BUILD_TRIPLE") {
triple.to_owned()
Expand Down
17 changes: 17 additions & 0 deletions tests/cli-misc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,3 +567,20 @@ r"info: no toolchain installed for 'test'
");
});
}

// issue #1297
#[test]
fn update_unavailable_rustc() {
clitools::setup(Scenario::Unavailable, &|config| {
set_current_dist_date(config, "2015-01-01");
expect_ok(config, &["rustup", "default", "nightly"]);

expect_stdout_ok(config, &["rustc", "--version"], "hash-n-1");

set_current_dist_date(config, "2015-01-02");
expect_err(config, &["rustup", "update", "nightly"],
"some components unavailable for download: 'rustc', 'cargo'");

expect_stdout_ok(config, &["rustc", "--version"], "hash-n-1");
});
}
4 changes: 2 additions & 2 deletions tests/cli-v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -683,8 +683,8 @@ fn make_component_unavailable(config: &Config, name: &str, target: &TargetTriple
let ref manifest_str = rustup_utils::raw::read_file(manifest_path).unwrap();
let mut manifest = Manifest::parse(manifest_str).unwrap();
{
let mut std_pkg = manifest.packages.get_mut(name).unwrap();
let mut target_pkg = std_pkg.targets.get_mut(target).unwrap();
let std_pkg = manifest.packages.get_mut(name).unwrap();
let target_pkg = std_pkg.targets.get_mut(target).unwrap();
target_pkg.bins = None;
}
let ref manifest_str = manifest.stringify();
Expand Down

0 comments on commit 1a2dd23

Please sign in to comment.