diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index bf4d85dcb0..2b7a461e56 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -12,21 +12,18 @@ mod ast_src; use std::{ env, - io::Write, path::{Path, PathBuf}, - process::{Command, Stdio}, }; + use walkdir::{DirEntry, WalkDir}; use crate::{ codegen::Mode, - not_bash::{date_iso, fs2, pushd, rm_rf, run}, + not_bash::{date_iso, fs2, pushd, pushenv, rm_rf, run}, }; pub use anyhow::{bail, Context as _, Result}; -const RUSTFMT_TOOLCHAIN: &str = "stable"; - pub fn project_root() -> PathBuf { Path::new( &env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| env!("CARGO_MANIFEST_DIR").to_owned()), @@ -54,77 +51,44 @@ pub fn rust_files(path: &Path) -> impl Iterator { pub fn run_rustfmt(mode: Mode) -> Result<()> { let _dir = pushd(project_root()); + let _e = pushenv("RUSTUP_TOOLCHAIN", "stable"); ensure_rustfmt()?; - - if Command::new("cargo") - .env("RUSTUP_TOOLCHAIN", RUSTFMT_TOOLCHAIN) - .args(&["fmt", "--"]) - .args(if mode == Mode::Verify { &["--check"][..] } else { &[] }) - .stderr(Stdio::inherit()) - .status()? - .success() - { - Ok(()) - } else { - bail!("Rustfmt failed"); - } + match mode { + Mode::Overwrite => run!("cargo fmt"), + Mode::Verify => run!("cargo fmt -- --check"), + }?; + Ok(()) } fn reformat(text: impl std::fmt::Display) -> Result { + let _e = pushenv("RUSTUP_TOOLCHAIN", "stable"); ensure_rustfmt()?; - let mut rustfmt = Command::new("rustfmt") - .env("RUSTUP_TOOLCHAIN", RUSTFMT_TOOLCHAIN) - .args(&["--config-path"]) - .arg(project_root().join("rustfmt.toml")) - .args(&["--config", "fn_single_line=true"]) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn()?; - write!(rustfmt.stdin.take().unwrap(), "{}", text)?; - let output = rustfmt.wait_with_output()?; - let stdout = String::from_utf8(output.stdout)?; + let stdout = run!( + "rustfmt --config-path {} --config fn_single_line=true", project_root().join("rustfmt.toml").display(); + Result<()> { - match Command::new("rustfmt") - .args(&["--version"]) - .env("RUSTUP_TOOLCHAIN", RUSTFMT_TOOLCHAIN) - .stdout(Stdio::piped()) - .stderr(Stdio::null()) - .spawn() - .and_then(|child| child.wait_with_output()) - { - Ok(output) - if output.status.success() - && std::str::from_utf8(&output.stdout)?.contains(RUSTFMT_TOOLCHAIN) => - { - Ok(()) - } - _ => { - bail!( - "Failed to run rustfmt from toolchain '{0}'. \ - Please run `rustup component add rustfmt --toolchain {0}` to install it.", - RUSTFMT_TOOLCHAIN, - ); - } + let out = run!("rustfmt --version")?; + if !out.contains("stable") { + bail!( + "Failed to run rustfmt from toolchain 'stable'. \ + Please run `rustup component add rustfmt --toolchain stable` to install it.", + ) } + Ok(()) } pub fn run_clippy() -> Result<()> { - match Command::new("cargo") - .args(&["clippy", "--version"]) - .stderr(Stdio::null()) - .stdout(Stdio::null()) - .status() - { - Ok(status) if status.success() => (), - _ => bail!( + if run!("cargo clippy --version").is_err() { + bail!( "Failed run cargo clippy. \ Please run `rustup component add clippy` to install it.", - ), - }; + ) + } let allowed_lints = [ "clippy::collapsible_if", @@ -138,33 +102,18 @@ pub fn run_clippy() -> Result<()> { pub fn run_fuzzer() -> Result<()> { let _d = pushd("./crates/ra_syntax"); + let _e = pushenv("RUSTUP_TOOLCHAIN", "nightly"); if run!("cargo fuzz --help").is_err() { run!("cargo install cargo-fuzz")?; }; // Expecting nightly rustc - match Command::new("rustc") - .args(&["--version"]) - .env("RUSTUP_TOOLCHAIN", "nightly") - .stdout(Stdio::piped()) - .stderr(Stdio::null()) - .spawn() - .and_then(|child| child.wait_with_output()) - { - Ok(output) - if output.status.success() - && std::str::from_utf8(&output.stdout)?.contains("nightly") => {} - _ => bail!("fuzz tests require nightly rustc"), + let out = run!("rustc --version")?; + if !out.contains("nightly") { + bail!("fuzz tests require nightly rustc") } - let status = Command::new("cargo") - .env("RUSTUP_TOOLCHAIN", "nightly") - .args(&["fuzz", "run", "parser"]) - .stderr(Stdio::inherit()) - .status()?; - if !status.success() { - bail!("{}", status); - } + run!("cargo fuzz run parser")?; Ok(()) } diff --git a/xtask/src/not_bash.rs b/xtask/src/not_bash.rs index ef16999349..a6431e5863 100644 --- a/xtask/src/not_bash.rs +++ b/xtask/src/not_bash.rs @@ -3,6 +3,8 @@ use std::{ cell::RefCell, env, + ffi::OsString, + io::Write, path::{Path, PathBuf}, process::{Command, Stdio}, }; @@ -57,7 +59,10 @@ macro_rules! _run { run!($($expr),*; echo = true) }; ($($expr:expr),* ; echo = $echo:expr) => { - $crate::not_bash::run_process(format!($($expr),*), $echo) + $crate::not_bash::run_process(format!($($expr),*), $echo, None) + }; + ($($expr:expr),* ; <$stdin:expr) => { + $crate::not_bash::run_process(format!($($expr),*), false, Some($stdin)) }; } pub(crate) use _run as run; @@ -77,6 +82,21 @@ impl Drop for Pushd { } } +pub struct Pushenv { + _p: (), +} + +pub fn pushenv(var: &str, value: &str) -> Pushenv { + Env::with(|env| env.pushenv(var.into(), value.into())); + Pushenv { _p: () } +} + +impl Drop for Pushenv { + fn drop(&mut self) { + Env::with(|env| env.popenv()) + } +} + pub fn rm_rf(path: impl AsRef) -> Result<()> { let path = path.as_ref(); if !path.exists() { @@ -90,15 +110,15 @@ pub fn rm_rf(path: impl AsRef) -> Result<()> { } #[doc(hidden)] -pub fn run_process(cmd: String, echo: bool) -> Result { - run_process_inner(&cmd, echo).with_context(|| format!("process `{}` failed", cmd)) +pub fn run_process(cmd: String, echo: bool, stdin: Option<&[u8]>) -> Result { + run_process_inner(&cmd, echo, stdin).with_context(|| format!("process `{}` failed", cmd)) } pub fn date_iso() -> Result { run!("date --iso --utc") } -fn run_process_inner(cmd: &str, echo: bool) -> Result { +fn run_process_inner(cmd: &str, echo: bool, stdin: Option<&[u8]>) -> Result { let mut args = shelx(cmd); let binary = args.remove(0); let current_dir = Env::with(|it| it.cwd().to_path_buf()); @@ -107,12 +127,17 @@ fn run_process_inner(cmd: &str, echo: bool) -> Result { println!("> {}", cmd) } - let output = Command::new(binary) - .args(args) - .current_dir(current_dir) - .stdin(Stdio::null()) - .stderr(Stdio::inherit()) - .output()?; + let mut command = Command::new(binary); + command.args(args).current_dir(current_dir).stderr(Stdio::inherit()); + let output = match stdin { + None => command.stdin(Stdio::null()).output(), + Some(stdin) => { + command.stdin(Stdio::piped()).stdout(Stdio::piped()); + let mut process = command.spawn()?; + process.stdin.take().unwrap().write_all(stdin)?; + process.wait_with_output() + } + }?; let stdout = String::from_utf8(output.stdout)?; if echo { @@ -133,13 +158,15 @@ fn shelx(cmd: &str) -> Vec { struct Env { pushd_stack: Vec, + pushenv_stack: Vec<(OsString, Option)>, } impl Env { fn with T, T>(f: F) -> T { thread_local! { static ENV: RefCell = RefCell::new(Env { - pushd_stack: vec![env::current_dir().unwrap()] + pushd_stack: vec![env::current_dir().unwrap()], + pushenv_stack: vec![], }); } ENV.with(|it| f(&mut *it.borrow_mut())) @@ -154,6 +181,17 @@ impl Env { self.pushd_stack.pop().unwrap(); env::set_current_dir(self.cwd()).unwrap(); } + fn pushenv(&mut self, var: OsString, value: OsString) { + self.pushenv_stack.push((var.clone(), env::var_os(&var))); + env::set_var(var, value) + } + fn popenv(&mut self) { + let (var, value) = self.pushenv_stack.pop().unwrap(); + match value { + None => env::remove_var(var), + Some(value) => env::set_var(var, value), + } + } fn cwd(&self) -> &Path { self.pushd_stack.last().unwrap() }