Skip to content

Commit 442350b

Browse files
committed
Use platform-defined directories for cargo state
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
1 parent c75216f commit 442350b

File tree

7 files changed

+109
-23
lines changed

7 files changed

+109
-23
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ crossbeam-utils = "0.7"
2727
crypto-hash = "0.3.1"
2828
curl = { version = "0.4.23", features = ["http2"] }
2929
curl-sys = "0.4.22"
30+
directories = "2.0"
3031
env_logger = "0.7.0"
3132
pretty_env_logger = { version = "0.4", optional = true }
3233
anyhow = "1.0"

src/cargo/util/config/dirs.rs

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//! An abstraction over what directories cargo should use for state
2+
3+
use crate::util::{
4+
config::Filesystem,
5+
errors::{CargoResult, CargoResultExt},
6+
};
7+
use directories::ProjectDirs;
8+
use log::debug;
9+
use std::env;
10+
use std::path::PathBuf;
11+
12+
#[derive(Clone, Debug)]
13+
pub struct CargoDirs {
14+
/// Main directory for cargo data
15+
pub data_dir: Filesystem,
16+
/// Caching registry artefacts (previously .cargo/registry/cache)
17+
pub cache_dir: Filesystem,
18+
/// Kept to walk upwards the directory tree to find a Cargo.toml
19+
pub home_dir: Filesystem,
20+
}
21+
22+
impl CargoDirs {
23+
/// Constructs the hierarchy of directories that cargo will use
24+
pub fn new(home_dir: PathBuf) -> CargoResult<CargoDirs> {
25+
let current_dir =
26+
env::current_dir().chain_err(|| "couldn't get the current directory of the process")?;
27+
28+
let mut cache_dir = PathBuf::default();
29+
let mut data_dir = PathBuf::default();
30+
31+
// 1. CARGO_HOME set
32+
let cargo_home_env = env::var_os("CARGO_HOME").map(|home| current_dir.join(home));
33+
if let Some(cargo_home) = cargo_home_env.clone() {
34+
cache_dir = cargo_home.clone();
35+
data_dir = cargo_home.clone();
36+
}
37+
38+
// 2. CARGO_CACHE_DIR, CARGO_CONFIG_DIR, CARGO_BIN_DIR, ... set
39+
let cargo_cache_env = env::var_os("CARGO_CACHE_DIR").map(|home| current_dir.join(home));
40+
let cargo_data_env = env::var_os("CARGO_DATA_DIR").map(|home| current_dir.join(home));
41+
42+
if let Some(cargo_cache) = cargo_cache_env.clone() {
43+
cache_dir = cargo_cache.clone();
44+
}
45+
if let Some(cargo_data) = cargo_data_env.clone() {
46+
data_dir = cargo_data.clone();
47+
}
48+
49+
// none of the env vars are set ...
50+
if cargo_home_env.is_none() && cargo_cache_env.is_none() && cargo_data_env.is_none() {
51+
let legacy_cargo_dir = home_dir.join(".cargo");
52+
53+
// 3. ... and .cargo exist
54+
if legacy_cargo_dir.exists() {
55+
debug!("Using legacy paths at $HOME, consider moving to $XDG_DATA_HOME");
56+
cache_dir = legacy_cargo_dir.clone();
57+
data_dir = legacy_cargo_dir.clone();
58+
59+
// 4. ... otherwise follow platform conventions
60+
} else {
61+
let xdg_dirs = match ProjectDirs::from("org", "rust-lang", "cargo") {
62+
Some(d) => Ok(d),
63+
None => Err(anyhow::format_err!(
64+
"failed to get directories according to XDG settings"
65+
)),
66+
}?;
67+
68+
cache_dir = xdg_dirs.cache_dir().to_path_buf();
69+
data_dir = xdg_dirs.data_dir().to_path_buf();
70+
}
71+
}
72+
73+
dbg!(Ok(CargoDirs {
74+
cache_dir: Filesystem::new(cache_dir),
75+
data_dir: Filesystem::new(data_dir),
76+
home_dir: Filesystem::new(home_dir),
77+
}))
78+
}
79+
}

src/cargo/util/config/mod.rs

+23-19
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ use crate::util::{FileLock, Filesystem, IntoUrl, IntoUrlWithBase, Rustc};
8181
mod de;
8282
use de::Deserializer;
8383

84+
mod dirs;
85+
use dirs::CargoDirs;
86+
8487
mod value;
8588
pub use value::{Definition, OptValue, Value};
8689

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

@@ -209,8 +212,8 @@ impl Config {
209212
_ => true,
210213
};
211214

212-
Config {
213-
home_path: Filesystem::new(homedir),
215+
Ok(Config {
216+
dirs: CargoDirs::new(homedir)?,
214217
shell: RefCell::new(shell),
215218
cwd,
216219
values: LazyCell::new(),
@@ -241,7 +244,7 @@ impl Config {
241244
net_config: LazyCell::new(),
242245
build_config: LazyCell::new(),
243246
target_cfgs: LazyCell::new(),
244-
}
247+
})
245248
}
246249

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

264267
/// Gets the user's Cargo home directory (OS-dependent).
265268
pub fn home(&self) -> &Filesystem {
266-
&self.home_path
269+
&self.dirs.data_dir
267270
}
268271

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

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

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

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

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

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

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

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

15681571
let mut file = {
1569-
cfg.home_path.create_dir()?;
1570-
cfg.home_path
1572+
cfg.dirs.data_dir.create_dir()?;
1573+
cfg.dirs
1574+
.data_dir
15711575
.open_rw(filename, cfg, "credentials' config file")?
15721576
};
15731577

tests/testsuite/config.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ impl ConfigBuilder {
7474
let shell = Shell::from_write(output);
7575
let cwd = self.cwd.clone().unwrap_or_else(|| paths::root());
7676
let homedir = paths::home();
77-
let mut config = Config::new(shell, cwd, homedir);
77+
let mut config = Config::new(shell, cwd, homedir)?;
7878
config.set_env(self.env.clone());
7979
config.configure(
8080
0,

tests/testsuite/login.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ fn new_credentials_is_used_instead_old() {
138138
.arg(TOKEN)
139139
.run();
140140

141-
let mut config = Config::new(Shell::new(), cargo_home(), cargo_home());
141+
let mut config = Config::new(Shell::new(), cargo_home(), cargo_home()).unwrap();
142142
let _ = config.values();
143143
let _ = config.load_credentials();
144144

tests/testsuite/member_errors.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,8 @@ fn member_manifest_version_error() {
149149
Shell::from_write(Box::new(Vec::new())),
150150
cargo_home(),
151151
cargo_home(),
152-
);
152+
)
153+
.unwrap();
153154
let ws = Workspace::new(&p.root().join("Cargo.toml"), &config).unwrap();
154155
let compile_options = CompileOptions::new(&config, CompileMode::Build).unwrap();
155156
let member_bar = ws.members().find(|m| &*m.name() == "bar").unwrap();

tests/testsuite/search.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ fn not_update() {
113113
Shell::from_write(Box::new(Vec::new())),
114114
paths::root(),
115115
paths::home().join(".cargo"),
116-
);
116+
)
117+
.unwrap();
117118
let lock = cfg.acquire_package_cache_lock().unwrap();
118119
let mut regsrc = RegistrySource::remote(sid, &HashSet::new(), &cfg);
119120
regsrc.update().unwrap();

0 commit comments

Comments
 (0)