Skip to content

Reevaluate getting and parsing of iOS/macOS build settings #134669

@vashworth

Description

@vashworth

We use xcodebuild -showBuildSettings to get the build settings for iOS and macOS projects.

/// 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:

  1. If both -target and -scheme are used, it will error
  2. 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
Screenshot 2023-09-13 at 10 44 01 AM

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listc: tech-debtTechnical debt, code quality, testing, etc.platform-iosiOS applications specificallyteam-iosOwned by iOS platform teamtoolAffects the "flutter" command-line tool. See also t: labels.triaged-iosTriaged by iOS platform team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions