@@ -20,30 +20,23 @@ use cargo::util::style;
20
20
21
21
fn closest_valid_root < ' a > (
22
22
cwd : & std:: path:: Path ,
23
- config_root : Option < & ' a std:: path:: Path > ,
24
- env_root : Option < & ' a std:: path:: Path > ,
25
- cli_root : Option < & ' a std:: path:: Path > ,
23
+ roots : & [ & ' a std:: path:: Path ] ,
26
24
) -> anyhow:: Result < Option < & ' a std:: path:: Path > > {
27
- for ( name, root) in [
28
- ( ".cargo/root" , config_root) ,
29
- ( "CARGO_ROOT" , env_root) ,
30
- ( "--root" , cli_root) ,
31
- ] {
32
- if let Some ( root) = root {
33
- if !cwd. starts_with ( root) {
34
- return Err ( anyhow:: format_err!(
35
- "the {} `{}` is not a parent of the current working directory `{}`" ,
36
- name,
37
- root. display( ) ,
38
- cwd. display( )
39
- ) ) ;
40
- }
41
- }
42
- }
43
- Ok ( [ config_root, env_root, cli_root]
44
- . into_iter ( )
45
- . flatten ( )
46
- . max_by_key ( |root| root. components ( ) . count ( ) ) )
25
+ let cwd = cwd
26
+ . canonicalize ( )
27
+ . context ( "could not canonicalize current working directory" ) ?;
28
+
29
+ // Assumes that all roots are canonicalized.
30
+ let ancestor_roots =
31
+ roots
32
+ . iter ( )
33
+ . filter_map ( |r| if cwd. starts_with ( r) { Some ( * r) } else { None } ) ;
34
+
35
+ Ok ( ancestor_roots. into_iter ( ) . max_by_key ( |root| {
36
+ // Prefer the root that is closest to the current working directory.
37
+ // This is done by counting the number of components in the path.
38
+ root. components ( ) . count ( )
39
+ } ) )
47
40
}
48
41
49
42
#[ tracing:: instrument( skip_all) ]
@@ -54,7 +47,6 @@ pub fn main(gctx: &mut GlobalContext) -> CliResult {
54
47
55
48
let args = cli ( gctx) . try_get_matches ( ) ?;
56
49
57
- let mut need_reload = false ;
58
50
// Update the process-level notion of cwd
59
51
if let Some ( new_cwd) = args. get_one :: < std:: path:: PathBuf > ( "directory" ) {
60
52
// This is a temporary hack.
@@ -76,47 +68,77 @@ pub fn main(gctx: &mut GlobalContext) -> CliResult {
76
68
. into ( ) ) ;
77
69
}
78
70
std:: env:: set_current_dir ( & new_cwd) . context ( "could not change to requested directory" ) ?;
79
- need_reload = true ;
80
71
}
81
72
82
- // A root directory can be specified via CARGO_ROOT, --root or the existence of a `.cargo/root` file.
83
- // If more than one is specified, the effective root is the one closest to the current working directory.
73
+ // A root directories can be specified via CARGO_ROOTS, --root or the existence of a `.cargo/root` files.
74
+ // If more than one root is specified, the effective root is the one closest to the current working directory.
75
+ // If CARGO_ROOTS is not set, the user's home directory is used as a default root.
84
76
85
77
let cwd = std:: env:: current_dir ( ) . context ( "could not get current working directory" ) ?;
86
78
// Windows UNC paths are OK here
87
79
let cwd = cwd
88
80
. canonicalize ( )
89
81
. context ( "could not canonicalize current working directory" ) ?;
90
- let config_root = paths:: ancestors ( & cwd, gctx. search_stop_path ( ) )
82
+
83
+ // XXX: before looking for `.cargo/root` should we first try and resolve roots
84
+ // from `CARGO_ROOTS` + --root so we can avoid triggering automounter issues?
85
+ let root_marker = paths:: ancestors ( & cwd, gctx. search_stop_path ( ) )
91
86
. find ( |current| current. join ( ".cargo" ) . join ( "root" ) . exists ( ) ) ;
92
- let env_root = gctx
93
- . get_env_os ( "CARGO_ROOT" )
94
- . map ( std:: path:: PathBuf :: from)
95
- . map ( |p| {
96
- p. canonicalize ( )
97
- . context ( "could not canonicalize CARGO_ROOT" )
98
- } )
99
- . transpose ( ) ?;
100
- let env_root = env_root. as_deref ( ) ;
101
-
102
- let cli_root = args
103
- . get_one :: < std:: path:: PathBuf > ( "root" )
104
- . map ( |p| {
105
- p. canonicalize ( )
106
- . context ( "could not canonicalize requested root directory" )
107
- } )
108
- . transpose ( ) ?;
109
- let cli_root = cli_root. as_deref ( ) ;
110
87
111
- if let Some ( root) = closest_valid_root ( & cwd, config_root, env_root, cli_root) ? {
88
+ let mut roots: Vec < std:: path:: PathBuf > = Vec :: new ( ) ;
89
+
90
+ if let Some ( root_marker) = root_marker {
91
+ let pb = root_marker
92
+ . canonicalize ( )
93
+ . context ( "could not canonicalize .cargo/root" ) ?;
94
+ roots. push ( pb) ;
95
+ }
96
+
97
+ if let Some ( paths_os) = gctx. get_env_os ( "CARGO_ROOTS" ) {
98
+ for path in std:: env:: split_paths ( & paths_os) {
99
+ let pb = path. canonicalize ( ) . context ( format ! (
100
+ "could not canonicalize CARGO_ROOTS entry `{}`" ,
101
+ path. display( )
102
+ ) ) ?;
103
+ roots. push ( pb) ;
104
+ }
105
+ } else if let Some ( home) = std:: env:: home_dir ( ) {
106
+ // To be safe by default, and not attempt to read config files outside of the
107
+ // user's home directory, we implicitly add the home directory as a root.
108
+ // Ref: https://github.com/rust-lang/rfcs/pull/3279
109
+ let home = home
110
+ . canonicalize ( )
111
+ . context ( "could not canonicalize home directory" ) ?;
112
+ tracing:: debug!(
113
+ "implicitly adding home directory as root: {}" ,
114
+ home. display( )
115
+ ) ;
116
+ roots. push ( home) ;
117
+ }
118
+
119
+ if let Some ( cli_roots) = args. get_many :: < std:: path:: PathBuf > ( "root" ) {
120
+ for cli_root in cli_roots {
121
+ let pb = cli_root
122
+ . canonicalize ( )
123
+ . context ( "could not canonicalize requested root directory" ) ?;
124
+ roots. push ( pb) ;
125
+ }
126
+ }
127
+
128
+ let roots: Vec < _ > = roots. iter ( ) . map ( |p| p. as_path ( ) ) . collect ( ) ;
129
+
130
+ if let Some ( root) = closest_valid_root ( & cwd, & roots) ? {
112
131
tracing:: debug!( "root directory: {}" , root. display( ) ) ;
113
132
gctx. set_search_stop_path ( root) ;
114
- need_reload = true ;
133
+ } else {
134
+ tracing:: debug!( "root limited to cwd: {}" , cwd. display( ) ) ;
135
+ // If we are not running with _any_ root then we are conservative and don't
136
+ // allow any ancestor traversal.
137
+ gctx. set_search_stop_path ( & cwd) ;
115
138
}
116
139
117
- if need_reload {
118
- gctx. reload_cwd ( ) ?;
119
- }
140
+ // Reload now that we have established the cwd and root
141
+ gctx. reload_cwd ( ) ?;
120
142
121
143
let ( expanded_args, global_args) = expand_aliases ( gctx, args, vec ! [ ] ) ?;
122
144
@@ -714,6 +736,7 @@ See '<cyan,bold>cargo help</> <cyan><<command>></>' for more information on a sp
714
736
. help ( "Define a root that limits searching for workspaces and .cargo/ directories" )
715
737
. long ( "root" )
716
738
. value_name ( "ROOT" )
739
+ . action ( ArgAction :: Append )
717
740
. value_hint ( clap:: ValueHint :: DirPath )
718
741
. value_parser ( clap:: builder:: ValueParser :: path_buf ( ) ) ,
719
742
)
0 commit comments