aboutsummaryrefslogtreecommitdiff
path: root/xtask/src/install.rs
diff options
context:
space:
mode:
Diffstat (limited to 'xtask/src/install.rs')
-rw-r--r--xtask/src/install.rs178
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
3use std::{env, path::PathBuf, str};
4
5use anyhow::{Context, Result};
6
7use crate::cmd::{run, run_with_output, Cmd};
8
9// Latest stable, feel free to send a PR if this lags behind.
10const REQUIRED_RUST_VERSION: u32 = 40;
11
12pub struct InstallCmd {
13 pub client: Option<ClientOpt>,
14 pub server: Option<ServerOpt>,
15}
16
17pub enum ClientOpt {
18 VsCode,
19}
20
21pub struct ServerOpt {
22 pub jemalloc: bool,
23}
24
25impl 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
40fn 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
73fn 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
136fn 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
170fn 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}