From 91f9bc2b866fc87dbaadb29a8c72eba10ae57c5c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 7 Jan 2020 14:42:56 +0100 Subject: Refactor xtasks --- xtask/src/help.rs | 33 ------ xtask/src/install.rs | 178 ++++++++++++++++++++++++++++++ xtask/src/lib.rs | 41 +------ xtask/src/main.rs | 283 ++++++++++++------------------------------------ xtask/src/pre_commit.rs | 36 ++++++ 5 files changed, 290 insertions(+), 281 deletions(-) delete mode 100644 xtask/src/help.rs create mode 100644 xtask/src/install.rs create mode 100644 xtask/src/pre_commit.rs (limited to 'xtask') diff --git a/xtask/src/help.rs b/xtask/src/help.rs deleted file mode 100644 index 9c78ba37f..000000000 --- a/xtask/src/help.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! FIXME: write short doc here - -pub const GLOBAL_HELP: &str = "tasks - -USAGE: - ra_tools - -FLAGS: - -h, --help Prints help information - -SUBCOMMANDS: - format - install-pre-commit-hook - fuzz-tests - codegen - install - lint"; - -pub const INSTALL_HELP: &str = "ra_tools-install - -USAGE: - ra_tools.exe install [FLAGS] - -FLAGS: - --client-code - -h, --help Prints help information - --jemalloc - --server"; - -pub const INSTALL_RA_CONFLICT: &str = - "error: The argument `--server` cannot be used with `--client-code` - -For more information try --help"; diff --git a/xtask/src/install.rs b/xtask/src/install.rs new file mode 100644 index 000000000..ab6ed92f7 --- /dev/null +++ b/xtask/src/install.rs @@ -0,0 +1,178 @@ +//! Installs rust-analyzer langauge server and/or editor plugin. + +use std::{env, path::PathBuf, str}; + +use anyhow::{Context, Result}; + +use crate::{run, run_with_output, Cmd}; + +// Latest stable, feel free to send a PR if this lags behind. +const REQUIRED_RUST_VERSION: u32 = 40; + +pub struct InstallCmd { + pub client: Option, + pub server: Option, +} + +pub enum ClientOpt { + VsCode, +} + +pub struct ServerOpt { + pub jemalloc: bool, +} + +impl InstallCmd { + pub fn run(self) -> Result<()> { + if cfg!(target_os = "macos") { + fix_path_for_mac().context("Fix path for mac")? + } + if let Some(server) = self.server { + install_server(server).context("install server")?; + } + if let Some(client) = self.client { + install_client(client).context("install client")?; + } + Ok(()) + } +} + +fn fix_path_for_mac() -> Result<()> { + let mut vscode_path: Vec = { + const COMMON_APP_PATH: &str = + r"/Applications/Visual Studio Code.app/Contents/Resources/app/bin"; + const ROOT_DIR: &str = ""; + let home_dir = match env::var("HOME") { + Ok(home) => home, + Err(e) => anyhow::bail!("Failed getting HOME from environment with error: {}.", e), + }; + + [ROOT_DIR, &home_dir] + .iter() + .map(|dir| String::from(*dir) + COMMON_APP_PATH) + .map(PathBuf::from) + .filter(|path| path.exists()) + .collect() + }; + + if !vscode_path.is_empty() { + let vars = match env::var_os("PATH") { + Some(path) => path, + None => anyhow::bail!("Could not get PATH variable from env."), + }; + + let mut paths = env::split_paths(&vars).collect::>(); + paths.append(&mut vscode_path); + let new_paths = env::join_paths(paths).context("build env PATH")?; + env::set_var("PATH", &new_paths); + } + + Ok(()) +} + +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() { + eprintln!("\nERROR: `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()?; + Cmd { + unix: r"npm run package --scripts-prepend-node-path", + windows: r"cmd.exe /c npm run package", + work_dir: "./editors/code", + } + .run()?; + + let code_binary = ["code", "code-insiders", "codium"].iter().find(|bin| { + Cmd { + unix: &format!("{} --version", bin), + windows: &format!("cmd.exe /c {}.cmd --version", bin), + work_dir: "./editors/code", + } + .run() + .is_ok() + }); + + let code_binary = match code_binary { + Some(it) => it, + None => anyhow::bail!("Can't execute `code --version`. Perhaps it is not in $PATH?"), + }; + + Cmd { + unix: &format!(r"{} --install-extension ./ra-lsp-0.0.1.vsix --force", code_binary), + windows: &format!( + r"cmd.exe /c {}.cmd --install-extension ./ra-lsp-0.0.1.vsix --force", + code_binary + ), + work_dir: "./editors/code", + } + .run()?; + + let output = 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 !str::from_utf8(&output.stdout)?.contains("ra-lsp") { + anyhow::bail!( + "Could not install the Visual Studio Code extension. \ + Please make sure you have at least NodeJS 10.x together with the latest version of VS Code installed and try again." + ); + } + + Ok(()) +} + +fn install_server(opts: ServerOpt) -> Result<()> { + let mut old_rust = false; + if let Ok(output) = run_with_output("cargo --version", ".") { + if let Ok(stdout) = String::from_utf8(output.stdout) { + println!("{}", stdout); + if !check_version(&stdout, REQUIRED_RUST_VERSION) { + old_rust = true; + } + } + } + + if old_rust { + eprintln!( + "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n", + REQUIRED_RUST_VERSION, + ) + } + + 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", ".") + }; + + if res.is_err() && old_rust { + eprintln!( + "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n", + REQUIRED_RUST_VERSION, + ) + } + + res +} + +fn check_version(version_output: &str, min_minor_version: u32) -> bool { + // Parse second the number out of + // cargo 1.39.0-beta (1c6ec66d5 2019-09-30) + let minor: Option = version_output.split('.').nth(1).and_then(|it| it.parse().ok()); + match minor { + None => true, + Some(minor) => minor >= min_minor_version, + } +} diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 51a868dee..fb853e71a 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -1,13 +1,14 @@ //! FIXME: write short doc here pub mod codegen; +pub mod install; +pub mod pre_commit; mod ast_src; use anyhow::Context; pub use anyhow::Result; use std::{ - env, fs, - io::{Error as IoError, ErrorKind}, + env, path::{Path, PathBuf}, process::{Command, Output, Stdio}, }; @@ -79,23 +80,11 @@ pub fn run_rustfmt(mode: Mode) -> Result<()> { Ok(()) } -pub fn install_rustfmt() -> Result<()> { +fn install_rustfmt() -> Result<()> { run(&format!("rustup toolchain install {}", TOOLCHAIN), ".")?; run(&format!("rustup component add rustfmt --toolchain {}", TOOLCHAIN), ".") } -pub fn install_pre_commit_hook() -> Result<()> { - let result_path = - PathBuf::from(format!("./.git/hooks/pre-commit{}", std::env::consts::EXE_SUFFIX)); - if !result_path.exists() { - let me = std::env::current_exe()?; - fs::copy(me, result_path)?; - } else { - Err(IoError::new(ErrorKind::AlreadyExists, "Git hook already created"))?; - } - Ok(()) -} - pub fn run_clippy() -> Result<()> { match Command::new("rustup") .args(&["run", TOOLCHAIN, "--", "cargo", "clippy", "--version"]) @@ -144,28 +133,6 @@ pub fn run_fuzzer() -> Result<()> { run("rustup run nightly -- cargo fuzz run parser", "./crates/ra_syntax") } -pub fn reformat_staged_files() -> Result<()> { - run_rustfmt(Mode::Overwrite)?; - let root = project_root(); - let output = Command::new("git") - .arg("diff") - .arg("--diff-filter=MAR") - .arg("--name-only") - .arg("--cached") - .current_dir(&root) - .output()?; - if !output.status.success() { - anyhow::bail!( - "`git diff --diff-filter=MAR --name-only --cached` exited with {}", - output.status - ); - } - for line in String::from_utf8(output.stdout)?.lines() { - run(&format!("git update-index --add {}", root.join(line).to_string_lossy()), ".")?; - } - Ok(()) -} - fn do_run(cmdline: &str, dir: &str, mut f: F) -> Result where F: FnMut(&mut Command), diff --git a/xtask/src/main.rs b/xtask/src/main.rs index b42946a4c..9309b2fbd 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -7,244 +7,105 @@ //! //! This binary is integrated into the `cargo` command line by using an alias in //! `.cargo/config`. -mod help; -use std::{env, fmt::Write, path::PathBuf, str}; +use std::env; -use anyhow::Context; use pico_args::Arguments; use xtask::{ codegen::{self, Mode}, - install_pre_commit_hook, reformat_staged_files, run, run_clippy, run_fuzzer, run_rustfmt, - run_with_output, Cmd, Result, + install::{ClientOpt, InstallCmd, ServerOpt}, + pre_commit, run_clippy, run_fuzzer, run_rustfmt, Result, }; -// Latest stable, feel free to send a PR if this lags behind. -const REQUIRED_RUST_VERSION: u32 = 40; - -struct InstallOpt { - client: Option, - server: Option, -} - -enum ClientOpt { - VsCode, -} - -struct ServerOpt { - jemalloc: bool, -} - fn main() -> Result<()> { if env::args().next().map(|it| it.contains("pre-commit")) == Some(true) { - return reformat_staged_files(); + return pre_commit::run_hook(); } - let subcommand = match std::env::args_os().nth(1) { - None => { - eprintln!("{}", help::GLOBAL_HELP); - return Ok(()); - } - Some(s) => s, - }; - let mut matches = Arguments::from_vec(std::env::args_os().skip(2).collect()); - let subcommand = &*subcommand.to_string_lossy(); - match subcommand { + let subcommand = std::env::args().nth(1).unwrap_or_default(); + let mut args = Arguments::from_vec(std::env::args_os().skip(2).collect()); + + match subcommand.as_str() { "install" => { - if matches.contains(["-h", "--help"]) { - eprintln!("{}", help::INSTALL_HELP); + if args.contains(["-h", "--help"]) { + eprintln!( + "\ +cargo xtask install +Install rust-analyzer server or editor plugin. + +USAGE: + cargo xtask install [FLAGS] + +FLAGS: + --client-code Install only VS Code plugin + --server Install only the language server + --jemalloc Use jemalloc for server + -h, --help Prints help information + " + ); return Ok(()); } - let server = matches.contains("--server"); - let client_code = matches.contains("--client-code"); + let server = args.contains("--server"); + let client_code = args.contains("--client-code"); if server && client_code { - eprintln!("{}", help::INSTALL_RA_CONFLICT); + eprintln!( + "error: The argument `--server` cannot be used with `--client-code`\n\n\ + For more information try --help" + ); return Ok(()); } - let jemalloc = matches.contains("--jemalloc"); - matches.finish().or_else(handle_extra_flags)?; - let opts = InstallOpt { + + let jemalloc = args.contains("--jemalloc"); + + args.finish()?; + + InstallCmd { client: if server { None } else { Some(ClientOpt::VsCode) }, server: if client_code { None } else { Some(ServerOpt { jemalloc }) }, - }; - install(opts)? + } + .run() } "codegen" => { + args.finish()?; codegen::generate_syntax(Mode::Overwrite)?; codegen::generate_parser_tests(Mode::Overwrite)?; codegen::generate_assists_docs(Mode::Overwrite)?; + Ok(()) } - "format" => run_rustfmt(Mode::Overwrite)?, - "install-pre-commit-hook" => install_pre_commit_hook()?, - "lint" => run_clippy()?, - "fuzz-tests" => run_fuzzer()?, - _ => eprintln!("{}", help::GLOBAL_HELP), - } - Ok(()) -} - -fn handle_extra_flags(e: pico_args::Error) -> Result<()> { - if let pico_args::Error::UnusedArgsLeft(flags) = e { - let mut invalid_flags = String::new(); - for flag in flags { - write!(&mut invalid_flags, "{}, ", flag)?; + "format" => { + args.finish()?; + run_rustfmt(Mode::Overwrite) } - let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2); - anyhow::bail!("Invalid flags: {}", invalid_flags) - } else { - anyhow::bail!(e.to_string()) - } -} - -fn install(opts: InstallOpt) -> Result<()> { - if cfg!(target_os = "macos") { - fix_path_for_mac().context("Fix path for mac")? - } - if let Some(server) = opts.server { - install_server(server).context("install server")?; - } - if let Some(client) = opts.client { - install_client(client).context("install client")?; - } - Ok(()) -} - -fn fix_path_for_mac() -> Result<()> { - let mut vscode_path: Vec = { - const COMMON_APP_PATH: &str = - r"/Applications/Visual Studio Code.app/Contents/Resources/app/bin"; - const ROOT_DIR: &str = ""; - let home_dir = match env::var("HOME") { - Ok(home) => home, - Err(e) => anyhow::bail!("Failed getting HOME from environment with error: {}.", e), - }; - - [ROOT_DIR, &home_dir] - .iter() - .map(|dir| String::from(*dir) + COMMON_APP_PATH) - .map(PathBuf::from) - .filter(|path| path.exists()) - .collect() - }; - - if !vscode_path.is_empty() { - let vars = match env::var_os("PATH") { - Some(path) => path, - None => anyhow::bail!("Could not get PATH variable from env."), - }; - - let mut paths = env::split_paths(&vars).collect::>(); - paths.append(&mut vscode_path); - let new_paths = env::join_paths(paths).context("build env PATH")?; - env::set_var("PATH", &new_paths); - } - - Ok(()) -} - -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() { - eprintln!("\nERROR: `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()?; - Cmd { - unix: r"npm run package --scripts-prepend-node-path", - windows: r"cmd.exe /c npm run package", - work_dir: "./editors/code", - } - .run()?; - - let code_binary = ["code", "code-insiders", "codium"].iter().find(|bin| { - Cmd { - unix: &format!("{} --version", bin), - windows: &format!("cmd.exe /c {}.cmd --version", bin), - work_dir: "./editors/code", + "install-pre-commit-hook" => { + args.finish()?; + pre_commit::install_hook() } - .run() - .is_ok() - }); - - let code_binary = match code_binary { - Some(it) => it, - None => anyhow::bail!("Can't execute `code --version`. Perhaps it is not in $PATH?"), - }; - - Cmd { - unix: &format!(r"{} --install-extension ./ra-lsp-0.0.1.vsix --force", code_binary), - windows: &format!( - r"cmd.exe /c {}.cmd --install-extension ./ra-lsp-0.0.1.vsix --force", - code_binary - ), - work_dir: "./editors/code", - } - .run()?; - - let output = 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 !str::from_utf8(&output.stdout)?.contains("ra-lsp") { - anyhow::bail!( - "Could not install the Visual Studio Code extension. \ - Please make sure you have at least NodeJS 10.x together with the latest version of VS Code installed and try again." - ); - } - - Ok(()) -} - -fn install_server(opts: ServerOpt) -> Result<()> { - let mut old_rust = false; - if let Ok(output) = run_with_output("cargo --version", ".") { - if let Ok(stdout) = String::from_utf8(output.stdout) { - println!("{}", stdout); - if !check_version(&stdout, REQUIRED_RUST_VERSION) { - old_rust = true; - } + "lint" => { + args.finish()?; + run_clippy() + } + "fuzz-tests" => { + args.finish()?; + run_fuzzer() + } + _ => { + eprintln!( + "\ +cargo xtask +Run custom build command. + +USAGE: + cargo xtask + +SUBCOMMANDS: + format + install-pre-commit-hook + fuzz-tests + codegen + install + lint" + ); + Ok(()) } - } - - if old_rust { - eprintln!( - "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n", - REQUIRED_RUST_VERSION, - ) - } - - 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", ".") - }; - - if res.is_err() && old_rust { - eprintln!( - "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n", - REQUIRED_RUST_VERSION, - ) - } - - res -} - -fn check_version(version_output: &str, min_minor_version: u32) -> bool { - // Parse second the number out of - // cargo 1.39.0-beta (1c6ec66d5 2019-09-30) - let minor: Option = version_output.split('.').nth(1).and_then(|it| it.parse().ok()); - match minor { - None => true, - Some(minor) => minor >= min_minor_version, } } diff --git a/xtask/src/pre_commit.rs b/xtask/src/pre_commit.rs new file mode 100644 index 000000000..7984ba963 --- /dev/null +++ b/xtask/src/pre_commit.rs @@ -0,0 +1,36 @@ +//! pre-commit hook for code formatting. + +use std::{fs, path::PathBuf}; + +use anyhow::{bail, Result}; + +use crate::{project_root, run, run_rustfmt, run_with_output, 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 root = project_root(); + for line in String::from_utf8(diff.stdout)?.lines() { + run(&format!("git update-index --add {}", root.join(line).to_string_lossy()), ".")?; + } + + Ok(()) +} + +pub fn install_hook() -> Result<()> { + let hook_path: PathBuf = + format!("./.git/hooks/pre-commit{}", std::env::consts::EXE_SUFFIX).into(); + + if hook_path.exists() { + bail!("Git hook already created"); + } + + let me = std::env::current_exe()?; + fs::copy(me, hook_path)?; + + Ok(()) +} -- cgit v1.2.3