Skip to content

Commit

Permalink
Use platform-defined directories for cargo state
Browse files Browse the repository at this point in the history
This commit is a continuation and adaptation of #5183, which aimed to
make cargo no longer reliant on the `$HOME/.cargo` directory in user's
home's, and instead uses the `directories` crate to get
platform-defined standard directories for data, caches, and configs.

The priority of paths cargo will check is as follows:

1. Use `$CARGO_HOME`, if it is set
2. Use `$CARGO_CACHE_DIR`, `$CARGO_CONFIG_DIR`, etc, if they are set
3. If no environment variables are set, and `$HOME/.cargo` is present,
   use that
4. Finally, use the platform-default directory paths
  • Loading branch information
spacekookie committed Apr 1, 2020
1 parent c75216f commit 442350b
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 23 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ crossbeam-utils = "0.7"
crypto-hash = "0.3.1"
curl = { version = "0.4.23", features = ["http2"] }
curl-sys = "0.4.22"
directories = "2.0"
env_logger = "0.7.0"
pretty_env_logger = { version = "0.4", optional = true }
anyhow = "1.0"
Expand Down
79 changes: 79 additions & 0 deletions src/cargo/util/config/dirs.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//! An abstraction over what directories cargo should use for state

use crate::util::{
config::Filesystem,
errors::{CargoResult, CargoResultExt},
};
use directories::ProjectDirs;
use log::debug;
use std::env;
use std::path::PathBuf;

#[derive(Clone, Debug)]
pub struct CargoDirs {
/// Main directory for cargo data
pub data_dir: Filesystem,
/// Caching registry artefacts (previously .cargo/registry/cache)
pub cache_dir: Filesystem,
/// Kept to walk upwards the directory tree to find a Cargo.toml
pub home_dir: Filesystem,
}

impl CargoDirs {
/// Constructs the hierarchy of directories that cargo will use
pub fn new(home_dir: PathBuf) -> CargoResult<CargoDirs> {
let current_dir =
env::current_dir().chain_err(|| "couldn't get the current directory of the process")?;

let mut cache_dir = PathBuf::default();
let mut data_dir = PathBuf::default();

// 1. CARGO_HOME set
let cargo_home_env = env::var_os("CARGO_HOME").map(|home| current_dir.join(home));
if let Some(cargo_home) = cargo_home_env.clone() {
cache_dir = cargo_home.clone();
data_dir = cargo_home.clone();
}

// 2. CARGO_CACHE_DIR, CARGO_CONFIG_DIR, CARGO_BIN_DIR, ... set
let cargo_cache_env = env::var_os("CARGO_CACHE_DIR").map(|home| current_dir.join(home));
let cargo_data_env = env::var_os("CARGO_DATA_DIR").map(|home| current_dir.join(home));

if let Some(cargo_cache) = cargo_cache_env.clone() {
cache_dir = cargo_cache.clone();
}
if let Some(cargo_data) = cargo_data_env.clone() {
data_dir = cargo_data.clone();
}

// none of the env vars are set ...
if cargo_home_env.is_none() && cargo_cache_env.is_none() && cargo_data_env.is_none() {
let legacy_cargo_dir = home_dir.join(".cargo");

// 3. ... and .cargo exist
if legacy_cargo_dir.exists() {
debug!("Using legacy paths at $HOME, consider moving to $XDG_DATA_HOME");
cache_dir = legacy_cargo_dir.clone();
data_dir = legacy_cargo_dir.clone();

// 4. ... otherwise follow platform conventions
} else {
let xdg_dirs = match ProjectDirs::from("org", "rust-lang", "cargo") {
Some(d) => Ok(d),
None => Err(anyhow::format_err!(
"failed to get directories according to XDG settings"
)),
}?;

cache_dir = xdg_dirs.cache_dir().to_path_buf();
data_dir = xdg_dirs.data_dir().to_path_buf();
}
}

dbg!(Ok(CargoDirs {
cache_dir: Filesystem::new(cache_dir),
data_dir: Filesystem::new(data_dir),
home_dir: Filesystem::new(home_dir),
}))
}
}
42 changes: 23 additions & 19 deletions src/cargo/util/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@ use crate::util::{FileLock, Filesystem, IntoUrl, IntoUrlWithBase, Rustc};
mod de;
use de::Deserializer;

mod dirs;
use dirs::CargoDirs;

mod value;
pub use value::{Definition, OptValue, Value};

Expand Down Expand Up @@ -122,7 +125,7 @@ macro_rules! get_value_typed {
#[derive(Debug)]
pub struct Config {
/// The location of the user's 'home' directory. OS-dependent.
home_path: Filesystem,
dirs: CargoDirs,
/// Information about how to write messages to the shell
shell: RefCell<Shell>,
/// A collection of configuration options
Expand Down Expand Up @@ -182,7 +185,7 @@ impl Config {
///
/// This does only minimal initialization. In particular, it does not load
/// any config files from disk. Those will be loaded lazily as-needed.
pub fn new(shell: Shell, cwd: PathBuf, homedir: PathBuf) -> Config {
pub fn new(shell: Shell, cwd: PathBuf, homedir: PathBuf) -> CargoResult<Config> {
static mut GLOBAL_JOBSERVER: *mut jobserver::Client = 0 as *mut _;
static INIT: Once = Once::new();

Expand All @@ -209,8 +212,8 @@ impl Config {
_ => true,
};

Config {
home_path: Filesystem::new(homedir),
Ok(Config {
dirs: CargoDirs::new(homedir)?,
shell: RefCell::new(shell),
cwd,
values: LazyCell::new(),
Expand Down Expand Up @@ -241,7 +244,7 @@ impl Config {
net_config: LazyCell::new(),
build_config: LazyCell::new(),
target_cfgs: LazyCell::new(),
}
})
}

/// Creates a new Config instance, with all default settings.
Expand All @@ -258,32 +261,32 @@ impl Config {
This probably means that $HOME was not set."
)
})?;
Ok(Config::new(shell, cwd, homedir))
Config::new(shell, cwd, homedir)
}

/// Gets the user's Cargo home directory (OS-dependent).
pub fn home(&self) -> &Filesystem {
&self.home_path
&self.dirs.data_dir
}

/// Gets the Cargo Git directory (`<cargo_home>/git`).
pub fn git_path(&self) -> Filesystem {
self.home_path.join("git")
self.dirs.data_dir.join("git")
}

/// Gets the Cargo registry index directory (`<cargo_home>/registry/index`).
pub fn registry_index_path(&self) -> Filesystem {
self.home_path.join("registry").join("index")
self.dirs.data_dir.join("registry").join("index")
}

/// Gets the Cargo registry cache directory (`<cargo_home>/registry/path`).
pub fn registry_cache_path(&self) -> Filesystem {
self.home_path.join("registry").join("cache")
self.dirs.cache_dir.clone()
}

/// Gets the Cargo registry source directory (`<cargo_home>/registry/src`).
pub fn registry_source_path(&self) -> Filesystem {
self.home_path.join("registry").join("src")
self.dirs.data_dir.join("registry").join("src")
}

/// Gets the default Cargo registry.
Expand Down Expand Up @@ -781,7 +784,7 @@ impl Config {
// This definition path is ignored, this is just a temporary container
// representing the entire file.
let mut cfg = CV::Table(HashMap::new(), Definition::Path(PathBuf::from(".")));
let home = self.home_path.clone().into_path_unlocked();
let home = self.dirs.home_dir.clone().into_path_unlocked();

self.walk_tree(path, &home, |path| {
let value = self.load_file(path)?;
Expand Down Expand Up @@ -1040,7 +1043,7 @@ impl Config {

/// Loads credentials config from the credentials file, if present.
pub fn load_credentials(&mut self) -> CargoResult<()> {
let home_path = self.home_path.clone().into_path_unlocked();
let home_path = self.dirs.data_dir.clone().into_path_unlocked();
let credentials = match self.get_file_path(&home_path, "credentials", true)? {
Some(credentials) => credentials,
None => return Ok(()),
Expand Down Expand Up @@ -1197,7 +1200,7 @@ impl Config {
"package cache lock is not currently held, Cargo forgot to call \
`acquire_package_cache_lock` before we got to this stack frame",
);
assert!(ret.starts_with(self.home_path.as_path_unlocked()));
assert!(ret.starts_with(self.dirs.cache_dir.as_path_unlocked()));
ret
}

Expand Down Expand Up @@ -1234,11 +1237,11 @@ impl Config {
// someone else on the system we should synchronize with them,
// but if we can't even do that then we did our best and we just
// keep on chugging elsewhere.
match self.home_path.open_rw(path, self, desc) {
match self.dirs.data_dir.open_rw(path, self, desc) {
Ok(lock) => *slot = Some((Some(lock), 1)),
Err(e) => {
if maybe_readonly(&e) {
let lock = self.home_path.open_ro(path, self, desc).ok();
let lock = self.dirs.data_dir.open_ro(path, self, desc).ok();
*slot = Some((lock, 1));
return Ok(PackageCacheLock(self));
}
Expand Down Expand Up @@ -1556,7 +1559,7 @@ pub fn save_credentials(cfg: &Config, token: String, registry: Option<String>) -
// If 'credentials.toml' exists, we should write to that, otherwise
// use the legacy 'credentials'. There's no need to print the warning
// here, because it would already be printed at load time.
let home_path = cfg.home_path.clone().into_path_unlocked();
let home_path = cfg.dirs.data_dir.clone().into_path_unlocked();
let filename = match cfg.get_file_path(&home_path, "credentials", false)? {
Some(path) => match path.file_name() {
Some(filename) => Path::new(filename).to_owned(),
Expand All @@ -1566,8 +1569,9 @@ pub fn save_credentials(cfg: &Config, token: String, registry: Option<String>) -
};

let mut file = {
cfg.home_path.create_dir()?;
cfg.home_path
cfg.dirs.data_dir.create_dir()?;
cfg.dirs
.data_dir
.open_rw(filename, cfg, "credentials' config file")?
};

Expand Down
2 changes: 1 addition & 1 deletion tests/testsuite/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ impl ConfigBuilder {
let shell = Shell::from_write(output);
let cwd = self.cwd.clone().unwrap_or_else(|| paths::root());
let homedir = paths::home();
let mut config = Config::new(shell, cwd, homedir);
let mut config = Config::new(shell, cwd, homedir)?;
config.set_env(self.env.clone());
config.configure(
0,
Expand Down
2 changes: 1 addition & 1 deletion tests/testsuite/login.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ fn new_credentials_is_used_instead_old() {
.arg(TOKEN)
.run();

let mut config = Config::new(Shell::new(), cargo_home(), cargo_home());
let mut config = Config::new(Shell::new(), cargo_home(), cargo_home()).unwrap();
let _ = config.values();
let _ = config.load_credentials();

Expand Down
3 changes: 2 additions & 1 deletion tests/testsuite/member_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ fn member_manifest_version_error() {
Shell::from_write(Box::new(Vec::new())),
cargo_home(),
cargo_home(),
);
)
.unwrap();
let ws = Workspace::new(&p.root().join("Cargo.toml"), &config).unwrap();
let compile_options = CompileOptions::new(&config, CompileMode::Build).unwrap();
let member_bar = ws.members().find(|m| &*m.name() == "bar").unwrap();
Expand Down
3 changes: 2 additions & 1 deletion tests/testsuite/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ fn not_update() {
Shell::from_write(Box::new(Vec::new())),
paths::root(),
paths::home().join(".cargo"),
);
)
.unwrap();
let lock = cfg.acquire_package_cache_lock().unwrap();
let mut regsrc = RegistrySource::remote(sid, &HashSet::new(), &cfg);
regsrc.update().unwrap();
Expand Down

0 comments on commit 442350b

Please sign in to comment.