Try current_exe, then argv0 canonicalize, then PATH
authorAshe Connor <ashe@kivikakk.ee>
Thu, 19 Oct 2017 05:15:16 +0000 (16:15 +1100)
committerAshe Connor <ashe@kivikakk.ee>
Thu, 19 Oct 2017 05:15:16 +0000 (16:15 +1100)
src/cargo/util/config.rs

index 039993567b8b4800106c1b786741f2cd23070906..4f523ed074677683027fb8a2cf0979842cf44cb5 100644 (file)
@@ -153,10 +153,42 @@ impl Config {
 
     /// Get the path to the `cargo` executable
     pub fn cargo_exe(&self) -> CargoResult<&Path> {
-        self.cargo_exe.get_or_try_init(||
-            env::current_exe().and_then(|path| path.canonicalize())
-            .chain_err(|| "couldn't get the path to cargo executable")
-        ).map(AsRef::as_ref)
+        self.cargo_exe.get_or_try_init(|| {
+            fn from_current_exe() -> CargoResult<PathBuf> {
+                env::current_exe()
+                    .and_then(|path| path.canonicalize())
+                    .map_err(CargoError::from)
+            }
+
+            fn from_argv() -> CargoResult<PathBuf> {
+                env::args_os()
+                    .next()
+                    .ok_or(CargoError::from("no argv[0]"))
+                    .map(PathBuf::from)
+                    .and_then(|argv0| argv0.canonicalize().or_else(|_| probe_path(argv0)))
+            }
+
+            fn probe_path(argv0: PathBuf) -> CargoResult<PathBuf> {
+                // canonicalize failed, probe PATH if no dir sep in argv0
+                if argv0.components().count() > 1 {
+                    return Err(CargoError::from("no cargo executable candidate found"));
+                }
+
+                let paths = env::var_os("PATH").ok_or(CargoError::from("no PATH"))?;
+                for path in env::split_paths(&paths) {
+                    let candidate = PathBuf::from(path).join(&argv0);
+                    if candidate.is_file() {
+                        return candidate.canonicalize().map_err(CargoError::from);
+                    }
+                }
+
+                Err(CargoError::from("no cargo executable candidate found in PATH"))
+            }
+
+            from_current_exe()
+                .or_else(|_| from_argv())
+                .chain_err(|| "couldn't get the path to cargo executable")
+        }).map(AsRef::as_ref)
     }
 
     pub fn values(&self) -> CargoResult<&HashMap<String, ConfigValue>> {