aboutsummaryrefslogtreecommitdiff
path: root/xtask
diff options
context:
space:
mode:
Diffstat (limited to 'xtask')
-rw-r--r--xtask/src/cmd.rs56
-rw-r--r--xtask/src/install.rs116
-rw-r--r--xtask/src/lib.rs58
-rw-r--r--xtask/src/not_bash.rs123
-rw-r--r--xtask/src/pre_commit.rs6
5 files changed, 199 insertions, 160 deletions
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 @@
1use std::process::{Command, Output, Stdio};
2
3use anyhow::{Context, Result};
4
5use crate::project_root;
6
7pub struct Cmd<'a> {
8 pub unix: &'a str,
9 pub windows: &'a str,
10 pub work_dir: &'a str,
11}
12
13impl Cmd<'_> {
14 pub fn run(self) -> Result<()> {
15 if cfg!(windows) {
16 run(self.windows, self.work_dir)
17 } else {
18 run(self.unix, self.work_dir)
19 }
20 }
21 pub fn run_with_output(self) -> Result<String> {
22 if cfg!(windows) {
23 run_with_output(self.windows, self.work_dir)
24 } else {
25 run_with_output(self.unix, self.work_dir)
26 }
27 }
28}
29
30pub fn run(cmdline: &str, dir: &str) -> Result<()> {
31 do_run(cmdline, dir, &mut |c| {
32 c.stdout(Stdio::inherit());
33 })
34 .map(|_| ())
35}
36
37pub fn run_with_output(cmdline: &str, dir: &str) -> Result<String> {
38 let output = do_run(cmdline, dir, &mut |_| {})?;
39 let stdout = String::from_utf8(output.stdout)?;
40 let stdout = stdout.trim().to_string();
41 Ok(stdout)
42}
43
44fn do_run(cmdline: &str, dir: &str, f: &mut dyn FnMut(&mut Command)) -> Result<Output> {
45 eprintln!("\nwill run: {}", cmdline);
46 let proj_dir = project_root().join(dir);
47 let mut args = cmdline.split_whitespace();
48 let exec = args.next().unwrap();
49 let mut cmd = Command::new(exec);
50 f(cmd.args(args).current_dir(proj_dir).stderr(Stdio::inherit()));
51 let output = cmd.output().with_context(|| format!("running `{}`", cmdline))?;
52 if !output.status.success() {
53 anyhow::bail!("`{}` exited with {}", cmdline, output.status);
54 }
55 Ok(output)
56}
diff --git a/xtask/src/install.rs b/xtask/src/install.rs
index 99e1eddb1..540a66130 100644
--- a/xtask/src/install.rs
+++ b/xtask/src/install.rs
@@ -2,9 +2,9 @@
2 2
3use std::{env, path::PathBuf, str}; 3use std::{env, path::PathBuf, str};
4 4
5use anyhow::{Context, Result}; 5use anyhow::{bail, format_err, Context, Result};
6 6
7use crate::cmd::{run, run_with_output, Cmd}; 7use crate::not_bash::{ls, pushd, rm, run};
8 8
9// Latest stable, feel free to send a PR if this lags behind. 9// Latest stable, feel free to send a PR if this lags behind.
10const REQUIRED_RUST_VERSION: u32 = 41; 10const REQUIRED_RUST_VERSION: u32 = 41;
@@ -55,7 +55,7 @@ fn fix_path_for_mac() -> Result<()> {
55 const ROOT_DIR: &str = ""; 55 const ROOT_DIR: &str = "";
56 let home_dir = match env::var("HOME") { 56 let home_dir = match env::var("HOME") {
57 Ok(home) => home, 57 Ok(home) => home,
58 Err(e) => anyhow::bail!("Failed getting HOME from environment with error: {}.", e), 58 Err(e) => bail!("Failed getting HOME from environment with error: {}.", e),
59 }; 59 };
60 60
61 [ROOT_DIR, &home_dir] 61 [ROOT_DIR, &home_dir]
@@ -69,7 +69,7 @@ fn fix_path_for_mac() -> Result<()> {
69 if !vscode_path.is_empty() { 69 if !vscode_path.is_empty() {
70 let vars = match env::var_os("PATH") { 70 let vars = match env::var_os("PATH") {
71 Some(path) => path, 71 Some(path) => path,
72 None => anyhow::bail!("Could not get PATH variable from env."), 72 None => bail!("Could not get PATH variable from env."),
73 }; 73 };
74 74
75 let mut paths = env::split_paths(&vars).collect::<Vec<_>>(); 75 let mut paths = env::split_paths(&vars).collect::<Vec<_>>();
@@ -82,84 +82,61 @@ fn fix_path_for_mac() -> Result<()> {
82} 82}
83 83
84fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { 84fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
85 let npm_version = Cmd { 85 let _dir = pushd("./editors/code");
86 unix: r"npm --version",
87 windows: r"cmd.exe /c npm --version",
88 work_dir: "./editors/code",
89 }
90 .run();
91
92 if npm_version.is_err() {
93 eprintln!("\nERROR: `npm --version` failed, `npm` is required to build the VS Code plugin")
94 }
95 86
96 Cmd { unix: r"npm install", windows: r"cmd.exe /c npm install", work_dir: "./editors/code" } 87 let find_code = |f: fn(&str) -> bool| -> Result<&'static str> {
97 .run()?; 88 ["code", "code-insiders", "codium", "code-oss"]
98 Cmd { 89 .iter()
99 unix: r"npm run package --scripts-prepend-node-path", 90 .copied()
100 windows: r"cmd.exe /c npm run package", 91 .find(|bin| f(bin))
101 work_dir: "./editors/code", 92 .ok_or_else(|| {
102 } 93 format_err!("Can't execute `code --version`. Perhaps it is not in $PATH?")
103 .run()?; 94 })
95 };
104 96
105 let code_binary = ["code", "code-insiders", "codium", "code-oss"].iter().find(|bin| { 97 let installed_extensions;
106 Cmd { 98 if cfg!(unix) {
107 unix: &format!("{} --version", bin), 99 run!("npm --version").context("`npm` is required to build the VS Code plugin")?;
108 windows: &format!("cmd.exe /c {}.cmd --version", bin), 100 run!("npm install")?;
109 work_dir: "./editors/code",
110 }
111 .run()
112 .is_ok()
113 });
114 101
115 let code_binary = match code_binary { 102 let vsix_pkg = {
116 Some(it) => it, 103 rm("*.vsix")?;
117 None => anyhow::bail!("Can't execute `code --version`. Perhaps it is not in $PATH?"), 104 run!("npm run package --scripts-prepend-node-path")?;
118 }; 105 ls("*.vsix")?.pop().unwrap()
106 };
119 107
120 Cmd { 108 let code = find_code(|bin| run!("{} --version", bin).is_ok())?;
121 unix: &format!(r"{} --install-extension ./rust-analyzer-0.1.0.vsix --force", code_binary), 109 run!("{} --install-extension {} --force", code, vsix_pkg.display())?;
122 windows: &format!( 110 installed_extensions = run!("{} --list-extensions", code; echo = false)?;
123 r"cmd.exe /c {}.cmd --install-extension ./rust-analyzer-0.1.0.vsix --force", 111 } else {
124 code_binary 112 run!("cmd.exe /c npm --version")
125 ), 113 .context("`npm` is required to build the VS Code plugin")?;
126 work_dir: "./editors/code", 114 run!("cmd.exe /c npm install")?;
127 } 115
128 .run()?; 116 let vsix_pkg = {
117 rm("*.vsix")?;
118 run!("cmd.exe /c npm run package")?;
119 ls("*.vsix")?.pop().unwrap()
120 };
129 121
130 let installed_extensions = Cmd { 122 let code = find_code(|bin| run!("cmd.exe /c {}.cmd --version", bin).is_ok())?;
131 unix: &format!(r"{} --list-extensions", code_binary), 123 run!(r"cmd.exe /c {}.cmd --install-extension {} --force", code, vsix_pkg.display())?;
132 windows: &format!(r"cmd.exe /c {}.cmd --list-extensions", code_binary), 124 installed_extensions = run!("cmd.exe /c {}.cmd --list-extensions", code; echo = false)?;
133 work_dir: ".",
134 } 125 }
135 .run_with_output()?;
136 126
137 if !installed_extensions.contains("rust-analyzer") { 127 if !installed_extensions.contains("rust-analyzer") {
138 anyhow::bail!( 128 bail!(
139 "Could not install the Visual Studio Code extension. \ 129 "Could not install the Visual Studio Code extension. \
140 Please make sure you have at least NodeJS 10.x together with the latest version of VS Code installed and try again." 130 Please make sure you have at least NodeJS 10.x together with the latest version of VS Code installed and try again."
141 ); 131 );
142 } 132 }
143 133
144 if installed_extensions.contains("ra-lsp") {
145 Cmd {
146 unix: &format!(r"{} --uninstall-extension matklad.ra-lsp", code_binary),
147 windows: &format!(
148 r"cmd.exe /c {}.cmd --uninstall-extension matklad.ra-lsp",
149 code_binary
150 ),
151 work_dir: "./editors/code",
152 }
153 .run()?;
154 }
155
156 Ok(()) 134 Ok(())
157} 135}
158 136
159fn install_server(opts: ServerOpt) -> Result<()> { 137fn install_server(opts: ServerOpt) -> Result<()> {
160 let mut old_rust = false; 138 let mut old_rust = false;
161 if let Ok(stdout) = run_with_output("cargo --version", ".") { 139 if let Ok(stdout) = run!("cargo --version") {
162 println!("{}", stdout);
163 if !check_version(&stdout, REQUIRED_RUST_VERSION) { 140 if !check_version(&stdout, REQUIRED_RUST_VERSION) {
164 old_rust = true; 141 old_rust = true;
165 } 142 }
@@ -172,20 +149,17 @@ fn install_server(opts: ServerOpt) -> Result<()> {
172 ) 149 )
173 } 150 }
174 151
175 let res = if opts.jemalloc { 152 let jemalloc = if opts.jemalloc { "--features jemalloc" } else { "" };
176 run("cargo install --path crates/ra_lsp_server --locked --force --features jemalloc", ".") 153 let res = run!("cargo install --path crates/ra_lsp_server --locked --force {}", jemalloc);
177 } else {
178 run("cargo install --path crates/ra_lsp_server --locked --force", ".")
179 };
180 154
181 if res.is_err() && old_rust { 155 if res.is_err() && old_rust {
182 eprintln!( 156 eprintln!(
183 "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n", 157 "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n",
184 REQUIRED_RUST_VERSION, 158 REQUIRED_RUST_VERSION,
185 ) 159 );
186 } 160 }
187 161
188 res 162 res.map(drop)
189} 163}
190 164
191fn check_version(version_output: &str, min_minor_version: u32) -> bool { 165fn 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 @@
1//! FIXME: write short doc here 1//! FIXME: write short doc here
2 2
3mod cmd; 3pub mod not_bash;
4pub mod install; 4pub mod install;
5pub mod pre_commit; 5pub mod pre_commit;
6 6
@@ -16,8 +16,8 @@ use std::{
16}; 16};
17 17
18use crate::{ 18use crate::{
19 cmd::{run, run_with_output},
20 codegen::Mode, 19 codegen::Mode,
20 not_bash::{pushd, run},
21}; 21};
22 22
23pub use anyhow::Result; 23pub use anyhow::Result;
@@ -38,9 +38,9 @@ pub fn run_rustfmt(mode: Mode) -> Result<()> {
38 ensure_rustfmt()?; 38 ensure_rustfmt()?;
39 39
40 if mode == Mode::Verify { 40 if mode == Mode::Verify {
41 run(&format!("rustup run {} -- cargo fmt -- --check", TOOLCHAIN), ".")?; 41 run!("rustup run {} -- cargo fmt -- --check", TOOLCHAIN)?;
42 } else { 42 } else {
43 run(&format!("rustup run {} -- cargo fmt", TOOLCHAIN), ".")?; 43 run!("rustup run {} -- cargo fmt", TOOLCHAIN)?;
44 } 44 }
45 Ok(()) 45 Ok(())
46} 46}
@@ -70,8 +70,9 @@ fn ensure_rustfmt() -> Result<()> {
70 Ok(status) if status.success() => return Ok(()), 70 Ok(status) if status.success() => return Ok(()),
71 _ => (), 71 _ => (),
72 }; 72 };
73 run(&format!("rustup toolchain install {}", TOOLCHAIN), ".")?; 73 run!("rustup toolchain install {}", TOOLCHAIN)?;
74 run(&format!("rustup component add rustfmt --toolchain {}", TOOLCHAIN), ".") 74 run!("rustup component add rustfmt --toolchain {}", TOOLCHAIN)?;
75 Ok(())
75} 76}
76 77
77pub fn run_clippy() -> Result<()> { 78pub fn run_clippy() -> Result<()> {
@@ -92,34 +93,31 @@ pub fn run_clippy() -> Result<()> {
92 "clippy::nonminimal_bool", 93 "clippy::nonminimal_bool",
93 "clippy::redundant_pattern_matching", 94 "clippy::redundant_pattern_matching",
94 ]; 95 ];
95 run( 96 run!(
96 &format!( 97 "rustup run {} -- cargo clippy --all-features --all-targets -- -A {}",
97 "rustup run {} -- cargo clippy --all-features --all-targets -- -A {}", 98 TOOLCHAIN,
98 TOOLCHAIN, 99 allowed_lints.join(" -A ")
99 allowed_lints.join(" -A ")
100 ),
101 ".",
102 )?; 100 )?;
103 Ok(()) 101 Ok(())
104} 102}
105 103
106fn install_clippy() -> Result<()> { 104fn install_clippy() -> Result<()> {
107 run(&format!("rustup toolchain install {}", TOOLCHAIN), ".")?; 105 run!("rustup toolchain install {}", TOOLCHAIN)?;
108 run(&format!("rustup component add clippy --toolchain {}", TOOLCHAIN), ".") 106 run!("rustup component add clippy --toolchain {}", TOOLCHAIN)?;
107 Ok(())
109} 108}
110 109
111pub fn run_fuzzer() -> Result<()> { 110pub fn run_fuzzer() -> Result<()> {
112 match Command::new("cargo") 111 let _d = pushd("./crates/ra_syntax");
113 .args(&["fuzz", "--help"]) 112 match run!("cargo fuzz --help") {
114 .stderr(Stdio::null()) 113 Ok(_) => (),
115 .stdout(Stdio::null()) 114 _ => {
116 .status() 115 run!("cargo install cargo-fuzz")?;
117 { 116 }
118 Ok(status) if status.success() => (),
119 _ => run("cargo install cargo-fuzz", ".")?,
120 }; 117 };
121 118
122 run("rustup run nightly -- cargo fuzz run parser", "./crates/ra_syntax") 119 run!("rustup run nightly -- cargo fuzz run parser")?;
120 Ok(())
123} 121}
124 122
125/// Cleans the `./target` dir after the build such that only 123/// Cleans the `./target` dir after the build such that only
@@ -161,15 +159,15 @@ fn rm_rf(path: &Path) -> Result<()> {
161} 159}
162 160
163pub fn run_release() -> Result<()> { 161pub fn run_release() -> Result<()> {
164 run("git switch release", ".")?; 162 run!("git switch release")?;
165 run("git fetch upstream", ".")?; 163 run!("git fetch upstream")?;
166 run("git reset --hard upstream/master", ".")?; 164 run!("git reset --hard upstream/master")?;
167 run("git push", ".")?; 165 run!("git push")?;
168 166
169 let changelog_dir = project_root().join("../rust-analyzer.github.io/thisweek/_posts"); 167 let changelog_dir = project_root().join("../rust-analyzer.github.io/thisweek/_posts");
170 168
171 let today = run_with_output("date --iso", ".")?; 169 let today = run!("date --iso")?;
172 let commit = run_with_output("git rev-parse HEAD", ".")?; 170 let commit = run!("git rev-parse HEAD")?;
173 let changelog_n = fs::read_dir(changelog_dir.as_path())?.count(); 171 let changelog_n = fs::read_dir(changelog_dir.as_path())?.count();
174 172
175 let contents = format!( 173 let contents = format!(
diff --git a/xtask/src/not_bash.rs b/xtask/src/not_bash.rs
new file mode 100644
index 000000000..56d6c6c2d
--- /dev/null
+++ b/xtask/src/not_bash.rs
@@ -0,0 +1,123 @@
1//! A bad shell -- small cross platform module for writing glue code
2use std::{
3 cell::RefCell,
4 env,
5 ffi::OsStr,
6 fs,
7 path::PathBuf,
8 process::{Command, Stdio},
9};
10
11use anyhow::{bail, Context, Result};
12
13macro_rules! _run {
14 ($($expr:expr),*) => {
15 run!($($expr),*; echo = true)
16 };
17 ($($expr:expr),* ; echo = $echo:expr) => {
18 $crate::not_bash::run_process(format!($($expr),*), $echo)
19 };
20}
21pub(crate) use _run as run;
22
23pub struct Pushd {
24 _p: (),
25}
26
27pub fn pushd(path: impl Into<PathBuf>) -> Pushd {
28 Env::with(|env| env.pushd(path.into()));
29 Pushd { _p: () }
30}
31
32impl Drop for Pushd {
33 fn drop(&mut self) {
34 Env::with(|env| env.popd())
35 }
36}
37
38pub fn rm(glob: &str) -> Result<()> {
39 let cwd = Env::with(|env| env.cwd());
40 ls(glob)?.into_iter().try_for_each(|it| fs::remove_file(cwd.join(it)))?;
41 Ok(())
42}
43
44pub fn ls(glob: &str) -> Result<Vec<PathBuf>> {
45 let cwd = Env::with(|env| env.cwd());
46 let mut res = Vec::new();
47 for entry in fs::read_dir(&cwd)? {
48 let entry = entry?;
49 if matches(&entry.file_name(), glob) {
50 let path = entry.path();
51 let path = path.strip_prefix(&cwd).unwrap();
52 res.push(path.to_path_buf())
53 }
54 }
55 return Ok(res);
56
57 fn matches(file_name: &OsStr, glob: &str) -> bool {
58 assert!(glob.starts_with('*'));
59 file_name.to_string_lossy().ends_with(&glob[1..])
60 }
61}
62
63#[doc(hidden)]
64pub fn run_process(cmd: String, echo: bool) -> Result<String> {
65 run_process_inner(&cmd, echo).with_context(|| format!("process `{}` failed", cmd))
66}
67
68fn run_process_inner(cmd: &str, echo: bool) -> Result<String> {
69 let cwd = Env::with(|env| env.cwd());
70 let mut args = shelx(cmd);
71 let binary = args.remove(0);
72
73 if echo {
74 println!("> {}", cmd)
75 }
76
77 let output = Command::new(binary)
78 .args(args)
79 .current_dir(cwd)
80 .stdin(Stdio::null())
81 .stderr(Stdio::inherit())
82 .output()?;
83 let stdout = String::from_utf8(output.stdout)?;
84
85 if echo {
86 print!("{}", stdout)
87 }
88
89 if !output.status.success() {
90 bail!("{}", output.status)
91 }
92
93 Ok(stdout)
94}
95
96// FIXME: some real shell lexing here
97fn shelx(cmd: &str) -> Vec<String> {
98 cmd.split_whitespace().map(|it| it.to_string()).collect()
99}
100
101#[derive(Default)]
102struct Env {
103 pushd_stack: Vec<PathBuf>,
104}
105
106impl Env {
107 fn with<F: FnOnce(&mut Env) -> T, T>(f: F) -> T {
108 thread_local! {
109 static ENV: RefCell<Env> = Default::default();
110 }
111 ENV.with(|it| f(&mut *it.borrow_mut()))
112 }
113
114 fn pushd(&mut self, dir: PathBuf) {
115 self.pushd_stack.push(dir)
116 }
117 fn popd(&mut self) {
118 self.pushd_stack.pop().unwrap();
119 }
120 fn cwd(&self) -> PathBuf {
121 self.pushd_stack.last().cloned().unwrap_or_else(|| env::current_dir().unwrap())
122 }
123}
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};
4 4
5use anyhow::{bail, Result}; 5use anyhow::{bail, Result};
6 6
7use crate::{cmd::run_with_output, project_root, run, run_rustfmt, Mode}; 7use crate::{not_bash::run, project_root, run_rustfmt, Mode};
8 8
9// FIXME: if there are changed `.ts` files, also reformat TypeScript (by 9// FIXME: if there are changed `.ts` files, also reformat TypeScript (by
10// shelling out to `npm fmt`). 10// shelling out to `npm fmt`).
11pub fn run_hook() -> Result<()> { 11pub fn run_hook() -> Result<()> {
12 run_rustfmt(Mode::Overwrite)?; 12 run_rustfmt(Mode::Overwrite)?;
13 13
14 let diff = run_with_output("git diff --diff-filter=MAR --name-only --cached", ".")?; 14 let diff = run!("git diff --diff-filter=MAR --name-only --cached")?;
15 15
16 let root = project_root(); 16 let root = project_root();
17 for line in diff.lines() { 17 for line in diff.lines() {
18 run(&format!("git update-index --add {}", root.join(line).to_string_lossy()), ".")?; 18 run!("git update-index --add {}", root.join(line).display())?;
19 } 19 }
20 20
21 Ok(()) 21 Ok(())