aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_toolchain/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_toolchain/src')
-rw-r--r--crates/ra_toolchain/src/lib.rs68
1 files changed, 68 insertions, 0 deletions
diff --git a/crates/ra_toolchain/src/lib.rs b/crates/ra_toolchain/src/lib.rs
new file mode 100644
index 000000000..110c92c88
--- /dev/null
+++ b/crates/ra_toolchain/src/lib.rs
@@ -0,0 +1,68 @@
1//! This crate contains a single public function
2//! [`get_path_for_executable`](fn.get_path_for_executable.html).
3//! See docs there for more information.
4use std::{
5 env,
6 path::{Path, PathBuf},
7 process::Command,
8};
9
10use anyhow::{bail, Result};
11
12/// Return a `PathBuf` to use for the given executable.
13///
14/// E.g., `get_path_for_executable("cargo")` may return just `cargo` if that
15/// gives a valid Cargo executable; or it may return a full path to a valid
16/// Cargo.
17pub fn get_path_for_executable(executable_name: impl AsRef<str>) -> Result<PathBuf> {
18 // The current implementation checks three places for an executable to use:
19 // 1) Appropriate environment variable (erroring if this is set but not a usable executable)
20 // example: for cargo, this checks $CARGO environment variable; for rustc, $RUSTC; etc
21 // 2) `<executable_name>`
22 // example: for cargo, this tries just `cargo`, which will succeed if `cargo` is on the $PATH
23 // 3) `~/.cargo/bin/<executable_name>`
24 // example: for cargo, this tries ~/.cargo/bin/cargo
25 // It seems that this is a reasonable place to try for cargo, rustc, and rustup
26 let executable_name = executable_name.as_ref();
27 let env_var = executable_name.to_ascii_uppercase();
28 if let Ok(path) = env::var(&env_var) {
29 if is_valid_executable(&path) {
30 Ok(path.into())
31 } else {
32 bail!(
33 "`{}` environment variable points to something that's not a valid executable",
34 env_var
35 )
36 }
37 } else {
38 if is_valid_executable(executable_name) {
39 return Ok(executable_name.into());
40 }
41 if let Some(mut path) = ::home::home_dir() {
42 path.push(".cargo");
43 path.push("bin");
44 path.push(executable_name);
45 if is_valid_executable(&path) {
46 return Ok(path);
47 }
48 }
49 // This error message may also be caused by $PATH or $CARGO/$RUSTC/etc not being set correctly
50 // for VSCode, even if they are set correctly in a terminal.
51 // On macOS in particular, launching VSCode from terminal with `code <dirname>` causes VSCode
52 // to inherit environment variables including $PATH, $CARGO, $RUSTC, etc from that terminal;
53 // but launching VSCode from Dock does not inherit environment variables from a terminal.
54 // For more discussion, see #3118.
55 bail!(
56 "Failed to find `{}` executable. Make sure `{}` is in `$PATH`, or set `${}` to point to a valid executable.",
57 executable_name, executable_name, env_var
58 )
59 }
60}
61
62/// Does the given `Path` point to a usable executable?
63///
64/// (assumes the executable takes a `--version` switch and writes to stdout,
65/// which is true for `cargo`, `rustc`, and `rustup`)
66fn is_valid_executable(p: impl AsRef<Path>) -> bool {
67 Command::new(p.as_ref()).arg("--version").output().is_ok()
68}