Skip to content

Commit 51e0c71

Browse files
committed
Allow package.exclude patterns to match directories.
1 parent 9ed56ca commit 51e0c71

File tree

2 files changed

+60
-26
lines changed

2 files changed

+60
-26
lines changed

src/cargo/sources/path.rs

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -120,17 +120,21 @@ impl<'cfg> PathSource<'cfg> {
120120
}
121121
let ignore_include = include_builder.build()?;
122122

123-
let ignore_should_package = |relative_path: &Path| -> CargoResult<bool> {
123+
let ignore_should_package = |relative_path: &Path, is_dir: bool| -> CargoResult<bool> {
124124
// "Include" and "exclude" options are mutually exclusive.
125125
if no_include_option {
126-
match ignore_exclude
127-
.matched_path_or_any_parents(relative_path, /* is_dir */ false)
128-
{
126+
match ignore_exclude.matched_path_or_any_parents(relative_path, is_dir) {
129127
Match::None => Ok(true),
130128
Match::Ignore(_) => Ok(false),
131129
Match::Whitelist(_) => Ok(true),
132130
}
133131
} 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+
}
134138
match ignore_include
135139
.matched_path_or_any_parents(relative_path, /* is_dir */ false)
136140
{
@@ -141,7 +145,7 @@ impl<'cfg> PathSource<'cfg> {
141145
}
142146
};
143147

144-
let mut filter = |path: &Path| -> CargoResult<bool> {
148+
let mut filter = |path: &Path, is_dir: bool| -> CargoResult<bool> {
145149
let relative_path = path.strip_prefix(root)?;
146150

147151
let rel = relative_path.as_os_str();
@@ -151,7 +155,7 @@ impl<'cfg> PathSource<'cfg> {
151155
return Ok(true);
152156
}
153157

154-
ignore_should_package(relative_path)
158+
ignore_should_package(relative_path, is_dir)
155159
};
156160

157161
// Attempt Git-prepopulate only if no `include` (see rust-lang/cargo#4135).
@@ -171,7 +175,7 @@ impl<'cfg> PathSource<'cfg> {
171175
&self,
172176
pkg: &Package,
173177
root: &Path,
174-
filter: &mut dyn FnMut(&Path) -> CargoResult<bool>,
178+
filter: &mut dyn FnMut(&Path, bool) -> CargoResult<bool>,
175179
) -> Option<CargoResult<Vec<PathBuf>>> {
176180
// If this package is in a Git repository, then we really do want to
177181
// query the Git repository as it takes into account items such as
@@ -214,7 +218,7 @@ impl<'cfg> PathSource<'cfg> {
214218
&self,
215219
pkg: &Package,
216220
repo: &git2::Repository,
217-
filter: &mut dyn FnMut(&Path) -> CargoResult<bool>,
221+
filter: &mut dyn FnMut(&Path, bool) -> CargoResult<bool>,
218222
) -> CargoResult<Vec<PathBuf>> {
219223
warn!("list_files_git {}", pkg.package_id());
220224
let index = repo.index()?;
@@ -298,7 +302,10 @@ impl<'cfg> PathSource<'cfg> {
298302
continue;
299303
}
300304

301-
if is_dir.unwrap_or_else(|| file_path.is_dir()) {
305+
// `is_dir` is None for symlinks. The `unwrap` checks if the
306+
// symlink points to a directory.
307+
let is_dir = is_dir.unwrap_or_else(|| file_path.is_dir());
308+
if is_dir {
302309
warn!(" found submodule {}", file_path.display());
303310
let rel = file_path.strip_prefix(root)?;
304311
let rel = rel.to_str().ok_or_else(|| {
@@ -316,7 +323,8 @@ impl<'cfg> PathSource<'cfg> {
316323
PathSource::walk(&file_path, &mut ret, false, filter)?;
317324
}
318325
}
319-
} else if (*filter)(&file_path)? {
326+
} else if (*filter)(&file_path, is_dir)? {
327+
assert!(!is_dir);
320328
// We found a file!
321329
warn!(" found {}", file_path.display());
322330
ret.push(file_path);
@@ -347,29 +355,28 @@ impl<'cfg> PathSource<'cfg> {
347355
fn list_files_walk_except_dot_files_and_dirs(
348356
&self,
349357
pkg: &Package,
350-
filter: &mut dyn FnMut(&Path) -> CargoResult<bool>,
358+
filter: &mut dyn FnMut(&Path, bool) -> CargoResult<bool>,
351359
) -> CargoResult<Vec<PathBuf>> {
352360
let root = pkg.root();
353361
let mut exclude_dot_files_dir_builder = GitignoreBuilder::new(root);
354362
exclude_dot_files_dir_builder.add_line(None, ".*")?;
355363
let ignore_dot_files_and_dirs = exclude_dot_files_dir_builder.build()?;
356364

357-
let mut filter_ignore_dot_files_and_dirs = |path: &Path| -> CargoResult<bool> {
358-
let relative_path = path.strip_prefix(root)?;
359-
match ignore_dot_files_and_dirs
360-
.matched_path_or_any_parents(relative_path, /* is_dir */ false)
361-
{
362-
Match::Ignore(_) => Ok(false),
363-
_ => filter(path),
364-
}
365-
};
365+
let mut filter_ignore_dot_files_and_dirs =
366+
|path: &Path, is_dir: bool| -> CargoResult<bool> {
367+
let relative_path = path.strip_prefix(root)?;
368+
match ignore_dot_files_and_dirs.matched_path_or_any_parents(relative_path, is_dir) {
369+
Match::Ignore(_) => Ok(false),
370+
_ => filter(path, is_dir),
371+
}
372+
};
366373
self.list_files_walk(pkg, &mut filter_ignore_dot_files_and_dirs)
367374
}
368375

369376
fn list_files_walk(
370377
&self,
371378
pkg: &Package,
372-
filter: &mut dyn FnMut(&Path) -> CargoResult<bool>,
379+
filter: &mut dyn FnMut(&Path, bool) -> CargoResult<bool>,
373380
) -> CargoResult<Vec<PathBuf>> {
374381
let mut ret = Vec::new();
375382
PathSource::walk(pkg.root(), &mut ret, true, filter)?;
@@ -380,12 +387,14 @@ impl<'cfg> PathSource<'cfg> {
380387
path: &Path,
381388
ret: &mut Vec<PathBuf>,
382389
is_root: bool,
383-
filter: &mut dyn FnMut(&Path) -> CargoResult<bool>,
390+
filter: &mut dyn FnMut(&Path, bool) -> CargoResult<bool>,
384391
) -> CargoResult<()> {
385-
if !path.is_dir() {
386-
if (*filter)(path)? {
387-
ret.push(path.to_path_buf());
388-
}
392+
let is_dir = path.is_dir();
393+
if !is_root && !(*filter)(path, is_dir)? {
394+
return Ok(());
395+
}
396+
if !is_dir {
397+
ret.push(path.to_path_buf());
389398
return Ok(());
390399
}
391400
// Don't recurse into any sub-packages that we have.

tests/testsuite/build_script.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3977,7 +3977,9 @@ fn links_interrupted_can_restart() {
39773977
fn build_script_scan_eacces() {
39783978
// build.rs causes a scan of the whole project, which can be a problem if
39793979
// a directory is not accessible.
3980+
use cargo_test_support::git;
39803981
use std::os::unix::fs::PermissionsExt;
3982+
39813983
let p = project()
39823984
.file("src/lib.rs", "")
39833985
.file("build.rs", "fn main() {}")
@@ -4007,5 +4009,28 @@ Caused by:
40074009
)
40084010
.with_status(101)
40094011
.run();
4012+
4013+
// Try `package.exclude` to skip a directory.
4014+
p.change_file(
4015+
"Cargo.toml",
4016+
r#"
4017+
[package]
4018+
name = "foo"
4019+
version = "0.0.1"
4020+
exclude = ["secrets"]
4021+
"#,
4022+
);
4023+
p.cargo("build").run();
4024+
4025+
// Try with git. This succeeds because the git status walker ignores
4026+
// directories it can't access.
4027+
p.change_file("Cargo.toml", &basic_manifest("foo", "0.0.1"));
4028+
p.build_dir().rm_rf();
4029+
let repo = git::init(&p.root());
4030+
git::add(&repo);
4031+
git::commit(&repo);
4032+
p.cargo("build").run();
4033+
4034+
// Restore permissions so that the directory can be deleted.
40104035
fs::set_permissions(&path, fs::Permissions::from_mode(0o755)).unwrap();
40114036
}

0 commit comments

Comments
 (0)