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