Skip to content

Add ability to get build settings for macOS Xcode project #146204

Description

@vashworth

Currently (from what I can tell), there's no way to get the build settings for macOS Xcode projects. We already have tooling to do this for iOS Xcode projects, but are not usable for macOS project. Would be great to make methods universal so they can be used for either macOS or iOS - but they would need to be tweaked to work.

/// 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();
_analytics.send(Event.flutterBuildInfo(
label: 'xcode-show-build-settings-timeout',
buildType: 'ios',
command: showBuildSettingsCommand.join(' '),
));
}
_logger.printTrace('Unexpected failure to get Xcode build settings: $error.');
return const <String, String>{};
} finally {
status.stop();
}
}

/// The build settings for the host app of this project, as a detached map.
///
/// Returns null, if iOS tooling is unavailable.
Future<Map<String, String>?> buildSettingsForBuildInfo(
BuildInfo? buildInfo, {
String? scheme,
String? configuration,
String? target,
EnvironmentType environmentType = EnvironmentType.physical,
String? deviceId,
bool isWatch = false,
}) async {
if (!existsSync()) {
return null;
}
final XcodeProjectInfo? info = await projectInfo();
if (info == null) {
return null;
}
scheme ??= info.schemeFor(buildInfo);
if (scheme == null) {
info.reportFlavorNotFoundAndExit();
}
configuration ??= (await projectInfo())?.buildConfigurationFor(
buildInfo,
scheme,
);
return _buildSettingsForXcodeProjectBuildContext(
XcodeProjectBuildContext(
environmentType: environmentType,
scheme: scheme,
configuration: configuration,
target: target,
deviceId: deviceId,
isWatch: isWatch,
),
);
}
Future<Map<String, String>?> _buildSettingsForXcodeProjectBuildContext(XcodeProjectBuildContext buildContext) async {
if (!existsSync()) {
return null;
}
final Map<String, String>? currentBuildSettings = _buildSettingsByBuildContext[buildContext];
if (currentBuildSettings == null) {
final Map<String, String>? calculatedBuildSettings = await _xcodeProjectBuildSettings(buildContext);
if (calculatedBuildSettings != null) {
_buildSettingsByBuildContext[buildContext] = calculatedBuildSettings;
}
}
return _buildSettingsByBuildContext[buildContext];
}
final Map<XcodeProjectBuildContext, Map<String, String>> _buildSettingsByBuildContext = <XcodeProjectBuildContext, Map<String, String>>{};
Future<XcodeProjectInfo?> projectInfo() async {
final XcodeProjectInterpreter? xcodeProjectInterpreter = globals.xcodeProjectInterpreter;
if (!xcodeProject.existsSync() || xcodeProjectInterpreter == null || !xcodeProjectInterpreter.isInstalled) {
return null;
}
return _projectInfo ??= await xcodeProjectInterpreter.getInfo(hostAppRoot.path);
}
XcodeProjectInfo? _projectInfo;
Future<Map<String, String>?> _xcodeProjectBuildSettings(XcodeProjectBuildContext buildContext) async {
final XcodeProjectInterpreter? xcodeProjectInterpreter = globals.xcodeProjectInterpreter;
if (xcodeProjectInterpreter == null || !xcodeProjectInterpreter.isInstalled) {
return null;
}
final Map<String, String> buildSettings = await xcodeProjectInterpreter.getBuildSettings(
xcodeProject.path,
buildContext: buildContext,
);
if (buildSettings.isNotEmpty) {
// No timeouts, flakes, or errors.
return buildSettings;
}
return null;
}

Activity

  1. added
    P2Important issues not at the top of the work list
    team-iosOwned by iOS platform team
    toolAffects the "flutter" command-line tool. See also t: labels.
    triaged-iosTriaged by iOS platform team
    on Apr 3, 2024
  2. vashworth commented on Apr 8, 2024

    @vashworth
    ContributorAuthor

    For Swift Package Manager, we need to the build settings to be able to update the minimum deployment using MACOSX_DEPLOYMENT_TARGET:

    /// If the project's IPHONEOS_DEPLOYMENT_TARGET/MACOSX_DEPLOYMENT_TARGET is
    /// higher than the FlutterGeneratedPluginSwiftPackage's default
    /// SupportedPlatform, increase the SupportedPlatform to match the project's
    /// deployment target.
    ///
    /// This is done for the use case of a plugin requiring a higher iOS/macOS
    /// version than FlutterGeneratedPluginSwiftPackage.
    ///
    /// Swift Package Manager emits an error if a dependency isn’t compatible
    /// with the top-level package’s deployment version. The deployment target of
    /// a package’s dependencies must be lower than or equal to the top-level
    /// package’s deployment target version for a particular platform.
    ///
    /// To still be able to use the plugin, the user can increase the Xcode
    /// project's iOS/macOS deployment target and this will then increase the
    /// deployment target for FlutterGeneratedPluginSwiftPackage.
    static void updateMinimumDeployment({
    required XcodeBasedProject project,
    required SupportedPlatform platform,
    required String deploymentTarget,
    }) {

    iOS example:

    if (project.usesSwiftPackageManager) {
    final String? iosDeploymentTarget = buildSettings['IPHONEOS_DEPLOYMENT_TARGET'];
    if (iosDeploymentTarget != null) {
    SwiftPackageManager.updateMinimumDeployment(
    platform: SupportedPlatform.ios,
    project: project.ios,
    deploymentTarget: iosDeploymentTarget,
    );
    }
    }

  3. github-actions commented on Aug 9, 2024

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  4. locked as resolved and limited conversation to collaborators on Aug 9, 2024
  5. added theissue type on Apr 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Important issues not at the top of the work listteam-iosOwned by iOS platform teamtoolAffects the "flutter" command-line tool. See also t: labels.triaged-iosTriaged by iOS platform team

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions