@@ -8,7 +8,7 @@ use std::{
8
8
process:: Command ,
9
9
} ;
10
10
11
- use anyhow:: { Context , Result } ;
11
+ use anyhow:: { Context , Error , Result } ;
12
12
use cargo_metadata:: { BuildScript , CargoOpt , Message , MetadataCommand , PackageId } ;
13
13
use ra_arena:: { Arena , Idx } ;
14
14
use ra_db:: Edition ;
@@ -145,12 +145,8 @@ impl CargoWorkspace {
145
145
cargo_toml : & Path ,
146
146
cargo_features : & CargoConfig ,
147
147
) -> 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
-
153
148
let mut meta = MetadataCommand :: new ( ) ;
149
+ meta. cargo_path ( cargo_binary ( ) ?) ;
154
150
meta. manifest_path ( cargo_toml) ;
155
151
if cargo_features. all_features {
156
152
meta. features ( CargoOpt :: AllFeatures ) ;
@@ -288,7 +284,7 @@ pub fn load_extern_resources(
288
284
cargo_toml : & Path ,
289
285
cargo_features : & CargoConfig ,
290
286
) -> Result < ExternResources > {
291
- let mut cmd = Command :: new ( cargo_binary ( ) ) ;
287
+ let mut cmd = Command :: new ( cargo_binary ( ) ? ) ;
292
288
cmd. args ( & [ "check" , "--message-format=json" , "--manifest-path" ] ) . arg ( cargo_toml) ;
293
289
if cargo_features. all_features {
294
290
cmd. arg ( "--all-features" ) ;
@@ -337,6 +333,51 @@ fn is_dylib(path: &Path) -> bool {
337
333
}
338
334
}
339
335
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 ( )
342
383
}
0 commit comments