pub enum WorkspaceConfig {
/// Indicates that `[workspace]` was present and the members were
/// optionally specified as well.
- Root {
- members: Option<Vec<String>>,
- exclude: Vec<String>,
- },
+ Root(WorkspaceRootConfig),
/// Indicates that `[workspace]` was present and the `root` field is the
/// optional value of `package.workspace`, if present.
Member { root: Option<String> },
}
+/// Configuration of a workspace root in a manifest.
+#[derive(Debug, Clone)]
+pub struct WorkspaceRootConfig {
+ root_dir: PathBuf,
+ members: Option<Vec<String>>,
+ exclude: Vec<String>,
+}
+
/// An iterator over the member packages of a workspace, returned by
/// `Workspace::members`
pub struct Members<'a, 'cfg: 'a> {
{
let current = self.packages.load(manifest_path)?;
match *current.workspace_config() {
- WorkspaceConfig::Root { .. } => {
+ WorkspaceConfig::Root(_) => {
debug!("find_root - is root {}", manifest_path.display());
return Ok(Some(manifest_path.to_path_buf()))
}
debug!("find_root - trying {}", manifest.display());
if manifest.exists() {
match *self.packages.load(&manifest)?.workspace_config() {
- WorkspaceConfig::Root { ref exclude, ref members } => {
+ WorkspaceConfig::Root(ref root_config) => {
debug!("find_root - found a root checking exclusion");
- if !is_excluded(members, exclude, path, manifest_path) {
+ if !root_config.is_excluded(manifest_path) {
debug!("find_root - found!");
return Ok(Some(manifest))
}
/// will transitively follow all `path` dependencies looking for members of
/// the workspace.
fn find_members(&mut self) -> CargoResult<()> {
- let root_manifest = match self.root_manifest {
+ let root_manifest_path = match self.root_manifest {
Some(ref path) => path.clone(),
None => {
debug!("find_members - only me as a member");
return Ok(())
}
};
- let members = {
- let root = self.packages.load(&root_manifest)?;
- match *root.workspace_config() {
- WorkspaceConfig::Root { ref members, .. } => members.clone(),
+
+ let members_paths = {
+ let root_package = self.packages.load(&root_manifest_path)?;
+ match *root_package.workspace_config() {
+ WorkspaceConfig::Root(ref root_config) => root_config.members_paths()?,
_ => bail!("root of a workspace inferred but wasn't a root: {}",
- root_manifest.display()),
+ root_manifest_path.display()),
}
};
- if let Some(list) = members {
- let root = root_manifest.parent().unwrap();
-
- let mut expanded_list = Vec::new();
- for path in list {
- let pathbuf = root.join(path);
- let expanded_paths = expand_member_path(&pathbuf)?;
-
- // If glob does not find any valid paths, then put the original
- // path in the expanded list to maintain backwards compatibility.
- if expanded_paths.is_empty() {
- expanded_list.push(pathbuf);
- } else {
- 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)?;
- }
+ for path in members_paths {
+ self.find_path_deps(&path.join("Cargo.toml"), &root_manifest_path, false)?;
}
- self.find_path_deps(&root_manifest, &root_manifest, false)
+ self.find_path_deps(&root_manifest_path, &root_manifest_path, false)
}
fn find_path_deps(&mut self,
return Ok(())
}
- let root = root_manifest.parent().unwrap();
match *self.packages.load(root_manifest)?.workspace_config() {
- WorkspaceConfig::Root { ref members, ref exclude } => {
- if is_excluded(members, exclude, root, &manifest_path) {
+ WorkspaceConfig::Root(ref root_config) => {
+ if root_config.is_excluded(&manifest_path) {
return Ok(())
}
}
for member in self.members.iter() {
let package = self.packages.get(member);
match *package.workspace_config() {
- WorkspaceConfig::Root { .. } => {
+ WorkspaceConfig::Root(_) => {
roots.push(member.parent().unwrap().to_path_buf());
}
WorkspaceConfig::Member { .. } => {}
let extra = match *root_pkg {
MaybePackage::Virtual(_) => members_msg,
MaybePackage::Package(ref p) => {
- let members = match *p.manifest().workspace_config() {
- WorkspaceConfig::Root { ref members, .. } => members,
+ let has_members_list = match *p.manifest().workspace_config() {
+ WorkspaceConfig::Root(ref root_config) => root_config.has_members_list(),
WorkspaceConfig::Member { .. } => unreachable!(),
};
- if members.is_none() {
+ if !has_members_list {
format!("this may be fixable by ensuring that this \
crate is depended on by the workspace \
root: {}", root.display())
}
}
-fn expand_member_path(path: &Path) -> CargoResult<Vec<PathBuf>> {
- let path = match path.to_str() {
- Some(p) => p,
- None => return Ok(Vec::new()),
- };
- let res = glob(path).chain_err(|| {
- format!("could not parse pattern `{}`", &path)
- })?;
- res.map(|p| {
- p.chain_err(|| {
- format!("unable to match path to pattern `{}`", &path)
- })
- }).collect()
-}
-
-fn is_excluded(members: &Option<Vec<String>>,
- exclude: &[String],
- root_path: &Path,
- manifest_path: &Path) -> bool {
- let excluded = exclude.iter().any(|ex| {
- manifest_path.starts_with(root_path.join(ex))
- });
-
- let explicit_member = match *members {
- Some(ref members) => {
- members.iter().any(|mem| {
- manifest_path.starts_with(root_path.join(mem))
- })
- }
- None => false,
- };
-
- !explicit_member && excluded
-}
-
impl<'cfg> Packages<'cfg> {
fn get(&self, manifest_path: &Path) -> &MaybePackage {
}
}
}
+
+impl WorkspaceRootConfig {
+ pub fn new(
+ root_dir: &Path,
+ members: &Option<Vec<String>>,
+ exclude: &Option<Vec<String>>,
+ ) -> WorkspaceRootConfig {
+ WorkspaceRootConfig {
+ root_dir: root_dir.to_path_buf(),
+ members: members.clone(),
+ exclude: exclude.clone().unwrap_or_default(),
+ }
+ }
+
+ fn is_excluded(&self, manifest_path: &Path) -> bool {
+ let excluded = self.exclude.iter().any(|ex| {
+ manifest_path.starts_with(self.root_dir.join(ex))
+ });
+
+ let explicit_member = match self.members {
+ Some(ref members) => {
+ members.iter().any(|mem| {
+ manifest_path.starts_with(self.root_dir.join(mem))
+ })
+ }
+ None => false,
+ };
+
+ !explicit_member && excluded
+ }
+
+ fn has_members_list(&self) -> bool {
+ self.members.is_some()
+ }
+
+ fn members_paths(&self) -> CargoResult<Vec<PathBuf>> {
+ let mut expanded_list = Vec::new();
+
+ if let Some(globs) = self.members.clone() {
+ for glob in globs {
+ let pathbuf = self.root_dir.join(glob);
+ let expanded_paths = Self::expand_member_path(&pathbuf)?;
+
+ // If glob does not find any valid paths, then put the original
+ // path in the expanded list to maintain backwards compatibility.
+ if expanded_paths.is_empty() {
+ expanded_list.push(pathbuf);
+ } else {
+ expanded_list.extend(expanded_paths);
+ }
+ }
+ }
+
+ Ok(expanded_list)
+ }
+
+ fn expand_member_path(path: &Path) -> CargoResult<Vec<PathBuf>> {
+ let path = match path.to_str() {
+ Some(p) => p,
+ None => return Ok(Vec::new()),
+ };
+ let res = glob(path).chain_err(|| {
+ format!("could not parse pattern `{}`", &path)
+ })?;
+ res.map(|p| {
+ p.chain_err(|| {
+ format!("unable to match path to pattern `{}`", &path)
+ })
+ }).collect()
+ }
+}
use toml;
use url::Url;
-use core::{SourceId, Profiles, PackageIdSpec, GitReference, WorkspaceConfig};
+use core::{SourceId, Profiles, PackageIdSpec, GitReference, WorkspaceConfig, WorkspaceRootConfig};
use core::{Summary, Manifest, Target, Dependency, PackageId};
use core::{EitherManifest, VirtualManifest, Features};
use core::dependency::{Kind, Platform};
let workspace_config = match (me.workspace.as_ref(),
project.workspace.as_ref()) {
(Some(config), None) => {
- WorkspaceConfig::Root {
- members: config.members.clone(),
- exclude: config.exclude.clone().unwrap_or_default(),
- }
+ WorkspaceConfig::Root(
+ WorkspaceRootConfig::new(&package_root, &config.members, &config.exclude)
+ )
}
(None, root) => {
WorkspaceConfig::Member { root: root.cloned() }
let profiles = build_profiles(&me.profile);
let workspace_config = match me.workspace {
Some(ref config) => {
- WorkspaceConfig::Root {
- members: config.members.clone(),
- exclude: config.exclude.clone().unwrap_or_default(),
- }
+ WorkspaceConfig::Root(
+ WorkspaceRootConfig::new(&root, &config.members, &config.exclude)
+ )
}
None => {
bail!("virtual manifests must be configured with [workspace]");
assert_that(&p.root().join("crates/qux/Cargo.lock"), existing_file());
}
+/*FIXME: This fails because of how workspace.exclude and workspace.members are working.
+#[test]
+fn glob_syntax_2() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.1.0"
+ authors = []
+
+ [workspace]
+ members = ["crates/b*"]
+ exclude = ["crates/q*"]
+ "#)
+ .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());
+}
+*/
+
#[test]
fn glob_syntax_invalid_members() {
let p = project("foo")
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
"));
}
+
+/*FIXME: This fails because of how workspace.exclude and workspace.members are working.
+#[test]
+fn include_and_exclude() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [workspace]
+ members = ["foo"]
+ exclude = ["foo/bar"]
+ "#)
+ .file("foo/Cargo.toml", r#"
+ [project]
+ name = "foo"
+ version = "0.1.0"
+ authors = []
+ "#)
+ .file("foo/src/lib.rs", "")
+ .file("foo/bar/Cargo.toml", r#"
+ [project]
+ name = "bar"
+ version = "0.1.0"
+ authors = []
+ "#)
+ .file("foo/bar/src/lib.rs", "");
+ p.build();
+
+ assert_that(p.cargo("build").cwd(p.root().join("foo")),
+ execs().with_status(0));
+ assert_that(&p.root().join("target"), existing_dir());
+ assert_that(&p.root().join("foo/target"), is_not(existing_dir()));
+ assert_that(p.cargo("build").cwd(p.root().join("foo/bar")),
+ execs().with_status(0));
+ assert_that(&p.root().join("foo/bar/target"), existing_dir());
+}
+*/