-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Fix cargo locate-project --workspace performance issue #16423
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
… in locate-project
af4174a to
e6bd4cb
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might also want to add some extra failing cases:
- Run from a nested directory of a package. The package specifies
package.workspaceto the other workspace but the workspace in its parent directory - Run from a nested directory of a package. The outer workspace manifest doesn't have that package as a member.
The current implementation may forget to take this into account #15107 (comment).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
e6bd4cb to
6667ca2
Compare
tests/testsuite/locate_project.rs
Outdated
| name = "nested" | ||
| version = "0.0.0" | ||
| [package.workspace] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test setup is wrong.
package.workspace should be a valid workspace path
https://doc.rust-lang.org/cargo/reference/manifest.html#the-workspace-field
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done in what way?
I expected that point to a valid workspace rootz and that affects what this command returns
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’m sorry if I’m missing something, but I don’t understand why this is not considered valid.
nested/Cargo.toml has workspace = "..", which resolves to the parent directory.
The parent Cargo.toml contains a [workspace] section with members = ["nested"], which is a valid workspace root.
Therefore, workspace = ".." points to a valid workspace path according to the documentation.
Is there something I’m misunderstanding?
In this test:
workspace = ".." points to the parent directory
The parent workspace lists ["nested"] in members, which includes this package
Given this, the cargo metadata command returns [ROOT]/foo/Cargo.toml (the parent workspace root).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was reading the wrong diff. Sorry.
In that comment I was looking for failure cases, where package.workspace points to some other workspaces, not the parent one. With that we can validate when workspace manifest and package manifest disagree with each other, and should return the workspace the package manifest belongs to.
This test itself is nice to have to validate the package.workspace working, but not the case I was looking for.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW, by failing I didn't meant they really need to fail. They are edge cases where Cargo might want to point to the right workspace rather than the wrong workspace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added these tests, plus one more to validate cases like nested/*. If the tests and solution look good, I’ll add them along with the commits made before the fix, as usual.
| .with_stdout_data( | ||
| str![[r#" | ||
| { | ||
| "root": "[ROOT]/foo/Cargo.toml" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this point to the non-member package?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With find_workspace_root, it returns the parent workspace root, even though the package is not a member
before change it should fails with error "current package believes it's in a workspace when it's not"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With find_workspace_root, it returns the parent workspace root, even though the package is not a member
Is this the desired behavior?
Please see #15107 (comment) that we still want a minimal verification
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
… return appropriate error message
f04c198 to
f928e9e
Compare
tests/testsuite/locate_project.rs
Outdated
| .with_stdout_data( | ||
| str![[r#" | ||
| { | ||
| "root": "[ROOT]/foo/inner-workspace/Cargo.toml" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test doesn't make much sense to me. The inner-workspace/pkg/Cargo.toml will always point to the inner-workspace/Cargo.toml because it is the immediate parent directory that contain a workspace manifest, regardless of having a outer workspace manifest or not.
To avoid the parent directory probiding in your code path, you might want to change workspace = ".." to point to a workspace manifest that is in a sibiling directory of the package manifest.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
src/cargo/core/workspace.rs
Outdated
| } | ||
| match self.members { | ||
| Some(ref members) => members.iter().any(|mem| { | ||
| if mem.contains('*') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not the correct way to handle glob syntax, see this function for the right approach (and we might want reuse it or refactor to reuse)
cargo/src/cargo/core/workspace.rs
Lines 2066 to 2101 in 1f5feec
| /// 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<'g>( | |
| &self, | |
| globs: &'g [String], | |
| ) -> CargoResult<Vec<(PathBuf, Option<&'g str>)>> { | |
| let mut expanded_list = Vec::new(); | |
| 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, 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. | |
| // Check and filter out non-directory paths to prevent pushing such accidental unwanted path | |
| // as a member. | |
| for expanded_path in expanded_paths { | |
| if expanded_path.is_dir() { | |
| expanded_list.push((expanded_path, glob)); | |
| } | |
| } | |
| } | |
| } | |
| Ok(expanded_list) | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| /// - The path is the workspace root manifest itself, or | ||
| /// - No explicit `members` list is specified (implicit membership), or | ||
| /// - The path matches one of the `members` patterns | ||
| fn is_path_member(&self, manifest_path: &Path) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is missing an important check: path dependencies.
@epage what is your though on this, as it may provide a false result?
f928e9e to
ae37cf6
Compare
Fix cargo locate-project --workspace performance issue
cargo locate-project --workspace was unnecessarily loading and validating all workspace members, causing slowdowns in large workspaces (>1s for 1500+ members).
Changed to use find_workspace_root() instead of args.workspace(), which only parses the minimal manifests needed to locate the workspace root path without loading all members.
Fixes #15107