Sitelet https://github.com/flutter/flutter/pull/188451
Skip to content

[Tool] Fix SwiftPM race condition during parallel Xcode builds - #188451

Merged
auto-submit[bot] merged 15 commits into
flutter:masterfrom
bkonyi:fix-swiftpm-concurrency-race
Aug 7, 2026
Merged

auto-submit[bot] merged 15 commits into
flutter:masterfrom
bkonyi:fix-swiftpm-concurrency-race

Conversation

@bkonyi

@bkonyi bkonyi commented Jun 23, 2026 •

Copy link
Copy Markdown
Contributor

Xcode builds multi-target applications in parallel, invoking the Flutter build pipeline concurrently. This leads to a destructive race condition in generatePluginsSwiftPackage where one process deletes the ephemeral packages directory while another is writing to it, causing FileSystemException or PathExistsException.

This change implements:

  1. Process-safe file locking (.swift_pm.lock) with retry loops to serialize directory preparation across parallel builds.

  2. Non-destructive, incremental cleanup of obsolete symlinks rather than deleting the entire directory.

  3. Content-aware write skipping for Package.swift and placeholder source files to avoid redundant writes and prevent unnecessary Xcode project re-indexing.

Fixes #188446

Xcode builds multi-target applications in parallel, invoking the Flutter build pipeline concurrently. This leads to a destructive race condition in `generatePluginsSwiftPackage` where one process deletes the ephemeral packages directory while another is writing to it, causing `FileSystemException` or `PathExistsException`.

This change implements:

1. Process-safe file locking (`.swift_pm.lock`) with retry loops to serialize directory preparation across parallel builds.

2. Non-destructive, incremental cleanup of obsolete symlinks rather than deleting the entire directory.

3. Content-aware write skipping for `Package.swift` and placeholder source files to avoid redundant writes and prevent unnecessary Xcode project re-indexing.

Fixes flutter#188446
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Jun 23, 2026
@github-actions github-actions Bot added tool Affects the "flutter" command-line tool. See also t: labels. platform-macos Building on or for macOS specifically a: desktop Running on desktop team-ios Owned by iOS platform team team-macos Owned by the macOS platform team labels Jun 23, 2026
@bkonyi
bkonyi changed the base branch from main to master June 23, 2026 21:50
@bkonyi bkonyi added CICD Run CI/CD and removed CICD Run CI/CD labels Jun 23, 2026
@fluttergithubbot

Copy link
Copy Markdown
Contributor

An existing Git SHA, 9fe5721f7683225a75c1a979c56d68d7533f002b, was detected, and no actions were taken.

To re-trigger presubmits after closing or re-opeing a PR, or pushing a HEAD commit (i.e. with --force) that already was pushed before, push a blank commit (git commit --allow-empty -m "Trigger Build") or rebase to continue.

@github-actions github-actions Bot removed the CICD Run CI/CD label Jun 23, 2026
@bkonyi bkonyi added the CICD Run CI/CD label Jun 23, 2026
bkonyi added 2 commits June 23, 2026 22:02
Implement `ephemeralDirectory` on `FakeMacOSProject` and `FakeIosProject` inside `cocoapod_utils_test.dart` to fix test failures introduced by the SwiftPM locking mechanism.
@github-actions github-actions Bot removed the CICD Run CI/CD label Jun 23, 2026
@bkonyi bkonyi added the CICD Run CI/CD label Jun 23, 2026
Restore the check to skip writing the placeholder `TargetName.swift` if the target directory already contains other source files.

Optimized to only perform `listSync()` when the placeholder file does not exist, avoiding file system overhead during incremental builds where it is already present.
@github-actions github-actions Bot removed the CICD Run CI/CD label Jun 23, 2026
@bkonyi bkonyi added the CICD Run CI/CD label Jun 23, 2026
Always skip writing the dummy empty source template if the required Swift file already exists in `createSwiftPackage`.

Previously, the tool would overwrite any existing source file that had the name `<target>.swift` (such as `FlutterToolHelper.swift`) if its content didn't match the dummy empty template. This caused template-generated source files in the Swift package to be destroyed and replaced with the empty placeholder, breaking compilation in macOS integration tests.
@github-actions github-actions Bot removed the CICD Run CI/CD label Jun 23, 2026
@bkonyi bkonyi added the CICD Run CI/CD label Jun 23, 2026
@bkonyi
bkonyi marked this pull request as ready for review June 25, 2026 20:27
@bkonyi
bkonyi requested review from a team as code owners June 25, 2026 20:27

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces concurrency control and optimization to the Swift Package Manager integration in Flutter. It implements a file-locking mechanism (.swift_pm.lock) to prevent concurrent processes from corrupting the packages directory, cleans up stale symlinks non-destructively, and optimizes file writes by skipping generation when Package.swift or placeholder source files are unchanged. The reviewer suggests adding a timeout to the lock acquisition loop to prevent potential infinite hangs during build execution.

Comment thread packages/flutter_tools/lib/src/macos/swift_package_manager.dart Outdated
@bkonyi
bkonyi marked this pull request as draft June 25, 2026 20:50
bkonyi added 2 commits July 6, 2026 15:35
Extract shared locking logic into a unified `FileSystem.runLocked` extension
method in packages/flutter_tools.

Fixes two critical bugs in `runLocked`:
- Moves the execution of `scope()` outside of the lock acquisition retry
  loop. This prevents an infinite retry loop if the callback throws a
  `FileSystemException`.
- Saves the file open state in a local variable before closing and
  nullifying it, ensuring the trace details log message correctly appends
  the exception details only on open failures.

Simplifies `SwiftPackageManager` using the new `runLocked` API.
Add a doc comment explaining that `_cleanStaleSymlinks` is responsible
for removing stale or unreferenced plugin symlinks from the Xcode
runner's symlinks directory when project dependencies change.
This clarifies why the cleanup is necessary to prevent broken link errors during build.
@bkonyi
bkonyi marked this pull request as ready for review July 6, 2026 16:03

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a file locking mechanism (runLocked) on FileSystem to synchronize Swift Package Manager operations and prevent concurrent modification issues. It updates SwiftPackageManager to run package generation within this lock, cleans up stale symlinks, and optimizes Package.swift generation by avoiding rewrites when the content is identical. Feedback suggests adding a timeout or retry limit to the lock acquisition loop in runLocked to prevent potential infinite loops or build hangs in environments with persistent file system issues.

Comment thread packages/flutter_tools/lib/src/base/file_system.dart
@bkonyi

bkonyi commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

FYI @vashworth, this is ready for review.

if (!hasSources) {
requiredSwiftFile.createSync(recursive: true);
requiredSwiftFile.writeAsStringSync(_swiftPackageSourceTemplate);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why was this refactor required?

@vashworth vashworth left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Overall, LGTM, but we may want to have a discussion on why this is happening and how to fix it in a broader sense. Doing all these workarounds specific to certain parts of code seems fragile.

@bkonyi
bkonyi requested a review from vashworth August 4, 2026 21:11
…dart

Add inline comments in  explaining why  and placeholder  file creation is skipped when contents are unchanged. This preserves file modification timestamps () and prevents Xcode and Swift Package Manager from invalidating caches during parallel builds.
@bkonyi bkonyi added the autosubmit Merge PR when tree becomes green via auto submit App label Aug 7, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Aug 7, 2026
Merged via the queue into flutter:master with commit 2cb26f7 Aug 7, 2026
22 checks passed
@flutter-dashboard flutter-dashboard Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Aug 7, 2026
@bkonyi bkonyi added cp: stable cherry pick this pull request to stable release candidate branch and removed cp:stable labels Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: desktop Running on desktop CICD Run CI/CD cp: stable cherry pick this pull request to stable release candidate branch platform-macos Building on or for macOS specifically team-ios Owned by iOS platform team team-macos Owned by the macOS platform team tool Affects the "flutter" command-line tool. See also t: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Tool] Concurrency & Race Conditions in SwiftPM Integration

3 participants