Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use platform-defined directories for cargo state #8063

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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