From ffaef1b7aeb61984992e231d9af20f39486403ea Mon Sep 17 00:00:00 2001 From: Craig Disselkoen Date: Tue, 5 May 2020 11:59:41 -0700 Subject: ra_project_model: look for Cargo in more places See #3118 --- crates/ra_project_model/Cargo.toml | 2 + crates/ra_project_model/src/cargo_workspace.rs | 59 ++++++++++++++++++++++---- 2 files changed, 52 insertions(+), 9 deletions(-) (limited to 'crates') diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml index 5e651fe70..91958ed94 100644 --- a/crates/ra_project_model/Cargo.toml +++ b/crates/ra_project_model/Cargo.toml @@ -22,3 +22,5 @@ serde = { version = "1.0.106", features = ["derive"] } serde_json = "1.0.48" anyhow = "1.0.26" + +dirs = "2.0" diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index 59f46a2a0..a15a8c68e 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs @@ -8,7 +8,7 @@ use std::{ process::Command, }; -use anyhow::{Context, Result}; +use anyhow::{Context, Error, Result}; use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId}; use ra_arena::{Arena, Idx}; use ra_db::Edition; @@ -145,12 +145,8 @@ impl CargoWorkspace { cargo_toml: &Path, cargo_features: &CargoConfig, ) -> Result { - let _ = Command::new(cargo_binary()) - .arg("--version") - .output() - .context("failed to run `cargo --version`, is `cargo` in PATH?")?; - let mut meta = MetadataCommand::new(); + meta.cargo_path(cargo_binary()?); meta.manifest_path(cargo_toml); if cargo_features.all_features { meta.features(CargoOpt::AllFeatures); @@ -288,7 +284,7 @@ pub fn load_extern_resources( cargo_toml: &Path, cargo_features: &CargoConfig, ) -> Result { - let mut cmd = Command::new(cargo_binary()); + let mut cmd = Command::new(cargo_binary()?); cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml); if cargo_features.all_features { cmd.arg("--all-features"); @@ -337,6 +333,51 @@ fn is_dylib(path: &Path) -> bool { } } -fn cargo_binary() -> String { - env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()) +/// Return a `String` to use for executable `cargo`. +/// +/// E.g., this may just be `cargo` if that gives a valid Cargo executable; or it +/// may be a full path to a valid Cargo. +fn cargo_binary() -> Result { + // The current implementation checks three places for a `cargo` to use: + // 1) $CARGO environment variable (erroring if this is set but not a usable Cargo) + // 2) `cargo` + // 3) `~/.cargo/bin/cargo` + if let Ok(path) = env::var("CARGO") { + if is_valid_cargo(&path) { + Ok(path) + } else { + Err(Error::msg("`CARGO` environment variable points to something that's not a valid Cargo executable")) + } + } else { + let final_path: Option = if is_valid_cargo("cargo") { + Some("cargo".to_owned()) + } else { + if let Some(mut path) = dirs::home_dir() { + path.push(".cargo"); + path.push("bin"); + path.push("cargo"); + if is_valid_cargo(&path) { + Some(path.into_os_string().into_string().expect("Invalid Unicode in path")) + } else { + None + } + } else { + None + } + }; + final_path.ok_or( + // This error message may also be caused by $PATH or $CARGO not being set correctly for VSCode, + // even if they are set correctly in a terminal. + // On macOS in particular, launching VSCode from terminal with `code ` causes VSCode + // to inherit environment variables including $PATH and $CARGO from that terminal; but + // launching VSCode from Dock does not inherit environment variables from a terminal. + // For more discussion, see #3118. + Error::msg("Failed to find `cargo` executable. Make sure `cargo` is in `$PATH`, or set `$CARGO` to point to a valid Cargo executable.") + ) + } +} + +/// Does the given `Path` point to a usable `Cargo`? +fn is_valid_cargo(p: impl AsRef) -> bool { + Command::new(p.as_ref()).arg("--version").output().is_ok() } -- cgit v1.2.3