diff --git a/src/cargo/core/workspace.rs b/src/cargo/core/workspace.rs index ccde5b9ecce..2a05ace132e 100644 --- a/src/cargo/core/workspace.rs +++ b/src/cargo/core/workspace.rs @@ -538,14 +538,26 @@ impl<'cfg> Workspace<'cfg> { None }; - for path in members_paths { + 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")); + let normalized_path = paths::normalize_path(&path); + let manifest_path = normalized_path.join("Cargo.toml"); if !self.members.contains(&manifest_path) { + // default-members are allowed to be excluded, but they + // still must be referred to by the original (unfiltered) + // members list. Note that we aren't testing against the + // manifest path, both because `members_paths` doesn't + // include `/Cargo.toml`, and because excluded paths may not + // be crates. + let exclude = members_paths.contains(&normalized_path) + && workspace_config.is_excluded(&normalized_path); + if exclude { + continue; + } anyhow::bail!( "package `{}` is listed in workspace’s default-members \ but is not a member.", diff --git a/tests/testsuite/workspaces.rs b/tests/testsuite/workspaces.rs index 42ba045a6ab..e620c975d18 100644 --- a/tests/testsuite/workspaces.rs +++ b/tests/testsuite/workspaces.rs @@ -1628,6 +1628,96 @@ fn exclude_but_also_depend() { assert!(p.root().join("foo/bar/target").is_dir()); } +#[cargo_test] +fn excluded_default_members_still_must_be_members() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo"] + default-members = ["foo", "bar"] + exclude = ["bar"] + "#, + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/lib.rs", "") + .file("bar/something.txt", ""); + let p = p.build(); + p.cargo("build") + .with_status(101) + .with_stderr( + "\ +error: package `[..]bar` is listed in workspace’s default-members \ +but is not a member. +", + ) + .run(); +} + +#[cargo_test] +fn excluded_default_members_crate_glob() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar/*"] + default-members = ["bar/*"] + exclude = ["bar/quux"] + "#, + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/main.rs", "fn main() {}") + .file("bar/baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("bar/baz/src/main.rs", "fn main() {}") + .file("bar/quux/Cargo.toml", &basic_manifest("quux", "0.1.0")) + .file("bar/quux/src/main.rs", "fn main() {}"); + + let p = p.build(); + p.cargo("build").run(); + + assert!(p.root().join("target").is_dir()); + assert!(!p.bin("foo").is_file()); + assert!(p.bin("baz").is_file()); + assert!(!p.bin("quux").exists()); + + p.cargo("build --workspace").run(); + assert!(p.root().join("target").is_dir()); + assert!(p.bin("foo").is_file()); + assert!(!p.bin("quux").exists()); + + p.cargo("build").cwd("bar/quux").run(); + assert!(p.root().join("bar/quux/target").is_dir()); +} + +#[cargo_test] +fn excluded_default_members_not_crate_glob() { + let p = project() + .file( + "Cargo.toml", + r#" + [workspace] + members = ["foo", "bar/*"] + default-members = ["bar/*"] + exclude = ["bar/docs"] + "#, + ) + .file("foo/Cargo.toml", &basic_manifest("foo", "0.1.0")) + .file("foo/src/main.rs", "fn main() {}") + .file("bar/baz/Cargo.toml", &basic_manifest("baz", "0.1.0")) + .file("bar/baz/src/main.rs", "fn main() {}") + .file("bar/docs/readme.txt", "This folder is not a crate!"); + + let p = p.build(); + p.cargo("build").run(); + + assert!(!p.bin("foo").is_file()); + assert!(p.bin("baz").is_file()); + p.cargo("build --workspace").run(); + assert!(p.bin("foo").is_file()); +} + #[cargo_test] fn glob_syntax() { let p = project()