aboutsummaryrefslogtreecommitdiff
path: root/xtask/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'xtask/src/main.rs')
-rw-r--r--xtask/src/main.rs299
1 files changed, 66 insertions, 233 deletions
diff --git a/xtask/src/main.rs b/xtask/src/main.rs
index 9cefad925..9309b2fbd 100644
--- a/xtask/src/main.rs
+++ b/xtask/src/main.rs
@@ -7,272 +7,105 @@
7//! 7//!
8//! This binary is integrated into the `cargo` command line by using an alias in 8//! This binary is integrated into the `cargo` command line by using an alias in
9//! `.cargo/config`. 9//! `.cargo/config`.
10mod help;
11 10
12use std::{env, fmt::Write, path::PathBuf, str}; 11use std::env;
13 12
14use anyhow::Context;
15use pico_args::Arguments; 13use pico_args::Arguments;
16use xtask::{ 14use xtask::{
17 codegen::{self, Mode}, 15 codegen::{self, Mode},
18 install_pre_commit_hook, reformat_staged_files, run, run_clippy, run_fuzzer, run_rustfmt, 16 install::{ClientOpt, InstallCmd, ServerOpt},
19 run_with_output, Cmd, Result, 17 pre_commit, run_clippy, run_fuzzer, run_rustfmt, Result,
20}; 18};
21 19
22// Latest stable, feel free to send a PR if this lags behind.
23const REQUIRED_RUST_VERSION: u32 = 40;
24
25struct InstallOpt {
26 client: Option<ClientOpt>,
27 server: Option<ServerOpt>,
28}
29
30enum ClientOpt {
31 VsCode,
32}
33
34struct ServerOpt {
35 jemalloc: bool,
36}
37
38fn main() -> Result<()> { 20fn main() -> Result<()> {
39 if env::args().next().map(|it| it.contains("pre-commit")) == Some(true) { 21 if env::args().next().map(|it| it.contains("pre-commit")) == Some(true) {
40 return reformat_staged_files(); 22 return pre_commit::run_hook();
41 } 23 }
42 24
43 let subcommand = match std::env::args_os().nth(1) { 25 let subcommand = std::env::args().nth(1).unwrap_or_default();
44 None => { 26 let mut args = Arguments::from_vec(std::env::args_os().skip(2).collect());
45 eprintln!("{}", help::GLOBAL_HELP); 27
46 return Ok(()); 28 match subcommand.as_str() {
47 }
48 Some(s) => s,
49 };
50 let mut matches = Arguments::from_vec(std::env::args_os().skip(2).collect());
51 let subcommand = &*subcommand.to_string_lossy();
52 match subcommand {
53 "install" => { 29 "install" => {
54 if matches.contains(["-h", "--help"]) { 30 if args.contains(["-h", "--help"]) {
55 eprintln!("{}", help::INSTALL_HELP); 31 eprintln!(
32 "\
33cargo xtask install
34Install rust-analyzer server or editor plugin.
35
36USAGE:
37 cargo xtask install [FLAGS]
38
39FLAGS:
40 --client-code Install only VS Code plugin
41 --server Install only the language server
42 --jemalloc Use jemalloc for server
43 -h, --help Prints help information
44 "
45 );
56 return Ok(()); 46 return Ok(());
57 } 47 }
58 let server = matches.contains("--server"); 48 let server = args.contains("--server");
59 let client_code = matches.contains("--client-code"); 49 let client_code = args.contains("--client-code");
60 if server && client_code { 50 if server && client_code {
61 eprintln!("{}", help::INSTALL_RA_CONFLICT); 51 eprintln!(
52 "error: The argument `--server` cannot be used with `--client-code`\n\n\
53 For more information try --help"
54 );
62 return Ok(()); 55 return Ok(());
63 } 56 }
64 let jemalloc = matches.contains("--jemalloc"); 57
65 matches.finish().or_else(handle_extra_flags)?; 58 let jemalloc = args.contains("--jemalloc");
66 let opts = InstallOpt { 59
60 args.finish()?;
61
62 InstallCmd {
67 client: if server { None } else { Some(ClientOpt::VsCode) }, 63 client: if server { None } else { Some(ClientOpt::VsCode) },
68 server: if client_code { None } else { Some(ServerOpt { jemalloc }) }, 64 server: if client_code { None } else { Some(ServerOpt { jemalloc }) },
69 }; 65 }
70 install(opts)? 66 .run()
71 } 67 }
72 "codegen" => { 68 "codegen" => {
73 if matches.contains(["-h", "--help"]) { 69 args.finish()?;
74 help::print_no_param_subcommand_help(&subcommand);
75 return Ok(());
76 }
77 codegen::generate_syntax(Mode::Overwrite)?; 70 codegen::generate_syntax(Mode::Overwrite)?;
78 codegen::generate_parser_tests(Mode::Overwrite)?; 71 codegen::generate_parser_tests(Mode::Overwrite)?;
79 codegen::generate_assists_docs(Mode::Overwrite)?; 72 codegen::generate_assists_docs(Mode::Overwrite)?;
73 Ok(())
80 } 74 }
81 "format" => { 75 "format" => {
82 if matches.contains(["-h", "--help"]) { 76 args.finish()?;
83 help::print_no_param_subcommand_help(&subcommand); 77 run_rustfmt(Mode::Overwrite)
84 return Ok(());
85 }
86 run_rustfmt(Mode::Overwrite)?
87 } 78 }
88 "install-pre-commit-hook" => { 79 "install-pre-commit-hook" => {
89 if matches.contains(["-h", "--help"]) { 80 args.finish()?;
90 help::print_no_param_subcommand_help(&subcommand); 81 pre_commit::install_hook()
91 return Ok(());
92 }
93 install_pre_commit_hook()?
94 } 82 }
95 "lint" => { 83 "lint" => {
96 if matches.contains(["-h", "--help"]) { 84 args.finish()?;
97 help::print_no_param_subcommand_help(&subcommand); 85 run_clippy()
98 return Ok(());
99 }
100 run_clippy()?
101 } 86 }
102 "fuzz-tests" => { 87 "fuzz-tests" => {
103 if matches.contains(["-h", "--help"]) { 88 args.finish()?;
104 help::print_no_param_subcommand_help(&subcommand); 89 run_fuzzer()
105 return Ok(());
106 }
107 run_fuzzer()?
108 } 90 }
109 _ => eprintln!("{}", help::GLOBAL_HELP), 91 _ => {
110 } 92 eprintln!(
111 Ok(()) 93 "\
112} 94cargo xtask
113 95Run custom build command.
114fn handle_extra_flags(e: pico_args::Error) -> Result<()> { 96
115 if let pico_args::Error::UnusedArgsLeft(flags) = e { 97USAGE:
116 let mut invalid_flags = String::new(); 98 cargo xtask <SUBCOMMAND>
117 for flag in flags { 99
118 write!(&mut invalid_flags, "{}, ", flag)?; 100SUBCOMMANDS:
101 format
102 install-pre-commit-hook
103 fuzz-tests
104 codegen
105 install
106 lint"
107 );
108 Ok(())
119 } 109 }
120 let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2);
121 anyhow::bail!("Invalid flags: {}", invalid_flags)
122 } else {
123 anyhow::bail!(e.to_string())
124 }
125}
126
127fn install(opts: InstallOpt) -> Result<()> {
128 if cfg!(target_os = "macos") {
129 fix_path_for_mac().context("Fix path for mac")?
130 }
131 if let Some(server) = opts.server {
132 install_server(server).context("install server")?;
133 }
134 if let Some(client) = opts.client {
135 install_client(client).context("install client")?;
136 }
137 Ok(())
138}
139
140fn fix_path_for_mac() -> Result<()> {
141 let mut vscode_path: Vec<PathBuf> = {
142 const COMMON_APP_PATH: &str =
143 r"/Applications/Visual Studio Code.app/Contents/Resources/app/bin";
144 const ROOT_DIR: &str = "";
145 let home_dir = match env::var("HOME") {
146 Ok(home) => home,
147 Err(e) => anyhow::bail!("Failed getting HOME from environment with error: {}.", e),
148 };
149
150 [ROOT_DIR, &home_dir]
151 .iter()
152 .map(|dir| String::from(*dir) + COMMON_APP_PATH)
153 .map(PathBuf::from)
154 .filter(|path| path.exists())
155 .collect()
156 };
157
158 if !vscode_path.is_empty() {
159 let vars = match env::var_os("PATH") {
160 Some(path) => path,
161 None => anyhow::bail!("Could not get PATH variable from env."),
162 };
163
164 let mut paths = env::split_paths(&vars).collect::<Vec<_>>();
165 paths.append(&mut vscode_path);
166 let new_paths = env::join_paths(paths).context("build env PATH")?;
167 env::set_var("PATH", &new_paths);
168 }
169
170 Ok(())
171}
172
173fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> {
174 let npm_version = Cmd {
175 unix: r"npm --version",
176 windows: r"cmd.exe /c npm --version",
177 work_dir: "./editors/code",
178 }
179 .run();
180
181 if npm_version.is_err() {
182 eprintln!("\nERROR: `npm --version` failed, `npm` is required to build the VS Code plugin")
183 }
184
185 Cmd { unix: r"npm install", windows: r"cmd.exe /c npm install", work_dir: "./editors/code" }
186 .run()?;
187 Cmd {
188 unix: r"npm run package --scripts-prepend-node-path",
189 windows: r"cmd.exe /c npm run package",
190 work_dir: "./editors/code",
191 }
192 .run()?;
193
194 let code_binary = ["code", "code-insiders", "codium"].iter().find(|bin| {
195 Cmd {
196 unix: &format!("{} --version", bin),
197 windows: &format!("cmd.exe /c {}.cmd --version", bin),
198 work_dir: "./editors/code",
199 }
200 .run()
201 .is_ok()
202 });
203
204 let code_binary = match code_binary {
205 Some(it) => it,
206 None => anyhow::bail!("Can't execute `code --version`. Perhaps it is not in $PATH?"),
207 };
208
209 Cmd {
210 unix: &format!(r"{} --install-extension ./ra-lsp-0.0.1.vsix --force", code_binary),
211 windows: &format!(
212 r"cmd.exe /c {}.cmd --install-extension ./ra-lsp-0.0.1.vsix --force",
213 code_binary
214 ),
215 work_dir: "./editors/code",
216 }
217 .run()?;
218
219 let output = Cmd {
220 unix: &format!(r"{} --list-extensions", code_binary),
221 windows: &format!(r"cmd.exe /c {}.cmd --list-extensions", code_binary),
222 work_dir: ".",
223 }
224 .run_with_output()?;
225
226 if !str::from_utf8(&output.stdout)?.contains("ra-lsp") {
227 anyhow::bail!(
228 "Could not install the Visual Studio Code extension. \
229 Please make sure you have at least NodeJS 10.x together with the latest version of VS Code installed and try again."
230 );
231 }
232
233 Ok(())
234}
235
236fn install_server(opts: ServerOpt) -> Result<()> {
237 let mut old_rust = false;
238 if let Ok(output) = run_with_output("cargo --version", ".") {
239 if let Ok(stdout) = String::from_utf8(output.stdout) {
240 println!("{}", stdout);
241 if !check_version(&stdout, REQUIRED_RUST_VERSION) {
242 old_rust = true;
243 }
244 }
245 }
246
247 if old_rust {
248 eprintln!(
249 "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n",
250 REQUIRED_RUST_VERSION,
251 )
252 }
253
254 let res = if opts.jemalloc {
255 run("cargo install --path crates/ra_lsp_server --locked --force --features jemalloc", ".")
256 } else {
257 run("cargo install --path crates/ra_lsp_server --locked --force", ".")
258 };
259
260 if res.is_err() && old_rust {
261 eprintln!(
262 "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n",
263 REQUIRED_RUST_VERSION,
264 )
265 }
266
267 res
268}
269
270fn check_version(version_output: &str, min_minor_version: u32) -> bool {
271 // Parse second the number out of
272 // cargo 1.39.0-beta (1c6ec66d5 2019-09-30)
273 let minor: Option<u32> = version_output.split('.').nth(1).and_then(|it| it.parse().ok());
274 match minor {
275 None => true,
276 Some(minor) => minor >= min_minor_version,
277 } 110 }
278} 111}