Skip to content

Commit ee0b711

Browse files
alexcrichtonbar
authored and
bar
committed
Canonicalize CARGO_HOME fallback on Windows
This commit ensures that we always return the same fallback value on Windows regardless of whichever shell we happen to be run from. We do this by removing the `$HOME` environment variable which `std::env::home_dir` will inspect to force it to fall back to the system APIs. If the old directory exists then we favor that one, but otherwise we favor locations like `C:\Users\$user` Supercedes and closes rust-lang#2604
1 parent 89a2f2b commit ee0b711

File tree

2 files changed

+46
-3
lines changed

2 files changed

+46
-3
lines changed

src/cargo/util/config.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -600,8 +600,50 @@ fn homedir(cwd: &Path) -> Option<PathBuf> {
600600
let cargo_home = env::var_os("CARGO_HOME").map(|home| {
601601
cwd.join(home)
602602
});
603-
let user_home = env::home_dir().map(|p| p.join(".cargo"));
604-
cargo_home.or(user_home)
603+
if cargo_home.is_some() {
604+
return cargo_home
605+
}
606+
607+
// If `CARGO_HOME` wasn't defined then we want to fall back to
608+
// `$HOME/.cargo`. Note that currently, however, the implementation of
609+
// `env::home_dir()` uses the $HOME environment variable *on all platforms*.
610+
// Platforms like Windows then have *another* fallback based on system APIs
611+
// if this isn't set.
612+
//
613+
// Specifically on Windows this can lead to some weird behavior where if you
614+
// invoke cargo inside an MSYS shell it'll have $HOME defined and it'll
615+
// place output there by default. If, however, you run in another shell
616+
// (like cmd.exe or powershell) it'll place output in
617+
// `C:\Users\$user\.cargo` by default.
618+
//
619+
// This snippet is meant to handle this case to ensure that on Windows we
620+
// always place output in the same location, regardless of the shell we were
621+
// invoked from. We first check `env::home_dir()` without tampering the
622+
// environment, and then afterwards we remove `$HOME` and call it again to
623+
// see what happened. If they both returned success then on Windows we only
624+
// return the first (with the $HOME in place) if it already exists. This
625+
// should help existing installs of Cargo continue using the same cargo home
626+
// directory.
627+
let home_dir_with_env = env::home_dir().map(|p| p.join(".cargo"));
628+
let home_dir = env::var_os("HOME");
629+
env::remove_var("HOME");
630+
let home_dir_without_env = env::home_dir().map(|p| p.join(".cargo"));
631+
if let Some(home_dir) = home_dir {
632+
env::set_var("HOME", home_dir);
633+
}
634+
635+
match (home_dir_with_env, home_dir_without_env) {
636+
(None, None) => None,
637+
(None, Some(p)) |
638+
(Some(p), None) => Some(p),
639+
(Some(a), Some(b)) => {
640+
if cfg!(windows) && !a.exists() {
641+
Some(b)
642+
} else {
643+
Some(a)
644+
}
645+
}
646+
}
605647
}
606648

607649
fn walk_tree<F>(pwd: &Path, mut walk: F) -> CargoResult<()>

tests/tests.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,8 +92,9 @@ fn is_nightly() -> bool {
9292
fn process<T: AsRef<OsStr>>(t: T) -> cargo::util::ProcessBuilder {
9393
let mut p = cargo::util::process(t.as_ref());
9494
p.cwd(&support::paths::root())
95-
.env("HOME", &support::paths::home())
9695
.env_remove("CARGO_HOME")
96+
.env("HOME", support::paths::home())
97+
.env("CARGO_HOME", support::paths::home().join(".cargo"))
9798
.env_remove("RUSTC")
9899
.env_remove("RUSTFLAGS")
99100
.env_remove("XDG_CONFIG_HOME") // see #2345

0 commit comments

Comments
 (0)