aboutsummaryrefslogtreecommitdiff
path: root/xtask/src
diff options
context:
space:
mode:
Diffstat (limited to 'xtask/src')
-rw-r--r--xtask/src/cmd.rs56
-rw-r--r--xtask/src/install.rs109
-rw-r--r--xtask/src/lib.rs58
-rw-r--r--xtask/src/not_bash.rs96
-rw-r--r--xtask/src/pre_commit.rs6
5 files changed, 171 insertions, 154 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 9bddc8d7f..f89c939b5 100644
--- a/xtask/src/install.rs
+++ b/xtask/src/install.rs
@@ -5,7 +5,7 @@ use std::{env, fs, path::PathBuf, str};
5use anyhow::{bail, format_err, Context, Result}; 5use anyhow::{bail, format_err, Context, Result};
6use walkdir::WalkDir; 6use walkdir::WalkDir;
7 7
8use crate::cmd::{run, run_with_output, Cmd}; 8use crate::not_bash::{pushd, run};
9 9
10// Latest stable, feel free to send a PR if this lags behind. 10// Latest stable, feel free to send a PR if this lags behind.
11const REQUIRED_RUST_VERSION: u32 = 41; 11const REQUIRED_RUST_VERSION: u32 = 41;
@@ -83,21 +83,9 @@ fn fix_path_for_mac() -> Result<()> {
83} 83}
84 84
85fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { 85fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
86 let npm_version = Cmd { 86 let _dir = pushd("./editors/code");
87 unix: r"npm --version",
88 windows: r"cmd.exe /c npm --version",
89 work_dir: "./editors/code",
90 }
91 .run();
92
93 if npm_version.is_err() {
94 bail!("`npm --version` failed, `npm` is required to build the VS Code plugin")
95 }
96 87
97 Cmd { unix: r"npm install", windows: r"cmd.exe /c npm install", work_dir: "./editors/code" } 88 let list_vsixes = || {
98 .run()?;
99
100 let vsixes = || {
101 WalkDir::new("./editors/code") 89 WalkDir::new("./editors/code")
102 .max_depth(1) 90 .max_depth(1)
103 .into_iter() 91 .into_iter()
@@ -106,50 +94,45 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
106 .filter(|it| it.file_name().unwrap_or_default().to_string_lossy().ends_with(".vsix")) 94 .filter(|it| it.file_name().unwrap_or_default().to_string_lossy().ends_with(".vsix"))
107 }; 95 };
108 96
109 for path in vsixes() { 97 let find_code = |f: fn(&str) -> bool| -> Result<&'static str> {
110 fs::remove_file(path)? 98 ["code", "code-insiders", "codium", "code-oss"]
111 } 99 .iter()
100 .copied()
101 .find(|bin| f(bin))
102 .ok_or_else(|| {
103 format_err!("Can't execute `code --version`. Perhaps it is not in $PATH?")
104 })
105 };
112 106
113 Cmd { 107 let installed_extensions;
114 unix: r"npm run package --scripts-prepend-node-path", 108 if cfg!(unix) {
115 windows: r"cmd.exe /c npm run package", 109 run!("npm --version").context("`npm` is required to build the VS Code plugin")?;
116 work_dir: "./editors/code", 110 run!("npm install")?;
117 }
118 .run()?;
119
120 let extension = vsixes().next().unwrap().file_name().unwrap().to_string_lossy().to_string();
121
122 let code_binary = ["code", "code-insiders", "codium", "code-oss"]
123 .iter()
124 .find(|bin| {
125 Cmd {
126 unix: &format!("{} --version", bin),
127 windows: &format!("cmd.exe /c {}.cmd --version", bin),
128 work_dir: "./editors/code",
129 }
130 .run()
131 .is_ok()
132 })
133 .ok_or_else(|| {
134 format_err!("Can't execute `code --version`. Perhaps it is not in $PATH?")
135 })?;
136
137 Cmd {
138 unix: &format!(r"{} --install-extension ./{} --force", code_binary, extension),
139 windows: &format!(
140 r"cmd.exe /c {}.cmd --install-extension ./{} --force",
141 code_binary, extension
142 ),
143 work_dir: "./editors/code",
144 }
145 .run()?;
146 111
147 let installed_extensions = Cmd { 112 let vsix_pkg = {
148 unix: &format!(r"{} --list-extensions", code_binary), 113 list_vsixes().try_for_each(fs::remove_file)?;
149 windows: &format!(r"cmd.exe /c {}.cmd --list-extensions", code_binary), 114 run!("npm run package --scripts-prepend-node-path")?;
150 work_dir: ".", 115 list_vsixes().next().unwrap().file_name().unwrap().to_string_lossy().to_string()
116 };
117
118 let code = find_code(|bin| run!("{} --version", bin).is_ok())?;
119 run!("{} --install-extension ./{} --force", code, vsix_pkg)?;
120 installed_extensions = run!("{} --list-extensions", code; echo = false)?;
121 } else {
122 run!("cmd.exe /c npm --version")
123 .context("`npm` is required to build the VS Code plugin")?;
124 run!("cmd.exe /c npm install")?;
125
126 let vsix_pkg = {
127 list_vsixes().try_for_each(fs::remove_file)?;
128 run!("cmd.exe /c npm run package")?;
129 list_vsixes().next().unwrap().file_name().unwrap().to_string_lossy().to_string()
130 };
131
132 let code = find_code(|bin| run!("cmd.exe /c {}.cmd --version", bin).is_ok())?;
133 run!(r"cmd.exe /c {}.cmd --install-extension ./{} --force", code, vsix_pkg)?;
134 installed_extensions = run!("cmd.exe /c {}.cmd --list-extensions", code; echo = false)?;
151 } 135 }
152 .run_with_output()?;
153 136
154 if !installed_extensions.contains("rust-analyzer") { 137 if !installed_extensions.contains("rust-analyzer") {
155 bail!( 138 bail!(
@@ -163,8 +146,7 @@ fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
163 146
164fn install_server(opts: ServerOpt) -> Result<()> { 147fn install_server(opts: ServerOpt) -> Result<()> {
165 let mut old_rust = false; 148 let mut old_rust = false;
166 if let Ok(stdout) = run_with_output("cargo --version", ".") { 149 if let Ok(stdout) = run!("cargo --version") {
167 println!("{}", stdout);
168 if !check_version(&stdout, REQUIRED_RUST_VERSION) { 150 if !check_version(&stdout, REQUIRED_RUST_VERSION) {
169 old_rust = true; 151 old_rust = true;
170 } 152 }
@@ -177,20 +159,17 @@ fn install_server(opts: ServerOpt) -> Result<()> {
177 ) 159 )
178 } 160 }
179 161
180 let res = if opts.jemalloc { 162 let jemalloc = if opts.jemalloc { "--features jemalloc" } else { "" };
181 run("cargo install --path crates/ra_lsp_server --locked --force --features jemalloc", ".") 163 let res = run!("cargo install --path crates/ra_lsp_server --locked --force {}", jemalloc);
182 } else {
183 run("cargo install --path crates/ra_lsp_server --locked --force", ".")
184 };
185 164
186 if res.is_err() && old_rust { 165 if res.is_err() && old_rust {
187 eprintln!( 166 eprintln!(
188 "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n", 167 "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n",
189 REQUIRED_RUST_VERSION, 168 REQUIRED_RUST_VERSION,
190 ) 169 );
191 } 170 }
192 171
193 res 172 res.map(drop)
194} 173}
195 174
196fn check_version(version_output: &str, min_minor_version: u32) -> bool { 175fn 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..a2e47c7af
--- /dev/null
+++ b/xtask/src/not_bash.rs
@@ -0,0 +1,96 @@
1//! A bad shell -- small cross platform module for writing glue code
2use std::{
3 cell::RefCell,
4 env,
5 path::PathBuf,
6 process::{Command, Stdio},
7};
8
9use anyhow::{bail, Context, Result};
10
11macro_rules! _run {
12 ($($expr:expr),*) => {
13 run!($($expr),*; echo = true)
14 };
15 ($($expr:expr),* ; echo = $echo:expr) => {
16 $crate::not_bash::run_process(format!($($expr),*), $echo)
17 };
18}
19pub(crate) use _run as run;
20
21pub struct Pushd {
22 _p: (),
23}
24
25pub fn pushd(path: impl Into<PathBuf>) -> Pushd {
26 Env::with(|env| env.pushd(path.into()));
27 Pushd { _p: () }
28}
29
30impl Drop for Pushd {
31 fn drop(&mut self) {
32 Env::with(|env| env.popd())
33 }
34}
35
36#[doc(hidden)]
37pub fn run_process(cmd: String, echo: bool) -> Result<String> {
38 run_process_inner(&cmd, echo).with_context(|| format!("process `{}` failed", cmd))
39}
40
41fn run_process_inner(cmd: &str, echo: bool) -> Result<String> {
42 let cwd = Env::with(|env| env.cwd());
43 let mut args = shelx(cmd);
44 let binary = args.remove(0);
45
46 if echo {
47 println!("> {}", cmd)
48 }
49
50 let output = Command::new(binary)
51 .args(args)
52 .current_dir(cwd)
53 .stdin(Stdio::null())
54 .stderr(Stdio::inherit())
55 .output()?;
56 let stdout = String::from_utf8(output.stdout)?;
57
58 if echo {
59 print!("{}", stdout)
60 }
61
62 if !output.status.success() {
63 bail!("returned non-zero status: {}", output.status)
64 }
65
66 Ok(stdout)
67}
68
69// FIXME: some real shell lexing here
70fn shelx(cmd: &str) -> Vec<String> {
71 cmd.split_whitespace().map(|it| it.to_string()).collect()
72}
73
74#[derive(Default)]
75struct Env {
76 pushd_stack: Vec<PathBuf>,
77}
78
79impl Env {
80 fn with<F: FnOnce(&mut Env) -> T, T>(f: F) -> T {
81 thread_local! {
82 static ENV: RefCell<Env> = Default::default();
83 }
84 ENV.with(|it| f(&mut *it.borrow_mut()))
85 }
86
87 fn pushd(&mut self, dir: PathBuf) {
88 self.pushd_stack.push(dir)
89 }
90 fn popd(&mut self) {
91 self.pushd_stack.pop().unwrap();
92 }
93 fn cwd(&self) -> PathBuf {
94 self.pushd_stack.last().cloned().unwrap_or_else(|| env::current_dir().unwrap())
95 }
96}
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(())