let root = find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())?;
let ws = Workspace::new(&root, config)?;
- let spec = Packages::from_flags(ws.is_virtual(),
- options.flag_all,
+ let spec = Packages::from_flags(options.flag_all,
&options.flag_exclude,
&options.flag_package)?;
let root = find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())?;
let ws = Workspace::new(&root, config)?;
- let spec = Packages::from_flags(ws.is_virtual(),
- options.flag_all,
+ let spec = Packages::from_flags(options.flag_all,
&options.flag_exclude,
&options.flag_package)?;
let root = find_root_manifest_for_wd(options.flag_manifest_path, config.cwd())?;
let ws = Workspace::new(&root, config)?;
- let spec = Packages::from_flags(ws.is_virtual(),
- options.flag_all,
+ let spec = Packages::from_flags(options.flag_all,
&options.flag_exclude,
&options.flag_package)?;
options.flag_all_targets);
}
- let spec = Packages::from_flags(ws.is_virtual(),
- options.flag_all,
+ let spec = Packages::from_flags(options.flag_all,
&options.flag_exclude,
&options.flag_package)?;
// set above.
members: Vec<PathBuf>,
+ // The subset of `members` that are “default”.
+ default_members: Vec<PathBuf>,
+
// True, if this is a temporary workspace created for the purposes of
// cargo install or cargo package.
is_ephemeral: bool,
pub struct WorkspaceRootConfig {
root_dir: PathBuf,
members: Option<Vec<String>>,
+ default_members: Option<Vec<String>>,
exclude: Vec<String>,
}
root_manifest: None,
target_dir: target_dir,
members: Vec::new(),
+ default_members: Vec::new(),
is_ephemeral: false,
require_optional_deps: true,
};
root_manifest: None,
target_dir: None,
members: Vec::new(),
+ default_members: Vec::new(),
is_ephemeral: true,
require_optional_deps: require_optional_deps,
};
ws.config.target_dir()?
};
ws.members.push(ws.current_manifest.clone());
+ ws.default_members.push(ws.current_manifest.clone());
}
Ok(ws)
}
}
}
+ /// Returns an iterator over default packages in this workspace
+ pub fn default_members<'a>(&'a self) -> Members<'a, 'cfg> {
+ Members {
+ ws: self,
+ iter: self.default_members.iter(),
+ }
+ }
+
pub fn is_ephemeral(&self) -> bool {
self.is_ephemeral
}
None => {
debug!("find_members - only me as a member");
self.members.push(self.current_manifest.clone());
+ self.default_members.push(self.current_manifest.clone());
return Ok(())
}
};
- let members_paths = {
+ let members_paths;
+ let default_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()?,
+ WorkspaceConfig::Root(ref root_config) => {
+ members_paths = root_config.members_paths(
+ root_config.members.as_ref().unwrap_or(&vec![])
+ )?;
+ default_members_paths = if let Some(ref default) = root_config.default_members {
+ Some(root_config.members_paths(default)?)
+ } else {
+ None
+ }
+ }
_ => bail!("root of a workspace inferred but wasn't a root: {}",
root_manifest_path.display()),
}
- };
+ }
for path in members_paths {
self.find_path_deps(&path.join("Cargo.toml"), &root_manifest_path, false)?;
}
+ if let Some(default) = default_members_paths {
+ for path in default {
+ let manifest_path = paths::normalize_path(&path.join("Cargo.toml"));
+ if !self.members.contains(&manifest_path) {
+ bail!("package `{}` is listed in workspace’s default-members \
+ but is not a member.",
+ path.display())
+ }
+ self.default_members.push(manifest_path)
+ }
+ } else {
+ self.default_members = self.members.clone()
+ }
+
self.find_path_deps(&root_manifest_path, &root_manifest_path, false)
}
root_manifest: &Path,
is_path_dep: bool) -> CargoResult<()> {
let manifest_path = paths::normalize_path(manifest_path);
- if self.members.iter().any(|p| p == &manifest_path) {
+ if self.members.contains(&manifest_path) {
return Ok(())
}
if is_path_dep
pub fn new(
root_dir: &Path,
members: &Option<Vec<String>>,
+ default_members: &Option<Vec<String>>,
exclude: &Option<Vec<String>>,
) -> WorkspaceRootConfig {
WorkspaceRootConfig {
root_dir: root_dir.to_path_buf(),
members: members.clone(),
+ default_members: default_members.clone(),
exclude: exclude.clone().unwrap_or_default(),
}
}
self.members.is_some()
}
- fn members_paths(&self) -> CargoResult<Vec<PathBuf>> {
+ fn members_paths(&self, globs: &[String]) -> 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);
- }
+ 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);
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Packages<'a> {
+ Default,
All,
OptOut(&'a [String]),
Packages(&'a [String]),
}
impl<'a> Packages<'a> {
- pub fn from_flags(virtual_ws: bool, all: bool, exclude: &'a [String], package: &'a [String])
+ pub fn from_flags(all: bool, exclude: &'a [String], package: &'a [String])
-> CargoResult<Self>
{
- let all = all || (virtual_ws && package.is_empty());
-
- let packages = match (all, &exclude) {
- (true, exclude) if exclude.is_empty() => Packages::All,
- (true, exclude) => Packages::OptOut(exclude),
- (false, exclude) if !exclude.is_empty() => bail!("--exclude can only be used together \
- with --all"),
- _ => Packages::Packages(package),
- };
-
- Ok(packages)
+ Ok(match (all, exclude.len(), package.len()) {
+ (false, 0, 0) => Packages::Default,
+ (false, 0, _) => Packages::Packages(package),
+ (false, _, _) => bail!("--exclude can only be used together with --all"),
+ (true, 0, _) => Packages::All,
+ (true, _, _) => Packages::OptOut(exclude),
+ })
}
pub fn into_package_id_specs(self, ws: &Workspace) -> CargoResult<Vec<PackageIdSpec>> {
Packages::Packages(packages) => {
packages.iter().map(|p| PackageIdSpec::parse(p)).collect::<CargoResult<Vec<_>>>()?
}
+ Packages::Default if ws.is_virtual() => {
+ ws.default_members()
+ .map(Package::package_id)
+ .map(PackageIdSpec::from_package_id)
+ .collect()
+ }
+ Packages::Default => {
+ ws.current_opt()
+ .map(Package::package_id)
+ .map(PackageIdSpec::from_package_id)
+ .into_iter().collect()
+ }
};
Ok(specs)
}
let config = ws.config();
let pkg = match options.spec {
- Packages::All => unreachable!("cargo run supports single package only"),
+ Packages::All |
+ Packages::Default |
Packages::OptOut(_) => unreachable!("cargo run supports single package only"),
Packages::Packages(xs) => match xs.len() {
0 => ws.current()?,
#[derive(Debug, Deserialize, Serialize)]
pub struct TomlWorkspace {
members: Option<Vec<String>>,
+ #[serde(rename = "default-members")]
+ default_members: Option<Vec<String>>,
exclude: Option<Vec<String>>,
}
project.workspace.as_ref()) {
(Some(config), None) => {
WorkspaceConfig::Root(
- WorkspaceRootConfig::new(&package_root, &config.members, &config.exclude)
+ WorkspaceRootConfig::new(
+ &package_root, &config.members, &config.default_members, &config.exclude,
+ )
)
}
(None, root) => {
let workspace_config = match me.workspace {
Some(ref config) => {
WorkspaceConfig::Root(
- WorkspaceRootConfig::new(&root, &config.members, &config.exclude)
+ WorkspaceRootConfig::new(
+ &root, &config.members, &config.default_members, &config.exclude,
+ )
)
}
None => {
execs().with_status(0));
}
+#[test]
+fn virtual_default_members() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [workspace]
+ members = ["bar", "baz"]
+ default-members = ["bar"]
+ "#)
+ .file("bar/Cargo.toml", r#"
+ [project]
+ name = "bar"
+ version = "0.1.0"
+ authors = []
+ "#)
+ .file("baz/Cargo.toml", r#"
+ [project]
+ name = "baz"
+ version = "0.1.0"
+ authors = []
+ "#)
+ .file("bar/src/main.rs", "fn main() {}")
+ .file("baz/src/main.rs", "fn main() {}");
+ let p = p.build();
+ assert_that(p.cargo("build"),
+ execs().with_status(0));
+ assert_that(&p.bin("bar"), existing_file());
+ assert_that(&p.bin("baz"), is_not(existing_file()));
+}
+
+#[test]
+fn virtual_default_member_is_not_a_member() {
+ let p = project("foo")
+ .file("Cargo.toml", r#"
+ [workspace]
+ members = ["bar"]
+ default-members = ["something-else"]
+ "#)
+ .file("bar/Cargo.toml", r#"
+ [project]
+ name = "bar"
+ version = "0.1.0"
+ authors = []
+ "#)
+ .file("bar/src/main.rs", "fn main() {}");
+ let p = p.build();
+ assert_that(p.cargo("build"),
+ execs().with_status(101)
+ .with_stderr("\
+error: package `[..]something-else` is listed in workspace’s default-members \
+but is not a member.
+"));
+}
+
#[test]
fn virtual_build_no_members() {
let p = project("foo")