Print globs when workspace members can't be found (#15093)

Cargo expands globs when loading workspace members. If the glob happens
to match some non-crate directory, it causes an error that may be tricky
to understand, because it makes Cargo complain that it failed to read a
`Cargo.toml` from a specific directory, but that directory name won't be
listed explicitly anywhere in `Cargo.toml`, so it may seem that Cargo is
trying to read some made-up phantom manifest.

I've made the error messages mention which glob has been used to select
the missing workspace member.
This commit is contained in:
Ed Page 2025-01-23 21:13:33 +00:00 committed by GitHub
commit 91d8140d66
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 28 additions and 16 deletions

View File

@ -742,8 +742,8 @@ impl<'gctx> Workspace<'gctx> {
// self.root_manifest must be Some to have retrieved workspace_config // self.root_manifest must be Some to have retrieved workspace_config
let root_manifest_path = self.root_manifest.clone().unwrap(); let root_manifest_path = self.root_manifest.clone().unwrap();
let members_paths = let members_paths = workspace_config
workspace_config.members_paths(workspace_config.members.as_ref().unwrap_or(&vec![]))?; .members_paths(workspace_config.members.as_deref().unwrap_or_default())?;
let default_members_paths = if root_manifest_path == self.current_manifest { let default_members_paths = if root_manifest_path == self.current_manifest {
if let Some(ref default) = workspace_config.default_members { if let Some(ref default) = workspace_config.default_members {
Some(workspace_config.members_paths(default)?) Some(workspace_config.members_paths(default)?)
@ -754,14 +754,15 @@ impl<'gctx> Workspace<'gctx> {
None None
}; };
for path in &members_paths { for (path, glob) in &members_paths {
self.find_path_deps(&path.join("Cargo.toml"), &root_manifest_path, false) self.find_path_deps(&path.join("Cargo.toml"), &root_manifest_path, false)
.with_context(|| { .with_context(|| {
format!( format!(
"failed to load manifest for workspace member `{}`\n\ "failed to load manifest for workspace member `{}`\n\
referenced by workspace at `{}`", referenced{} by workspace at `{}`",
path.display(), path.display(),
root_manifest_path.display() glob.map(|g| format!(" via `{g}`")).unwrap_or_default(),
root_manifest_path.display(),
) )
})?; })?;
} }
@ -769,7 +770,7 @@ impl<'gctx> Workspace<'gctx> {
self.find_path_deps(&root_manifest_path, &root_manifest_path, false)?; self.find_path_deps(&root_manifest_path, &root_manifest_path, false)?;
if let Some(default) = default_members_paths { if let Some(default) = default_members_paths {
for path in default { for (path, default_member_glob) in default {
let normalized_path = paths::normalize_path(&path); let normalized_path = paths::normalize_path(&path);
let manifest_path = normalized_path.join("Cargo.toml"); let manifest_path = normalized_path.join("Cargo.toml");
if !self.members.contains(&manifest_path) { if !self.members.contains(&manifest_path) {
@ -779,16 +780,19 @@ impl<'gctx> Workspace<'gctx> {
// manifest path, both because `members_paths` doesn't // manifest path, both because `members_paths` doesn't
// include `/Cargo.toml`, and because excluded paths may not // include `/Cargo.toml`, and because excluded paths may not
// be crates. // be crates.
let exclude = members_paths.contains(&normalized_path) let exclude = members_paths.iter().any(|(m, _)| *m == normalized_path)
&& workspace_config.is_excluded(&normalized_path); && workspace_config.is_excluded(&normalized_path);
if exclude { if exclude {
continue; continue;
} }
bail!( bail!(
"package `{}` is listed in default-members but is not a member\n\ "package `{}` is listed in default-members{} but is not a member\n\
for workspace at {}.", for workspace at `{}`.",
path.display(), path.display(),
root_manifest_path.display() default_member_glob
.map(|g| format!(" via `{g}`"))
.unwrap_or_default(),
root_manifest_path.display(),
) )
} }
self.default_members.push(manifest_path) self.default_members.push(manifest_path)
@ -1872,8 +1876,13 @@ impl WorkspaceRootConfig {
self.members.is_some() self.members.is_some()
} }
/// Returns expanded paths along with the glob that they were expanded from.
/// The glob is `None` if the path matched exactly.
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
fn members_paths(&self, globs: &[String]) -> CargoResult<Vec<PathBuf>> { fn members_paths<'g>(
&self,
globs: &'g [String],
) -> CargoResult<Vec<(PathBuf, Option<&'g str>)>> {
let mut expanded_list = Vec::new(); let mut expanded_list = Vec::new();
for glob in globs { for glob in globs {
@ -1883,8 +1892,11 @@ impl WorkspaceRootConfig {
// If glob does not find any valid paths, then put the original // If glob does not find any valid paths, then put the original
// path in the expanded list to maintain backwards compatibility. // path in the expanded list to maintain backwards compatibility.
if expanded_paths.is_empty() { if expanded_paths.is_empty() {
expanded_list.push(pathbuf); expanded_list.push((pathbuf, None));
} else { } else {
let used_glob_pattern = expanded_paths.len() > 1 || expanded_paths[0] != pathbuf;
let glob = used_glob_pattern.then_some(glob.as_str());
// Some OS can create system support files anywhere. // Some OS can create system support files anywhere.
// (e.g. macOS creates `.DS_Store` file if you visit a directory using Finder.) // (e.g. macOS creates `.DS_Store` file if you visit a directory using Finder.)
// Such files can be reported as a member path unexpectedly. // Such files can be reported as a member path unexpectedly.
@ -1892,7 +1904,7 @@ impl WorkspaceRootConfig {
// as a member. // as a member.
for expanded_path in expanded_paths { for expanded_path in expanded_paths {
if expanded_path.is_dir() { if expanded_path.is_dir() {
expanded_list.push(expanded_path); expanded_list.push((expanded_path, glob));
} }
} }
} }

View File

@ -948,7 +948,7 @@ fn virtual_default_member_is_not_a_member() {
.with_status(101) .with_status(101)
.with_stderr_data(str![[r#" .with_stderr_data(str![[r#"
[ERROR] package `[ROOT]/foo/something-else` is listed in default-members but is not a member [ERROR] package `[ROOT]/foo/something-else` is listed in default-members but is not a member
for workspace at [ROOT]/foo/Cargo.toml. for workspace at `[ROOT]/foo/Cargo.toml`.
"#]]) "#]])
.run(); .run();
@ -1731,7 +1731,7 @@ fn excluded_default_members_still_must_be_members() {
.with_status(101) .with_status(101)
.with_stderr_data(str![[r#" .with_stderr_data(str![[r#"
[ERROR] package `[ROOT]/foo/bar` is listed in default-members but is not a member [ERROR] package `[ROOT]/foo/bar` is listed in default-members but is not a member
for workspace at [ROOT]/foo/Cargo.toml. for workspace at `[ROOT]/foo/Cargo.toml`.
"#]]) "#]])
.run(); .run();
@ -1968,7 +1968,7 @@ fn glob_syntax_invalid_members() {
.with_status(101) .with_status(101)
.with_stderr_data(str![[r#" .with_stderr_data(str![[r#"
[ERROR] failed to load manifest for workspace member `[ROOT]/foo/crates/bar` [ERROR] failed to load manifest for workspace member `[ROOT]/foo/crates/bar`
referenced by workspace at `[ROOT]/foo/Cargo.toml` referenced via `crates/*` by workspace at `[ROOT]/foo/Cargo.toml`
Caused by: Caused by:
failed to read `[ROOT]/foo/crates/bar/Cargo.toml` failed to read `[ROOT]/foo/crates/bar/Cargo.toml`