3136: Not bash r=matklad a=matklad

More declarative installation

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2020-02-14 16:14:25 +00:00 committed by GitHub
commit 94323c8996
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 203 additions and 163 deletions

View file

@ -1,6 +1,6 @@
{ {
"name": "rust-analyzer", "name": "rust-analyzer",
"version": "0.1.0", "version": "0.2.0-dev",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View file

@ -5,7 +5,8 @@
"preview": true, "preview": true,
"private": true, "private": true,
"icon": "icon.png", "icon": "icon.png",
"version": "0.1.0", "//": "The real version is in release.yaml, this one just needs to be bigger",
"version": "0.2.0-dev",
"publisher": "matklad", "publisher": "matklad",
"repository": { "repository": {
"url": "https://github.com/rust-analyzer/rust-analyzer.git", "url": "https://github.com/rust-analyzer/rust-analyzer.git",

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

@ -2,9 +2,9 @@
use std::{env, path::PathBuf, str}; use std::{env, path::PathBuf, str};
use anyhow::{Context, Result}; use anyhow::{bail, format_err, Context, Result};
use crate::cmd::{run, run_with_output, Cmd}; use crate::not_bash::{ls, pushd, rm, 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;
@ -55,7 +55,7 @@ fn fix_path_for_mac() -> Result<()> {
const ROOT_DIR: &str = ""; const ROOT_DIR: &str = "";
let home_dir = match env::var("HOME") { let home_dir = match env::var("HOME") {
Ok(home) => home, Ok(home) => home,
Err(e) => anyhow::bail!("Failed getting HOME from environment with error: {}.", e), Err(e) => bail!("Failed getting HOME from environment with error: {}.", e),
}; };
[ROOT_DIR, &home_dir] [ROOT_DIR, &home_dir]
@ -69,7 +69,7 @@ fn fix_path_for_mac() -> Result<()> {
if !vscode_path.is_empty() { if !vscode_path.is_empty() {
let vars = match env::var_os("PATH") { let vars = match env::var_os("PATH") {
Some(path) => path, Some(path) => path,
None => anyhow::bail!("Could not get PATH variable from env."), None => bail!("Could not get PATH variable from env."),
}; };
let mut paths = env::split_paths(&vars).collect::<Vec<_>>(); let mut paths = env::split_paths(&vars).collect::<Vec<_>>();
@ -82,84 +82,61 @@ 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 find_code = |f: fn(&str) -> bool| -> Result<&'static str> {
eprintln!("\nERROR: `npm --version` failed, `npm` is required to build the VS Code plugin") ["code", "code-insiders", "codium", "code-oss"]
} .iter()
.copied()
Cmd { unix: r"npm install", windows: r"cmd.exe /c npm install", work_dir: "./editors/code" } .find(|bin| f(bin))
.run()?; .ok_or_else(|| {
Cmd { format_err!("Can't execute `code --version`. Perhaps it is not in $PATH?")
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", "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()
});
let code_binary = match code_binary {
Some(it) => it,
None => anyhow::bail!("Can't execute `code --version`. Perhaps it is not in $PATH?"),
}; };
Cmd { let installed_extensions;
unix: &format!(r"{} --install-extension ./rust-analyzer-0.1.0.vsix --force", code_binary), if cfg!(unix) {
windows: &format!( run!("npm --version").context("`npm` is required to build the VS Code plugin")?;
r"cmd.exe /c {}.cmd --install-extension ./rust-analyzer-0.1.0.vsix --force", run!("npm install")?;
code_binary
),
work_dir: "./editors/code",
}
.run()?;
let installed_extensions = Cmd { let vsix_pkg = {
unix: &format!(r"{} --list-extensions", code_binary), rm("*.vsix")?;
windows: &format!(r"cmd.exe /c {}.cmd --list-extensions", code_binary), run!("npm run package --scripts-prepend-node-path")?;
work_dir: ".", ls("*.vsix")?.pop().unwrap()
};
let code = find_code(|bin| run!("{} --version", bin).is_ok())?;
run!("{} --install-extension {} --force", code, vsix_pkg.display())?;
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 = {
rm("*.vsix")?;
run!("cmd.exe /c npm run package")?;
ls("*.vsix")?.pop().unwrap()
};
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.display())?;
installed_extensions = run!("cmd.exe /c {}.cmd --list-extensions", code; echo = false)?;
} }
.run_with_output()?;
if !installed_extensions.contains("rust-analyzer") { if !installed_extensions.contains("rust-analyzer") {
anyhow::bail!( bail!(
"Could not install the Visual Studio Code extension. \ "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." Please make sure you have at least NodeJS 10.x together with the latest version of VS Code installed and try again."
); );
} }
if installed_extensions.contains("ra-lsp") {
Cmd {
unix: &format!(r"{} --uninstall-extension matklad.ra-lsp", code_binary),
windows: &format!(
r"cmd.exe /c {}.cmd --uninstall-extension matklad.ra-lsp",
code_binary
),
work_dir: "./editors/code",
}
.run()?;
}
Ok(()) Ok(())
} }
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;
} }
@ -172,20 +149,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!(

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

@ -0,0 +1,123 @@
//! A bad shell -- small cross platform module for writing glue code
use std::{
cell::RefCell,
env,
ffi::OsStr,
fs,
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())
}
}
pub fn rm(glob: &str) -> Result<()> {
let cwd = Env::with(|env| env.cwd());
ls(glob)?.into_iter().try_for_each(|it| fs::remove_file(cwd.join(it)))?;
Ok(())
}
pub fn ls(glob: &str) -> Result<Vec<PathBuf>> {
let cwd = Env::with(|env| env.cwd());
let mut res = Vec::new();
for entry in fs::read_dir(&cwd)? {
let entry = entry?;
if matches(&entry.file_name(), glob) {
let path = entry.path();
let path = path.strip_prefix(&cwd).unwrap();
res.push(path.to_path_buf())
}
}
return Ok(res);
fn matches(file_name: &OsStr, glob: &str) -> bool {
assert!(glob.starts_with('*'));
file_name.to_string_lossy().ends_with(&glob[1..])
}
}
#[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!("{}", 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(())