@@ -6,6 +6,7 @@ use std::ffi::OsString;
6
6
use std:: fmt;
7
7
use std:: fs:: { self , File } ;
8
8
use std:: io:: prelude:: * ;
9
+ use std:: iter;
9
10
use std:: mem;
10
11
use std:: path:: { Path , PathBuf } ;
11
12
@@ -18,8 +19,15 @@ use util::toml as cargo_toml;
18
19
19
20
use self :: ConfigValue as CV ;
20
21
22
+ struct Paths {
23
+ pub bin : PathBuf ,
24
+ pub cache : PathBuf ,
25
+ pub config : PathBuf ,
26
+ pub additional_configs : Vec < PathBuf > ,
27
+ }
28
+
21
29
pub struct Config {
22
- home_path : PathBuf ,
30
+ paths : Paths ,
23
31
shell : RefCell < MultiShell > ,
24
32
rustc_info : Rustc ,
25
33
values : RefCell < HashMap < String , ConfigValue > > ,
@@ -37,9 +45,8 @@ impl Config {
37
45
} ) ) ;
38
46
39
47
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." )
43
50
} ) ) ,
44
51
shell : RefCell :: new ( shell) ,
45
52
rustc_info : Rustc :: blank ( ) ,
@@ -58,26 +65,28 @@ impl Config {
58
65
Ok ( cfg)
59
66
}
60
67
61
- pub fn home ( & self ) -> & Path { & self . home_path }
68
+ pub fn bin_path ( & self ) -> PathBuf {
69
+ self . paths . bin . clone ( )
70
+ }
62
71
63
72
pub fn git_db_path ( & self ) -> PathBuf {
64
- self . home_path . join ( "git" ) . join ( "db" )
73
+ self . paths . cache . join ( "git" ) . join ( "db" )
65
74
}
66
75
67
76
pub fn git_checkout_path ( & self ) -> PathBuf {
68
- self . home_path . join ( "git" ) . join ( "checkouts" )
77
+ self . paths . cache . join ( "git" ) . join ( "checkouts" )
69
78
}
70
79
71
80
pub fn registry_index_path ( & self ) -> PathBuf {
72
- self . home_path . join ( "registry" ) . join ( "index" )
81
+ self . paths . cache . join ( "registry" ) . join ( "index" )
73
82
}
74
83
75
84
pub fn registry_cache_path ( & self ) -> PathBuf {
76
- self . home_path . join ( "registry" ) . join ( "cache" )
85
+ self . paths . cache . join ( "registry" ) . join ( "cache" )
77
86
}
78
87
79
88
pub fn registry_source_path ( & self ) -> PathBuf {
80
- self . home_path . join ( "registry" ) . join ( "src" )
89
+ self . paths . cache . join ( "registry" ) . join ( "src" )
81
90
}
82
91
83
92
pub fn shell ( & self ) -> RefMut < MultiShell > {
@@ -200,7 +209,7 @@ impl Config {
200
209
fn load_values ( & self ) -> CargoResult < ( ) > {
201
210
let mut cfg = CV :: Table ( HashMap :: new ( ) , PathBuf :: from ( "." ) ) ;
202
211
203
- try!( walk_tree ( & self . cwd , |mut file, path| {
212
+ try!( walk_tree ( self , |mut file, path| {
204
213
let mut contents = String :: new ( ) ;
205
214
try!( file. read_to_string ( & mut contents) ) ;
206
215
let table = try!( cargo_toml:: parse ( & contents, & path) . chain_error ( || {
@@ -457,18 +466,87 @@ impl ConfigValue {
457
466
}
458
467
}
459
468
460
- fn homedir ( cwd : & Path ) -> Option < PathBuf > {
469
+ #[ cfg( windows) ]
470
+ fn determine_paths ( cwd : & Path ) -> Option < Paths > {
461
471
let cargo_home = env:: var_os ( "CARGO_HOME" ) . map ( |home| {
462
472
cwd. join ( home)
463
473
} ) ;
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
+ } )
466
544
}
467
545
468
- fn walk_tree < F > ( pwd : & Path , mut walk : F ) -> CargoResult < ( ) >
546
+ fn walk_tree < F > ( config : & Config , mut walk : F ) -> CargoResult < ( ) >
469
547
where F : FnMut ( File , & Path ) -> CargoResult < ( ) >
470
548
{
471
- let mut current = pwd ;
549
+ let mut current: & Path = & config . cwd ;
472
550
473
551
loop {
474
552
let possible = current. join ( ".cargo" ) . join ( "config" ) ;
@@ -486,18 +564,15 @@ fn walk_tree<F>(pwd: &Path, mut walk: F) -> CargoResult<()>
486
564
// Once we're done, also be sure to walk the home directory even if it's not
487
565
// in our history to be sure we pick up that standard location for
488
566
// 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
+ }
498
574
}
499
575
}
500
-
501
576
Ok ( ( ) )
502
577
}
503
578
@@ -509,7 +584,7 @@ pub fn set_config(cfg: &Config, loc: Location, key: &str,
509
584
// 2. This blows away all comments in a file
510
585
// 3. This blows away the previous ordering of a file.
511
586
let file = match loc {
512
- Location :: Global => cfg. home_path . join ( "config" ) ,
587
+ Location :: Global => cfg. paths . config . join ( "config" ) ,
513
588
Location :: Project => unimplemented ! ( ) ,
514
589
} ;
515
590
try!( fs:: create_dir_all ( file. parent ( ) . unwrap ( ) ) ) ;
0 commit comments