Repository navigation
Reevaluate getting and parsing of iOS/macOS build settings #134669
Copy link
Copy link
Open
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work listc: tech-debtTechnical debt, code quality, testing, etc.Technical debt, code quality, testing, etc.platform-iosiOS applications specificallyiOS applications specificallyteam-iosOwned by iOS platform teamOwned by iOS platform teamtoolAffects the "flutter" command-line tool. See also t: labels.Affects the "flutter" command-line tool. See also t: labels.triaged-iosTriaged by iOS platform teamTriaged by iOS platform team
Description
We use xcodebuild -showBuildSettings to get the build settings for iOS and macOS projects.
flutter/packages/flutter_tools/lib/src/ios/xcodeproj.dart
Lines 175 to 244 in adaf78a
| /// Asynchronously retrieve xcode build settings. This one is preferred for | |
| /// new call-sites. | |
| /// | |
| /// If [scheme] is null, xcodebuild will return build settings for the first discovered | |
| /// target (by default this is Runner). | |
| Future<Map<String, String>> getBuildSettings( | |
| String projectPath, { | |
| required XcodeProjectBuildContext buildContext, | |
| Duration timeout = const Duration(minutes: 1), | |
| }) async { | |
| final Status status = _logger.startSpinner(); | |
| final String? scheme = buildContext.scheme; | |
| final String? configuration = buildContext.configuration; | |
| final String? target = buildContext.target; | |
| final String? deviceId = buildContext.deviceId; | |
| final List<String> showBuildSettingsCommand = <String>[ | |
| ...xcrunCommand(), | |
| 'xcodebuild', | |
| '-project', | |
| _fileSystem.path.absolute(projectPath), | |
| if (scheme != null) | |
| ...<String>['-scheme', scheme], | |
| if (configuration != null) | |
| ...<String>['-configuration', configuration], | |
| if (target != null) | |
| ...<String>['-target', target], | |
| if (buildContext.environmentType == EnvironmentType.simulator) | |
| ...<String>['-sdk', 'iphonesimulator'], | |
| '-destination', | |
| if (buildContext.isWatch && buildContext.environmentType == EnvironmentType.physical) | |
| 'generic/platform=watchOS' | |
| else if (buildContext.isWatch) | |
| 'generic/platform=watchOS Simulator' | |
| else if (deviceId != null) | |
| 'id=$deviceId' | |
| else if (buildContext.environmentType == EnvironmentType.physical) | |
| 'generic/platform=iOS' | |
| else | |
| 'generic/platform=iOS Simulator', | |
| '-showBuildSettings', | |
| 'BUILD_DIR=${_fileSystem.path.absolute(getIosBuildDirectory())}', | |
| ...environmentVariablesAsXcodeBuildSettings(_platform), | |
| ]; | |
| try { | |
| // showBuildSettings is reported to occasionally timeout. Here, we give it | |
| // a lot of wiggle room (locally on Flutter Gallery, this takes ~1s). | |
| // When there is a timeout, we retry once. | |
| final RunResult result = await _processUtils.run( | |
| showBuildSettingsCommand, | |
| throwOnError: true, | |
| workingDirectory: projectPath, | |
| timeout: timeout, | |
| timeoutRetries: 1, | |
| ); | |
| final String out = result.stdout.trim(); | |
| return parseXcodeBuildSettings(out); | |
| } on Exception catch (error) { | |
| if (error is ProcessException && error.toString().contains('timed out')) { | |
| BuildEvent('xcode-show-build-settings-timeout', | |
| type: 'ios', | |
| command: showBuildSettingsCommand.join(' '), | |
| flutterUsage: _usage, | |
| ).send(); | |
| } | |
| _logger.printTrace('Unexpected failure to get Xcode build settings: $error.'); | |
| return const <String, String>{}; | |
| } finally { | |
| status.stop(); | |
| } | |
| } |
However, there appears to be some potential problems with this functionality:
- If both
-targetand-schemeare used, it will error - If the scheme has multiple targets, it will use the build settings of whichever target it lists last, unless the setting is not found in the last target
For example, if you set two targets to Run for the scheme

And then did xcodebuild -project xxx -configuration xxx -scheme Runner -showBuildSettings, it would show something like:
Build settings for action build and target Runner:
...
Build settings for action build and target RunnerTests:
...
This has caused uncertainty in what to use, as seen in #134571 (comment).
We should make this functionality safer and easier to use.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
P2Important issues not at the top of the work listImportant issues not at the top of the work listc: tech-debtTechnical debt, code quality, testing, etc.Technical debt, code quality, testing, etc.platform-iosiOS applications specificallyiOS applications specificallyteam-iosOwned by iOS platform teamOwned by iOS platform teamtoolAffects the "flutter" command-line tool. See also t: labels.Affects the "flutter" command-line tool. See also t: labels.triaged-iosTriaged by iOS platform teamTriaged by iOS platform team
Activity
alperduzgun commentedon Apr 28, 2026
Adding a data point on this with Xcode 26 + SWBBuildService, where the performance of
xcodebuild -showBuildSettingsonPods.xcodeprojhas regressed significantly on large projects.Reproduction
Measurement
Timings from a
flutter run --verbose --flavor developmentagainst a booted iPhone 16e simulator:Runner.xcodeproj -showBuildSettings(generic/iOS + iphonesimulator)Pods.xcodeproj -alltargets -showBuildSettingsxcodebuildcompile (122 pods + app)simctl installSo
pluginsBuildSettingsOutput(called viapluginsSupportArmSimulator()inxcode_project.dart) is the single largest contributor toflutter runtime — ~48% of the total wall clock. This runs on everyflutter run, not just afterpod install.Root cause observation
On Xcode 26, SWBBuildService opens a fresh build graph per pod target when
-alltargetsis passed. With 122 targets that's ~1.7s/target on this machine, totaling ~208s. On Xcode 16.x the same project's-alltargetsquery completed in ~30s, so this looks like a regression tied to SWBBuildService's per-target graph initialization cost rather than the historicaldeveloperservices2.apple.comnetwork delay (which was fixed in Xcode 26.0).Functional result
The check exists to decide whether to append
arm64toEXCLUDED_ARCHSfor the simulator. On this project, no pod excludes arm64 (verified by grepping the captured-showBuildSettingsoutput andPods.xcodeproj/project.pbxprojforEXCLUDED_ARCHS.*arm64— zero matches), so the 208s is spent to produce a result the build would have produced anyway.Workaround (local patch)
I patched
xcode_project.dartlocally to cache the result ofpluginsSupportArmSimulator()atbuild/ios/.arm64_sim_support_cachekeyed offPodfile.lock's mtime. Cache invalidates onpod install(Podfile.lock rewrite) and onflutter clean(build dir cleared). After the first run seeds the cache,Running Xcode build...now starts at ~13s instead of ~222s — a ~209s saving per run. Fullflutter runwall clock dropped from ~432s to ~225s.Happy to turn this into a PR if a Podfile.lock-keyed disk cache is an acceptable approach. An alternative would be to avoid the
xcodebuildinvocation entirely by reading the relevant build settings directly from thexcconfigfiles CocoaPods writes.