From 454cbd19f60f57f15d7c5ac561770b1577365bae Mon Sep 17 00:00:00 2001 From: Behnam Esfahbod Date: Fri, 7 Jul 2017 12:52:03 -0600 Subject: [PATCH] [path] Also match parent dirs in include/exclude NOTE: This is a major change in pattern matching for `include` and `exclude` fields, and can result in additional inclusion/exclusion for some patterns. Previously, for inclusion/exclusion matters, Cargo only works with paths of files in a package/repository, and glob pattern matching has been applying only to these file paths. The old behavior results in some unexpected behavior. For example, having: ```toml exclude = ["data"] ``` in a manifest next to a `data` directory, it will not exclude the directory. To make it work, a pattern must be provided that matches the *files* under this directory, like: ```toml exclude = ["data/*"] ``` To make Cargo's inclusion/exclusion behavior more intutional, and bring it on par with similar systems, like `gitignore`, we need to also match these patterns with the *directories*. The directories are seen internally as *parents* of the files. Therefore, this diff expands the pattern matching to files and their parent directories. Now, it's easier to exclude all data files: ```toml exclude = ["data"] ``` or include only the `src` directory: ```toml include = ["src"] ``` Fixes --- src/cargo/sources/path.rs | 22 +++++++++++++++++++--- tests/package.rs | 24 ++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/cargo/sources/path.rs b/src/cargo/sources/path.rs index 7a21713f49a..784520535f8 100644 --- a/src/cargo/sources/path.rs +++ b/src/cargo/sources/path.rs @@ -106,11 +106,27 @@ impl<'cfg> PathSource<'cfg> { .map(|p| parse(p)) .collect::, _>>()?; + fn matches_path_or_parents(pattern: &Pattern, target: &Path) -> bool { + if pattern.matches_path(target) { + return true; + } + let mut current = target; + while let Some(parent) = current.parent() { + if pattern.matches_path(parent) { + return true; + } + current = parent; + } + false + } + let mut filter = |p: &Path| { let relative_path = util::without_prefix(p, root).unwrap(); - include.iter().any(|p| p.matches_path(relative_path)) || { - include.is_empty() && - !exclude.iter().any(|p| p.matches_path(relative_path)) + // mutually exclusive: setting include will override exclude + if include.is_empty() { + !exclude.iter().any(|exc| matches_path_or_parents(exc, relative_path)) + } else { + include.iter().any(|inc| matches_path_or_parents(inc, relative_path)) } }; diff --git a/tests/package.rs b/tests/package.rs index d54fdc2375a..0abfc8d0ece 100644 --- a/tests/package.rs +++ b/tests/package.rs @@ -251,13 +251,23 @@ fn exclude() { name = "foo" version = "0.0.1" authors = [] - exclude = ["*.txt"] + exclude = [ + "*.txt", + "data1", + "data2/", # Does NOT match + "data3/*", + "data4/**", + ] "#) .file("src/main.rs", r#" fn main() { println!("hello"); } "#) .file("bar.txt", "") - .file("src/bar.txt", ""); + .file("src/bar.txt", "") + .file("data1/hello1.bin", "") + .file("data2/hello2.bin", "") + .file("data3/hello3.bin", "") + .file("data4/hello4.bin", ""); assert_that(p.cargo_process("package").arg("--no-verify").arg("-v"), execs().with_status(0).with_stderr("\ @@ -266,6 +276,16 @@ See http://doc.crates.io/manifest.html#package-metadata for more info. [PACKAGING] foo v0.0.1 ([..]) [ARCHIVING] [..] [ARCHIVING] [..] +[ARCHIVING] [..] +")); + + assert_that(&p.root().join("target/package/foo-0.0.1.crate"), existing_file()); + + assert_that(p.cargo("package").arg("-l"), + execs().with_status(0).with_stdout("\ +Cargo.toml +data2[/]hello2.bin +src[/]main.rs ")); }