diff options
Diffstat (limited to 'xtask/src/install.rs')
-rw-r--r-- | xtask/src/install.rs | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/xtask/src/install.rs b/xtask/src/install.rs new file mode 100644 index 000000000..c5bb7351a --- /dev/null +++ b/xtask/src/install.rs | |||
@@ -0,0 +1,178 @@ | |||
1 | //! Installs rust-analyzer language server and/or editor plugin. | ||
2 | |||
3 | use std::{env, path::PathBuf, str}; | ||
4 | |||
5 | use anyhow::{Context, Result}; | ||
6 | |||
7 | use crate::cmd::{run, run_with_output, Cmd}; | ||
8 | |||
9 | // Latest stable, feel free to send a PR if this lags behind. | ||
10 | const REQUIRED_RUST_VERSION: u32 = 40; | ||
11 | |||
12 | pub struct InstallCmd { | ||
13 | pub client: Option<ClientOpt>, | ||
14 | pub server: Option<ServerOpt>, | ||
15 | } | ||
16 | |||
17 | pub enum ClientOpt { | ||
18 | VsCode, | ||
19 | } | ||
20 | |||
21 | pub struct ServerOpt { | ||
22 | pub jemalloc: bool, | ||
23 | } | ||
24 | |||
25 | impl InstallCmd { | ||
26 | pub fn run(self) -> Result<()> { | ||
27 | if cfg!(target_os = "macos") { | ||
28 | fix_path_for_mac().context("Fix path for mac")? | ||
29 | } | ||
30 | if let Some(server) = self.server { | ||
31 | install_server(server).context("install server")?; | ||
32 | } | ||
33 | if let Some(client) = self.client { | ||
34 | install_client(client).context("install client")?; | ||
35 | } | ||
36 | Ok(()) | ||
37 | } | ||
38 | } | ||
39 | |||
40 | fn fix_path_for_mac() -> Result<()> { | ||
41 | let mut vscode_path: Vec<PathBuf> = { | ||
42 | const COMMON_APP_PATH: &str = | ||
43 | r"/Applications/Visual Studio Code.app/Contents/Resources/app/bin"; | ||
44 | const ROOT_DIR: &str = ""; | ||
45 | let home_dir = match env::var("HOME") { | ||
46 | Ok(home) => home, | ||
47 | Err(e) => anyhow::bail!("Failed getting HOME from environment with error: {}.", e), | ||
48 | }; | ||
49 | |||
50 | [ROOT_DIR, &home_dir] | ||
51 | .iter() | ||
52 | .map(|dir| String::from(*dir) + COMMON_APP_PATH) | ||
53 | .map(PathBuf::from) | ||
54 | .filter(|path| path.exists()) | ||
55 | .collect() | ||
56 | }; | ||
57 | |||
58 | if !vscode_path.is_empty() { | ||
59 | let vars = match env::var_os("PATH") { | ||
60 | Some(path) => path, | ||
61 | None => anyhow::bail!("Could not get PATH variable from env."), | ||
62 | }; | ||
63 | |||
64 | let mut paths = env::split_paths(&vars).collect::<Vec<_>>(); | ||
65 | paths.append(&mut vscode_path); | ||
66 | let new_paths = env::join_paths(paths).context("build env PATH")?; | ||
67 | env::set_var("PATH", &new_paths); | ||
68 | } | ||
69 | |||
70 | Ok(()) | ||
71 | } | ||
72 | |||
73 | fn install_client(ClientOpt::VsCode: ClientOpt) -> Result<()> { | ||
74 | let npm_version = Cmd { | ||
75 | unix: r"npm --version", | ||
76 | windows: r"cmd.exe /c npm --version", | ||
77 | work_dir: "./editors/code", | ||
78 | } | ||
79 | .run(); | ||
80 | |||
81 | if npm_version.is_err() { | ||
82 | eprintln!("\nERROR: `npm --version` failed, `npm` is required to build the VS Code plugin") | ||
83 | } | ||
84 | |||
85 | Cmd { unix: r"npm install", windows: r"cmd.exe /c npm install", work_dir: "./editors/code" } | ||
86 | .run()?; | ||
87 | Cmd { | ||
88 | unix: r"npm run package --scripts-prepend-node-path", | ||
89 | windows: r"cmd.exe /c npm run package", | ||
90 | work_dir: "./editors/code", | ||
91 | } | ||
92 | .run()?; | ||
93 | |||
94 | let code_binary = ["code", "code-insiders", "codium"].iter().find(|bin| { | ||
95 | Cmd { | ||
96 | unix: &format!("{} --version", bin), | ||
97 | windows: &format!("cmd.exe /c {}.cmd --version", bin), | ||
98 | work_dir: "./editors/code", | ||
99 | } | ||
100 | .run() | ||
101 | .is_ok() | ||
102 | }); | ||
103 | |||
104 | let code_binary = match code_binary { | ||
105 | Some(it) => it, | ||
106 | None => anyhow::bail!("Can't execute `code --version`. Perhaps it is not in $PATH?"), | ||
107 | }; | ||
108 | |||
109 | Cmd { | ||
110 | unix: &format!(r"{} --install-extension ./ra-lsp-0.0.1.vsix --force", code_binary), | ||
111 | windows: &format!( | ||
112 | r"cmd.exe /c {}.cmd --install-extension ./ra-lsp-0.0.1.vsix --force", | ||
113 | code_binary | ||
114 | ), | ||
115 | work_dir: "./editors/code", | ||
116 | } | ||
117 | .run()?; | ||
118 | |||
119 | let output = Cmd { | ||
120 | unix: &format!(r"{} --list-extensions", code_binary), | ||
121 | windows: &format!(r"cmd.exe /c {}.cmd --list-extensions", code_binary), | ||
122 | work_dir: ".", | ||
123 | } | ||
124 | .run_with_output()?; | ||
125 | |||
126 | if !str::from_utf8(&output.stdout)?.contains("ra-lsp") { | ||
127 | anyhow::bail!( | ||
128 | "Could not install the Visual Studio Code extension. \ | ||
129 | Please make sure you have at least NodeJS 10.x together with the latest version of VS Code installed and try again." | ||
130 | ); | ||
131 | } | ||
132 | |||
133 | Ok(()) | ||
134 | } | ||
135 | |||
136 | fn install_server(opts: ServerOpt) -> Result<()> { | ||
137 | let mut old_rust = false; | ||
138 | if let Ok(output) = run_with_output("cargo --version", ".") { | ||
139 | if let Ok(stdout) = String::from_utf8(output.stdout) { | ||
140 | println!("{}", stdout); | ||
141 | if !check_version(&stdout, REQUIRED_RUST_VERSION) { | ||
142 | old_rust = true; | ||
143 | } | ||
144 | } | ||
145 | } | ||
146 | |||
147 | if old_rust { | ||
148 | eprintln!( | ||
149 | "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n", | ||
150 | REQUIRED_RUST_VERSION, | ||
151 | ) | ||
152 | } | ||
153 | |||
154 | let res = if opts.jemalloc { | ||
155 | run("cargo install --path crates/ra_lsp_server --locked --force --features jemalloc", ".") | ||
156 | } else { | ||
157 | run("cargo install --path crates/ra_lsp_server --locked --force", ".") | ||
158 | }; | ||
159 | |||
160 | if res.is_err() && old_rust { | ||
161 | eprintln!( | ||
162 | "\nWARNING: at least rust 1.{}.0 is required to compile rust-analyzer\n", | ||
163 | REQUIRED_RUST_VERSION, | ||
164 | ) | ||
165 | } | ||
166 | |||
167 | res | ||
168 | } | ||
169 | |||
170 | fn check_version(version_output: &str, min_minor_version: u32) -> bool { | ||
171 | // Parse second the number out of | ||
172 | // cargo 1.39.0-beta (1c6ec66d5 2019-09-30) | ||
173 | let minor: Option<u32> = version_output.split('.').nth(1).and_then(|it| it.parse().ok()); | ||
174 | match minor { | ||
175 | None => true, | ||
176 | Some(minor) => minor >= min_minor_version, | ||
177 | } | ||
178 | } | ||