diff --git a/Cargo.lock b/Cargo.lock index f2eaf470658b3..9ec76d6c3cf8b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -201,7 +201,9 @@ dependencies = [ name = "build-manifest" version = "0.1.0" dependencies = [ + "reqwest", "serde", + "serde_json", "toml", ] diff --git a/src/tools/build-manifest/Cargo.toml b/src/tools/build-manifest/Cargo.toml index c364479d8db13..63ae445f99b60 100644 --- a/src/tools/build-manifest/Cargo.toml +++ b/src/tools/build-manifest/Cargo.toml @@ -7,3 +7,5 @@ edition = "2018" [dependencies] toml = "0.5" serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +reqwest = "0.9" diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs index 9ffa9391c820b..c2d642bb136be 100644 --- a/src/tools/build-manifest/src/main.rs +++ b/src/tools/build-manifest/src/main.rs @@ -1,12 +1,19 @@ +//! Build a dist manifest, hash and sign everything. +//! This gets called by `promote-release` +//! (https://github.com/rust-lang/rust-central-station/tree/master/promote-release) +//! via `x.py dist hash-and-sign`; the cmdline arguments are set up +//! by rustbuild (in `src/bootstrap/dist.rs`). + use toml; use serde::Serialize; use std::collections::BTreeMap; use std::env; use std::fs; -use std::io::{self, Read, Write}; +use std::io::{self, Read, Write, BufRead, BufReader}; use std::path::{PathBuf, Path}; use std::process::{Command, Stdio}; +use std::collections::HashMap; static HOSTS: &[&str] = &[ "aarch64-unknown-linux-gnu", @@ -146,6 +153,9 @@ static MINGW: &[&str] = &[ "x86_64-pc-windows-gnu", ]; +static TOOLSTATE: &str = + "https://raw.githubusercontent.com/rust-lang-nursery/rust-toolstate/master/history/linux.tsv"; + #[derive(Serialize)] #[serde(rename_all = "kebab-case")] struct Manifest { @@ -270,6 +280,7 @@ fn main() { // Do not ask for a passphrase while manually testing let mut passphrase = String::new(); if should_sign { + // `x.py` passes the passphrase via stdin. t!(io::stdin().read_to_string(&mut passphrase)); } @@ -353,6 +364,7 @@ impl Builder { self.lldb_git_commit_hash = self.git_commit_hash("lldb", "x86_64-unknown-linux-gnu"); self.miri_git_commit_hash = self.git_commit_hash("miri", "x86_64-unknown-linux-gnu"); + self.check_toolstate(); self.digest_and_sign(); let manifest = self.build_manifest(); self.write_channel_files(&self.rust_release, &manifest); @@ -362,6 +374,37 @@ impl Builder { } } + /// If a tool does not pass its tests, don't ship it. + /// Right now, we do this only for Miri. + fn check_toolstate(&mut self) { + // Get the toolstate for this rust revision. + let rev = self.rust_git_commit_hash.as_ref().expect("failed to determine rust git hash"); + let toolstates = reqwest::get(TOOLSTATE).expect("failed to get toolstates"); + let toolstates = BufReader::new(toolstates); + let toolstate = toolstates.lines() + .find_map(|line| { + let line = line.expect("failed to read toolstate lines"); + let mut pieces = line.splitn(2, '\t'); + let commit = pieces.next().expect("malformed toolstate line"); + if commit != rev { + // Not the right commit. + return None; + } + // Return the 2nd piece, the JSON. + Some(pieces.next().expect("malformed toolstate line").to_owned()) + }) + .expect("failed to find toolstate for rust commit"); + let toolstate: HashMap = + serde_json::from_str(&toolstate).expect("toolstate is malformed JSON"); + // Mark some tools as missing based on toolstate. + if toolstate.get("miri").map(|s| &*s as &str) != Some("test-pass") { + println!("Miri tests are not passing, removing component"); + self.miri_version = None; + self.miri_git_commit_hash = None; + } + } + + /// Hash all files, compute their signatures, and collect the hashes in `self.digests`. fn digest_and_sign(&mut self) { for file in t!(self.input.read_dir()).map(|e| t!(e).path()) { let filename = file.file_name().unwrap().to_str().unwrap(); @@ -532,19 +575,20 @@ impl Builder { .as_ref() .cloned() .map(|version| (version, true)) - .unwrap_or_default(); + .unwrap_or_default(); // `is_present` defaults to `false` here. - // miri needs to build std with xargo, which doesn't allow stable/beta: - // + // Miri is nightly-only; never ship it for other trains. if pkgname == "miri-preview" && self.rust_release != "nightly" { - is_present = false; // ignore it + is_present = false; // Pretend the component is entirely missing. } let targets = targets.iter().map(|name| { if is_present { + // The component generally exists, but it might still be missing for this target. let filename = self.filename(pkgname, name); let digest = match self.digests.remove(&filename) { Some(digest) => digest, + // This component does not exist for this target -- skip it. None => return (name.to_string(), Target::unavailable()), }; let xz_filename = filename.replace(".tar.gz", ".tar.xz");