@@ -96,6 +96,15 @@ impl<'cfg> PathSource<'cfg> {
96
96
/// are relevant for building this package, but it also contains logic to
97
97
/// use other methods like .gitignore to filter the list of files.
98
98
pub fn list_files ( & self , pkg : & Package ) -> CargoResult < Vec < PathBuf > > {
99
+ self . _list_files ( pkg) . chain_err ( || {
100
+ format ! (
101
+ "failed to determine list of files in {}" ,
102
+ pkg. root( ) . display( )
103
+ )
104
+ } )
105
+ }
106
+
107
+ fn _list_files ( & self , pkg : & Package ) -> CargoResult < Vec < PathBuf > > {
99
108
let root = pkg. root ( ) ;
100
109
let no_include_option = pkg. manifest ( ) . include ( ) . is_empty ( ) ;
101
110
@@ -111,17 +120,21 @@ impl<'cfg> PathSource<'cfg> {
111
120
}
112
121
let ignore_include = include_builder. build ( ) ?;
113
122
114
- let ignore_should_package = |relative_path : & Path | -> CargoResult < bool > {
123
+ let ignore_should_package = |relative_path : & Path , is_dir : bool | -> CargoResult < bool > {
115
124
// "Include" and "exclude" options are mutually exclusive.
116
125
if no_include_option {
117
- match ignore_exclude
118
- . matched_path_or_any_parents ( relative_path, /* is_dir */ false )
119
- {
126
+ match ignore_exclude. matched_path_or_any_parents ( relative_path, is_dir) {
120
127
Match :: None => Ok ( true ) ,
121
128
Match :: Ignore ( _) => Ok ( false ) ,
122
129
Match :: Whitelist ( _) => Ok ( true ) ,
123
130
}
124
131
} else {
132
+ if is_dir {
133
+ // Generally, include directives don't list every
134
+ // directory (nor should they!). Just skip all directory
135
+ // checks, and only check files.
136
+ return Ok ( true ) ;
137
+ }
125
138
match ignore_include
126
139
. matched_path_or_any_parents ( relative_path, /* is_dir */ false )
127
140
{
@@ -132,7 +145,7 @@ impl<'cfg> PathSource<'cfg> {
132
145
}
133
146
} ;
134
147
135
- let mut filter = |path : & Path | -> CargoResult < bool > {
148
+ let mut filter = |path : & Path , is_dir : bool | -> CargoResult < bool > {
136
149
let relative_path = path. strip_prefix ( root) ?;
137
150
138
151
let rel = relative_path. as_os_str ( ) ;
@@ -142,13 +155,13 @@ impl<'cfg> PathSource<'cfg> {
142
155
return Ok ( true ) ;
143
156
}
144
157
145
- ignore_should_package ( relative_path)
158
+ ignore_should_package ( relative_path, is_dir )
146
159
} ;
147
160
148
161
// Attempt Git-prepopulate only if no `include` (see rust-lang/cargo#4135).
149
162
if no_include_option {
150
- if let Some ( result) = self . discover_git_and_list_files ( pkg, root, & mut filter) {
151
- return result;
163
+ if let Some ( result) = self . discover_git_and_list_files ( pkg, root, & mut filter) ? {
164
+ return Ok ( result) ;
152
165
}
153
166
// no include option and not git repo discovered (see rust-lang/cargo#7183).
154
167
return self . list_files_walk_except_dot_files_and_dirs ( pkg, & mut filter) ;
@@ -162,50 +175,48 @@ impl<'cfg> PathSource<'cfg> {
162
175
& self ,
163
176
pkg : & Package ,
164
177
root : & Path ,
165
- filter : & mut dyn FnMut ( & Path ) -> CargoResult < bool > ,
166
- ) -> Option < CargoResult < Vec < PathBuf > > > {
167
- // If this package is in a Git repository, then we really do want to
168
- // query the Git repository as it takes into account items such as
169
- // `.gitignore`. We're not quite sure where the Git repository is,
170
- // however, so we do a bit of a probe.
171
- //
172
- // We walk this package's path upwards and look for a sibling
173
- // `Cargo.toml` and `.git` directory. If we find one then we assume that
174
- // we're part of that repository.
175
- let mut cur = root;
176
- loop {
177
- if cur. join ( "Cargo.toml" ) . is_file ( ) {
178
- // If we find a Git repository next to this `Cargo.toml`, we still
179
- // check to see if we are indeed part of the index. If not, then
180
- // this is likely an unrelated Git repo, so keep going.
181
- if let Ok ( repo) = git2:: Repository :: open ( cur) {
182
- let index = match repo. index ( ) {
183
- Ok ( index) => index,
184
- Err ( err) => return Some ( Err ( err. into ( ) ) ) ,
185
- } ;
186
- let path = root. strip_prefix ( cur) . unwrap ( ) . join ( "Cargo.toml" ) ;
187
- if index. get_path ( & path, 0 ) . is_some ( ) {
188
- return Some ( self . list_files_git ( pkg, & repo, filter) ) ;
189
- }
190
- }
191
- }
192
- // Don't cross submodule boundaries.
193
- if cur. join ( ".git" ) . is_dir ( ) {
194
- break ;
195
- }
196
- match cur. parent ( ) {
197
- Some ( parent) => cur = parent,
198
- None => break ,
178
+ filter : & mut dyn FnMut ( & Path , bool ) -> CargoResult < bool > ,
179
+ ) -> CargoResult < Option < Vec < PathBuf > > > {
180
+ let repo = match git2:: Repository :: discover ( root) {
181
+ Ok ( repo) => repo,
182
+ Err ( e) => {
183
+ log:: debug!(
184
+ "could not discover git repo at or above {}: {}" ,
185
+ root. display( ) ,
186
+ e
187
+ ) ;
188
+ return Ok ( None ) ;
199
189
}
190
+ } ;
191
+ let index = repo
192
+ . index ( )
193
+ . chain_err ( || format ! ( "failed to open git index at {}" , repo. path( ) . display( ) ) ) ?;
194
+ let repo_root = repo. workdir ( ) . ok_or_else ( || {
195
+ anyhow:: format_err!(
196
+ "did not expect repo at {} to be bare" ,
197
+ repo. path( ) . display( )
198
+ )
199
+ } ) ?;
200
+ let repo_relative_path = root. strip_prefix ( repo_root) . chain_err ( || {
201
+ format ! (
202
+ "expected git repo {} to be parent of package {}" ,
203
+ repo. path( ) . display( ) ,
204
+ root. display( )
205
+ )
206
+ } ) ?;
207
+ let manifest_path = repo_relative_path. join ( "Cargo.toml" ) ;
208
+ if index. get_path ( & manifest_path, 0 ) . is_some ( ) {
209
+ return Ok ( Some ( self . list_files_git ( pkg, & repo, filter) ?) ) ;
200
210
}
201
- None
211
+ // Package Cargo.toml is not in git, don't use git to guide our selection.
212
+ Ok ( None )
202
213
}
203
214
204
215
fn list_files_git (
205
216
& self ,
206
217
pkg : & Package ,
207
218
repo : & git2:: Repository ,
208
- filter : & mut dyn FnMut ( & Path ) -> CargoResult < bool > ,
219
+ filter : & mut dyn FnMut ( & Path , bool ) -> CargoResult < bool > ,
209
220
) -> CargoResult < Vec < PathBuf > > {
210
221
warn ! ( "list_files_git {}" , pkg. package_id( ) ) ;
211
222
let index = repo. index ( ) ?;
@@ -289,7 +300,10 @@ impl<'cfg> PathSource<'cfg> {
289
300
continue ;
290
301
}
291
302
292
- if is_dir. unwrap_or_else ( || file_path. is_dir ( ) ) {
303
+ // `is_dir` is None for symlinks. The `unwrap` checks if the
304
+ // symlink points to a directory.
305
+ let is_dir = is_dir. unwrap_or_else ( || file_path. is_dir ( ) ) ;
306
+ if is_dir {
293
307
warn ! ( " found submodule {}" , file_path. display( ) ) ;
294
308
let rel = file_path. strip_prefix ( root) ?;
295
309
let rel = rel. to_str ( ) . ok_or_else ( || {
@@ -307,7 +321,8 @@ impl<'cfg> PathSource<'cfg> {
307
321
PathSource :: walk ( & file_path, & mut ret, false , filter) ?;
308
322
}
309
323
}
310
- } else if ( * filter) ( & file_path) ? {
324
+ } else if ( * filter) ( & file_path, is_dir) ? {
325
+ assert ! ( !is_dir) ;
311
326
// We found a file!
312
327
warn ! ( " found {}" , file_path. display( ) ) ;
313
328
ret. push ( file_path) ;
@@ -338,29 +353,28 @@ impl<'cfg> PathSource<'cfg> {
338
353
fn list_files_walk_except_dot_files_and_dirs (
339
354
& self ,
340
355
pkg : & Package ,
341
- filter : & mut dyn FnMut ( & Path ) -> CargoResult < bool > ,
356
+ filter : & mut dyn FnMut ( & Path , bool ) -> CargoResult < bool > ,
342
357
) -> CargoResult < Vec < PathBuf > > {
343
358
let root = pkg. root ( ) ;
344
359
let mut exclude_dot_files_dir_builder = GitignoreBuilder :: new ( root) ;
345
360
exclude_dot_files_dir_builder. add_line ( None , ".*" ) ?;
346
361
let ignore_dot_files_and_dirs = exclude_dot_files_dir_builder. build ( ) ?;
347
362
348
- let mut filter_ignore_dot_files_and_dirs = |path : & Path | -> CargoResult < bool > {
349
- let relative_path = path. strip_prefix ( root) ?;
350
- match ignore_dot_files_and_dirs
351
- . matched_path_or_any_parents ( relative_path, /* is_dir */ false )
352
- {
353
- Match :: Ignore ( _) => Ok ( false ) ,
354
- _ => filter ( path) ,
355
- }
356
- } ;
363
+ let mut filter_ignore_dot_files_and_dirs =
364
+ |path : & Path , is_dir : bool | -> CargoResult < bool > {
365
+ let relative_path = path. strip_prefix ( root) ?;
366
+ match ignore_dot_files_and_dirs. matched_path_or_any_parents ( relative_path, is_dir) {
367
+ Match :: Ignore ( _) => Ok ( false ) ,
368
+ _ => filter ( path, is_dir) ,
369
+ }
370
+ } ;
357
371
self . list_files_walk ( pkg, & mut filter_ignore_dot_files_and_dirs)
358
372
}
359
373
360
374
fn list_files_walk (
361
375
& self ,
362
376
pkg : & Package ,
363
- filter : & mut dyn FnMut ( & Path ) -> CargoResult < bool > ,
377
+ filter : & mut dyn FnMut ( & Path , bool ) -> CargoResult < bool > ,
364
378
) -> CargoResult < Vec < PathBuf > > {
365
379
let mut ret = Vec :: new ( ) ;
366
380
PathSource :: walk ( pkg. root ( ) , & mut ret, true , filter) ?;
@@ -371,12 +385,14 @@ impl<'cfg> PathSource<'cfg> {
371
385
path : & Path ,
372
386
ret : & mut Vec < PathBuf > ,
373
387
is_root : bool ,
374
- filter : & mut dyn FnMut ( & Path ) -> CargoResult < bool > ,
388
+ filter : & mut dyn FnMut ( & Path , bool ) -> CargoResult < bool > ,
375
389
) -> CargoResult < ( ) > {
376
- if !path. is_dir ( ) {
377
- if ( * filter) ( path) ? {
378
- ret. push ( path. to_path_buf ( ) ) ;
379
- }
390
+ let is_dir = path. is_dir ( ) ;
391
+ if !is_root && !( * filter) ( path, is_dir) ? {
392
+ return Ok ( ( ) ) ;
393
+ }
394
+ if !is_dir {
395
+ ret. push ( path. to_path_buf ( ) ) ;
380
396
return Ok ( ( ) ) ;
381
397
}
382
398
// Don't recurse into any sub-packages that we have.
@@ -415,7 +431,12 @@ impl<'cfg> PathSource<'cfg> {
415
431
416
432
let mut max = FileTime :: zero ( ) ;
417
433
let mut max_path = PathBuf :: new ( ) ;
418
- for file in self . list_files ( pkg) ? {
434
+ for file in self . list_files ( pkg) . chain_err ( || {
435
+ format ! (
436
+ "failed to determine the most recently modified file in {}" ,
437
+ pkg. root( ) . display( )
438
+ )
439
+ } ) ? {
419
440
// An `fs::stat` error here is either because path is a
420
441
// broken symlink, a permissions error, or a race
421
442
// condition where this path was `rm`-ed -- either way,
0 commit comments