Warn about slow SwiftPM downloads and centralize SwiftPM cache#183747
Conversation
There was a problem hiding this comment.
Code Review
The pull request refactors Xcode build command execution within flutter_tools by introducing a new xcodebuildCommand method in XcodeProjectInterpreter to consistently handle Swift Package Manager (SPM) cache paths and resolution options. It also adds a prefetchSwiftPackages method to proactively resolve SPM dependencies, including logic to manage concurrent processes. The getIosBuildDirectory and getMacOSBuildDirectory functions were updated to accept optional Config and FileSystem parameters for greater flexibility. These changes are integrated across various flutter_tools commands and migrations, such as clean and build_macos, and reflected in updated test cases. The review identified two areas for improvement: ensuring the buildDirectory parameter is correctly utilized when calling xcodebuildCommand within cleanWorkspace to prevent Swift package resolution issues, and adding null checks for config and fileSystem in the buildDirectory method to avoid potential null pointer exceptions.
| String? projectFilename, | ||
| required Directory buildDirectory, | ||
| }) async { | ||
| await prefetchSwiftPackages(projectPath, buildDirectory: buildDirectory, quiet: false); |
There was a problem hiding this comment.
Drive by comment (i haven't looked closer into this PR): I wonder if there's a better place to put prefetchSwiftPackages. Putting it in getInfo feels like a hidden magic.
There was a problem hiding this comment.
Yeah I had a hard time deciding where to put it. Essentially it needs to be run before any other xcodebuild command and getInfo is the first to get called from what I could tell. There are perhaps different scenarios that I may not have accounted for so I've since decided to refactor and put it in an xcodebuild command wrapper function so it's called before every xcodebuild project command (it'll skip if already started)
There was a problem hiding this comment.
I think we should be very cautious when adding more workarounds and shortcuts, especially when we are already know it would become a tech debt.
There are perhaps different scenarios that I may not have accounted for
Can consider covering the most common paths (that we have tested) first? This also helps us to audit our use cases.
I've since decided to refactor and put it in an xcodebuild command wrapper function
If we put it an xcodebuild wrapper, is it too late to have any saving in time? (I assume xcodebuild is called when we do a flutter build?)
There was a problem hiding this comment.
I'm not sure I understand. I think you'll need to take a look at the code to understand what I did
There was a problem hiding this comment.
If we put it an xcodebuild wrapper, is it too late to have any saving in time? (I assume xcodebuild is called when we do a flutter build?)
@vashworth I think i may need some help understanding this part. My understanding is that we only need to pre-fetch during a flutter pub get, but not during xcodebuild (which won't be helpful in saving time), but I could be getting this problem wrong.
There was a problem hiding this comment.
Fetching during pub get is to get the fetching started early on to save time in following command. Fetching during flutter build and therefore before xcodebuild is about giving helpful logging about the progress of the downloads. See PR description
| String getIosBuildDirectory() { | ||
| return globals.fs.path.join(getBuildDirectory(), FlutterDarwinPlatform.ios.name); | ||
| String getIosBuildDirectory([Config? config, FileSystem? fileSystem]) { | ||
| final Config localConfig = config ?? globals.config; |
There was a problem hiding this comment.
Nit: I'd just make config and fileSystem required arguments and pass them in here. We'd like to eventually move away from using globals.
There was a problem hiding this comment.
That would be a very large refactor since this is used all over the place so I think I'll defer for now. The changes I made here are essentially allowed you to pass in the needed variables to replace the globals so my code can avoid using globals. Whereas before, it was always using globals.
| }) async { | ||
| // All `xcodebuild` project commands will download and resolve Swift packages. | ||
| // We should always prefetch Swift packages before running any `xcodebuild` project command | ||
| // to control the output. |
There was a problem hiding this comment.
@vashworth What does "control the output" mean?
There was a problem hiding this comment.
The logging, see PR description
There was a problem hiding this comment.
Every xcodebuild command will try to fetch the packages. We specifically do the prefetch before any other so we can control what's logged to the user. Otherwise it looks like the command is just hanging for 4 minutes
There was a problem hiding this comment.
Ah ok... it's probably too late, but it isn't clear when we read this code that output is the log, and what control the output means.
There was a problem hiding this comment.
Discussed offline - "control the output" means:
when we prefetch (flutter pub get), we don't print out logs. but when we do flutter build ios, we do want to print it out.
hellohuanlin
left a comment
There was a problem hiding this comment.
We discussed offline - since the PR is already landed, we can leave it, but I'd still want to make sure this PR has enough info to clarify a few things for the sake of future reference (also for sanity check my understanding).
| /// | ||
| /// When [skipPackageResolution] is true, it uses arguments to attempt skipping any Swift package | ||
| /// resolution or updates. This should be false when running [prefetchSwiftPackages], so packages | ||
| /// should already be resolved, downloaded, and updated on subsquent `xcodebuild` commands. |
There was a problem hiding this comment.
Discussed offline:
prefetchSwiftPackagesis alsofalseforflutter build ios/macoscommands just to be safe, since they actually need the updated packages.prefetchSwiftPackagesdoes not actually callxcodebuildProjectCommand, but instead it callsxcodebuildProjectCommandArguments(comment nit)
| }) async { | ||
| // All `xcodebuild` project commands will download and resolve Swift packages. | ||
| // We should always prefetch Swift packages before running any `xcodebuild` project command | ||
| // to control the output. |
There was a problem hiding this comment.
Discussed offline - "control the output" means:
when we prefetch (flutter pub get), we don't print out logs. but when we do flutter build ios, we do want to print it out.
| '-disableAutomaticPackageResolution', | ||
| '-skipPackageUpdates', | ||
| '-skipPackagePluginValidation', | ||
| '-skipPackageSignatureValidation', |
There was a problem hiding this comment.
Discussed offline: skipPackageResolution actually doesn't prevent downloading missing packages (so the naming isn't 100% accurate). Instead, these arguments simply prevents packages from being updated. If packages are missing, calling any xcodebuild cmd would still try to download packages.
flutter/flutter@3d69471...0f401ee 2026-04-02 matej.knopp@gmail.com Remove isSizedToContent from _window_linux.dart. (flutter/flutter#184506) 2026-04-02 matej.knopp@gmail.com Windows: Get graphics adapter from engine instead of view (flutter/flutter#184479) 2026-04-02 robert.ancell@canonical.com Reduce number of mallocs in FFI call (flutter/flutter#184166) 2026-04-02 robert.ancell@canonical.com Handle events without a device (flutter/flutter#184163) 2026-04-02 robert.ancell@canonical.com Implement tooltip windows on Linux (flutter/flutter#182348) 2026-04-02 engine-flutter-autoroll@skia.org Roll Skia from bdeebacf23c8 to bb9fd8653739 (4 revisions) (flutter/flutter#184494) 2026-04-02 matej.knopp@gmail.com Implement popup windows for macOS (flutter/flutter#182371) 2026-04-02 engine-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from fV-JIWUt4FQGeDtEe... to BFLjk6Uwd0gs_Hkdk... (flutter/flutter#184492) 2026-04-02 victorsanniay@gmail.com Add await or ignore to future-returning methods defined in Dart SDK (flutter/flutter#184229) 2026-04-02 engine-flutter-autoroll@skia.org Roll Dart SDK from 043a2bfd56ff to d84bdfeb45eb (2 revisions) (flutter/flutter#184487) 2026-04-01 engine-flutter-autoroll@skia.org Roll Skia from c2363c39c283 to bdeebacf23c8 (9 revisions) (flutter/flutter#184480) 2026-04-01 ksanaullah383.khan@gmail.com Remove sliver_test_utils cross-import from sliver_app_bar_test (flutter/flutter#184193) 2026-04-01 116356835+AbdeMohlbi@users.noreply.github.com Improve error message when `dart-define` content are not `base64 encoded` and add more test cases (flutter/flutter#184219) 2026-04-01 116356835+AbdeMohlbi@users.noreply.github.com Replace usages of `MediaQuery.of(context).property` with `MediaQuery.propertyOf(context)` (flutter/flutter#184211) 2026-04-01 techonlabs@gmail.com [ios] Add opt-in inline prediction text input support (flutter/flutter#183650) 2026-04-01 jesswon@google.com [fix-forward] fix build_android_host_app_with_module_source integration test (flutter/flutter#184466) 2026-04-01 katelovett@google.com Update style guide (flutter/flutter#184478) 2026-04-01 engine-flutter-autoroll@skia.org Roll Packages from b04f3e5 to b3fcf14 (3 revisions) (flutter/flutter#184474) 2026-04-01 15619084+vashworth@users.noreply.github.com Warn about slow SwiftPM downloads and centralize SwiftPM cache (flutter/flutter#183747) 2026-04-01 15619084+vashworth@users.noreply.github.com Inject FlutterFramework dependency in iOS Add2App swift packages (flutter/flutter#184365) 2026-04-01 1063596+reidbaker@users.noreply.github.com Prepare for skills adoption (flutter/flutter#184129) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC louisehsu@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
…er#183747) SwiftPM can take several minutes to download remote dependencies. It can also take several seconds (~30) to fetch cached dependencies. To mitigate this, we start fetching the SwiftPM dependencies asynchronously in `injectPlugins`->`DarwinDependencyManagement.setUp` but do not wait for it to complete. This is called when the developer/IDE does `flutter pub get`, `flutter build ios`, or `flutter run`. However, when doing `flutter build ios` or `flutter run`, it waits for the dependencies to finish being fetched and will display some output to let the user know progress is being made. This PR also forces our commands to use a single cache place instead of a different one per scheme. Example: ```console Xcode is fetching Swift Package Manager dependencies. This may take several minutes... Fetching from https://github.com/google/app-check.git (cached)... Fetching from https://github.com/google/GoogleAppMeasurement.git (cached)... Fetching from https://github.com/google/grpc-binary.git (cached)... Fetching from https://github.com/google/abseil-cpp-binary.git (cached)... Fetching from https://github.com/google/interop-ios-for-google-sdks.git (cached)... Fetching from https://github.com/firebase/flutterfire (cached)... Fetching from https://github.com/firebase/leveldb.git (cached)... Fetching from https://github.com/google/gtm-session-fetcher.git (cached)... Fetching from https://github.com/google/promises.git (cached)... Fetching from https://github.com/firebase/firebase-ios-sdk (cached)... Fetching from https://github.com/googleads/google-ads-on-device-conversion-ios-sdk (cached)... Fetching from https://github.com/google/GoogleUtilities.git (cached)... Fetching from https://github.com/firebase/nanopb.git (cached)... Fetching from https://github.com/google/GoogleDataTransport.git (cached)... ``` Addresses flutter#183659. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [AI contribution guidelines]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
…er#183747) SwiftPM can take several minutes to download remote dependencies. It can also take several seconds (~30) to fetch cached dependencies. To mitigate this, we start fetching the SwiftPM dependencies asynchronously in `injectPlugins`->`DarwinDependencyManagement.setUp` but do not wait for it to complete. This is called when the developer/IDE does `flutter pub get`, `flutter build ios`, or `flutter run`. However, when doing `flutter build ios` or `flutter run`, it waits for the dependencies to finish being fetched and will display some output to let the user know progress is being made. This PR also forces our commands to use a single cache place instead of a different one per scheme. Example: ```console Xcode is fetching Swift Package Manager dependencies. This may take several minutes... Fetching from https://github.com/google/app-check.git (cached)... Fetching from https://github.com/google/GoogleAppMeasurement.git (cached)... Fetching from https://github.com/google/grpc-binary.git (cached)... Fetching from https://github.com/google/abseil-cpp-binary.git (cached)... Fetching from https://github.com/google/interop-ios-for-google-sdks.git (cached)... Fetching from https://github.com/firebase/flutterfire (cached)... Fetching from https://github.com/firebase/leveldb.git (cached)... Fetching from https://github.com/google/gtm-session-fetcher.git (cached)... Fetching from https://github.com/google/promises.git (cached)... Fetching from https://github.com/firebase/firebase-ios-sdk (cached)... Fetching from https://github.com/googleads/google-ads-on-device-conversion-ios-sdk (cached)... Fetching from https://github.com/google/GoogleUtilities.git (cached)... Fetching from https://github.com/firebase/nanopb.git (cached)... Fetching from https://github.com/google/GoogleDataTransport.git (cached)... ``` Addresses flutter#183659. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [AI contribution guidelines]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#ai-contribution-guidelines [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
flutter#183747)" This reverts commit 1460ed7
…he (flutter#183747)" This reverts commit 6d448b7.
…r#11420) flutter/flutter@3d69471...0f401ee 2026-04-02 matej.knopp@gmail.com Remove isSizedToContent from _window_linux.dart. (flutter/flutter#184506) 2026-04-02 matej.knopp@gmail.com Windows: Get graphics adapter from engine instead of view (flutter/flutter#184479) 2026-04-02 robert.ancell@canonical.com Reduce number of mallocs in FFI call (flutter/flutter#184166) 2026-04-02 robert.ancell@canonical.com Handle events without a device (flutter/flutter#184163) 2026-04-02 robert.ancell@canonical.com Implement tooltip windows on Linux (flutter/flutter#182348) 2026-04-02 engine-flutter-autoroll@skia.org Roll Skia from bdeebacf23c8 to bb9fd8653739 (4 revisions) (flutter/flutter#184494) 2026-04-02 matej.knopp@gmail.com Implement popup windows for macOS (flutter/flutter#182371) 2026-04-02 engine-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from fV-JIWUt4FQGeDtEe... to BFLjk6Uwd0gs_Hkdk... (flutter/flutter#184492) 2026-04-02 victorsanniay@gmail.com Add await or ignore to future-returning methods defined in Dart SDK (flutter/flutter#184229) 2026-04-02 engine-flutter-autoroll@skia.org Roll Dart SDK from 043a2bfd56ff to d84bdfeb45eb (2 revisions) (flutter/flutter#184487) 2026-04-01 engine-flutter-autoroll@skia.org Roll Skia from c2363c39c283 to bdeebacf23c8 (9 revisions) (flutter/flutter#184480) 2026-04-01 ksanaullah383.khan@gmail.com Remove sliver_test_utils cross-import from sliver_app_bar_test (flutter/flutter#184193) 2026-04-01 116356835+AbdeMohlbi@users.noreply.github.com Improve error message when `dart-define` content are not `base64 encoded` and add more test cases (flutter/flutter#184219) 2026-04-01 116356835+AbdeMohlbi@users.noreply.github.com Replace usages of `MediaQuery.of(context).property` with `MediaQuery.propertyOf(context)` (flutter/flutter#184211) 2026-04-01 techonlabs@gmail.com [ios] Add opt-in inline prediction text input support (flutter/flutter#183650) 2026-04-01 jesswon@google.com [fix-forward] fix build_android_host_app_with_module_source integration test (flutter/flutter#184466) 2026-04-01 katelovett@google.com Update style guide (flutter/flutter#184478) 2026-04-01 engine-flutter-autoroll@skia.org Roll Packages from b04f3e5 to b3fcf14 (3 revisions) (flutter/flutter#184474) 2026-04-01 15619084+vashworth@users.noreply.github.com Warn about slow SwiftPM downloads and centralize SwiftPM cache (flutter/flutter#183747) 2026-04-01 15619084+vashworth@users.noreply.github.com Inject FlutterFramework dependency in iOS Add2App swift packages (flutter/flutter#184365) 2026-04-01 1063596+reidbaker@users.noreply.github.com Prepare for skills adoption (flutter/flutter#184129) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC louisehsu@google.com,stuartmorgan@google.com on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
| try { | ||
| final command = <String>[ | ||
| ..._xcodebuildProjectCommandArguments(buildDirectory, skipPackageResolution: false), | ||
| '-resolvePackageDependencies', |
There was a problem hiding this comment.
here's where we get the following issue if there are multiple .xcodeproj #78839
There was a problem hiding this comment.
it's the same output if using
markturner@Marks-MacBook-Pro ios % xcodebuild -list
Command line invocation:
/Applications/Xcode-26.3.0.app/Contents/Developer/usr/bin/xcodebuild -list
2026-06-15 22:31:56.879 xcodebuild[47691:3450243] Writing error result bundle to /var/folders/2g/vm5g1h1924dbm_vzmrhr_tg80000gp/T/ResultBundle_2026-15-06_22-31-0056.xcresult
xcodebuild: error: The directory /Users/markturner/Developer/mt/pbs-radio-flutter/ios contains 2 projects,including multiple projects with the current extension (.xcodeproj). Specify the project to use with the -project option.
There was a problem hiding this comment.
so really it should be explicit about passing the 'Runner' project:
xcodebuild -project ./Runner.xcodeproj -resolvePackageDependencies
though granted it is a particular edge case for users to have multiple Xcode projects in the ./ios directory
There was a problem hiding this comment.
@markst Is there an open issue for this? If not, please file one. Also, we'll gladly take a PR if you wish to fix it yourself.
There was a problem hiding this comment.
I chose not to create another issue as there were already multiple regarding, and was unable to re-open existing. I'll consider working on a fix when I'm next working on related project. But since it is an edge case if users have more than one xcodeproj, it's not a pressing concern.
SwiftPM can take several minutes to download remote dependencies. It can also take several seconds (~30) to fetch cached dependencies. To mitigate this, we start fetching the SwiftPM dependencies asynchronously in
injectPlugins->DarwinDependencyManagement.setUpbut do not wait for it to complete. This is called when the developer/IDE doesflutter pub get,flutter build ios, orflutter run. However, when doingflutter build iosorflutter run, it waits for the dependencies to finish being fetched and will display some output to let the user know progress is being made.This PR also forces our commands to use a single cache place instead of a different one per scheme.
Example:
Addresses #183659.
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.
Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the
gemini-code-assistbot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.