Replace Cmd with not-bash

This commit is contained in:
Aleksey Kladov 2020-02-14 15:59:19 +01:00
parent bd3a41cc33
commit ce29497e43
5 changed files with 173 additions and 156 deletions

View file

@ -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<String> {
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<String> {
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<Output> {
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)
}

View file

@ -5,7 +5,7 @@ use std::{env, fs, path::PathBuf, str};
use anyhow::{bail, format_err, Context, Result}; use anyhow::{bail, format_err, Context, Result};
use walkdir::WalkDir; 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. // Latest stable, feel free to send a PR if this lags behind.
const REQUIRED_RUST_VERSION: u32 = 41; const REQUIRED_RUST_VERSION: u32 = 41;
@ -83,21 +83,9 @@ fn fix_path_for_mac() -> Result<()> {
} }
fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
let npm_version = Cmd { let _dir = pushd("./editors/code");
unix: r"npm --version",
windows: r"cmd.exe /c npm --version",
work_dir: "./editors/code",
}
.run();
if npm_version.is_err() { let list_vsixes = || {
bail!("`npm --version` failed, `npm` is required to build the VS Code plugin")
}
Cmd { unix: r"npm install", windows: r"cmd.exe /c npm install", work_dir: "./editors/code" }
.run()?;
let vsixes = || {
WalkDir::new("./editors/code") WalkDir::new("./editors/code")
.max_depth(1) .max_depth(1)
.into_iter() .into_iter()
@ -106,51 +94,46 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
.filter(|it| it.file_name().unwrap_or_default().to_string_lossy().ends_with(".vsix")) .filter(|it| it.file_name().unwrap_or_default().to_string_lossy().ends_with(".vsix"))
}; };
for path in vsixes() { let find_code = |f: fn(&str) -> bool| -> Result<&'static str> {
fs::remove_file(path)? ["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?")
})
};
let installed_extensions;
if cfg!(unix) {
run!("npm --version").context("`npm` is required to build the VS Code plugin")?;
run!("npm install")?;
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)?;
} }
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 = Cmd {
unix: &format!(r"{} --list-extensions", code_binary),
windows: &format!(r"cmd.exe /c {}.cmd --list-extensions", code_binary),
work_dir: ".",
}
.run_with_output()?;
if !installed_extensions.contains("rust-analyzer") { if !installed_extensions.contains("rust-analyzer") {
bail!( bail!(
"Could not install the Visual Studio Code extension. \ "Could not install the Visual Studio Code extension. \
@ -163,8 +146,7 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
fn install_server(opts: ServerOpt) -> Result<()> { fn install_server(opts: ServerOpt) -> Result<()> {
let mut old_rust = false; let mut old_rust = false;
if let Ok(stdout) = run_with_output("cargo --version", ".") { if let Ok(stdout) = run!("cargo --version") {
println!("{}", stdout);
if !check_version(&stdout, REQUIRED_RUST_VERSION) { if !check_version(&stdout, REQUIRED_RUST_VERSION) {
old_rust = true; old_rust = true;
} }
@ -177,20 +159,17 @@ fn install_server(opts: ServerOpt) -> Result<()> {
) )
} }
let res = if opts.jemalloc { let jemalloc = if opts.jemalloc { "--features jemalloc" } else { "" };
run("cargo install --path crates/ra_lsp_server --locked --force --features jemalloc", ".") let res = run!("cargo install --path crates/ra_lsp_server --locked --force {}", jemalloc);
} else {
run("cargo install --path crates/ra_lsp_server --locked --force", ".")
};
if res.is_err() && old_rust { if res.is_err() && old_rust {
eprintln!( eprintln!(
"\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n", "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n",
REQUIRED_RUST_VERSION, REQUIRED_RUST_VERSION,
) );
} }
res res.map(drop)
} }
fn check_version(version_output: &str, min_minor_version: u32) -> bool { fn check_version(version_output: &str, min_minor_version: u32) -> bool {

View file

@ -1,6 +1,6 @@
//! FIXME: write short doc here //! FIXME: write short doc here
mod cmd; pub mod not_bash;
pub mod install; pub mod install;
pub mod pre_commit; pub mod pre_commit;
@ -16,8 +16,8 @@ use std::{
}; };
use crate::{ use crate::{
cmd::{run, run_with_output},
codegen::Mode, codegen::Mode,
not_bash::{pushd, run},
}; };
pub use anyhow::Result; pub use anyhow::Result;
@ -38,9 +38,9 @@ pub fn run_rustfmt(mode: Mode) -> Result<()> {
ensure_rustfmt()?; ensure_rustfmt()?;
if mode == Mode::Verify { if mode == Mode::Verify {
run(&format!("rustup run {} -- cargo fmt -- --check", TOOLCHAIN), ".")?; run!("rustup run {} -- cargo fmt -- --check", TOOLCHAIN)?;
} else { } else {
run(&format!("rustup run {} -- cargo fmt", TOOLCHAIN), ".")?; run!("rustup run {} -- cargo fmt", TOOLCHAIN)?;
} }
Ok(()) Ok(())
} }
@ -70,8 +70,9 @@ fn ensure_rustfmt() -> Result<()> {
Ok(status) if status.success() => return Ok(()), Ok(status) if status.success() => return Ok(()),
_ => (), _ => (),
}; };
run(&format!("rustup toolchain install {}", TOOLCHAIN), ".")?; run!("rustup toolchain install {}", TOOLCHAIN)?;
run(&format!("rustup component add rustfmt --toolchain {}", TOOLCHAIN), ".") run!("rustup component add rustfmt --toolchain {}", TOOLCHAIN)?;
Ok(())
} }
pub fn run_clippy() -> Result<()> { pub fn run_clippy() -> Result<()> {
@ -92,34 +93,31 @@ pub fn run_clippy() -> Result<()> {
"clippy::nonminimal_bool", "clippy::nonminimal_bool",
"clippy::redundant_pattern_matching", "clippy::redundant_pattern_matching",
]; ];
run( run!(
&format!( "rustup run {} -- cargo clippy --all-features --all-targets -- -A {}",
"rustup run {} -- cargo clippy --all-features --all-targets -- -A {}", TOOLCHAIN,
TOOLCHAIN, allowed_lints.join(" -A ")
allowed_lints.join(" -A ")
),
".",
)?; )?;
Ok(()) Ok(())
} }
fn install_clippy() -> Result<()> { fn install_clippy() -> Result<()> {
run(&format!("rustup toolchain install {}", TOOLCHAIN), ".")?; run!("rustup toolchain install {}", TOOLCHAIN)?;
run(&format!("rustup component add clippy --toolchain {}", TOOLCHAIN), ".") run!("rustup component add clippy --toolchain {}", TOOLCHAIN)?;
Ok(())
} }
pub fn run_fuzzer() -> Result<()> { pub fn run_fuzzer() -> Result<()> {
match Command::new("cargo") let _d = pushd("./crates/ra_syntax");
.args(&["fuzz", "--help"]) match run!("cargo fuzz --help") {
.stderr(Stdio::null()) Ok(_) => (),
.stdout(Stdio::null()) _ => {
.status() run!("cargo install cargo-fuzz")?;
{ }
Ok(status) if status.success() => (),
_ => 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 /// 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<()> { pub fn run_release() -> Result<()> {
run("git switch release", ".")?; run!("git switch release")?;
run("git fetch upstream", ".")?; run!("git fetch upstream")?;
run("git reset --hard upstream/master", ".")?; run!("git reset --hard upstream/master")?;
run("git push", ".")?; run!("git push")?;
let changelog_dir = project_root().join("../rust-analyzer.github.io/thisweek/_posts"); let changelog_dir = project_root().join("../rust-analyzer.github.io/thisweek/_posts");
let today = run_with_output("date --iso", ".")?; let today = run!("date --iso")?;
let commit = run_with_output("git rev-parse HEAD", ".")?; let commit = run!("git rev-parse HEAD")?;
let changelog_n = fs::read_dir(changelog_dir.as_path())?.count(); let changelog_n = fs::read_dir(changelog_dir.as_path())?.count();
let contents = format!( let contents = format!(

96
xtask/src/not_bash.rs Normal file
View file

@ -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<PathBuf>) -> 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<String> {
run_process_inner(&cmd, echo).with_context(|| format!("process `{}` failed", cmd))
}
fn run_process_inner(cmd: &str, echo: bool) -> Result<String> {
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<String> {
cmd.split_whitespace().map(|it| it.to_string()).collect()
}
#[derive(Default)]
struct Env {
pushd_stack: Vec<PathBuf>,
}
impl Env {
fn with<F: FnOnce(&mut Env) -> T, T>(f: F) -> T {
thread_local! {
static ENV: RefCell<Env> = 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())
}
}

View file

@ -4,18 +4,18 @@ use std::{fs, path::PathBuf};
use anyhow::{bail, Result}; 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 // FIXME: if there are changed `.ts` files, also reformat TypeScript (by
// shelling out to `npm fmt`). // shelling out to `npm fmt`).
pub fn run_hook() -> Result<()> { pub fn run_hook() -> Result<()> {
run_rustfmt(Mode::Overwrite)?; 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(); let root = project_root();
for line in diff.lines() { 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(()) Ok(())