Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/utils/build/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,13 @@ where
&path.join("Products/**/*.app").to_string_lossy(),
MatchOptions::new(),
)?;
for app_path in paths.flatten().filter(|path| path.is_dir()) {

let app_paths: Vec<_> = paths.flatten().filter(|path| path.is_dir()).collect();
if app_paths.is_empty() {
anyhow::bail!("Invalid XCArchive: No .app bundles found in the Products/ directory");
}
Comment on lines +83 to +86
Copy link
Copy Markdown
Member

@szokeasaurusrex szokeasaurusrex Sep 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

l: Here's a little tip to make this code more efficient:

Suggested change
let app_paths: Vec<_> = paths.flatten().filter(|path| path.is_dir()).collect();
if app_paths.is_empty() {
error!("Invalid XCArchive: No .app bundles found in the Products/ directory");
return false;
}
let mut app_paths = paths.flatten().filter(|path| path.is_dir()).peekable();
if app_paths.peek().is_none() {
error!("Invalid XCArchive: No .app bundles found in the Products/ directory");
return false;
}

The difference is that, in your proposal, you are eagerly computing the filtered app_paths, and you are allocating a whole vector to store all of them. If paths is large, then this is potentially compute-inefficient and memory-inefficient.

With my suggestion, you avoid eagerly computing the entire app_paths. Instead, we only peek the first item, which means internally, the iterator only needs to compute and store the first value. Because iterators in Rust are lazy, we only compute the values as we iterate over them, saving ourselves a memory allocation, and potentially saving us from having to iterate all of the paths, in the case where we detect an invalid XCArchive and exit early.

I suspect the actual difference in performance is quite small here, but I want to highlight this, as collect is an expensive operation (it iterates the entire iterator), and in general, we should defer calling collect until as late as possible or avoid calling it entirely, as we do here.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think eagerly collecting makes more sense in this case since paths is going to be small, <= 5 for basically 99.9% of apps we see. Working directly with the iterator like that makes the code too clever and less readable IMO, e.g. now app_paths has to be mutable.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's also fair, admittedly I did not know how many paths we typically expect. If it's smaller than the readability improvement from collecting is likely worth it


for app_path in app_paths {
if !app_path.join("Info.plist").exists() {
anyhow::bail!(
"Invalid XCArchive: Missing required Info.plist file in .app bundle: {}",
Expand Down