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
let root_manifest_path = self.root_manifest.clone().unwrap();
let members_paths =
workspace_config.members_paths(workspace_config.members.as_ref().unwrap_or(&vec![]))?;
let members_paths = workspace_config
.members_paths(workspace_config.members.as_deref().unwrap_or_default())?;
let default_members_paths = if root_manifest_path == self.current_manifest {
if let Some(ref default) = workspace_config.default_members {
Some(workspace_config.members_paths(default)?)
@ -754,14 +754,15 @@ impl<'gctx> Workspace<'gctx> {
None
};
for path in &members_paths {
for (path, glob) in &members_paths {
self.find_path_deps(&path.join("Cargo.toml"), &root_manifest_path, false)
.with_context(|| {
format!(
"failed to load manifest for workspace member `{}`\n\
referenced by workspace at `{}`",
referenced{} by workspace at `{}`",
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)?;
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 manifest_path = normalized_path.join("Cargo.toml");
if !self.members.contains(&manifest_path) {
@ -779,16 +780,19 @@ impl<'gctx> Workspace<'gctx> {
// 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)
let exclude = members_paths.iter().any(|(m, _)| *m == normalized_path)
&& workspace_config.is_excluded(&normalized_path);
if exclude {
continue;
}
bail!(
"package `{}` is listed in default-members but is not a member\n\
for workspace at {}.",
"package `{}` is listed in default-members{} but is not a member\n\
for workspace at `{}`.",
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)
@ -1872,8 +1876,13 @@ impl WorkspaceRootConfig {
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)]
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();
for glob in globs {
@ -1883,8 +1892,11 @@ impl WorkspaceRootConfig {
// 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);
expanded_list.push((pathbuf, None));
} 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.
// (e.g. macOS creates `.DS_Store` file if you visit a directory using Finder.)
// Such files can be reported as a member path unexpectedly.
@ -1892,7 +1904,7 @@ impl WorkspaceRootConfig {
// as a member.
for expanded_path in expanded_paths {
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_stderr_data(str![[r#"
[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();
@ -1731,7 +1731,7 @@ fn excluded_default_members_still_must_be_members() {
.with_status(101)
.with_stderr_data(str![[r#"
[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();
@ -1968,7 +1968,7 @@ fn glob_syntax_invalid_members() {
.with_status(101)
.with_stderr_data(str![[r#"
[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:
failed to read `[ROOT]/foo/crates/bar/Cargo.toml`