11//! This crate contains a single public function
22//! [`get_path_for_executable`](fn.get_path_for_executable.html).
33//! See docs there for more information.
4- use std:: {
5- env,
6- path:: { Path , PathBuf } ,
7- process:: Command ,
8- } ;
4+ use std:: { env, iter, path:: PathBuf } ;
95
10- use anyhow:: { bail, Result } ;
6+ pub fn cargo ( ) -> PathBuf {
7+ get_path_for_executable ( "cargo" )
8+ }
9+
10+ pub fn rustc ( ) -> PathBuf {
11+ get_path_for_executable ( "rustc" )
12+ }
13+
14+ pub fn rustup ( ) -> PathBuf {
15+ get_path_for_executable ( "rustup" )
16+ }
1117
1218/// Return a `PathBuf` to use for the given executable.
1319///
1420/// E.g., `get_path_for_executable("cargo")` may return just `cargo` if that
1521/// gives a valid Cargo executable; or it may return a full path to a valid
1622/// Cargo.
17- pub fn get_path_for_executable ( executable_name : & ' static str ) -> Result < PathBuf > {
18- assert ! ( executable_name == "rustc" || executable_name == "cargo" ) ;
23+ fn get_path_for_executable ( executable_name : & ' static str ) -> PathBuf {
1924 // The current implementation checks three places for an executable to use:
2025 // 1) Appropriate environment variable (erroring if this is set but not a usable executable)
2126 // 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>
2530 // example: for cargo, this tries ~/.cargo/bin/cargo
2631 // It seems that this is a reasonable place to try for cargo, rustc, and rustup
2732 let env_var = executable_name. to_ascii_uppercase ( ) ;
28- if let Ok ( path) = env:: var ( & env_var) {
29- return 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- } ;
33+ if let Some ( path) = env:: var_os ( & env_var) {
34+ return path. into ( ) ;
3735 }
3836
39- if is_valid_executable ( executable_name) {
40- return Ok ( executable_name. into ( ) ) ;
37+ if lookup_in_path ( executable_name) {
38+ return executable_name. into ( ) ;
4139 }
4240
4341 if let Some ( mut path) = home:: home_dir ( ) {
4442 path. push ( ".cargo" ) ;
4543 path. push ( "bin" ) ;
4644 path. push ( executable_name) ;
47- if is_valid_executable ( & path) {
48- return Ok ( path) ;
45+ if path. is_file ( ) {
46+ return path;
4947 }
5048 }
51- // This error message may also be caused by $PATH or $CARGO/$RUSTC/etc not being set correctly
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- )
49+ executable_name. into ( )
6150}
6251
63- /// Does the given `Path` point to a usable executable?
64- ///
65- /// (assumes the executable takes a `--version` switch and writes to stdout,
66- /// which is true for `cargo`, `rustc`, and `rustup`)
67- fn is_valid_executable ( p : & ' static str ) -> bool {
68- Command :: new ( p) . arg ( "--version" ) . output ( ) . is_ok ( )
52+ fn lookup_in_path ( exec : & str ) -> bool {
53+ let paths = env:: var_os ( "PATH" ) . unwrap_or_default ( ) ;
54+ let mut candidates = env:: split_paths ( & paths) . flat_map ( |path| {
55+ let candidate = path. join ( & exec) ;
56+ let with_exe = if env:: consts:: EXE_EXTENSION == "" {
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 ( ) )
6964}
0 commit comments