Skip to content

Commit b2a0448

Browse files
committed
Make Cargo XDG-compliant (on non-Windows systems)
http://standards.freedesktop.org/basedir-spec/basedir-spec-0.8.html http://www.freedesktop.org/software/systemd/man/file-hierarchy.html Strategy for backward-compatiblity: When checking for the relevant cargo directories, check in the following order of preference: 1) Use the environment variable `CARGO_HOME`. 2) Use the XDG directories if they exist. 3) Use the legacy location (~/.cargo) if it exists. 4) Fall back the XDG directories if everything else fails. Fixes rust-lang#1734.
1 parent b01770c commit b2a0448

File tree

5 files changed

+111
-28
lines changed

5 files changed

+111
-28
lines changed

Cargo.lock

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ time = "0.1"
4242
toml = "0.1"
4343
url = "0.2"
4444
winapi = "0.2"
45+
xdg = "2.0"
4546

4647
[dev-dependencies]
4748
tempdir = "0.3"

src/cargo/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ extern crate term;
2222
extern crate time;
2323
extern crate toml;
2424
extern crate url;
25+
extern crate xdg;
2526

2627
use std::env;
2728
use std::error::Error;

src/cargo/ops/cargo_install.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,6 @@ fn resolve_root(flag: Option<&str>, config: &Config) -> CargoResult<PathBuf> {
333333
}).or_else(|| {
334334
config_root.clone().map(|(v, _)| PathBuf::from(v))
335335
}).unwrap_or_else(|| {
336-
config.home().to_owned()
336+
config.bin_path().to_owned()
337337
}))
338338
}

src/cargo/util/config.rs

+102-27
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use std::ffi::OsString;
66
use std::fmt;
77
use std::fs::{self, File};
88
use std::io::prelude::*;
9+
use std::iter;
910
use std::mem;
1011
use std::path::{Path, PathBuf};
1112

@@ -18,8 +19,15 @@ use util::toml as cargo_toml;
1819

1920
use self::ConfigValue as CV;
2021

22+
struct Paths {
23+
pub bin: PathBuf,
24+
pub cache: PathBuf,
25+
pub config: PathBuf,
26+
pub additional_configs: Vec<PathBuf>,
27+
}
28+
2129
pub struct Config {
22-
home_path: PathBuf,
30+
paths: Paths,
2331
shell: RefCell<MultiShell>,
2432
rustc_info: Rustc,
2533
values: RefCell<HashMap<String, ConfigValue>>,
@@ -37,9 +45,8 @@ impl Config {
3745
}));
3846

3947
let mut cfg = Config {
40-
home_path: try!(homedir(cwd.as_path()).chain_error(|| {
41-
human("Cargo couldn't find your home directory. \
42-
This probably means that $HOME was not set.")
48+
paths: try!(determine_paths(&cwd).chain_error(|| {
49+
human("Cargo couldn't find your home directory.")
4350
})),
4451
shell: RefCell::new(shell),
4552
rustc_info: Rustc::blank(),
@@ -58,26 +65,28 @@ impl Config {
5865
Ok(cfg)
5966
}
6067

61-
pub fn home(&self) -> &Path { &self.home_path }
68+
pub fn bin_path(&self) -> PathBuf {
69+
self.paths.bin.clone()
70+
}
6271

6372
pub fn git_db_path(&self) -> PathBuf {
64-
self.home_path.join("git").join("db")
73+
self.paths.cache.join("git").join("db")
6574
}
6675

6776
pub fn git_checkout_path(&self) -> PathBuf {
68-
self.home_path.join("git").join("checkouts")
77+
self.paths.cache.join("git").join("checkouts")
6978
}
7079

7180
pub fn registry_index_path(&self) -> PathBuf {
72-
self.home_path.join("registry").join("index")
81+
self.paths.cache.join("registry").join("index")
7382
}
7483

7584
pub fn registry_cache_path(&self) -> PathBuf {
76-
self.home_path.join("registry").join("cache")
85+
self.paths.cache.join("registry").join("cache")
7786
}
7887

7988
pub fn registry_source_path(&self) -> PathBuf {
80-
self.home_path.join("registry").join("src")
89+
self.paths.cache.join("registry").join("src")
8190
}
8291

8392
pub fn shell(&self) -> RefMut<MultiShell> {
@@ -200,7 +209,7 @@ impl Config {
200209
fn load_values(&self) -> CargoResult<()> {
201210
let mut cfg = CV::Table(HashMap::new(), PathBuf::from("."));
202211

203-
try!(walk_tree(&self.cwd, |mut file, path| {
212+
try!(walk_tree(self, |mut file, path| {
204213
let mut contents = String::new();
205214
try!(file.read_to_string(&mut contents));
206215
let table = try!(cargo_toml::parse(&contents, &path).chain_error(|| {
@@ -457,18 +466,87 @@ impl ConfigValue {
457466
}
458467
}
459468

460-
fn homedir(cwd: &Path) -> Option<PathBuf> {
469+
#[cfg(windows)]
470+
fn determine_paths(cwd: &Path) -> Option<Paths> {
461471
let cargo_home = env::var_os("CARGO_HOME").map(|home| {
462472
cwd.join(home)
463473
});
464-
let user_home = env::home_dir().map(|p| p.join(".cargo"));
465-
return cargo_home.or(user_home);
474+
let default = env::home_dir().map(|p| p.join(".cargo"));
475+
476+
cargo_home.or(default).map(|p| Paths {
477+
bin: p.clone(),
478+
cache: p.clone(),
479+
config: p.clone(),
480+
additional_configs: vec![],
481+
})
482+
}
483+
484+
#[cfg(unix)]
485+
fn determine_paths(cwd: &Path) -> Option<Paths> {
486+
use xdg;
487+
fn path_exists(path: PathBuf) -> Option<PathBuf> {
488+
fs::metadata(&path).ok().map(|_| path)
489+
}
490+
491+
let user_home = if let Some(p) = env::home_dir() { p } else { return None; };
492+
493+
let home_var = env::var_os("CARGO_HOME").map(|home| cwd.join(home));
494+
let xdg = xdg::BaseDirectories::with_prefix("cargo");
495+
let legacy = user_home.join(".cargo");
496+
497+
let bin_xdgish = user_home.join(".local").join("bin");
498+
let cache_xdg = xdg.get_cache_home();
499+
let config_xdg = xdg.get_config_home();
500+
let additional_configs_xdg = xdg.get_config_dirs();
501+
502+
let mut bin: Option<PathBuf>;
503+
let mut cache: Option<PathBuf>;
504+
let mut config: Option<PathBuf>;
505+
let additional_configs: Option<Vec<PathBuf>>;
506+
507+
// Strategy to determine where to put files:
508+
//
509+
// 1) Use the environment variable CARGO_HOME if it exists.
510+
// 2) Use the XDG specification if it exists.
511+
// 3) Use the legacy location (~/.cargo) if it exists.
512+
// 4) Fall back to the XDG specification if all of the above things fail.
513+
514+
// 1)
515+
bin = home_var.clone();
516+
cache = home_var.clone();
517+
config = home_var.clone();
518+
additional_configs = home_var.map(|_| vec![]);
519+
520+
// 2)
521+
bin = bin.or_else(|| path_exists(bin_xdgish.clone()));
522+
cache = cache.or_else(|| path_exists(cache_xdg.clone()));
523+
config = config.or_else(|| path_exists(config_xdg.clone()));
524+
let additional_configs = additional_configs.unwrap_or(additional_configs_xdg);
525+
526+
// 3)
527+
if let Some(l) = path_exists(legacy) {
528+
cache = cache.or_else(|| Some(l.clone()));
529+
bin = bin.or_else(|| Some(l.clone()));
530+
config = config.or_else(|| Some(l));
531+
}
532+
533+
// 4)
534+
let bin = bin.unwrap_or(bin_xdgish);
535+
let cache = cache.unwrap_or(cache_xdg);
536+
let config = config.unwrap_or(config_xdg);
537+
538+
Some(Paths {
539+
bin: bin,
540+
cache: cache,
541+
config: config,
542+
additional_configs: additional_configs,
543+
})
466544
}
467545

468-
fn walk_tree<F>(pwd: &Path, mut walk: F) -> CargoResult<()>
546+
fn walk_tree<F>(config: &Config, mut walk: F) -> CargoResult<()>
469547
where F: FnMut(File, &Path) -> CargoResult<()>
470548
{
471-
let mut current = pwd;
549+
let mut current: &Path = &config.cwd;
472550

473551
loop {
474552
let possible = current.join(".cargo").join("config");
@@ -486,18 +564,15 @@ fn walk_tree<F>(pwd: &Path, mut walk: F) -> CargoResult<()>
486564
// Once we're done, also be sure to walk the home directory even if it's not
487565
// in our history to be sure we pick up that standard location for
488566
// information.
489-
let home = try!(homedir(pwd).chain_error(|| {
490-
human("Cargo couldn't find your home directory. \
491-
This probably means that $HOME was not set.")
492-
}));
493-
if !pwd.starts_with(&home) {
494-
let config = home.join("config");
495-
if fs::metadata(&config).is_ok() {
496-
let file = try!(File::open(&config));
497-
try!(walk(file, &config));
567+
for confdir in iter::once(&config.paths.config).chain(&config.paths.additional_configs) {
568+
if !config.cwd.starts_with(&confdir) {
569+
let config = confdir.join("config");
570+
if fs::metadata(&config).is_ok() {
571+
let file = try!(File::open(&config));
572+
try!(walk(file, &config));
573+
}
498574
}
499575
}
500-
501576
Ok(())
502577
}
503578

@@ -509,7 +584,7 @@ pub fn set_config(cfg: &Config, loc: Location, key: &str,
509584
// 2. This blows away all comments in a file
510585
// 3. This blows away the previous ordering of a file.
511586
let file = match loc {
512-
Location::Global => cfg.home_path.join("config"),
587+
Location::Global => cfg.paths.config.join("config"),
513588
Location::Project => unimplemented!(),
514589
};
515590
try!(fs::create_dir_all(file.parent().unwrap()));

0 commit comments

Comments
 (0)