From c0cb319b6ccf1acd97ea4d46c8ca999d7a3e31db Mon Sep 17 00:00:00 2001 From: Jon Gjengset Date: Mon, 8 Feb 2021 17:29:06 -0800 Subject: [PATCH] Allow setting cargo home from .cargo/home This patch allows users to set the Cargo home directory through the file system using a pointer file in `.cargo/home`. As with `.cargo/config`, the file is searched for recursively up the tree from the current working directory. Unlike with `.cargo/config`, the search terminates when a matching file is found. The search for `.cargo/home` happens before any `.cargo/config` files are read, as that may be affected by the choice of Cargo home directory. The `CARGO_HOME` environment variable is preferred over `.cargo/home`. Fixes #6452. --- src/cargo/util/config/mod.rs | 41 +++++++++++++---- tests/testsuite/cargo_command.rs | 79 ++++++++++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 9 deletions(-) diff --git a/src/cargo/util/config/mod.rs b/src/cargo/util/config/mod.rs index 3302a027e8c..4c074b2f2d3 100644 --- a/src/cargo/util/config/mod.rs +++ b/src/cargo/util/config/mod.rs @@ -63,7 +63,7 @@ use std::str::FromStr; use std::sync::Once; use std::time::Instant; -use anyhow::{anyhow, bail, format_err}; +use anyhow::{anyhow, bail, format_err, Context}; use curl::easy::Easy; use lazycell::LazyCell; use serde::Deserialize; @@ -260,12 +260,7 @@ impl Config { let shell = Shell::new(); let cwd = env::current_dir().chain_err(|| "couldn't get the current directory of the process")?; - let homedir = homedir(&cwd).ok_or_else(|| { - anyhow!( - "Cargo couldn't find your home directory. \ - This probably means that $HOME was not set." - ) - })?; + let homedir = homedir(&cwd)?; Ok(Config::new(shell, cwd, homedir)) } @@ -1618,8 +1613,36 @@ impl ConfigValue { } } -pub fn homedir(cwd: &Path) -> Option { - ::home::cargo_home_with_cwd(cwd).ok() +pub fn homedir(cwd: &Path) -> CargoResult { + if std::env::var_os("CARGO_HOME") + .filter(|h| !h.is_empty()) + .is_none() + { + // CARGO_HOME is not set -- look for a .cargo/home file instead + let mut components = cwd.components(); + loop { + let cargo_home = components.as_path().join(".cargo/home"); + if cargo_home.exists() { + let home = std::fs::read_to_string(&cargo_home).with_context(|| { + format!( + "Cargo could not follow home pointer in '{}'", + cargo_home.display() + ) + })?; + return Ok(PathBuf::from(home)); + } + if components.next_back().is_none() { + break; + } + } + } + + ::home::cargo_home_with_cwd(cwd).map_err(|_| { + anyhow!( + "Cargo couldn't find your home directory. \ + This probably means that $HOME was not set." + ) + }) } pub fn save_credentials( diff --git a/tests/testsuite/cargo_command.rs b/tests/testsuite/cargo_command.rs index 04e3051c785..a3439a702af 100644 --- a/tests/testsuite/cargo_command.rs +++ b/tests/testsuite/cargo_command.rs @@ -271,6 +271,85 @@ fn override_cargo_home() { assert!(contents.contains(r#"authors = ["foo "]"#)); } +#[cargo_test] +fn cargo_home_pointer() { + let root = paths::root(); + let my_home = root.join("my_home"); + fs::create_dir(&my_home).unwrap(); + fs::write( + &my_home.join("config"), + r#" + [cargo-new] + name = "foo" + email = "bar" + git = false + "#, + ) + .unwrap(); + + fs::create_dir(root.join(".cargo")).unwrap(); + fs::write( + root.join(".cargo/home"), + my_home.as_os_str().to_str().unwrap().as_bytes(), + ) + .unwrap(); + + cargo_process("new foo") + .env("USER", "foo") + .env_remove("CARGO_HOME") + .run(); + + let toml = paths::root().join("foo/Cargo.toml"); + let contents = fs::read_to_string(&toml).unwrap(); + assert!(contents.contains(r#"authors = ["foo "]"#)); +} + +#[cargo_test] +fn cargo_home_prefer_env() { + let root = paths::root(); + let my_home = root.join("my_home"); + fs::create_dir(&my_home).unwrap(); + fs::write( + &my_home.join("config"), + r#" + [cargo-new] + name = "foo" + email = "bar" + git = false + "#, + ) + .unwrap(); + + let my_other_home = root.join("my_other_home"); + fs::create_dir(&my_other_home).unwrap(); + fs::write( + &my_other_home.join("config"), + r#" + [cargo-new] + name = "foo" + email = "baz" + git = false + "#, + ) + .unwrap(); + + fs::create_dir(root.join(".cargo")).unwrap(); + fs::write( + root.join(".cargo/home"), + my_other_home.as_os_str().to_str().unwrap().as_bytes(), + ) + .unwrap(); + + cargo_process("new foo") + .env("USER", "foo") + .env("CARGO_HOME", &my_home) + .run(); + + let toml = paths::root().join("foo/Cargo.toml"); + let contents = fs::read_to_string(&toml).unwrap(); + assert!(contents.contains(r#"authors = ["foo "]"#)); +} + #[cargo_test] fn cargo_subcommand_env() { let src = format!(