Line data Source code
1 : use anyhow::{anyhow, ensure, Context, Result}; 2 : use once_cell::sync::Lazy; 3 : use regex::Regex; 4 : use std::{ 5 : fs::File, 6 : io::{BufRead, BufReader}, 7 : path::Path, 8 : process::{Command, Stdio}, 9 : thread, 10 : }; 11 : 12 0 : fn main() -> Result<()> { 13 0 : let toolchains = collect_toolchains(&["cargo-dylint", "internal"])?; 14 : 15 0 : println!("{:#?}", &toolchains); 16 0 : 17 0 : let handles = std::iter::once("nightly".to_owned()) 18 0 : .chain(toolchains) 19 0 : .map(|toolchain| (thread::spawn(move || install_toolchain(&toolchain)))); 20 : 21 0 : for handle in handles { 22 0 : let () = handle 23 0 : .join() 24 0 : .map_err(|error| anyhow!("{error:?}")) 25 0 : .and_then(std::convert::identity)?; 26 : } 27 : 28 0 : Ok(()) 29 0 : } 30 : 31 0 : fn collect_toolchains(dirs: &[&str]) -> Result<Vec<String>> { 32 0 : let mut toolchains = Vec::new(); 33 : 34 0 : for dir in dirs { 35 0 : let toolchains_for_dir = collect_toolchains_for_dir(dir)?; 36 0 : toolchains.extend(toolchains_for_dir); 37 : } 38 : 39 0 : toolchains.sort(); 40 0 : toolchains.dedup(); 41 0 : 42 0 : Ok(toolchains) 43 0 : } 44 : 45 0 : fn collect_toolchains_for_dir(dir: &str) -> Result<Vec<String>> { 46 0 : let mut ls_files = Command::new("git") 47 0 : .args(["ls-files", dir]) 48 0 : .stdout(Stdio::piped()) 49 0 : .spawn() 50 0 : .with_context(|| "Could not spawn `git ls-files`")?; 51 : 52 0 : let stdout = ls_files.stdout.take().unwrap(); 53 0 : BufReader::new(stdout) 54 0 : .lines() 55 0 : .try_fold(Vec::new(), |mut toolchains, result| -> Result<_> { 56 0 : let path = result.with_context(|| "Could not read from `git ls-files`")?; 57 0 : let toolchains_for_path = collect_toolchains_for_path(path)?; 58 0 : toolchains.extend(toolchains_for_path); 59 0 : Ok(toolchains) 60 0 : }) 61 0 : } 62 : 63 : static RE: Lazy<Regex> = 64 0 : Lazy::new(|| Regex::new(r"\<nightly-[0-9]{4}-[0-9]{2}-[0-9]{2}\>").unwrap()); 65 : 66 0 : fn collect_toolchains_for_path(path: impl AsRef<Path>) -> Result<Vec<String>> { 67 0 : let file = File::open(&path).with_context(|| format!("Could not open {:?}", path.as_ref()))?; 68 0 : BufReader::new(file) 69 0 : .lines() 70 0 : .try_fold(Vec::new(), |mut toolchains, result| -> Result<_> { 71 0 : let line = 72 0 : result.with_context(|| format!("Could not read from {:?}", path.as_ref()))?; 73 0 : let n = line.find("//").unwrap_or(line.len()); 74 0 : toolchains.extend(RE.find_iter(&line[..n]).map(|m| m.as_str().to_owned())); 75 0 : Ok(toolchains) 76 0 : }) 77 0 : } 78 : 79 0 : fn install_toolchain(toolchain: &str) -> Result<()> { 80 0 : let status = Command::new("rustup") 81 0 : .args([ 82 0 : "install", 83 0 : toolchain, 84 0 : "--profile=minimal", 85 0 : "--no-self-update", 86 0 : ]) 87 0 : .status() 88 0 : .with_context(|| format!("Could not install {toolchain} with `rustup`"))?; 89 0 : ensure!(status.success()); 90 : 91 0 : let status = Command::new("rustup") 92 0 : .args([ 93 0 : "component", 94 0 : "add", 95 0 : "llvm-tools-preview", 96 0 : "rustc-dev", 97 0 : "--toolchain", 98 0 : toolchain, 99 0 : ]) 100 0 : .status() 101 0 : .with_context(|| format!("Could not add components to {toolchain} with `rustup`"))?; 102 0 : ensure!(status.success()); 103 : 104 0 : Ok(()) 105 0 : }