From b3a747cfe802a789155dadbefa9ae5e64562cf98 Mon Sep 17 00:00:00 2001 From: "Herman J. Radtke III" Date: Fri, 28 Apr 2017 19:08:07 -0700 Subject: [PATCH] Support glob syntax in workspace members Fixes #3911 --- src/cargo/core/workspace.rs | 26 +++++++++++++-- tests/workspaces.rs | 64 +++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/src/cargo/core/workspace.rs b/src/cargo/core/workspace.rs index 5015448a5..0054586c9 100644 --- a/src/cargo/core/workspace.rs +++ b/src/cargo/core/workspace.rs @@ -3,6 +3,8 @@ use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use std::slice; +use glob::glob; + use core::{Package, VirtualManifest, EitherManifest, SourceId}; use core::{PackageIdSpec, Dependency, Profile, Profiles}; use ops; @@ -316,9 +318,16 @@ impl<'cfg> Workspace<'cfg> { }; if let Some(list) = members { + let root = root_manifest.parent().unwrap(); + + let mut expanded_list = Vec::new(); for path in list { - let root = root_manifest.parent().unwrap(); - let manifest_path = root.join(path).join("Cargo.toml"); + let expanded_paths = expand_member_path(&path, root)?; + expanded_list.extend(expanded_paths); + } + + for path in expanded_list { + let manifest_path = path.join("Cargo.toml"); self.find_path_deps(&manifest_path, &root_manifest, false)?; } } @@ -527,6 +536,19 @@ impl<'cfg> Workspace<'cfg> { } } +fn expand_member_path(member_path: &str, root_path: &Path) -> CargoResult> { + let path = root_path.join(member_path); + let path = path.to_str().unwrap(); + let res = glob(path).map_err(|e| { + human(format!("could not parse pattern `{}`: {}", &path, e)) + })?; + res.map(|p| { + p.or_else(|e| { + Err(human(format!("unable to match path to pattern `{}`: {}", &path, e))) + }) + }).collect() +} + fn is_excluded(members: &Option>, exclude: &[String], root_path: &Path, diff --git a/tests/workspaces.rs b/tests/workspaces.rs index 7df7bf8da..a47c6074a 100644 --- a/tests/workspaces.rs +++ b/tests/workspaces.rs @@ -1378,3 +1378,67 @@ fn exclude_but_also_depend() { execs().with_status(0)); assert_that(&p.root().join("foo/bar/target"), existing_dir()); } + +#[test] +fn glob_syntax() { + let p = project("foo") + .file("Cargo.toml", r#" + [project] + name = "foo" + version = "0.1.0" + authors = [] + + [workspace] + members = ["crates/*"] + exclude = ["crates/qux"] + "#) + .file("src/main.rs", "fn main() {}") + .file("crates/bar/Cargo.toml", r#" + [project] + name = "bar" + version = "0.1.0" + authors = [] + workspace = "../.." + "#) + .file("crates/bar/src/main.rs", "fn main() {}") + .file("crates/baz/Cargo.toml", r#" + [project] + name = "baz" + version = "0.1.0" + authors = [] + workspace = "../.." + "#) + .file("crates/baz/src/main.rs", "fn main() {}") + .file("crates/qux/Cargo.toml", r#" + [project] + name = "qux" + version = "0.1.0" + authors = [] + "#) + .file("crates/qux/src/main.rs", "fn main() {}"); + p.build(); + + assert_that(p.cargo("build"), execs().with_status(0)); + assert_that(&p.bin("foo"), existing_file()); + assert_that(&p.bin("bar"), is_not(existing_file())); + assert_that(&p.bin("baz"), is_not(existing_file())); + + assert_that(p.cargo("build").cwd(p.root().join("crates/bar")), + execs().with_status(0)); + assert_that(&p.bin("foo"), existing_file()); + assert_that(&p.bin("bar"), existing_file()); + + assert_that(p.cargo("build").cwd(p.root().join("crates/baz")), + execs().with_status(0)); + assert_that(&p.bin("foo"), existing_file()); + assert_that(&p.bin("baz"), existing_file()); + + assert_that(p.cargo("build").cwd(p.root().join("crates/qux")), + execs().with_status(0)); + assert_that(&p.bin("qux"), is_not(existing_file())); + + assert_that(&p.root().join("Cargo.lock"), existing_file()); + assert_that(&p.root().join("crates/bar/Cargo.lock"), is_not(existing_file())); + assert_that(&p.root().join("crates/baz/Cargo.lock"), is_not(existing_file())); + assert_that(&p.root().join("crates/qux/Cargo.lock"), existing_file()); +}