From ce29497e4324d3e2f2c7c696a212672dbdb46884 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 14 Feb 2020 15:59:19 +0100 Subject: Replace Cmd with not-bash --- xtask/src/cmd.rs | 56 ------------------------- xtask/src/install.rs | 109 +++++++++++++++++++----------------------------- xtask/src/lib.rs | 58 +++++++++++++------------- xtask/src/not_bash.rs | 96 ++++++++++++++++++++++++++++++++++++++++++ xtask/src/pre_commit.rs | 6 +-- 5 files changed, 171 insertions(+), 154 deletions(-) delete mode 100644 xtask/src/cmd.rs create mode 100644 xtask/src/not_bash.rs diff --git a/xtask/src/cmd.rs b/xtask/src/cmd.rs deleted file mode 100644 index 37497fb74..000000000 --- a/xtask/src/cmd.rs +++ /dev/null @@ -1,56 +0,0 @@ -use std::process::{Command, Output, Stdio}; - -use anyhow::{Context, Result}; - -use crate::project_root; - -pub struct Cmd<'a> { - pub unix: &'a str, - pub windows: &'a str, - pub work_dir: &'a str, -} - -impl Cmd<'_> { - pub fn run(self) -> Result<()> { - if cfg!(windows) { - run(self.windows, self.work_dir) - } else { - run(self.unix, self.work_dir) - } - } - pub fn run_with_output(self) -> Result { - if cfg!(windows) { - run_with_output(self.windows, self.work_dir) - } else { - run_with_output(self.unix, self.work_dir) - } - } -} - -pub fn run(cmdline: &str, dir: &str) -> Result<()> { - do_run(cmdline, dir, &mut |c| { - c.stdout(Stdio::inherit()); - }) - .map(|_| ()) -} - -pub fn run_with_output(cmdline: &str, dir: &str) -> Result { - let output = do_run(cmdline, dir, &mut |_| {})?; - let stdout = String::from_utf8(output.stdout)?; - let stdout = stdout.trim().to_string(); - Ok(stdout) -} - -fn do_run(cmdline: &str, dir: &str, f: &mut dyn FnMut(&mut Command)) -> Result { - eprintln!("\nwill run: {}", cmdline); - let proj_dir = project_root().join(dir); - let mut args = cmdline.split_whitespace(); - let exec = args.next().unwrap(); - let mut cmd = Command::new(exec); - f(cmd.args(args).current_dir(proj_dir).stderr(Stdio::inherit())); - let output = cmd.output().with_context(|| format!("running `{}`", cmdline))?; - if !output.status.success() { - anyhow::bail!("`{}` exited with {}", cmdline, output.status); - } - Ok(output) -} diff --git a/xtask/src/install.rs b/xtask/src/install.rs index 9bddc8d7f..f89c939b5 100644 --- a/xtask/src/install.rs +++ b/xtask/src/install.rs @@ -5,7 +5,7 @@ use std::{env, fs, path::PathBuf, str}; use anyhow::{bail, format_err, Context, Result}; use walkdir::WalkDir; -use crate::cmd::{run, run_with_output, Cmd}; +use crate::not_bash::{pushd, run}; // Latest stable, feel free to send a PR if this lags behind. const REQUIRED_RUST_VERSION: u32 = 41; @@ -83,21 +83,9 @@ fn fix_path_for_mac() -> Result<()> { } fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { - let npm_version = Cmd { - unix: r"npm --version", - windows: r"cmd.exe /c npm --version", - work_dir: "./editors/code", - } - .run(); - - if npm_version.is_err() { - bail!("`npm --version` failed, `npm` is required to build the VS Code plugin") - } + let _dir = pushd("./editors/code"); - Cmd { unix: r"npm install", windows: r"cmd.exe /c npm install", work_dir: "./editors/code" } - .run()?; - - let vsixes = || { + let list_vsixes = || { WalkDir::new("./editors/code") .max_depth(1) .into_iter() @@ -106,50 +94,45 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { .filter(|it| it.file_name().unwrap_or_default().to_string_lossy().ends_with(".vsix")) }; - for path in vsixes() { - fs::remove_file(path)? - } + let find_code = |f: fn(&str) -> bool| -> Result<&'static str> { + ["code", "code-insiders", "codium", "code-oss"] + .iter() + .copied() + .find(|bin| f(bin)) + .ok_or_else(|| { + format_err!("Can't execute `code --version`. Perhaps it is not in $PATH?") + }) + }; - Cmd { - unix: r"npm run package --scripts-prepend-node-path", - windows: r"cmd.exe /c npm run package", - work_dir: "./editors/code", - } - .run()?; - - let extension = vsixes().next().unwrap().file_name().unwrap().to_string_lossy().to_string(); - - let code_binary = ["code", "code-insiders", "codium", "code-oss"] - .iter() - .find(|bin| { - Cmd { - unix: &format!("{} --version", bin), - windows: &format!("cmd.exe /c {}.cmd --version", bin), - work_dir: "./editors/code", - } - .run() - .is_ok() - }) - .ok_or_else(|| { - format_err!("Can't execute `code --version`. Perhaps it is not in $PATH?") - })?; - - Cmd { - unix: &format!(r"{} --install-extension ./{} --force", code_binary, extension), - windows: &format!( - r"cmd.exe /c {}.cmd --install-extension ./{} --force", - code_binary, extension - ), - work_dir: "./editors/code", - } - .run()?; + let installed_extensions; + if cfg!(unix) { + run!("npm --version").context("`npm` is required to build the VS Code plugin")?; + run!("npm install")?; - let installed_extensions = Cmd { - unix: &format!(r"{} --list-extensions", code_binary), - windows: &format!(r"cmd.exe /c {}.cmd --list-extensions", code_binary), - work_dir: ".", + let vsix_pkg = { + list_vsixes().try_for_each(fs::remove_file)?; + run!("npm run package --scripts-prepend-node-path")?; + list_vsixes().next().unwrap().file_name().unwrap().to_string_lossy().to_string() + }; + + let code = find_code(|bin| run!("{} --version", bin).is_ok())?; + run!("{} --install-extension ./{} --force", code, vsix_pkg)?; + installed_extensions = run!("{} --list-extensions", code; echo = false)?; + } else { + run!("cmd.exe /c npm --version") + .context("`npm` is required to build the VS Code plugin")?; + run!("cmd.exe /c npm install")?; + + let vsix_pkg = { + list_vsixes().try_for_each(fs::remove_file)?; + run!("cmd.exe /c npm run package")?; + list_vsixes().next().unwrap().file_name().unwrap().to_string_lossy().to_string() + }; + + let code = find_code(|bin| run!("cmd.exe /c {}.cmd --version", bin).is_ok())?; + run!(r"cmd.exe /c {}.cmd --install-extension ./{} --force", code, vsix_pkg)?; + installed_extensions = run!("cmd.exe /c {}.cmd --list-extensions", code; echo = false)?; } - .run_with_output()?; if !installed_extensions.contains("rust-analyzer") { bail!( @@ -163,8 +146,7 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { fn install_server(opts: ServerOpt) -> Result<()> { let mut old_rust = false; - if let Ok(stdout) = run_with_output("cargo --version", ".") { - println!("{}", stdout); + if let Ok(stdout) = run!("cargo --version") { if !check_version(&stdout, REQUIRED_RUST_VERSION) { old_rust = true; } @@ -177,20 +159,17 @@ fn install_server(opts: ServerOpt) -> Result<()> { ) } - let res = if opts.jemalloc { - run("cargo install --path crates/ra_lsp_server --locked --force --features jemalloc", ".") - } else { - run("cargo install --path crates/ra_lsp_server --locked --force", ".") - }; + let jemalloc = if opts.jemalloc { "--features jemalloc" } else { "" }; + let res = run!("cargo install --path crates/ra_lsp_server --locked --force {}", jemalloc); if res.is_err() && old_rust { eprintln!( "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n", REQUIRED_RUST_VERSION, - ) + ); } - res + res.map(drop) } fn check_version(version_output: &str, min_minor_version: u32) -> bool { diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 1bb1882b0..d2ef2e95b 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -1,6 +1,6 @@ //! FIXME: write short doc here -mod cmd; +pub mod not_bash; pub mod install; pub mod pre_commit; @@ -16,8 +16,8 @@ use std::{ }; use crate::{ - cmd::{run, run_with_output}, codegen::Mode, + not_bash::{pushd, run}, }; pub use anyhow::Result; @@ -38,9 +38,9 @@ pub fn run_rustfmt(mode: Mode) -> Result<()> { ensure_rustfmt()?; if mode == Mode::Verify { - run(&format!("rustup run {} -- cargo fmt -- --check", TOOLCHAIN), ".")?; + run!("rustup run {} -- cargo fmt -- --check", TOOLCHAIN)?; } else { - run(&format!("rustup run {} -- cargo fmt", TOOLCHAIN), ".")?; + run!("rustup run {} -- cargo fmt", TOOLCHAIN)?; } Ok(()) } @@ -70,8 +70,9 @@ fn ensure_rustfmt() -> Result<()> { Ok(status) if status.success() => return Ok(()), _ => (), }; - run(&format!("rustup toolchain install {}", TOOLCHAIN), ".")?; - run(&format!("rustup component add rustfmt --toolchain {}", TOOLCHAIN), ".") + run!("rustup toolchain install {}", TOOLCHAIN)?; + run!("rustup component add rustfmt --toolchain {}", TOOLCHAIN)?; + Ok(()) } pub fn run_clippy() -> Result<()> { @@ -92,34 +93,31 @@ pub fn run_clippy() -> Result<()> { "clippy::nonminimal_bool", "clippy::redundant_pattern_matching", ]; - run( - &format!( - "rustup run {} -- cargo clippy --all-features --all-targets -- -A {}", - TOOLCHAIN, - allowed_lints.join(" -A ") - ), - ".", + run!( + "rustup run {} -- cargo clippy --all-features --all-targets -- -A {}", + TOOLCHAIN, + allowed_lints.join(" -A ") )?; Ok(()) } fn install_clippy() -> Result<()> { - run(&format!("rustup toolchain install {}", TOOLCHAIN), ".")?; - run(&format!("rustup component add clippy --toolchain {}", TOOLCHAIN), ".") + run!("rustup toolchain install {}", TOOLCHAIN)?; + run!("rustup component add clippy --toolchain {}", TOOLCHAIN)?; + Ok(()) } pub fn run_fuzzer() -> Result<()> { - match Command::new("cargo") - .args(&["fuzz", "--help"]) - .stderr(Stdio::null()) - .stdout(Stdio::null()) - .status() - { - Ok(status) if status.success() => (), - _ => run("cargo install cargo-fuzz", ".")?, + let _d = pushd("./crates/ra_syntax"); + match run!("cargo fuzz --help") { + Ok(_) => (), + _ => { + run!("cargo install cargo-fuzz")?; + } }; - run("rustup run nightly -- cargo fuzz run parser", "./crates/ra_syntax") + run!("rustup run nightly -- cargo fuzz run parser")?; + Ok(()) } /// Cleans the `./target` dir after the build such that only @@ -161,15 +159,15 @@ fn rm_rf(path: &Path) -> Result<()> { } pub fn run_release() -> Result<()> { - run("git switch release", ".")?; - run("git fetch upstream", ".")?; - run("git reset --hard upstream/master", ".")?; - run("git push", ".")?; + run!("git switch release")?; + run!("git fetch upstream")?; + run!("git reset --hard upstream/master")?; + run!("git push")?; let changelog_dir = project_root().join("../rust-analyzer.github.io/thisweek/_posts"); - let today = run_with_output("date --iso", ".")?; - let commit = run_with_output("git rev-parse HEAD", ".")?; + let today = run!("date --iso")?; + let commit = run!("git rev-parse HEAD")?; let changelog_n = fs::read_dir(changelog_dir.as_path())?.count(); let contents = format!( diff --git a/xtask/src/not_bash.rs b/xtask/src/not_bash.rs new file mode 100644 index 000000000..a2e47c7af --- /dev/null +++ b/xtask/src/not_bash.rs @@ -0,0 +1,96 @@ +//! A bad shell -- small cross platform module for writing glue code +use std::{ + cell::RefCell, + env, + path::PathBuf, + process::{Command, Stdio}, +}; + +use anyhow::{bail, Context, Result}; + +macro_rules! _run { + ($($expr:expr),*) => { + run!($($expr),*; echo = true) + }; + ($($expr:expr),* ; echo = $echo:expr) => { + $crate::not_bash::run_process(format!($($expr),*), $echo) + }; +} +pub(crate) use _run as run; + +pub struct Pushd { + _p: (), +} + +pub fn pushd(path: impl Into) -> Pushd { + Env::with(|env| env.pushd(path.into())); + Pushd { _p: () } +} + +impl Drop for Pushd { + fn drop(&mut self) { + Env::with(|env| env.popd()) + } +} + +#[doc(hidden)] +pub fn run_process(cmd: String, echo: bool) -> Result { + run_process_inner(&cmd, echo).with_context(|| format!("process `{}` failed", cmd)) +} + +fn run_process_inner(cmd: &str, echo: bool) -> Result { + let cwd = Env::with(|env| env.cwd()); + let mut args = shelx(cmd); + let binary = args.remove(0); + + if echo { + println!("> {}", cmd) + } + + let output = Command::new(binary) + .args(args) + .current_dir(cwd) + .stdin(Stdio::null()) + .stderr(Stdio::inherit()) + .output()?; + let stdout = String::from_utf8(output.stdout)?; + + if echo { + print!("{}", stdout) + } + + if !output.status.success() { + bail!("returned non-zero status: {}", output.status) + } + + Ok(stdout) +} + +// FIXME: some real shell lexing here +fn shelx(cmd: &str) -> Vec { + cmd.split_whitespace().map(|it| it.to_string()).collect() +} + +#[derive(Default)] +struct Env { + pushd_stack: Vec, +} + +impl Env { + fn with T, T>(f: F) -> T { + thread_local! { + static ENV: RefCell = Default::default(); + } + ENV.with(|it| f(&mut *it.borrow_mut())) + } + + fn pushd(&mut self, dir: PathBuf) { + self.pushd_stack.push(dir) + } + fn popd(&mut self) { + self.pushd_stack.pop().unwrap(); + } + fn cwd(&self) -> PathBuf { + self.pushd_stack.last().cloned().unwrap_or_else(|| env::current_dir().unwrap()) + } +} diff --git a/xtask/src/pre_commit.rs b/xtask/src/pre_commit.rs index 1533f64dc..056f34acf 100644 --- a/xtask/src/pre_commit.rs +++ b/xtask/src/pre_commit.rs @@ -4,18 +4,18 @@ use std::{fs, path::PathBuf}; use anyhow::{bail, Result}; -use crate::{cmd::run_with_output, project_root, run, run_rustfmt, Mode}; +use crate::{not_bash::run, project_root, run_rustfmt, Mode}; // FIXME: if there are changed `.ts` files, also reformat TypeScript (by // shelling out to `npm fmt`). pub fn run_hook() -> Result<()> { run_rustfmt(Mode::Overwrite)?; - let diff = run_with_output("git diff --diff-filter=MAR --name-only --cached", ".")?; + let diff = run!("git diff --diff-filter=MAR --name-only --cached")?; let root = project_root(); for line in diff.lines() { - run(&format!("git update-index --add {}", root.join(line).to_string_lossy()), ".")?; + run!("git update-index --add {}", root.join(line).display())?; } Ok(()) -- cgit v1.2.3