Skip to content

Commit ffaef1b

Browse files
committed
ra_project_model: look for Cargo in more places
See #3118
1 parent 30eb458 commit ffaef1b

File tree

3 files changed

+128
-10
lines changed

3 files changed

+128
-10
lines changed

Cargo.lock

Lines changed: 76 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/ra_project_model/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ serde = { version = "1.0.106", features = ["derive"] }
2222
serde_json = "1.0.48"
2323

2424
anyhow = "1.0.26"
25+
26+
dirs = "2.0"

crates/ra_project_model/src/cargo_workspace.rs

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use std::{
88
process::Command,
99
};
1010

11-
use anyhow::{Context, Result};
11+
use anyhow::{Context, Error, Result};
1212
use cargo_metadata::{BuildScript, CargoOpt, Message, MetadataCommand, PackageId};
1313
use ra_arena::{Arena, Idx};
1414
use ra_db::Edition;
@@ -145,12 +145,8 @@ impl CargoWorkspace {
145145
cargo_toml: &Path,
146146
cargo_features: &CargoConfig,
147147
) -> Result<CargoWorkspace> {
148-
let _ = Command::new(cargo_binary())
149-
.arg("--version")
150-
.output()
151-
.context("failed to run `cargo --version`, is `cargo` in PATH?")?;
152-
153148
let mut meta = MetadataCommand::new();
149+
meta.cargo_path(cargo_binary()?);
154150
meta.manifest_path(cargo_toml);
155151
if cargo_features.all_features {
156152
meta.features(CargoOpt::AllFeatures);
@@ -288,7 +284,7 @@ pub fn load_extern_resources(
288284
cargo_toml: &Path,
289285
cargo_features: &CargoConfig,
290286
) -> Result<ExternResources> {
291-
let mut cmd = Command::new(cargo_binary());
287+
let mut cmd = Command::new(cargo_binary()?);
292288
cmd.args(&["check", "--message-format=json", "--manifest-path"]).arg(cargo_toml);
293289
if cargo_features.all_features {
294290
cmd.arg("--all-features");
@@ -337,6 +333,51 @@ fn is_dylib(path: &Path) -> bool {
337333
}
338334
}
339335

340-
fn cargo_binary() -> String {
341-
env::var("CARGO").unwrap_or_else(|_| "cargo".to_string())
336+
/// Return a `String` to use for executable `cargo`.
337+
///
338+
/// E.g., this may just be `cargo` if that gives a valid Cargo executable; or it
339+
/// may be a full path to a valid Cargo.
340+
fn cargo_binary() -> Result<String> {
341+
// The current implementation checks three places for a `cargo` to use:
342+
// 1) $CARGO environment variable (erroring if this is set but not a usable Cargo)
343+
// 2) `cargo`
344+
// 3) `~/.cargo/bin/cargo`
345+
if let Ok(path) = env::var("CARGO") {
346+
if is_valid_cargo(&path) {
347+
Ok(path)
348+
} else {
349+
Err(Error::msg("`CARGO` environment variable points to something that's not a valid Cargo executable"))
350+
}
351+
} else {
352+
let final_path: Option<String> = if is_valid_cargo("cargo") {
353+
Some("cargo".to_owned())
354+
} else {
355+
if let Some(mut path) = dirs::home_dir() {
356+
path.push(".cargo");
357+
path.push("bin");
358+
path.push("cargo");
359+
if is_valid_cargo(&path) {
360+
Some(path.into_os_string().into_string().expect("Invalid Unicode in path"))
361+
} else {
362+
None
363+
}
364+
} else {
365+
None
366+
}
367+
};
368+
final_path.ok_or(
369+
// This error message may also be caused by $PATH or $CARGO not being set correctly for VSCode,
370+
// even if they are set correctly in a terminal.
371+
// On macOS in particular, launching VSCode from terminal with `code <dirname>` causes VSCode
372+
// to inherit environment variables including $PATH and $CARGO from that terminal; but
373+
// launching VSCode from Dock does not inherit environment variables from a terminal.
374+
// For more discussion, see #3118.
375+
Error::msg("Failed to find `cargo` executable. Make sure `cargo` is in `$PATH`, or set `$CARGO` to point to a valid Cargo executable.")
376+
)
377+
}
378+
}
379+
380+
/// Does the given `Path` point to a usable `Cargo`?
381+
fn is_valid_cargo(p: impl AsRef<Path>) -> bool {
382+
Command::new(p.as_ref()).arg("--version").output().is_ok()
342383
}

0 commit comments

Comments
 (0)