@@ -6,6 +6,7 @@ use std::ffi::OsString;
66use std:: fmt;
77use std:: fs:: { self , File } ;
88use std:: io:: prelude:: * ;
9+ use std:: iter;
910use std:: mem;
1011use std:: path:: { Path , PathBuf } ;
1112
@@ -18,8 +19,15 @@ use util::toml as cargo_toml;
1819
1920use 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+
2129pub 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