Try to replicate shell behavior more accurately
authorAshe Connor <ashe@kivikakk.ee>
Thu, 26 Oct 2017 07:30:50 +0000 (18:30 +1100)
committerAshe Connor <ashe@kivikakk.ee>
Thu, 26 Oct 2017 07:30:50 +0000 (18:30 +1100)
src/cargo/util/config.rs

index 465dc344e369355589ad02f9c16c522d7ff0daba..e6aa79477ea9de73e9a8183335da7d3a9f056c05 100644 (file)
@@ -166,30 +166,33 @@ impl Config {
 
             fn from_argv() -> CargoResult<PathBuf> {
                 // Grab argv[0] and attempt to resolve it to an absolute path.
-                // Path::canonicalize succeeds only when the path is already absolute,
-                // or it's a relative path accessible from the current directory.
+                // If argv[0] has one component, it must have come from a PATH lookup,
+                // so probe PATH in that case.
+                // Otherwise, it has multiple components and is either:
+                // - a relative path (e.g. `./cargo`, `target/debug/cargo`), or
+                // - an absolute path (e.g. `/usr/local/bin/cargo`).
+                // In either case, Path::canonicalize will return the full absolute path
+                // to the target if it exists
                 env::args_os()
                     .next()
                     .ok_or(CargoError::from("no argv[0]"))
                     .map(PathBuf::from)
-                    .and_then(|argv0| argv0.canonicalize().or_else(|_| probe_path(argv0)))
+                    .and_then(|argv0| {
+                        if argv0.components().count() == 1 {
+                            probe_path(argv0)
+                        } else {
+                            argv0.canonicalize().map_err(CargoError::from)
+                        }
+                    })
             }
 
             fn probe_path(argv0: PathBuf) -> CargoResult<PathBuf> {
-                // Path::canonicalize failed, so finally, if argv[0] has no directory
-                // separator in it, we look through the PATH environment variable checking
-                // for a matching file in each directory.
-                // A directory separator implies either (a) an absolute path, so we
-                // shouldn't check PATH, or (b) a relative path with a directory component,
-                // which wouldn't search PATH in any shell anyway.
-                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() {
+                        // PATH may have a component like "." in it, so we still need to
+                        // canonicalize.
                         return candidate.canonicalize().map_err(CargoError::from);
                     }
                 }